From a9144ffec072a0863f4b2b81bedbfbe68c7876a3 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov Date: Sun, 13 Feb 2022 15:31:03 +0300 Subject: [PATCH 1/9] Fix CPU type jz->jzf --- .../configs/unknown_unknown_fh8833v100_openipc_defconfig | 2 +- .../configs/unknown_unknown_fh8852v100_openipc_defconfig | 2 +- .../configs/unknown_unknown_fh8856v100_openipc_defconfig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/br-ext-chip-fullhan/configs/unknown_unknown_fh8833v100_openipc_defconfig b/br-ext-chip-fullhan/configs/unknown_unknown_fh8833v100_openipc_defconfig index 8010af38..a2cb3876 100644 --- a/br-ext-chip-fullhan/configs/unknown_unknown_fh8833v100_openipc_defconfig +++ b/br-ext-chip-fullhan/configs/unknown_unknown_fh8833v100_openipc_defconfig @@ -1,6 +1,6 @@ # Architecture BR2_arm=y -BR2_arm1176jz_s=y +BR2_arm1176jzf_s=y BR2_ARM_EABI=y # BR2_ARM_INSTRUCTIONS_THUMB is not set BR2_KERNEL_HEADERS_VERSION=y diff --git a/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v100_openipc_defconfig b/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v100_openipc_defconfig index caed01c0..75a38fcc 100644 --- a/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v100_openipc_defconfig +++ b/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v100_openipc_defconfig @@ -1,6 +1,6 @@ # Architecture BR2_arm=y -BR2_arm1176jz_s=y +BR2_arm1176jzf_s=y BR2_ARM_EABI=y # BR2_ARM_INSTRUCTIONS_THUMB is not set BR2_KERNEL_HEADERS_VERSION=y diff --git a/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v100_openipc_defconfig b/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v100_openipc_defconfig index 418aa83d..05c96e37 100644 --- a/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v100_openipc_defconfig +++ b/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v100_openipc_defconfig @@ -1,6 +1,6 @@ # Architecture BR2_arm=y -BR2_arm1176jz_s=y +BR2_arm1176jzf_s=y BR2_ARM_EABI=y # BR2_ARM_INSTRUCTIONS_THUMB is not set BR2_KERNEL_HEADERS_VERSION=y From 2d0587eeed276dd5bb6f2f21e6b3e29b71ade080 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov Date: Sun, 13 Feb 2022 15:39:22 +0300 Subject: [PATCH 2/9] Set ARM1176JZF-S for all FullHan targets --- .../configs/unknown_unknown_fh8833v100_openipc_defconfig | 1 - .../configs/unknown_unknown_fh8852v100_openipc_defconfig | 1 - .../configs/unknown_unknown_fh8852v200_openipc_defconfig | 4 +--- .../configs/unknown_unknown_fh8852v210_openipc_defconfig | 4 +--- .../configs/unknown_unknown_fh8856v100_openipc_defconfig | 1 - .../configs/unknown_unknown_fh8856v200_openipc_defconfig | 4 +--- .../configs/unknown_unknown_fh8856v210_openipc_defconfig | 4 +--- .../configs/unknown_unknown_fh8858v200_openipc_defconfig | 4 +--- .../configs/unknown_unknown_fh8858v210_openipc_defconfig | 4 +--- 9 files changed, 6 insertions(+), 21 deletions(-) diff --git a/br-ext-chip-fullhan/configs/unknown_unknown_fh8833v100_openipc_defconfig b/br-ext-chip-fullhan/configs/unknown_unknown_fh8833v100_openipc_defconfig index a2cb3876..542f95f6 100644 --- a/br-ext-chip-fullhan/configs/unknown_unknown_fh8833v100_openipc_defconfig +++ b/br-ext-chip-fullhan/configs/unknown_unknown_fh8833v100_openipc_defconfig @@ -2,7 +2,6 @@ BR2_arm=y BR2_arm1176jzf_s=y BR2_ARM_EABI=y -# BR2_ARM_INSTRUCTIONS_THUMB is not set BR2_KERNEL_HEADERS_VERSION=y BR2_DEFAULT_KERNEL_VERSION="3.0.8" BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_3_0=y diff --git a/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v100_openipc_defconfig b/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v100_openipc_defconfig index 75a38fcc..1242c508 100644 --- a/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v100_openipc_defconfig +++ b/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v100_openipc_defconfig @@ -2,7 +2,6 @@ BR2_arm=y BR2_arm1176jzf_s=y BR2_ARM_EABI=y -# BR2_ARM_INSTRUCTIONS_THUMB is not set BR2_KERNEL_HEADERS_VERSION=y BR2_DEFAULT_KERNEL_VERSION="3.0.8" BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_3_0=y diff --git a/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v200_openipc_defconfig b/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v200_openipc_defconfig index 24e0ae05..03f8ba6b 100644 --- a/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v200_openipc_defconfig +++ b/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v200_openipc_defconfig @@ -1,9 +1,7 @@ # Architecture BR2_arm=y -BR2_cortex_a7=y +BR2_arm1176jzf_s=y BR2_ARM_EABI=y -BR2_ARM_FPU_NEON_VFPV4=y -BR2_ARM_INSTRUCTIONS_THUMB2=y BR2_KERNEL_HEADERS_VERSION=y BR2_DEFAULT_KERNEL_VERSION="4.9.129" BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_9=y diff --git a/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v210_openipc_defconfig b/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v210_openipc_defconfig index 889e12b9..797b2f28 100644 --- a/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v210_openipc_defconfig +++ b/br-ext-chip-fullhan/configs/unknown_unknown_fh8852v210_openipc_defconfig @@ -1,9 +1,7 @@ # Architecture BR2_arm=y -BR2_cortex_a7=y +BR2_arm1176jzf_s=y BR2_ARM_EABI=y -BR2_ARM_FPU_NEON_VFPV4=y -BR2_ARM_INSTRUCTIONS_THUMB2=y BR2_KERNEL_HEADERS_VERSION=y BR2_DEFAULT_KERNEL_VERSION="4.9.129" BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_9=y diff --git a/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v100_openipc_defconfig b/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v100_openipc_defconfig index 05c96e37..47b50bed 100644 --- a/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v100_openipc_defconfig +++ b/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v100_openipc_defconfig @@ -2,7 +2,6 @@ BR2_arm=y BR2_arm1176jzf_s=y BR2_ARM_EABI=y -# BR2_ARM_INSTRUCTIONS_THUMB is not set BR2_KERNEL_HEADERS_VERSION=y BR2_DEFAULT_KERNEL_VERSION="3.0.8" BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_3_0=y diff --git a/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v200_openipc_defconfig b/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v200_openipc_defconfig index 77bfe6ea..7c02c00a 100644 --- a/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v200_openipc_defconfig +++ b/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v200_openipc_defconfig @@ -1,9 +1,7 @@ # Architecture BR2_arm=y -BR2_cortex_a7=y +BR2_arm1176jzf_s=y BR2_ARM_EABI=y -BR2_ARM_FPU_NEON_VFPV4=y -BR2_ARM_INSTRUCTIONS_THUMB2=y BR2_KERNEL_HEADERS_VERSION=y BR2_DEFAULT_KERNEL_VERSION="4.9.129" BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_9=y diff --git a/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v210_openipc_defconfig b/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v210_openipc_defconfig index b8f1f2e3..335065b7 100644 --- a/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v210_openipc_defconfig +++ b/br-ext-chip-fullhan/configs/unknown_unknown_fh8856v210_openipc_defconfig @@ -1,9 +1,7 @@ # Architecture BR2_arm=y -BR2_cortex_a7=y +BR2_arm1176jzf_s=y BR2_ARM_EABI=y -BR2_ARM_FPU_NEON_VFPV4=y -BR2_ARM_INSTRUCTIONS_THUMB2=y BR2_KERNEL_HEADERS_VERSION=y BR2_DEFAULT_KERNEL_VERSION="4.9.129" BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_9=y diff --git a/br-ext-chip-fullhan/configs/unknown_unknown_fh8858v200_openipc_defconfig b/br-ext-chip-fullhan/configs/unknown_unknown_fh8858v200_openipc_defconfig index ff7592cc..a7e29a15 100644 --- a/br-ext-chip-fullhan/configs/unknown_unknown_fh8858v200_openipc_defconfig +++ b/br-ext-chip-fullhan/configs/unknown_unknown_fh8858v200_openipc_defconfig @@ -1,9 +1,7 @@ # Architecture BR2_arm=y -BR2_cortex_a7=y +BR2_arm1176jzf_s=y BR2_ARM_EABI=y -BR2_ARM_FPU_NEON_VFPV4=y -BR2_ARM_INSTRUCTIONS_THUMB2=y BR2_KERNEL_HEADERS_VERSION=y BR2_DEFAULT_KERNEL_VERSION="4.9.129" BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_9=y diff --git a/br-ext-chip-fullhan/configs/unknown_unknown_fh8858v210_openipc_defconfig b/br-ext-chip-fullhan/configs/unknown_unknown_fh8858v210_openipc_defconfig index 1d2b1e76..aa8542f0 100644 --- a/br-ext-chip-fullhan/configs/unknown_unknown_fh8858v210_openipc_defconfig +++ b/br-ext-chip-fullhan/configs/unknown_unknown_fh8858v210_openipc_defconfig @@ -1,9 +1,7 @@ # Architecture BR2_arm=y -BR2_cortex_a7=y +BR2_arm1176jzf_s=y BR2_ARM_EABI=y -BR2_ARM_FPU_NEON_VFPV4=y -BR2_ARM_INSTRUCTIONS_THUMB2=y BR2_KERNEL_HEADERS_VERSION=y BR2_DEFAULT_KERNEL_VERSION="4.9.129" BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_9=y From b6caaa5e080163dcd8722e03f3e643405adf2080 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov Date: Sun, 13 Feb 2022 15:40:29 +0300 Subject: [PATCH 3/9] Add BY25Q64 SPI NOR support for FH8852V200 --- .../kernel/patches/11_by25q64as-spi-nor.patch | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 br-ext-chip-fullhan/board/fh8852v200/kernel/patches/11_by25q64as-spi-nor.patch diff --git a/br-ext-chip-fullhan/board/fh8852v200/kernel/patches/11_by25q64as-spi-nor.patch b/br-ext-chip-fullhan/board/fh8852v200/kernel/patches/11_by25q64as-spi-nor.patch new file mode 100644 index 00000000..e5fbd7d7 --- /dev/null +++ b/br-ext-chip-fullhan/board/fh8852v200/kernel/patches/11_by25q64as-spi-nor.patch @@ -0,0 +1,12 @@ +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1159,6 +1160,9 @@ + { "by25q128as", INFO(0x684018, 0, 64 * 1024, 256, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + FLASH_DEFAULT_CLOCK_FREQ)}, ++ { "by25q64as", INFO(0x684017, 0, 64 * 1024, 128, ++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, ++ FLASH_DEFAULT_CLOCK_FREQ)}, + { }, + }; + From 331ccad3d438c043809d190b502db0aa93603d25 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov Date: Sun, 13 Feb 2022 21:36:39 +0300 Subject: [PATCH 4/9] Add board config selector for FH8852v200 --- .../kernel/patches/10_boardconfig.patch | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 br-ext-chip-fullhan/board/fh8852v200/kernel/patches/10_boardconfig.patch diff --git a/br-ext-chip-fullhan/board/fh8852v200/kernel/patches/10_boardconfig.patch b/br-ext-chip-fullhan/board/fh8852v200/kernel/patches/10_boardconfig.patch new file mode 100644 index 00000000..b8ad75f1 --- /dev/null +++ b/br-ext-chip-fullhan/board/fh8852v200/kernel/patches/10_boardconfig.patch @@ -0,0 +1,19 @@ +--- a/Makefile ++++ b/Makefile +@@ -612,11 +612,15 @@ + KBUILD_MODULES := 1 + endif + ++PHONY += boardconfig ++boardconfig: ++ cp arch/arm/mach-fh/$(PROJECT_NAME)/board_config.$(PROJECT_NAME).appboard arch/arm/mach-fh/include/mach/board_config.h ++ + # The all: target is the default when no target is given on the + # command line. + # This allow a user to issue only 'make' to build a kernel including modules + # Defaults to vmlinux, but the arch makefile usually adds further targets +-all: vmlinux ++all: boardconfig vmlinux + + KBUILD_CFLAGS += $(call cc-option,-fno-PIE) + KBUILD_AFLAGS += $(call cc-option,-fno-PIE) From 8cae6a481f4951e01990c8531d8b49805ffc648f Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov Date: Sun, 13 Feb 2022 21:55:43 +0300 Subject: [PATCH 5/9] Add board config selector for FH8852v100 --- .../include/mach/board_config.fh8852.appboard | 45 ++++++++++++++++++ .../include/mach/board_config.fh8856.appboard | 47 +++++++++++++++++++ .../kernel/patches/13_boardconfig.patch | 21 +++++++++ 3 files changed, 113 insertions(+) create mode 100644 br-ext-chip-fullhan/board/fh8852v100/kernel/overlay/arch/arm/mach-fh/include/mach/board_config.fh8852.appboard create mode 100644 br-ext-chip-fullhan/board/fh8852v100/kernel/overlay/arch/arm/mach-fh/include/mach/board_config.fh8856.appboard create mode 100644 br-ext-chip-fullhan/board/fh8852v100/kernel/patches/13_boardconfig.patch diff --git a/br-ext-chip-fullhan/board/fh8852v100/kernel/overlay/arch/arm/mach-fh/include/mach/board_config.fh8852.appboard b/br-ext-chip-fullhan/board/fh8852v100/kernel/overlay/arch/arm/mach-fh/include/mach/board_config.fh8852.appboard new file mode 100644 index 00000000..d63cd9b2 --- /dev/null +++ b/br-ext-chip-fullhan/board/fh8852v100/kernel/overlay/arch/arm/mach-fh/include/mach/board_config.fh8852.appboard @@ -0,0 +1,45 @@ +/* + * board_config.h + * + * Created on: Jan 9, 2017 + * Author: duobao + */ + +#ifndef BOARD_CONFIG_H_ +#define BOARD_CONFIG_H_ + +/* + * GPIO0 -> IRCUT_ON + * GPIO1 -> IRCUT_OFF + * GPIO2 -> USB_PWREN + * GPIO3 -> SD1_PWREN/WIFI_REG_ON + * GPIO7 -> IR + * GPIO11 -> EMAC PHY Reset + * GPIO12 -> CIS_CLK + * GPIO13 -> CIS_RSTN + * GPIO14 -> CIS_PDN + */ + +#define CONFIG_GPIO_EMACPHY_RESET 11 +#define CONFIG_GPIO_EMACPHY_RXDV 41 + +#define CONFIG_GPIO_USB_PWREN 2 + +#define CONFIG_ISP_CLK_RATE 150000000 +#define CONFIG_HEVC_CLK_RATE 200000000 +#define CONFIG_PAE_CLK_RATE 240000000 + +#define FH_BOARD_8852 +#define CONFIG_PINCTRL_SELECT \ + "I2C0", "MIPI", "RMII", "SD0_NO_WP", "SSI0_4BIT",\ + "UART0", "GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO7",\ + "GPIO11", "GPIO13", "GPIO14",\ + \ + /* 未引出的pad,默认配置为GPIO */ \ + "GPIO4", "GPIO5", "GPIO6", "GPIO8", "GPIO9", "GPIO10",\ + "GPIO19", "GPIO20", "GPIO21", "GPIO22", "GPIO23", "GPIO24",\ + "GPIO25", "GPIO26", "GPIO27", "GPIO28", "GPIO53",\ + "GPIO55" + + +#endif /* BOARD_CONFIG_H_ */ diff --git a/br-ext-chip-fullhan/board/fh8852v100/kernel/overlay/arch/arm/mach-fh/include/mach/board_config.fh8856.appboard b/br-ext-chip-fullhan/board/fh8852v100/kernel/overlay/arch/arm/mach-fh/include/mach/board_config.fh8856.appboard new file mode 100644 index 00000000..1cd8c86c --- /dev/null +++ b/br-ext-chip-fullhan/board/fh8852v100/kernel/overlay/arch/arm/mach-fh/include/mach/board_config.fh8856.appboard @@ -0,0 +1,47 @@ +/* + * board_config.h + * + * Created on: Jan 9, 2017 + * Author: duobao + */ + +#ifndef BOARD_CONFIG_H_ +#define BOARD_CONFIG_H_ + +/* + * GPIO0 -> IRCUT_ON + * GPIO1 -> IRCUT_OFF + * GPIO2 -> USB_PWREN + * GPIO11 -> EMAC PHY Reset + * GPIO12 -> CIS_CLK + * GPIO13 -> CIS_RSTN + * GPIO14 -> CIS_PDN + * GPIO19 -> SD1_PWREN/WIFI_REG_ON + * GPIO20 -> AK7755 Reset + * GPIO24 -> LED0 + * GPIO25 -> LED1 + * GPIO26 -> Reset Configs + * GPIO27 -> AK7755 PowerDown + * GPIO28 -> IR + * GPIO53 -> USB_PWREN/SD0_PWREN + * GPIO55 -> SD1 WIFI Interrupt + */ + +#define CONFIG_GPIO_EMACPHY_RESET 11 +#define CONFIG_GPIO_EMACPHY_RXDV 41 + +#define CONFIG_GPIO_USB_PWREN 2 + +#define CONFIG_ISP_CLK_RATE 200000000 +#define CONFIG_HEVC_CLK_RATE 300000000 +#define CONFIG_PAE_CLK_RATE 400000000 + +#define FH_BOARD_8856 +#define CONFIG_PINCTRL_SELECT \ + "I2C0", "I2C1", "MIPI", "RMII", "SD0_NO_WP", \ + "SD1_NO_WP", "SSI0_4BIT", "UART0", "GPIO0", "GPIO1", \ + "GPIO2", "GPIO3", "GPIO11", "GPIO13", "GPIO14", \ + "GPIO19", "GPIO20", "GPIO24", "GPIO25", "GPIO26", \ + "GPIO27", "GPIO28", "GPIO53", "GPIO55" + +#endif /* BOARD_CONFIG_H_ */ diff --git a/br-ext-chip-fullhan/board/fh8852v100/kernel/patches/13_boardconfig.patch b/br-ext-chip-fullhan/board/fh8852v100/kernel/patches/13_boardconfig.patch new file mode 100644 index 00000000..ab94ca98 --- /dev/null +++ b/br-ext-chip-fullhan/board/fh8852v100/kernel/patches/13_boardconfig.patch @@ -0,0 +1,21 @@ +--- a/Makefile ++++ b/Makefile +@@ -552,11 +552,17 @@ + include/config/auto.conf: ; + endif # $(dot-config) + ++export PROJECT_NAME = $(shell grep -e '^CONFIG_MACH_FH.*' .config|sed 's/CONFIG_MACH_\(.*\)=y/\1/'|awk '{print tolower($$0)}') ++ ++PHONY += boardconfig ++boardconfig: ++ cp arch/arm/mach-fh/include/mach/board_config.$(PROJECT_NAME).appboard arch/arm/mach-fh/include/mach/board_config.h ++ + # The all: target is the default when no target is given on the + # command line. + # This allow a user to issue only 'make' to build a kernel including modules + # Defaults to vmlinux, but the arch makefile usually adds further targets +-all: vmlinux ++all: boardconfig vmlinux + + ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE + KBUILD_CFLAGS += -Os From ba3f71add51e6c4b0a649ee8a5e08286b05f1030 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov Date: Mon, 14 Feb 2022 08:46:25 +0300 Subject: [PATCH 6/9] Add AK3918Ev200 support --- .github/workflows/ak3918ev200_images.yml | 139 + br-ext-chip-anyka/Config.in | 3 +- .../kernel/ak3918ev200.generic.config | 1741 + .../overlay/include/linux/compiler-gcc7.h | 65 + .../ak3918ev200/kernel/overlay/lib/libakaec.a | Bin 0 -> 24850 bytes .../kernel/overlay/lib/libakaec2.a | Bin 0 -> 14540 bytes .../ak3918ev200/kernel/overlay/lib/libfha.a | Bin 0 -> 74246 bytes .../patches/00_ak3918_kernel-3.4.35.patch | 214307 +++++++++++++++ .../11_fix_yylloc_for_modern_computers.patch | 11 + .../patches/overlayfs.v13-3.4-rc7.patch | 3363 + ...nown_unknown_ak3918ev200_openipc_defconfig | 99 + br-ext-chip-anyka/external.mk | 3 +- .../package/anyka-osdrv-ak3918ev200 | 1 + br-ext-chip-anyka/package/anyka_patcher | 1 + building.sh | 8 + .../package/anyka-osdrv-ak3918ev200/Config.in | 6 + .../anyka-osdrv-ak3918ev200.mk | 35 + .../files/kmod/akcamera.ko | Bin 0 -> 20869 bytes .../files/kmod/sensor_ar0130.ko | Bin 0 -> 7194 bytes .../files/kmod/sensor_gc1024.ko | Bin 0 -> 7594 bytes .../files/kmod/sensor_gc1034.ko | Bin 0 -> 7679 bytes .../files/kmod/sensor_h42.ko | Bin 0 -> 7533 bytes .../files/kmod/sensor_h61.ko | Bin 0 -> 7509 bytes .../files/kmod/sensor_h62.ko | Bin 0 -> 7139 bytes .../files/kmod/sensor_ov9712.ko | Bin 0 -> 6750 bytes .../files/kmod/sensor_sc1035.ko | Bin 0 -> 8385 bytes .../files/kmod/sensor_sc1045.ko | Bin 0 -> 8777 bytes .../files/kmod/sensor_sc1135.ko | Bin 0 -> 8370 bytes .../files/kmod/sensor_sc1145.ko | Bin 0 -> 8981 bytes .../files/kmod/sensor_sc1235.ko | Bin 0 -> 8218 bytes .../files/kmod/sensor_sc1245.ko | Bin 0 -> 8639 bytes .../files/script/S95anyka | 73 + .../files/script/ircut_demo | 87 + .../files/script/load_anyka | 196 + .../files/sensor/config/isp_9712.conf | Bin 0 -> 62152 bytes .../files/sensor/config/isp_ar0130.conf | Bin 0 -> 62744 bytes .../files/sensor/config/isp_gc1024.conf | Bin 0 -> 69112 bytes .../files/sensor/config/isp_h42.conf | Bin 0 -> 62104 bytes .../files/sensor/config/isp_h42_krt.conf | Bin 0 -> 62104 bytes .../files/sensor/config/isp_sc1035.conf | Bin 0 -> 62192 bytes .../files/sensor/config/isp_sc1035_ja.conf | Bin 0 -> 62192 bytes .../files/sensor/config/isp_sc1035_yws.conf | Bin 0 -> 62192 bytes .../files/sensor/config/isp_sc1045.conf | Bin 0 -> 61896 bytes .../files/sensor/config/isp_sc1045_ja.conf | Bin 0 -> 61896 bytes .../files/sensor/config/isp_sc1045_yws.conf | Bin 0 -> 61896 bytes .../files/sensor/config/isp_sc1135.conf | Bin 0 -> 69188 bytes .../files/sensor/config/isp_sc1135_yws.conf | Bin 0 -> 62192 bytes .../files/sensor/config/isp_sc1145.conf | Bin 0 -> 68892 bytes .../files/sensor/config/isp_sc1145_yws.conf | Bin 0 -> 61896 bytes .../files/sensor/config/isp_sc1235.conf | Bin 0 -> 69392 bytes .../files/sensor/config/isp_sc1235_1.conf | Bin 0 -> 69392 bytes .../files/sensor/config/isp_sc1235_2.conf | Bin 0 -> 69392 bytes .../files/sensor/config/isp_sc1245.conf | Bin 0 -> 69608 bytes general/package/anyka_patcher/Config.in | 3 + .../package/anyka_patcher/anyka_patcher.mk | 13 + general/package/anyka_patcher/apply.sh | 44 + 56 files changed, 220196 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ak3918ev200_images.yml create mode 100644 br-ext-chip-anyka/board/ak3918ev200/kernel/ak3918ev200.generic.config create mode 100644 br-ext-chip-anyka/board/ak3918ev200/kernel/overlay/include/linux/compiler-gcc7.h create mode 100755 br-ext-chip-anyka/board/ak3918ev200/kernel/overlay/lib/libakaec.a create mode 100644 br-ext-chip-anyka/board/ak3918ev200/kernel/overlay/lib/libakaec2.a create mode 100644 br-ext-chip-anyka/board/ak3918ev200/kernel/overlay/lib/libfha.a create mode 100644 br-ext-chip-anyka/board/ak3918ev200/kernel/patches/00_ak3918_kernel-3.4.35.patch create mode 100644 br-ext-chip-anyka/board/ak3918ev200/kernel/patches/11_fix_yylloc_for_modern_computers.patch create mode 100644 br-ext-chip-anyka/board/ak3918ev200/kernel/patches/overlayfs.v13-3.4-rc7.patch create mode 100644 br-ext-chip-anyka/configs/unknown_unknown_ak3918ev200_openipc_defconfig create mode 120000 br-ext-chip-anyka/package/anyka-osdrv-ak3918ev200 create mode 120000 br-ext-chip-anyka/package/anyka_patcher create mode 100644 general/package/anyka-osdrv-ak3918ev200/Config.in create mode 100644 general/package/anyka-osdrv-ak3918ev200/anyka-osdrv-ak3918ev200.mk create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/kmod/akcamera.ko create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_ar0130.ko create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_gc1024.ko create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_gc1034.ko create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_h42.ko create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_h61.ko create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_h62.ko create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_ov9712.ko create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1035.ko create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1045.ko create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1135.ko create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1145.ko create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1235.ko create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1245.ko create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/script/S95anyka create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/script/ircut_demo create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/script/load_anyka create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_9712.conf create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_ar0130.conf create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_gc1024.conf create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_h42.conf create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_h42_krt.conf create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1035.conf create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1035_ja.conf create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1035_yws.conf create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1045.conf create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1045_ja.conf create mode 100644 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1045_yws.conf create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1135.conf create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1135_yws.conf create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1145.conf create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1145_yws.conf create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1235.conf create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1235_1.conf create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1235_2.conf create mode 100755 general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1245.conf create mode 100644 general/package/anyka_patcher/Config.in create mode 100644 general/package/anyka_patcher/anyka_patcher.mk create mode 100755 general/package/anyka_patcher/apply.sh diff --git a/.github/workflows/ak3918ev200_images.yml b/.github/workflows/ak3918ev200_images.yml new file mode 100644 index 00000000..29131887 --- /dev/null +++ b/.github/workflows/ak3918ev200_images.yml @@ -0,0 +1,139 @@ +name: "AK3918Ev200 " + +on: + push: + branches: + - production + tags: + - "v*" + # schedule: + # - cron: "00 03 * * *" + workflow_dispatch: + +jobs: + build_core: + name: OpenIPC firmware for AK3918Ev200 + runs-on: ubuntu-latest + + steps: + + - name: Checkout + id: checkout + uses: actions/checkout@v2 + + - name: Install build dependencies + id: install + run: | + make install-deps + mkdir -p tmp + + - name: Free disk space + id: freshing + run: | + sudo swapoff -a + sudo rm -f /swapfile + sudo apt clean + docker rmi $(docker image ls -aq) + df -h + + - name: Prepare buildroot + id: prepare + run: | + HEAD_TAG=$(git tag --points-at HEAD) + GIT_HASH=$(git rev-parse --short $GITHUB_SHA) + BRANCH_NAME=$(echo $GITHUB_REF | cut -d'/' -f 3) + if [ -z "$HEAD_TAG" ]; then + TAG_NAME="latest" + RELEASE_NAME="Development Build" + PRERELEASE=true + else + TAG_NAME=${{ github.ref }} + RELEASE_NAME="Release ${{ github.ref }}" + PRERELEASE=false + fi + echo "GIT_HASH=$GIT_HASH" >> $GITHUB_ENV + echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV + echo "RELEASE_NAME=$RELEASE_NAME" >> $GITHUB_ENV + echo "PRERELEASE=$PRERELEASE" >> $GITHUB_ENV + echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV + cd $GITHUB_WORKSPACE + make prepare + + - name: Build Ak3918ev200 firmware + id: build-ak3918ev200-firmware + continue-on-error: true + run: | + ARCHIVE_FW="${GITHUB_WORKSPACE}/output/images/openipc.ak3918ev200-br.tgz" + echo "ARCHIVE_FW=$ARCHIVE_FW" >> $GITHUB_ENV + cd $GITHUB_WORKSPACE + make PLATFORM=anyka BOARD=unknown_unknown_ak3918ev200_openipc all + [[ $(stat --printf="%s" ${GITHUB_WORKSPACE}/output/images/uImage) -gt 2097152 ]] && echo "TG_NOTIFY=Warning, kernel size exceeded : $(stat --printf="%s" ${GITHUB_WORKSPACE}/output/images/uImage) vs 2097152... AK3918EV200" >> $GITHUB_ENV && exit 1 + [[ $(stat --printf="%s" ${GITHUB_WORKSPACE}/output/images/rootfs.squashfs) -gt 5242880 ]] && echo "TG_NOTIFY=Warning, rootfs size exceeded - $(stat --printf="%s" ${GITHUB_WORKSPACE}/output/images/rootfs.squashfs) vs 5242880... AK3918EV200" >> $GITHUB_ENV && exit 1 + cd ${GITHUB_WORKSPACE}/output/images + mv uImage uImage.ak3918ev200 + mv rootfs.squashfs rootfs.squashfs.ak3918ev200 + md5sum rootfs.squashfs.ak3918ev200 > rootfs.squashfs.ak3918ev200.md5sum + md5sum uImage.ak3918ev200 > uImage.ak3918ev200.md5sum + tar -cvzf $ARCHIVE_FW *ak3918ev200* + + - name: Build AK3918Ev200 SDK + id: build-ak3918ev200-sdk + continue-on-error: true + run: | + ARCHIVE_SDK="${GITHUB_WORKSPACE}/output/images/arm-openipc-linux-musleabi_sdk-buildroot.tar.gz" + echo "ARCHIVE_SDK=$ARCHIVE_SDK" >> $GITHUB_ENV + cd $GITHUB_WORKSPACE/output + make sdk + + - name: Send warning message to telegram channel + env: + TG_TOKEN: ${{ secrets.TELEGRAM_TOKEN_BOT_OPENIPC }} + TG_CHANNEL: ${{ secrets.TELEGRAM_CHANNEL_OPENIPC_DEV }} + if: steps.build-ak3918ev200-firmware.outcome != 'success' || steps.build-ak3918ev200-sdk.outcome != 'success' + run: | + TG_OPTIONS="-s --connect-timeout 30 --max-time 30" + TG_NOTIFY="${TG_NOTIFY:=Warning, Buildroot compiling error... AK3918EV200}" + TG_HEADER=$(echo -e "\r\n$TG_NOTIFY \r\n\r\nCommit: $GIT_HASH \r\nBranch: $BRANCH_NAME \r\nTag: $TAG_NAME \r\n\r\n\xE2\x9A\xA0 GitHub Actions") + curl $TG_OPTIONS -H "Content-Type: multipart/form-data" -X POST https://api.telegram.org/bot$TG_TOKEN/sendMessage \ + -F chat_id=$TG_CHANNEL -F text="$TG_HEADER" + + - name: Create release + uses: actions/create-release@v1 + continue-on-error: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.TAG_NAME }} + release_name: ${{ env.RELEASE_NAME }} + draft: false + prerelease: ${{ env.PRERELEASE }} + + - name: Upload FW to release + uses: svenstaro/upload-release-action@v2 + continue-on-error: true + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ${{ env.ARCHIVE_FW }} + asset_name: "openipc.ak3918ev200-br.tgz" + tag: ${{ env.TAG_NAME }} + overwrite: true + + - name: Upload SDK to release + uses: svenstaro/upload-release-action@v2 + continue-on-error: true + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ${{ env.ARCHIVE_SDK }} + asset_name: "arm-openipc-ak3918ev200-linux-musleabi_sdk-buildroot.tar.gz" + tag: ${{ env.TAG_NAME }} + overwrite: true + + - name: Send binary file to telegram channel + env: + TG_TOKEN: ${{ secrets.TELEGRAM_TOKEN_BOT_OPENIPC }} + TG_CHANNEL: ${{ secrets.TELEGRAM_CHANNEL_OPENIPC_DEV }} + run: | + TG_OPTIONS="-s --connect-timeout 30 --max-time 30" + TG_HEADER=$(echo -e "\r\nCommit: $GIT_HASH \r\nBranch: $BRANCH_NAME \r\nTag: $TAG_NAME \r\n\r\n\xE2\x9C\x85 GitHub Actions") + curl $TG_OPTIONS -H "Content-Type: multipart/form-data" -X POST https://api.telegram.org/bot$TG_TOKEN/sendDocument \ + -F chat_id=$TG_CHANNEL -F document="@$ARCHIVE_FW" -F caption="$TG_HEADER" diff --git a/br-ext-chip-anyka/Config.in b/br-ext-chip-anyka/Config.in index beb4267d..612dcbf4 100644 --- a/br-ext-chip-anyka/Config.in +++ b/br-ext-chip-anyka/Config.in @@ -1,4 +1,5 @@ source "$BR2_EXTERNAL_ANYKA_PATH/linux/Config.ext.in" +source "$BR2_EXTERNAL_ANYKA_PATH/package/anyka-osdrv-ak3918ev200/Config.in" source "$BR2_EXTERNAL_ANYKA_PATH/package/anyka_patcher/Config.in" source "$BR2_EXTERNAL_ANYKA_PATH/package/aura-httpd/Config.in" source "$BR2_EXTERNAL_ANYKA_PATH/package/fdk-aac-openipc/Config.in" @@ -14,7 +15,7 @@ source "$BR2_EXTERNAL_ANYKA_PATH/package/libsrt-openipc/Config.in" source "$BR2_EXTERNAL_ANYKA_PATH/package/libwebsockets-openipc/Config.in" source "$BR2_EXTERNAL_ANYKA_PATH/package/linux-firmware-openipc/Config.in" source "$BR2_EXTERNAL_ANYKA_PATH/package/majestic-fonts/Config.in" -source "$BR2_EXTERNAL_ANYKA_PATH/package/majestic/Config.in" +#source "$BR2_EXTERNAL_ANYKA_PATH/package/majestic/Config.in" source "$BR2_EXTERNAL_ANYKA_PATH/package/mbedtls-openipc/Config.in" source "$BR2_EXTERNAL_ANYKA_PATH/package/microbe-web/Config.in" source "$BR2_EXTERNAL_ANYKA_PATH/package/motors/Config.in" diff --git a/br-ext-chip-anyka/board/ak3918ev200/kernel/ak3918ev200.generic.config b/br-ext-chip-anyka/board/ak3918ev200/kernel/ak3918ev200.generic.config new file mode 100644 index 00000000..7c336a74 --- /dev/null +++ b/br-ext-chip-anyka/board/ak3918ev200/kernel/ak3918ev200.generic.config @@ -0,0 +1,1741 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.35 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_ARCH_USES_GETTIMEOFFSET=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_PHYS_OFFSET=0x81000000 +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +# CONFIG_EXPERIMENTAL is not set +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_SHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_HIGHBANK is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_PRIMA2 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PICOXCELL is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_VT8500 is not set +# CONFIG_ARCH_ZYNQ is not set +CONFIG_ARCH_AK39=y +# CONFIG_GPIO_PCA953X is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set + +# +# System MMU +# +CONFIG_CPU_AK3918=y +# CONFIG_ARCH_SDK3910 is not set +# CONFIG_ARCH_AIMER39_AK3916 is not set +CONFIG_ARCH_AIMER39_AK3918=y +CONFIG_ASIC_FREQ_VALUE=90000000 + +# +# Power management +# +# CONFIG_AK39_PM is not set +# CONFIG_AK39_PWM_TIMER is not set +CONFIG_PLAT_ANYKA=y + +# +# Processor Type +# +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_USE_DOMAINS=y + +# +# Processor Features +# +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_NR_BANKS=8 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_SECCOMP is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="" +# CONFIG_XIP_KERNEL is not set +# CONFIG_AUTO_ZRELADDR is not set +CONFIG_RAM_BASE=0x80000000 +CONFIG_VIDEO_RESERVED_MEM_SIZE=0x1000000 + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +CONFIG_IPV6=y +# CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET6_XFRM_MODE_TUNNEL is not set +# CONFIG_INET6_XFRM_MODE_BEET is not set +# CONFIG_IPV6_SIT is not set +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_ANDROID_PARANOID_NETWORK is not set +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_PHONET is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_CFG80211=m +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +CONFIG_MAC80211=m +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_SYNC is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_AK_SPIFLASH=y +# CONFIG_MTD_NAND_IDS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +CONFIG_AK_SERIAL_NUMBER=y +CONFIG_AK_MOTOR=y + +# +# user space generic gpio controller +# +CONFIG_GPIOS_AKCUSTOM=y + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_PROC_FS is not set + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +# CONFIG_SCSI_WAIT_SCAN is not set + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +# CONFIG_TARGET_CORE is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_MII is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_TUN=m +# CONFIG_VETH is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_AK_ETHERNET=y +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# USB Network Adapters +# +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_WLAN is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYBOARD_AKKEY=y +# CONFIG_KEYBOARD_AK_KEYPAD is not set +# CONFIG_KEYBOARD_ADKEY is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_UNIX98_PTYS is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +CONFIG_SERIAL_AK39_UART=y +CONFIG_SERIAL_AK39_CONSOLE=y +# CONFIG_SERIAL_GPIO_UART is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_SMBUS=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_ANYKA=y +CONFIG_I2C_AK39_HW=y +# CONFIG_I2C_GPIO_SOFT is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_ANYKA=y +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_SBS is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_BATTERY_ANDROID is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_BATTERY_AK is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +CONFIG_AK39_WATCHDOG=y + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_S5M_CORE is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_REGULATOR is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_AK=m +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_ANYKA_HCD=y +CONFIG_USB_AKOTG_HS_HCD=m +# CONFIG_USB_AKOTG_DMA is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_REALTEK is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_STORAGE_ENE_UB6250 is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_GADGET_AKUDC_PRODUCER is not set +CONFIG_USB_GADGET_AKUDC=y +CONFIG_USB_AKUDC=m +# CONFIG_USB_AKUDC_DEBUG_FS is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_FILE_STORAGE is not set +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_SDIO_WIFI is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +CONFIG_MMC_ANYKA=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_AK39=y +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_OT200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_AK=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +CONFIG_UIO_VCODEC=y + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y + +# +# Hardware Spinlock drivers +# +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set +CONFIG_OVERLAYFS_FS=y + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_HFSPLUS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_WRITEBUFFER is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +# CONFIG_SQUASHFS_ZLIB is not set +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set +# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set diff --git a/br-ext-chip-anyka/board/ak3918ev200/kernel/overlay/include/linux/compiler-gcc7.h b/br-ext-chip-anyka/board/ak3918ev200/kernel/overlay/include/linux/compiler-gcc7.h new file mode 100644 index 00000000..613f9936 --- /dev/null +++ b/br-ext-chip-anyka/board/ak3918ev200/kernel/overlay/include/linux/compiler-gcc7.h @@ -0,0 +1,65 @@ +#ifndef __LINUX_COMPILER_H +#error "Please don't include directly, include instead." +#endif + +#define __used __attribute__((__used__)) +#define __must_check __attribute__((warn_unused_result)) +#define __compiler_offsetof(a, b) __builtin_offsetof(a, b) + +/* Mark functions as cold. gcc will assume any path leading to a call + to them will be unlikely. This means a lot of manual unlikely()s + are unnecessary now for any paths leading to the usual suspects + like BUG(), printk(), panic() etc. [but let's keep them for now for + older compilers] + + Early snapshots of gcc 4.3 don't support this and we can't detect this + in the preprocessor, but we can live with this because they're unreleased. + Maketime probing would be overkill here. + + gcc also has a __attribute__((__hot__)) to move hot functions into + a special section, but I don't see any sense in this right now in + the kernel context */ +#define __cold __attribute__((__cold__)) + +#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) + +#ifndef __CHECKER__ +# define __compiletime_warning(message) __attribute__((warning(message))) +# define __compiletime_error(message) __attribute__((error(message))) +#endif /* __CHECKER__ */ + +/* + * Mark a position in code as unreachable. This can be used to + * suppress control flow warnings after asm blocks that transfer + * control elsewhere. + * + * Early snapshots of gcc 4.5 don't support this and we can't detect + * this in the preprocessor, but we can live with this because they're + * unreleased. Really, we need to have autoconf for the kernel. + */ +#define unreachable() __builtin_unreachable() + +/* Mark a function definition as prohibited from being cloned. */ +#define __noclone __attribute__((__noclone__)) + +/* + * Tell the optimizer that something else uses this function or variable. + */ +#define __visible __attribute__((externally_visible)) + +/* + * GCC 'asm goto' miscompiles certain code sequences: + * + * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58670 + * + * Work it around via a compiler barrier quirk suggested by Jakub Jelinek. + * + * (asm goto is automatically volatile - the naming reflects this.) + */ +#define asm_volatile_goto(x...) do { asm goto(x); asm (""); } while (0) + +#ifdef CONFIG_ARCH_USE_BUILTIN_BSWAP +#define __HAVE_BUILTIN_BSWAP32__ +#define __HAVE_BUILTIN_BSWAP64__ +#define __HAVE_BUILTIN_BSWAP16__ +#endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP */ diff --git a/br-ext-chip-anyka/board/ak3918ev200/kernel/overlay/lib/libakaec.a b/br-ext-chip-anyka/board/ak3918ev200/kernel/overlay/lib/libakaec.a new file mode 100755 index 0000000000000000000000000000000000000000..b6fd2b07326b879b4914e80d2ff62b0329163e66 GIT binary patch literal 24850 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 KyaF3g`hNkG@Cs@G literal 0 HcmV?d00001 diff --git a/br-ext-chip-anyka/board/ak3918ev200/kernel/overlay/lib/libakaec2.a b/br-ext-chip-anyka/board/ak3918ev200/kernel/overlay/lib/libakaec2.a new file mode 100644 index 0000000000000000000000000000000000000000..b39cab3bc2a10ab81f47cd6c3b6b794f3d9c3a8b GIT binary patch literal 14540 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 Rw}bsa#gl9$|0RON|6hjl-2eap literal 0 HcmV?d00001 diff --git a/br-ext-chip-anyka/board/ak3918ev200/kernel/patches/00_ak3918_kernel-3.4.35.patch b/br-ext-chip-anyka/board/ak3918ev200/kernel/patches/00_ak3918_kernel-3.4.35.patch new file mode 100644 index 00000000..dd8b6240 --- /dev/null +++ b/br-ext-chip-anyka/board/ak3918ev200/kernel/patches/00_ak3918_kernel-3.4.35.patch @@ -0,0 +1,214307 @@ +diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power +index 840f7d64..45000f0d 100644 +--- a/Documentation/ABI/testing/sysfs-devices-power ++++ b/Documentation/ABI/testing/sysfs-devices-power +@@ -96,16 +96,26 @@ Description: + is read-only. If the device is not enabled to wake up the + system from sleep states, this attribute is not present. + +-What: /sys/devices/.../power/wakeup_hit_count +-Date: September 2010 ++What: /sys/devices/.../power/wakeup_abort_count ++Date: February 2012 + Contact: Rafael J. Wysocki + Description: +- The /sys/devices/.../wakeup_hit_count attribute contains the ++ The /sys/devices/.../wakeup_abort_count attribute contains the + number of times the processing of a wakeup event associated with +- the device might prevent the system from entering a sleep state. +- This attribute is read-only. If the device is not enabled to +- wake up the system from sleep states, this attribute is not +- present. ++ the device might have aborted system transition into a sleep ++ state in progress. This attribute is read-only. If the device ++ is not enabled to wake up the system from sleep states, this ++ attribute is not present. ++ ++What: /sys/devices/.../power/wakeup_expire_count ++Date: February 2012 ++Contact: Rafael J. Wysocki ++Description: ++ The /sys/devices/.../wakeup_expire_count attribute contains the ++ number of times a wakeup event associated with the device has ++ been reported with a timeout that expired. This attribute is ++ read-only. If the device is not enabled to wake up the system ++ from sleep states, this attribute is not present. + + What: /sys/devices/.../power/wakeup_active + Date: September 2010 +@@ -148,6 +158,17 @@ Description: + not enabled to wake up the system from sleep states, this + attribute is not present. + ++What: /sys/devices/.../power/wakeup_prevent_sleep_time_ms ++Date: February 2012 ++Contact: Rafael J. Wysocki ++Description: ++ The /sys/devices/.../wakeup_prevent_sleep_time_ms attribute ++ contains the total time the device has been preventing ++ opportunistic transitions to sleep states from occuring. ++ This attribute is read-only. If the device is not enabled to ++ wake up the system from sleep states, this attribute is not ++ present. ++ + What: /sys/devices/.../power/autosuspend_delay_ms + Date: September 2010 + Contact: Alan Stern +diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power +index b464d127..31725ffe 100644 +--- a/Documentation/ABI/testing/sysfs-power ++++ b/Documentation/ABI/testing/sysfs-power +@@ -172,3 +172,62 @@ Description: + + Reading from this file will display the current value, which is + set to 1 MB by default. ++ ++What: /sys/power/autosleep ++Date: April 2012 ++Contact: Rafael J. Wysocki ++Description: ++ The /sys/power/autosleep file can be written one of the strings ++ returned by reads from /sys/power/state. If that happens, a ++ work item attempting to trigger a transition of the system to ++ the sleep state represented by that string is queued up. This ++ attempt will only succeed if there are no active wakeup sources ++ in the system at that time. After every execution, regardless ++ of whether or not the attempt to put the system to sleep has ++ succeeded, the work item requeues itself until user space ++ writes "off" to /sys/power/autosleep. ++ ++ Reading from this file causes the last string successfully ++ written to it to be returned. ++ ++What: /sys/power/wake_lock ++Date: February 2012 ++Contact: Rafael J. Wysocki ++Description: ++ The /sys/power/wake_lock file allows user space to create ++ wakeup source objects and activate them on demand (if one of ++ those wakeup sources is active, reads from the ++ /sys/power/wakeup_count file block or return false). When a ++ string without white space is written to /sys/power/wake_lock, ++ it will be assumed to represent a wakeup source name. If there ++ is a wakeup source object with that name, it will be activated ++ (unless active already). Otherwise, a new wakeup source object ++ will be registered, assigned the given name and activated. ++ If a string written to /sys/power/wake_lock contains white ++ space, the part of the string preceding the white space will be ++ regarded as a wakeup source name and handled as descrived above. ++ The other part of the string will be regarded as a timeout (in ++ nanoseconds) such that the wakeup source will be automatically ++ deactivated after it has expired. The timeout, if present, is ++ set regardless of the current state of the wakeup source object ++ in question. ++ ++ Reads from this file return a string consisting of the names of ++ wakeup sources created with the help of it that are active at ++ the moment, separated with spaces. ++ ++ ++What: /sys/power/wake_unlock ++Date: February 2012 ++Contact: Rafael J. Wysocki ++Description: ++ The /sys/power/wake_unlock file allows user space to deactivate ++ wakeup sources created with the help of /sys/power/wake_lock. ++ When a string is written to /sys/power/wake_unlock, it will be ++ assumed to represent the name of a wakeup source to deactivate. ++ If a wakeup source object of that name exists and is active at ++ the moment, it will be deactivated. ++ ++ Reads from this file return a string consisting of the names of ++ wakeup sources created with the help of /sys/power/wake_lock ++ that are inactive at the moment, separated with spaces. +diff --git a/Documentation/android.txt b/Documentation/android.txt +new file mode 100644 +index 00000000..72a62afd +--- /dev/null ++++ b/Documentation/android.txt +@@ -0,0 +1,121 @@ ++ ============= ++ A N D R O I D ++ ============= ++ ++Copyright (C) 2009 Google, Inc. ++Written by Mike Chan ++ ++CONTENTS: ++--------- ++ ++1. Android ++ 1.1 Required enabled config options ++ 1.2 Required disabled config options ++ 1.3 Recommended enabled config options ++2. Contact ++ ++ ++1. Android ++========== ++ ++Android (www.android.com) is an open source operating system for mobile devices. ++This document describes configurations needed to run the Android framework on ++top of the Linux kernel. ++ ++To see a working defconfig look at msm_defconfig or goldfish_defconfig ++which can be found at http://android.git.kernel.org in kernel/common.git ++and kernel/msm.git ++ ++ ++1.1 Required enabled config options ++----------------------------------- ++After building a standard defconfig, ensure that these options are enabled in ++your .config or defconfig if they are not already. Based off the msm_defconfig. ++You should keep the rest of the default options enabled in the defconfig ++unless you know what you are doing. ++ ++ANDROID_PARANOID_NETWORK ++ASHMEM ++CONFIG_FB_MODE_HELPERS ++CONFIG_FONT_8x16 ++CONFIG_FONT_8x8 ++CONFIG_YAFFS_SHORT_NAMES_IN_RAM ++DAB ++EARLYSUSPEND ++FB ++FB_CFB_COPYAREA ++FB_CFB_FILLRECT ++FB_CFB_IMAGEBLIT ++FB_DEFERRED_IO ++FB_TILEBLITTING ++HIGH_RES_TIMERS ++INOTIFY ++INOTIFY_USER ++INPUT_EVDEV ++INPUT_GPIO ++INPUT_MISC ++LEDS_CLASS ++LEDS_GPIO ++LOCK_KERNEL ++LkOGGER ++LOW_MEMORY_KILLER ++MISC_DEVICES ++NEW_LEDS ++NO_HZ ++POWER_SUPPLY ++PREEMPT ++RAMFS ++RTC_CLASS ++RTC_LIB ++SWITCH ++SWITCH_GPIO ++TMPFS ++UID_STAT ++UID16 ++USB_FUNCTION ++USB_FUNCTION_ADB ++USER_WAKELOCK ++VIDEO_OUTPUT_CONTROL ++WAKELOCK ++YAFFS_AUTO_YAFFS2 ++YAFFS_FS ++YAFFS_YAFFS1 ++YAFFS_YAFFS2 ++ ++ ++1.2 Required disabled config options ++------------------------------------ ++CONFIG_YAFFS_DISABLE_LAZY_LOAD ++DNOTIFY ++ ++ ++1.3 Recommended enabled config options ++------------------------------ ++ANDROID_PMEM ++ANDROID_RAM_CONSOLE ++ANDROID_RAM_CONSOLE_ERROR_CORRECTION ++SCHEDSTATS ++DEBUG_PREEMPT ++DEBUG_MUTEXES ++DEBUG_SPINLOCK_SLEEP ++DEBUG_INFO ++FRAME_POINTER ++CPU_FREQ ++CPU_FREQ_TABLE ++CPU_FREQ_DEFAULT_GOV_ONDEMAND ++CPU_FREQ_GOV_ONDEMAND ++CRC_CCITT ++EMBEDDED ++INPUT_TOUCHSCREEN ++I2C ++I2C_BOARDINFO ++LOG_BUF_SHIFT=17 ++SERIAL_CORE ++SERIAL_CORE_CONSOLE ++ ++ ++2. Contact ++========== ++website: http://android.git.kernel.org ++ ++mailing-lists: android-kernel@googlegroups.com +diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt +index 8e74980a..594ff17d 100644 +--- a/Documentation/cgroups/cgroups.txt ++++ b/Documentation/cgroups/cgroups.txt +@@ -592,6 +592,15 @@ there are not tasks in the cgroup. If pre_destroy() returns error code, + rmdir() will fail with it. From this behavior, pre_destroy() can be + called multiple times against a cgroup. + ++int allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) ++(cgroup_mutex held by caller) ++ ++Called prior to moving a task into a cgroup; if the subsystem ++returns an error, this will abort the attach operation. Used ++to extend the permission checks - if all subsystems in a cgroup ++return 0, the attach will be allowed to proceed, even if the ++default permission check (root or same user) fails. ++ + int can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) + (cgroup_mutex held by caller) + +diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt +index c7a2eb84..b4ae5e68 100644 +--- a/Documentation/cpu-freq/governors.txt ++++ b/Documentation/cpu-freq/governors.txt +@@ -28,6 +28,7 @@ Contents: + 2.3 Userspace + 2.4 Ondemand + 2.5 Conservative ++2.6 Interactive + + 3. The Governor Interface in the CPUfreq Core + +@@ -191,6 +192,81 @@ governor but for the opposite direction. For example when set to its + default value of '20' it means that if the CPU usage needs to be below + 20% between samples to have the frequency decreased. + ++ ++2.6 Interactive ++--------------- ++ ++The CPUfreq governor "interactive" is designed for latency-sensitive, ++interactive workloads. This governor sets the CPU speed depending on ++usage, similar to "ondemand" and "conservative" governors, but with a ++different set of configurable behaviors. ++ ++The tuneable values for this governor are: ++ ++target_loads: CPU load values used to adjust speed to influence the ++current CPU load toward that value. In general, the lower the target ++load, the more often the governor will raise CPU speeds to bring load ++below the target. The format is a single target load, optionally ++followed by pairs of CPU speeds and CPU loads to target at or above ++those speeds. Colons can be used between the speeds and associated ++target loads for readability. For example: ++ ++ 85 1000000:90 1700000:99 ++ ++targets CPU load 85% below speed 1GHz, 90% at or above 1GHz, until ++1.7GHz and above, at which load 99% is targeted. If speeds are ++specified these must appear in ascending order. Higher target load ++values are typically specified for higher speeds, that is, target load ++values also usually appear in an ascending order. The default is ++target load 90% for all speeds. ++ ++min_sample_time: The minimum amount of time to spend at the current ++frequency before ramping down. Default is 80000 uS. ++ ++hispeed_freq: An intermediate "hi speed" at which to initially ramp ++when CPU load hits the value specified in go_hispeed_load. If load ++stays high for the amount of time specified in above_hispeed_delay, ++then speed may be bumped higher. Default is the maximum speed ++allowed by the policy at governor initialization time. ++ ++go_hispeed_load: The CPU load at which to ramp to hispeed_freq. ++Default is 99%. ++ ++above_hispeed_delay: When speed is at or above hispeed_freq, wait for ++this long before raising speed in response to continued high load. ++Default is 20000 uS. ++ ++timer_rate: Sample rate for reevaluating CPU load when the CPU is not ++idle. A deferrable timer is used, such that the CPU will not be woken ++from idle to service this timer until something else needs to run. ++(The maximum time to allow deferring this timer when not running at ++minimum speed is configurable via timer_slack.) Default is 20000 uS. ++ ++timer_slack: Maximum additional time to defer handling the governor ++sampling timer beyond timer_rate when running at speeds above the ++minimum. For platforms that consume additional power at idle when ++CPUs are running at speeds greater than minimum, this places an upper ++bound on how long the timer will be deferred prior to re-evaluating ++load and dropping speed. For example, if timer_rate is 20000uS and ++timer_slack is 10000uS then timers will be deferred for up to 30msec ++when not at lowest speed. A value of -1 means defer timers ++indefinitely at all speeds. Default is 80000 uS. ++ ++boost: If non-zero, immediately boost speed of all CPUs to at least ++hispeed_freq until zero is written to this attribute. If zero, allow ++CPU speeds to drop below hispeed_freq according to load as usual. ++Default is zero. ++ ++boostpulse: On each write, immediately boost speed of all CPUs to ++hispeed_freq for at least the period of time specified by ++boostpulse_duration, after which speeds are allowed to drop below ++hispeed_freq according to load as usual. ++ ++boostpulse_duration: Length of time to hold CPU speed at hispeed_freq ++on a write to boostpulse, before allowing speed to drop according to ++load as usual. Default is 80000 uS. ++ ++ + 3. The Governor Interface in the CPUfreq Core + ============================================= + +diff --git a/Documentation/dma-buf-sharing.txt b/Documentation/dma-buf-sharing.txt +index 3bbd5c51..5ff4d2b8 100644 +--- a/Documentation/dma-buf-sharing.txt ++++ b/Documentation/dma-buf-sharing.txt +@@ -29,13 +29,6 @@ The buffer-user + in memory, mapped into its own address space, so it can access the same area + of memory. + +-*IMPORTANT*: [see https://lkml.org/lkml/2011/12/20/211 for more details] +-For this first version, A buffer shared using the dma_buf sharing API: +-- *may* be exported to user space using "mmap" *ONLY* by exporter, outside of +- this framework. +-- with this new iteration of the dma-buf api cpu access from the kernel has been +- enable, see below for the details. +- + dma-buf operations for device dma only + -------------------------------------- + +@@ -313,6 +306,83 @@ Access to a dma_buf from the kernel context involves three steps: + enum dma_data_direction dir); + + ++Direct Userspace Access/mmap Support ++------------------------------------ ++ ++Being able to mmap an export dma-buf buffer object has 2 main use-cases: ++- CPU fallback processing in a pipeline and ++- supporting existing mmap interfaces in importers. ++ ++1. CPU fallback processing in a pipeline ++ ++ In many processing pipelines it is sometimes required that the cpu can access ++ the data in a dma-buf (e.g. for thumbnail creation, snapshots, ...). To avoid ++ the need to handle this specially in userspace frameworks for buffer sharing ++ it's ideal if the dma_buf fd itself can be used to access the backing storage ++ from userspace using mmap. ++ ++ Furthermore Android's ION framework already supports this (and is otherwise ++ rather similar to dma-buf from a userspace consumer side with using fds as ++ handles, too). So it's beneficial to support this in a similar fashion on ++ dma-buf to have a good transition path for existing Android userspace. ++ ++ No special interfaces, userspace simply calls mmap on the dma-buf fd. ++ ++2. Supporting existing mmap interfaces in exporters ++ ++ Similar to the motivation for kernel cpu access it is again important that ++ the userspace code of a given importing subsystem can use the same interfaces ++ with a imported dma-buf buffer object as with a native buffer object. This is ++ especially important for drm where the userspace part of contemporary OpenGL, ++ X, and other drivers is huge, and reworking them to use a different way to ++ mmap a buffer rather invasive. ++ ++ The assumption in the current dma-buf interfaces is that redirecting the ++ initial mmap is all that's needed. A survey of some of the existing ++ subsystems shows that no driver seems to do any nefarious thing like syncing ++ up with outstanding asynchronous processing on the device or allocating ++ special resources at fault time. So hopefully this is good enough, since ++ adding interfaces to intercept pagefaults and allow pte shootdowns would ++ increase the complexity quite a bit. ++ ++ Interface: ++ int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *, ++ unsigned long); ++ ++ If the importing subsystem simply provides a special-purpose mmap call to set ++ up a mapping in userspace, calling do_mmap with dma_buf->file will equally ++ achieve that for a dma-buf object. ++ ++3. Implementation notes for exporters ++ ++ Because dma-buf buffers have invariant size over their lifetime, the dma-buf ++ core checks whether a vma is too large and rejects such mappings. The ++ exporter hence does not need to duplicate this check. ++ ++ Because existing importing subsystems might presume coherent mappings for ++ userspace, the exporter needs to set up a coherent mapping. If that's not ++ possible, it needs to fake coherency by manually shooting down ptes when ++ leaving the cpu domain and flushing caches at fault time. Note that all the ++ dma_buf files share the same anon inode, hence the exporter needs to replace ++ the dma_buf file stored in vma->vm_file with it's own if pte shootdown is ++ requred. This is because the kernel uses the underlying inode's address_space ++ for vma tracking (and hence pte tracking at shootdown time with ++ unmap_mapping_range). ++ ++ If the above shootdown dance turns out to be too expensive in certain ++ scenarios, we can extend dma-buf with a more explicit cache tracking scheme ++ for userspace mappings. But the current assumption is that using mmap is ++ always a slower path, so some inefficiencies should be acceptable. ++ ++ Exporters that shoot down mappings (for any reasons) shall not do any ++ synchronization at fault time with outstanding device operations. ++ Synchronization is an orthogonal issue to sharing the backing storage of a ++ buffer and hence should not be handled by dma-buf itself. This is explictly ++ mentioned here because many people seem to want something like this, but if ++ different exporters handle this differently, buffer sharing can fail in ++ interesting ways depending upong the exporter (if userspace starts depending ++ upon this implicit synchronization). ++ + Miscellaneous notes + ------------------- + +@@ -336,6 +406,20 @@ Miscellaneous notes + the exporting driver to create a dmabuf fd must provide a way to let + userspace control setting of O_CLOEXEC flag passed in to dma_buf_fd(). + ++- If an exporter needs to manually flush caches and hence needs to fake ++ coherency for mmap support, it needs to be able to zap all the ptes pointing ++ at the backing storage. Now linux mm needs a struct address_space associated ++ with the struct file stored in vma->vm_file to do that with the function ++ unmap_mapping_range. But the dma_buf framework only backs every dma_buf fd ++ with the anon_file struct file, i.e. all dma_bufs share the same file. ++ ++ Hence exporters need to setup their own file (and address_space) association ++ by setting vma->vm_file and adjusting vma->vm_pgoff in the dma_buf mmap ++ callback. In the specific case of a gem driver the exporter could use the ++ shmem file already provided by gem (and set vm_pgoff = 0). Exporters can then ++ zap ptes by unmapping the corresponding range of the struct address_space ++ associated with their own file. ++ + References: + [1] struct dma_buf_ops in include/linux/dma-buf.h + [2] All interfaces mentioned above defined in include/linux/dma-buf.h +diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.txt +new file mode 100644 +index 00000000..4627c424 +--- /dev/null ++++ b/Documentation/hid/uhid.txt +@@ -0,0 +1,169 @@ ++ UHID - User-space I/O driver support for HID subsystem ++ ======================================================== ++ ++The HID subsystem needs two kinds of drivers. In this document we call them: ++ ++ 1. The "HID I/O Driver" is the driver that performs raw data I/O to the ++ low-level device. Internally, they register an hid_ll_driver structure with ++ the HID core. They perform device setup, read raw data from the device and ++ push it into the HID subsystem and they provide a callback so the HID ++ subsystem can send data to the device. ++ ++ 2. The "HID Device Driver" is the driver that parses HID reports and reacts on ++ them. There are generic drivers like "generic-usb" and "generic-bluetooth" ++ which adhere to the HID specification and provide the standardizes features. ++ But there may be special drivers and quirks for each non-standard device out ++ there. Internally, they use the hid_driver structure. ++ ++Historically, the USB stack was the first subsystem to provide an HID I/O ++Driver. However, other standards like Bluetooth have adopted the HID specs and ++may provide HID I/O Drivers, too. The UHID driver allows to implement HID I/O ++Drivers in user-space and feed the data into the kernel HID-subsystem. ++ ++This allows user-space to operate on the same level as USB-HID, Bluetooth-HID ++and similar. It does not provide a way to write HID Device Drivers, though. Use ++hidraw for this purpose. ++ ++There is an example user-space application in ./samples/uhid/uhid-example.c ++ ++The UHID API ++------------ ++ ++UHID is accessed through a character misc-device. The minor-number is allocated ++dynamically so you need to rely on udev (or similar) to create the device node. ++This is /dev/uhid by default. ++ ++If a new device is detected by your HID I/O Driver and you want to register this ++device with the HID subsystem, then you need to open /dev/uhid once for each ++device you want to register. All further communication is done by read()'ing or ++write()'ing "struct uhid_event" objects. Non-blocking operations are supported ++by setting O_NONBLOCK. ++ ++struct uhid_event { ++ __u32 type; ++ union { ++ struct uhid_create_req create; ++ struct uhid_data_req data; ++ ... ++ } u; ++}; ++ ++The "type" field contains the ID of the event. Depending on the ID different ++payloads are sent. You must not split a single event across multiple read()'s or ++multiple write()'s. A single event must always be sent as a whole. Furthermore, ++only a single event can be sent per read() or write(). Pending data is ignored. ++If you want to handle multiple events in a single syscall, then use vectored ++I/O with readv()/writev(). ++ ++The first thing you should do is sending an UHID_CREATE event. This will ++register the device. UHID will respond with an UHID_START event. You can now ++start sending data to and reading data from UHID. However, unless UHID sends the ++UHID_OPEN event, the internally attached HID Device Driver has no user attached. ++That is, you might put your device asleep unless you receive the UHID_OPEN ++event. If you receive the UHID_OPEN event, you should start I/O. If the last ++user closes the HID device, you will receive an UHID_CLOSE event. This may be ++followed by an UHID_OPEN event again and so on. There is no need to perform ++reference-counting in user-space. That is, you will never receive multiple ++UHID_OPEN events without an UHID_CLOSE event. The HID subsystem performs ++ref-counting for you. ++You may decide to ignore UHID_OPEN/UHID_CLOSE, though. I/O is allowed even ++though the device may have no users. ++ ++If you want to send data to the HID subsystem, you send an HID_INPUT event with ++your raw data payload. If the kernel wants to send data to the device, you will ++read an UHID_OUTPUT or UHID_OUTPUT_EV event. ++ ++If your device disconnects, you should send an UHID_DESTROY event. This will ++unregister the device. You can now send UHID_CREATE again to register a new ++device. ++If you close() the fd, the device is automatically unregistered and destroyed ++internally. ++ ++write() ++------- ++write() allows you to modify the state of the device and feed input data into ++the kernel. The following types are supported: UHID_CREATE, UHID_DESTROY and ++UHID_INPUT. The kernel will parse the event immediately and if the event ID is ++not supported, it will return -EOPNOTSUPP. If the payload is invalid, then ++-EINVAL is returned, otherwise, the amount of data that was read is returned and ++the request was handled successfully. ++ ++ UHID_CREATE: ++ This creates the internal HID device. No I/O is possible until you send this ++ event to the kernel. The payload is of type struct uhid_create_req and ++ contains information about your device. You can start I/O now. ++ ++ UHID_DESTROY: ++ This destroys the internal HID device. No further I/O will be accepted. There ++ may still be pending messages that you can receive with read() but no further ++ UHID_INPUT events can be sent to the kernel. ++ You can create a new device by sending UHID_CREATE again. There is no need to ++ reopen the character device. ++ ++ UHID_INPUT: ++ You must send UHID_CREATE before sending input to the kernel! This event ++ contains a data-payload. This is the raw data that you read from your device. ++ The kernel will parse the HID reports and react on it. ++ ++ UHID_FEATURE_ANSWER: ++ If you receive a UHID_FEATURE request you must answer with this request. You ++ must copy the "id" field from the request into the answer. Set the "err" field ++ to 0 if no error occured or to EIO if an I/O error occurred. ++ If "err" is 0 then you should fill the buffer of the answer with the results ++ of the feature request and set "size" correspondingly. ++ ++read() ++------ ++read() will return a queued ouput report. These output reports can be of type ++UHID_START, UHID_STOP, UHID_OPEN, UHID_CLOSE, UHID_OUTPUT or UHID_OUTPUT_EV. No ++reaction is required to any of them but you should handle them according to your ++needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads. ++ ++ UHID_START: ++ This is sent when the HID device is started. Consider this as an answer to ++ UHID_CREATE. This is always the first event that is sent. ++ ++ UHID_STOP: ++ This is sent when the HID device is stopped. Consider this as an answer to ++ UHID_DESTROY. ++ If the kernel HID device driver closes the device manually (that is, you ++ didn't send UHID_DESTROY) then you should consider this device closed and send ++ an UHID_DESTROY event. You may want to reregister your device, though. This is ++ always the last message that is sent to you unless you reopen the device with ++ UHID_CREATE. ++ ++ UHID_OPEN: ++ This is sent when the HID device is opened. That is, the data that the HID ++ device provides is read by some other process. You may ignore this event but ++ it is useful for power-management. As long as you haven't received this event ++ there is actually no other process that reads your data so there is no need to ++ send UHID_INPUT events to the kernel. ++ ++ UHID_CLOSE: ++ This is sent when there are no more processes which read the HID data. It is ++ the counterpart of UHID_OPEN and you may as well ignore this event. ++ ++ UHID_OUTPUT: ++ This is sent if the HID device driver wants to send raw data to the I/O ++ device. You should read the payload and forward it to the device. The payload ++ is of type "struct uhid_data_req". ++ This may be received even though you haven't received UHID_OPEN, yet. ++ ++ UHID_OUTPUT_EV: ++ Same as UHID_OUTPUT but this contains a "struct input_event" as payload. This ++ is called for force-feedback, LED or similar events which are received through ++ an input device by the HID subsystem. You should convert this into raw reports ++ and send them to your device similar to events of type UHID_OUTPUT. ++ ++ UHID_FEATURE: ++ This event is sent if the kernel driver wants to perform a feature request as ++ described in the HID specs. The report-type and report-number are available in ++ the payload. ++ The kernel serializes feature requests so there will never be two in parallel. ++ However, if you fail to respond with a UHID_FEATURE_ANSWER in a time-span of 5 ++ seconds, then the requests will be dropped and a new one might be sent. ++ Therefore, the payload also contains an "id" field that identifies every ++ request. ++ ++Document by: ++ David Herrmann +diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt +index 753d18ae..693037fb 100644 +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -2377,6 +2377,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. + + resume= [SWSUSP] + Specify the partition device for software suspend ++ Format: ++ {/dev/ | PARTUUID= | : | } + + resume_offset= [SWSUSP] + Specify the offset from the beginning of the partition +diff --git a/Documentation/power/suspend-and-cpuhotplug.txt b/Documentation/power/suspend-and-cpuhotplug.txt +index f28f9a6f..e13dafc8 100644 +--- a/Documentation/power/suspend-and-cpuhotplug.txt ++++ b/Documentation/power/suspend-and-cpuhotplug.txt +@@ -29,7 +29,7 @@ More details follow: + + Write 'mem' to + /sys/power/state +- syfs file ++ sysfs file + | + v + Acquire pm_mutex lock +diff --git a/Documentation/sync.txt b/Documentation/sync.txt +new file mode 100644 +index 00000000..a2d05e7f +--- /dev/null ++++ b/Documentation/sync.txt +@@ -0,0 +1,75 @@ ++Motivation: ++ ++In complicated DMA pipelines such as graphics (multimedia, camera, gpu, display) ++a consumer of a buffer needs to know when the producer has finished producing ++it. Likewise the producer needs to know when the consumer is finished with the ++buffer so it can reuse it. A particular buffer may be consumed by multiple ++consumers which will retain the buffer for different amounts of time. In ++addition, a consumer may consume multiple buffers atomically. ++The sync framework adds an API which allows synchronization between the ++producers and consumers in a generic way while also allowing platforms which ++have shared hardware synchronization primitives to exploit them. ++ ++Goals: ++ * provide a generic API for expressing synchronization dependencies ++ * allow drivers to exploit hardware synchronization between hardware ++ blocks ++ * provide a userspace API that allows a compositor to manage ++ dependencies. ++ * provide rich telemetry data to allow debugging slowdowns and stalls of ++ the graphics pipeline. ++ ++Objects: ++ * sync_timeline ++ * sync_pt ++ * sync_fence ++ ++sync_timeline: ++ ++A sync_timeline is an abstract monotonically increasing counter. In general, ++each driver/hardware block context will have one of these. They can be backed ++by the appropriate hardware or rely on the generic sw_sync implementation. ++Timelines are only ever created through their specific implementations ++(i.e. sw_sync.) ++ ++sync_pt: ++ ++A sync_pt is an abstract value which marks a point on a sync_timeline. Sync_pts ++have a single timeline parent. They have 3 states: active, signaled, and error. ++They start in active state and transition, once, to either signaled (when the ++timeline counter advances beyond the sync_pt’s value) or error state. ++ ++sync_fence: ++ ++Sync_fences are the primary primitives used by drivers to coordinate ++synchronization of their buffers. They are a collection of sync_pts which may ++or may not have the same timeline parent. A sync_pt can only exist in one fence ++and the fence's list of sync_pts is immutable once created. Fences can be ++waited on synchronously or asynchronously. Two fences can also be merged to ++create a third fence containing a copy of the two fences’ sync_pts. Fences are ++backed by file descriptors to allow userspace to coordinate the display pipeline ++dependencies. ++ ++Use: ++ ++A driver implementing sync support should have a work submission function which: ++ * takes a fence argument specifying when to begin work ++ * asynchronously queues that work to kick off when the fence is signaled ++ * returns a fence to indicate when its work will be done. ++ * signals the returned fence once the work is completed. ++ ++Consider an imaginary display driver that has the following API: ++/* ++ * assumes buf is ready to be displayed. ++ * blocks until the buffer is on screen. ++ */ ++ void display_buffer(struct dma_buf *buf); ++ ++The new API will become: ++/* ++ * will display buf when fence is signaled. ++ * returns immediately with a fence that will signal when buf ++ * is no longer displayed. ++ */ ++struct sync_fence* display_buffer(struct dma_buf *buf, ++ struct sync_fence *fence); +diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt +index 6f51fed4..a5c45d7b 100644 +--- a/Documentation/trace/ftrace.txt ++++ b/Documentation/trace/ftrace.txt +@@ -1458,6 +1458,35 @@ will produce: + 1) 1.449 us | } + + ++You can disable the hierarchical function call formatting and instead print a ++flat list of function entry and return events. This uses the format described ++in the Output Formatting section and respects all the trace options that ++control that formatting. Hierarchical formatting is the default. ++ ++ hierachical: echo nofuncgraph-flat > trace_options ++ flat: echo funcgraph-flat > trace_options ++ ++ ie: ++ ++ # tracer: function_graph ++ # ++ # entries-in-buffer/entries-written: 68355/68355 #P:2 ++ # ++ # _-----=> irqs-off ++ # / _----=> need-resched ++ # | / _---=> hardirq/softirq ++ # || / _--=> preempt-depth ++ # ||| / delay ++ # TASK-PID CPU# |||| TIMESTAMP FUNCTION ++ # | | | |||| | | ++ sh-1806 [001] d... 198.843443: graph_ent: func=_raw_spin_lock ++ sh-1806 [001] d... 198.843445: graph_ent: func=__raw_spin_lock ++ sh-1806 [001] d..1 198.843447: graph_ret: func=__raw_spin_lock ++ sh-1806 [001] d..1 198.843449: graph_ret: func=_raw_spin_lock ++ sh-1806 [001] d..1 198.843451: graph_ent: func=_raw_spin_unlock_irqrestore ++ sh-1806 [001] d... 198.843453: graph_ret: func=_raw_spin_unlock_irqrestore ++ ++ + You might find other useful features for this tracer in the + following "dynamic ftrace" section such as tracing only specific + functions or tasks. +diff --git a/MAINTAINERS b/MAINTAINERS +index c744d9c6..c5b99af4 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -6855,6 +6855,13 @@ S: Maintained + F: Documentation/filesystems/ufs.txt + F: fs/ufs/ + ++UHID USERSPACE HID IO DRIVER: ++M: David Herrmann ++L: linux-input@vger.kernel.org ++S: Maintained ++F: drivers/hid/uhid.c ++F: include/linux/uhid.h ++ + ULTRA-WIDEBAND (UWB) SUBSYSTEM: + L: linux-usb@vger.kernel.org + S: Orphan +diff --git a/Makefile b/Makefile +index 282e8da3..fb92e2ab 100644 +--- a/Makefile ++++ b/Makefile +@@ -3,6 +3,7 @@ PATCHLEVEL = 4 + SUBLEVEL = 35 + EXTRAVERSION = + NAME = Saber-toothed Squirrel ++ANYKA_VERSION = 1.0.05 + + # *DOCUMENTATION* + # To see a list of typical targets execute "make help" +@@ -132,6 +133,20 @@ sub-make: FORCE + KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile \ + $(filter-out _all sub-make,$(MAKECMDGOALS)) + ++myPATH := $(KBUILD_OUTPUT)/lib ++myFILE := "libfha.a" ++myFILE_SOURCE := "lib/libfha.a" ++$(shell if [ ! -d $(myPATH) ]; then\ ++ mkdir -p $(myPATH); fi) ++$(shell cp -f $(myFILE_SOURCE) $(myPATH)) ++ ++aecPATH := $(KBUILD_OUTPUT)/lib ++aecFILE := "libakaec.a" ++aecFILE_SOURCE := "lib/libakaec.a" ++$(shell if [ ! -d $(aecPATH) ]; then\ ++ mkdir -p $(aecPATH); fi) ++$(shell cp -f $(aecFILE_SOURCE) $(aecPATH)) ++ + # Leave processing to above invocation of make + skip-makefile := 1 + endif # ifneq ($(KBUILD_OUTPUT),) +@@ -192,8 +207,11 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ + # Default value for CROSS_COMPILE is not to prefix executables + # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile + export KBUILD_BUILDHOST := $(SUBARCH) +-ARCH ?= $(SUBARCH) +-CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%) ++# ARCH ?= $(SUBARCH) ++# CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%) ++ ++ARCH ?= arm ++CROSS_COMPILE ?= arm-none-linux-gnueabi- + + # Architecture as present in compile.h + UTS_MACHINE := $(ARCH) +@@ -350,7 +368,7 @@ CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ + CFLAGS_MODULE = + AFLAGS_MODULE = + LDFLAGS_MODULE = +-CFLAGS_KERNEL = ++CFLAGS_KERNEL = -DANYKA_VERSION=$(ANYKA_VERSION) + AFLAGS_KERNEL = + CFLAGS_GCOV = -fprofile-arcs -ftest-coverage + +@@ -725,7 +743,7 @@ drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y)) + net-y := $(patsubst %/, %/built-in.o, $(net-y)) + libs-y1 := $(patsubst %/, %/lib.a, $(libs-y)) + libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y)) +-libs-y := $(libs-y1) $(libs-y2) ++libs-y := $(libs-y1) $(libs-y2) lib/libfha.a lib/libakaec.a + + # Build vmlinux + # --------------------------------------------------------------------------- +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 7fe19a38..1aad3ad0 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1010,6 +1010,19 @@ config ARCH_ZYNQ + select USE_OF + help + Support for Xilinx Zynq ARM Cortex A9 Platform ++ ++config ARCH_AK39 ++ bool "ANYKA AK39XX" ++ select CPU_ARM926T ++ select GENERIC_GPIO ++ select ARCH_REQUIRE_GPIOLIB ++ select CLKDEV_LOOKUP ++# select WIRELESS_EXT ++ select ARCH_USES_GETTIMEOFFSET ++ select ARCH_HAS_CPUFREQ ++ help ++ Support for Anyka AK39xx series Chips platform. ++ + endchoice + + # +@@ -1128,6 +1141,9 @@ source "arch/arm/mach-vt8500/Kconfig" + + source "arch/arm/mach-w90x900/Kconfig" + ++source "arch/arm/mach-ak39/Kconfig" ++source "arch/arm/plat-anyka/Kconfig" ++ + # Definitions to make life easier + config ARCH_ACORN + bool +@@ -1896,6 +1912,15 @@ config DEPRECATED_PARAM_STRUCT + This was deprecated in 2001 and announced to live on for 5 years. + Some old boot loaders still use this way. + ++config ARM_FLUSH_CONSOLE_ON_RESTART ++ bool "Force flush the console on restart" ++ help ++ If the console is locked while the system is rebooted, the messages ++ in the temporary logbuffer would not have propogated to all the ++ console drivers. This option forces the console lock to be ++ released if it failed to be acquired, which will cause all the ++ pending messages to be flushed. ++ + endmenu + + menu "Boot options" +@@ -2113,6 +2138,22 @@ config AUTO_ZRELADDR + 0xf8000000. This assumes the zImage being placed in the first 128MB + from start of memory. + ++config RAM_BASE ++ hex "RAM physical starting address" ++ depends on ARCH_AK39 ++ default 0x80000000 ++ help ++ ANYKA RAM physical starting address (in hex), default addr is 0x80000000 for Anyka platform. ++ ++config VIDEO_RESERVED_MEM_SIZE ++ hex "Memory reserved for video decoding" ++ depends on ARCH_AK39 ++ default 0x1A00000 ++ help ++ ANYKA H.264 decoder requires continuous physical RAM which do NOT cross ++ 32MB boundary (Decoder IP requirement). So we have to reserve enough RAM ++ for H.264 decoding. Default size is 0x1A00000 (26MB) for AK39xx. ++ + endmenu + + menu "CPU Power Management" +diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug +index 85348a09..037bd5af 100644 +--- a/arch/arm/Kconfig.debug ++++ b/arch/arm/Kconfig.debug +@@ -63,6 +63,27 @@ config DEBUG_USER + 8 - SIGSEGV faults + 16 - SIGBUS faults + ++config DEBUG_RODATA ++ bool "Write protect kernel text section" ++ default n ++ depends on DEBUG_KERNEL && MMU ++ ---help--- ++ Mark the kernel text section as write-protected in the pagetables, ++ in order to catch accidental (and incorrect) writes to such const ++ data. This will cause the size of the kernel, plus up to 4MB, to ++ be mapped as pages instead of sections, which will increase TLB ++ pressure. ++ If in doubt, say "N". ++ ++config DEBUG_RODATA_TEST ++ bool "Testcase for the DEBUG_RODATA feature" ++ depends on DEBUG_RODATA ++ default n ++ ---help--- ++ This option enables a testcase for the DEBUG_RODATA ++ feature. ++ If in doubt, say "N" ++ + # These options are only for real kernel hackers who want to get their hands dirty. + config DEBUG_LL + bool "Kernel low-level debugging functions (read help!)" +diff --git a/arch/arm/Makefile b/arch/arm/Makefile +index 1d6402cb..54bf4034 100644 +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -197,6 +197,7 @@ machine-$(CONFIG_MACH_SPEAR310) := spear3xx + machine-$(CONFIG_MACH_SPEAR320) := spear3xx + machine-$(CONFIG_MACH_SPEAR600) := spear6xx + machine-$(CONFIG_ARCH_ZYNQ) := zynq ++machine-$(CONFIG_ARCH_AK39) := ak39 + + # Platform directory name. This list is sorted alphanumerically + # by CONFIG_* macro name. +@@ -212,6 +213,7 @@ plat-$(CONFIG_PLAT_S3C24XX) := s3c24xx samsung + plat-$(CONFIG_PLAT_S5P) := s5p samsung + plat-$(CONFIG_PLAT_SPEAR) := spear + plat-$(CONFIG_PLAT_VERSATILE) := versatile ++plat-$(CONFIG_PLAT_ANYKA) := anyka + + ifeq ($(CONFIG_ARCH_EBSA110),y) + # This is what happens if you forget the IOCS16 line. +diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S +index 87278fc8..9e631db1 100644 +--- a/arch/arm/boot/compressed/head.S ++++ b/arch/arm/boot/compressed/head.S +@@ -9,6 +9,7 @@ + * published by the Free Software Foundation. + */ + #include ++#include + + /* + * Debugging stuff +@@ -132,7 +133,21 @@ start: + .word start @ absolute load/run zImage address + .word _edata @ zImage end address + THUMB( .thumb ) +-1: mov r7, r1 @ save architecture ID ++1: ++/* ++ * Actually `machine type' MUST be provided in register r1 by boot/loader according ++ * to ARM Linux booting requirements (Documentation/arm/Booting). However, it seems ++ * that AK39xx nandboot or boot/loader-like program do NOT follow this standard, ++ * we have to give a fake one here. ++ * ++ * NOTE: The following code assumes that machine type is NOT greater than 0xFFFF, that could be true for a very long time, so just keep the code. ++ */ ++#ifdef CONFIG_ARCH_AK39 ++ mov r1, #(MACH_TYPE_AK39XX & 0xFF) ++ orr r1, r1, #(MACH_TYPE_AK39XX & 0xFF00) ++#endif ++ ++ mov r7, r1 @ save architecture ID + mov r8, r2 @ save atags pointer + + #ifndef __ARM_ARCH_2__ +@@ -767,6 +782,8 @@ proc_types: + @ b __arm6_mmu_cache_off + @ b __armv3_mmu_cache_flush + ++#if !defined(CONFIG_CPU_V7) ++ /* This collides with some V7 IDs, preventing correct detection */ + .word 0x00000000 @ old ARM ID + .word 0x0000f000 + mov pc, lr +@@ -775,6 +792,7 @@ proc_types: + THUMB( nop ) + mov pc, lr + THUMB( nop ) ++#endif + + .word 0x41007000 @ ARM7/710 + .word 0xfff8fe00 +diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c +index 8e2a8fca..b05ee262 100644 +--- a/arch/arm/boot/compressed/misc.c ++++ b/arch/arm/boot/compressed/misc.c +@@ -27,6 +27,9 @@ extern void error(char *x); + + #include + ++#define _STR(x) #x ++#define STR(x) _STR(x) ++ + #ifdef CONFIG_DEBUG_ICEDCC + + #if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V6K) || defined(CONFIG_CPU_V7) +@@ -151,4 +154,6 @@ decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p, + error("decompressor returned an error"); + else + putstr(" done, booting the kernel.\n"); ++ ++ putstr("Anyka Linux Kernel Version: "STR(ANYKA_VERSION)"\n"); + } +diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig +index 283fa1d8..271dd136 100644 +--- a/arch/arm/common/Kconfig ++++ b/arch/arm/common/Kconfig +@@ -40,3 +40,53 @@ config SHARP_PARAM + + config SHARP_SCOOP + bool ++ ++config FIQ_GLUE ++ bool ++ select FIQ ++ ++config FIQ_DEBUGGER ++ bool "FIQ Mode Serial Debugger" ++ select FIQ ++ select FIQ_GLUE ++ default n ++ help ++ The FIQ serial debugger can accept commands even when the ++ kernel is unresponsive due to being stuck with interrupts ++ disabled. ++ ++ ++config FIQ_DEBUGGER_NO_SLEEP ++ bool "Keep serial debugger active" ++ depends on FIQ_DEBUGGER ++ default n ++ help ++ Enables the serial debugger at boot. Passing ++ fiq_debugger.no_sleep on the kernel commandline will ++ override this config option. ++ ++config FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON ++ bool "Don't disable wakeup IRQ when debugger is active" ++ depends on FIQ_DEBUGGER ++ default n ++ help ++ Don't disable the wakeup irq when enabling the uart clock. This will ++ cause extra interrupts, but it makes the serial debugger usable with ++ on some MSM radio builds that ignore the uart clock request in power ++ collapse. ++ ++config FIQ_DEBUGGER_CONSOLE ++ bool "Console on FIQ Serial Debugger port" ++ depends on FIQ_DEBUGGER ++ default n ++ help ++ Enables a console so that printk messages are displayed on ++ the debugger serial port as the occur. ++ ++config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE ++ bool "Put the FIQ debugger into console mode by default" ++ depends on FIQ_DEBUGGER_CONSOLE ++ default n ++ help ++ If enabled, this puts the fiq debugger into console mode by default. ++ Otherwise, the fiq debugger will start out in debug mode. +diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile +index 215816f1..11670f9d 100644 +--- a/arch/arm/common/Makefile ++++ b/arch/arm/common/Makefile +@@ -15,3 +15,5 @@ obj-$(CONFIG_ARCH_IXP2000) += uengine.o + obj-$(CONFIG_ARCH_IXP23XX) += uengine.o + obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o + obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o ++obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o ++obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o +diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c +new file mode 100644 +index 00000000..d0686388 +--- /dev/null ++++ b/arch/arm/common/fiq_debugger.c +@@ -0,0 +1,1388 @@ ++/* ++ * arch/arm/common/fiq_debugger.c ++ * ++ * Serial Debugger Interface accessed through an FIQ interrupt. ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "fiq_debugger_ringbuf.h" ++ ++#define DEBUG_MAX 64 ++#define MAX_UNHANDLED_FIQ_COUNT 1000000 ++ ++#define MAX_FIQ_DEBUGGER_PORTS 4 ++ ++#define THREAD_INFO(sp) ((struct thread_info *) \ ++ ((unsigned long)(sp) & ~(THREAD_SIZE - 1))) ++ ++struct fiq_debugger_state { ++ struct fiq_glue_handler handler; ++ ++ int fiq; ++ int uart_irq; ++ int signal_irq; ++ int wakeup_irq; ++ bool wakeup_irq_no_set_wake; ++ struct clk *clk; ++ struct fiq_debugger_pdata *pdata; ++ struct platform_device *pdev; ++ ++ char debug_cmd[DEBUG_MAX]; ++ int debug_busy; ++ int debug_abort; ++ ++ char debug_buf[DEBUG_MAX]; ++ int debug_count; ++ ++ bool no_sleep; ++ bool debug_enable; ++ bool ignore_next_wakeup_irq; ++ struct timer_list sleep_timer; ++ spinlock_t sleep_timer_lock; ++ bool uart_enabled; ++ struct wake_lock debugger_wake_lock; ++ bool console_enable; ++ int current_cpu; ++ atomic_t unhandled_fiq_count; ++ bool in_fiq; ++ ++ struct work_struct work; ++ spinlock_t work_lock; ++ char work_cmd[DEBUG_MAX]; ++ ++#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE ++ spinlock_t console_lock; ++ struct console console; ++ struct tty_struct *tty; ++ int tty_open_count; ++ struct fiq_debugger_ringbuf *tty_rbuf; ++ bool syslog_dumping; ++#endif ++ ++ unsigned int last_irqs[NR_IRQS]; ++ unsigned int last_local_timer_irqs[NR_CPUS]; ++}; ++ ++#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE ++struct tty_driver *fiq_tty_driver; ++#endif ++ ++#ifdef CONFIG_FIQ_DEBUGGER_NO_SLEEP ++static bool initial_no_sleep = true; ++#else ++static bool initial_no_sleep; ++#endif ++ ++#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE ++static bool initial_debug_enable = true; ++static bool initial_console_enable = true; ++#else ++static bool initial_debug_enable; ++static bool initial_console_enable; ++#endif ++ ++static bool fiq_kgdb_enable; ++ ++module_param_named(no_sleep, initial_no_sleep, bool, 0644); ++module_param_named(debug_enable, initial_debug_enable, bool, 0644); ++module_param_named(console_enable, initial_console_enable, bool, 0644); ++module_param_named(kgdb_enable, fiq_kgdb_enable, bool, 0644); ++ ++#ifdef CONFIG_FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON ++static inline void enable_wakeup_irq(struct fiq_debugger_state *state) {} ++static inline void disable_wakeup_irq(struct fiq_debugger_state *state) {} ++#else ++static inline void enable_wakeup_irq(struct fiq_debugger_state *state) ++{ ++ if (state->wakeup_irq < 0) ++ return; ++ enable_irq(state->wakeup_irq); ++ if (!state->wakeup_irq_no_set_wake) ++ enable_irq_wake(state->wakeup_irq); ++} ++static inline void disable_wakeup_irq(struct fiq_debugger_state *state) ++{ ++ if (state->wakeup_irq < 0) ++ return; ++ disable_irq_nosync(state->wakeup_irq); ++ if (!state->wakeup_irq_no_set_wake) ++ disable_irq_wake(state->wakeup_irq); ++} ++#endif ++ ++static bool inline debug_have_fiq(struct fiq_debugger_state *state) ++{ ++ return (state->fiq >= 0); ++} ++ ++static void debug_force_irq(struct fiq_debugger_state *state) ++{ ++ unsigned int irq = state->signal_irq; ++ ++ if (WARN_ON(!debug_have_fiq(state))) ++ return; ++ if (state->pdata->force_irq) { ++ state->pdata->force_irq(state->pdev, irq); ++ } else { ++ struct irq_chip *chip = irq_get_chip(irq); ++ if (chip && chip->irq_retrigger) ++ chip->irq_retrigger(irq_get_irq_data(irq)); ++ } ++} ++ ++static void debug_uart_enable(struct fiq_debugger_state *state) ++{ ++ if (state->clk) ++ clk_enable(state->clk); ++ if (state->pdata->uart_enable) ++ state->pdata->uart_enable(state->pdev); ++} ++ ++static void debug_uart_disable(struct fiq_debugger_state *state) ++{ ++ if (state->pdata->uart_disable) ++ state->pdata->uart_disable(state->pdev); ++ if (state->clk) ++ clk_disable(state->clk); ++} ++ ++static void debug_uart_flush(struct fiq_debugger_state *state) ++{ ++ if (state->pdata->uart_flush) ++ state->pdata->uart_flush(state->pdev); ++} ++ ++static void debug_putc(struct fiq_debugger_state *state, char c) ++{ ++ state->pdata->uart_putc(state->pdev, c); ++} ++ ++static void debug_puts(struct fiq_debugger_state *state, char *s) ++{ ++ unsigned c; ++ while ((c = *s++)) { ++ if (c == '\n') ++ debug_putc(state, '\r'); ++ debug_putc(state, c); ++ } ++} ++ ++static void debug_prompt(struct fiq_debugger_state *state) ++{ ++ debug_puts(state, "debug> "); ++} ++ ++int log_buf_copy(char *dest, int idx, int len); ++static void dump_kernel_log(struct fiq_debugger_state *state) ++{ ++ char buf[1024]; ++ int idx = 0; ++ int ret; ++ int saved_oip; ++ ++ /* setting oops_in_progress prevents log_buf_copy() ++ * from trying to take a spinlock which will make it ++ * very unhappy in some cases... ++ */ ++ saved_oip = oops_in_progress; ++ oops_in_progress = 1; ++ for (;;) { ++ ret = log_buf_copy(buf, idx, 1023); ++ if (ret <= 0) ++ break; ++ buf[ret] = 0; ++ debug_puts(state, buf); ++ idx += ret; ++ } ++ oops_in_progress = saved_oip; ++} ++ ++static char *mode_name(unsigned cpsr) ++{ ++ switch (cpsr & MODE_MASK) { ++ case USR_MODE: return "USR"; ++ case FIQ_MODE: return "FIQ"; ++ case IRQ_MODE: return "IRQ"; ++ case SVC_MODE: return "SVC"; ++ case ABT_MODE: return "ABT"; ++ case UND_MODE: return "UND"; ++ case SYSTEM_MODE: return "SYS"; ++ default: return "???"; ++ } ++} ++ ++static int debug_printf(void *cookie, const char *fmt, ...) ++{ ++ struct fiq_debugger_state *state = cookie; ++ char buf[256]; ++ va_list ap; ++ ++ va_start(ap, fmt); ++ vsnprintf(buf, sizeof(buf), fmt, ap); ++ va_end(ap); ++ ++ debug_puts(state, buf); ++ return state->debug_abort; ++} ++ ++/* Safe outside fiq context */ ++static int debug_printf_nfiq(void *cookie, const char *fmt, ...) ++{ ++ struct fiq_debugger_state *state = cookie; ++ char buf[256]; ++ va_list ap; ++ unsigned long irq_flags; ++ ++ va_start(ap, fmt); ++ vsnprintf(buf, 128, fmt, ap); ++ va_end(ap); ++ ++ local_irq_save(irq_flags); ++ debug_puts(state, buf); ++ debug_uart_flush(state); ++ local_irq_restore(irq_flags); ++ return state->debug_abort; ++} ++ ++static void dump_regs(struct fiq_debugger_state *state, unsigned *regs) ++{ ++ debug_printf(state, " r0 %08x r1 %08x r2 %08x r3 %08x\n", ++ regs[0], regs[1], regs[2], regs[3]); ++ debug_printf(state, " r4 %08x r5 %08x r6 %08x r7 %08x\n", ++ regs[4], regs[5], regs[6], regs[7]); ++ debug_printf(state, " r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n", ++ regs[8], regs[9], regs[10], regs[11], ++ mode_name(regs[16])); ++ if ((regs[16] & MODE_MASK) == USR_MODE) ++ debug_printf(state, " ip %08x sp %08x lr %08x pc %08x " ++ "cpsr %08x\n", regs[12], regs[13], regs[14], ++ regs[15], regs[16]); ++ else ++ debug_printf(state, " ip %08x sp %08x lr %08x pc %08x " ++ "cpsr %08x spsr %08x\n", regs[12], regs[13], ++ regs[14], regs[15], regs[16], regs[17]); ++} ++ ++struct mode_regs { ++ unsigned long sp_svc; ++ unsigned long lr_svc; ++ unsigned long spsr_svc; ++ ++ unsigned long sp_abt; ++ unsigned long lr_abt; ++ unsigned long spsr_abt; ++ ++ unsigned long sp_und; ++ unsigned long lr_und; ++ unsigned long spsr_und; ++ ++ unsigned long sp_irq; ++ unsigned long lr_irq; ++ unsigned long spsr_irq; ++ ++ unsigned long r8_fiq; ++ unsigned long r9_fiq; ++ unsigned long r10_fiq; ++ unsigned long r11_fiq; ++ unsigned long r12_fiq; ++ unsigned long sp_fiq; ++ unsigned long lr_fiq; ++ unsigned long spsr_fiq; ++}; ++ ++void __naked get_mode_regs(struct mode_regs *regs) ++{ ++ asm volatile ( ++ "mrs r1, cpsr\n" ++ "msr cpsr_c, #0xd3 @(SVC_MODE | PSR_I_BIT | PSR_F_BIT)\n" ++ "stmia r0!, {r13 - r14}\n" ++ "mrs r2, spsr\n" ++ "msr cpsr_c, #0xd7 @(ABT_MODE | PSR_I_BIT | PSR_F_BIT)\n" ++ "stmia r0!, {r2, r13 - r14}\n" ++ "mrs r2, spsr\n" ++ "msr cpsr_c, #0xdb @(UND_MODE | PSR_I_BIT | PSR_F_BIT)\n" ++ "stmia r0!, {r2, r13 - r14}\n" ++ "mrs r2, spsr\n" ++ "msr cpsr_c, #0xd2 @(IRQ_MODE | PSR_I_BIT | PSR_F_BIT)\n" ++ "stmia r0!, {r2, r13 - r14}\n" ++ "mrs r2, spsr\n" ++ "msr cpsr_c, #0xd1 @(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)\n" ++ "stmia r0!, {r2, r8 - r14}\n" ++ "mrs r2, spsr\n" ++ "stmia r0!, {r2}\n" ++ "msr cpsr_c, r1\n" ++ "bx lr\n"); ++} ++ ++ ++static void dump_allregs(struct fiq_debugger_state *state, unsigned *regs) ++{ ++ struct mode_regs mode_regs; ++ dump_regs(state, regs); ++ get_mode_regs(&mode_regs); ++ debug_printf(state, " svc: sp %08x lr %08x spsr %08x\n", ++ mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc); ++ debug_printf(state, " abt: sp %08x lr %08x spsr %08x\n", ++ mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt); ++ debug_printf(state, " und: sp %08x lr %08x spsr %08x\n", ++ mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und); ++ debug_printf(state, " irq: sp %08x lr %08x spsr %08x\n", ++ mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq); ++ debug_printf(state, " fiq: r8 %08x r9 %08x r10 %08x r11 %08x " ++ "r12 %08x\n", ++ mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq, ++ mode_regs.r11_fiq, mode_regs.r12_fiq); ++ debug_printf(state, " fiq: sp %08x lr %08x spsr %08x\n", ++ mode_regs.sp_fiq, mode_regs.lr_fiq, mode_regs.spsr_fiq); ++} ++ ++static void dump_irqs(struct fiq_debugger_state *state) ++{ ++ int n; ++ ++ debug_printf(state, "irqnr total since-last status name\n"); ++ for (n = 0; n < NR_IRQS; n++) { ++ struct irqaction *act = irq_desc[n].action; ++ if (!act && !kstat_irqs(n)) ++ continue; ++ debug_printf(state, "%5d: %10u %11u %8x %s\n", n, ++ kstat_irqs(n), ++ kstat_irqs(n) - state->last_irqs[n], ++ irq_desc[n].status_use_accessors, ++ (act && act->name) ? act->name : "???"); ++ state->last_irqs[n] = kstat_irqs(n); ++ } ++} ++ ++struct stacktrace_state { ++ struct fiq_debugger_state *state; ++ unsigned int depth; ++}; ++ ++static int report_trace(struct stackframe *frame, void *d) ++{ ++ struct stacktrace_state *sts = d; ++ ++ if (sts->depth) { ++ debug_printf(sts->state, ++ " pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n", ++ frame->pc, frame->pc, frame->lr, frame->lr, ++ frame->sp, frame->fp); ++ sts->depth--; ++ return 0; ++ } ++ debug_printf(sts->state, " ...\n"); ++ ++ return sts->depth == 0; ++} ++ ++struct frame_tail { ++ struct frame_tail *fp; ++ unsigned long sp; ++ unsigned long lr; ++} __attribute__((packed)); ++ ++static struct frame_tail *user_backtrace(struct fiq_debugger_state *state, ++ struct frame_tail *tail) ++{ ++ struct frame_tail buftail[2]; ++ ++ /* Also check accessibility of one struct frame_tail beyond */ ++ if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) { ++ debug_printf(state, " invalid frame pointer %p\n", tail); ++ return NULL; ++ } ++ if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) { ++ debug_printf(state, ++ " failed to copy frame pointer %p\n", tail); ++ return NULL; ++ } ++ ++ debug_printf(state, " %p\n", buftail[0].lr); ++ ++ /* frame pointers should strictly progress back up the stack ++ * (towards higher addresses) */ ++ if (tail >= buftail[0].fp) ++ return NULL; ++ ++ return buftail[0].fp-1; ++} ++ ++void dump_stacktrace(struct fiq_debugger_state *state, ++ struct pt_regs * const regs, unsigned int depth, void *ssp) ++{ ++ struct frame_tail *tail; ++ struct thread_info *real_thread_info = THREAD_INFO(ssp); ++ struct stacktrace_state sts; ++ ++ sts.depth = depth; ++ sts.state = state; ++ *current_thread_info() = *real_thread_info; ++ ++ if (!current) ++ debug_printf(state, "current NULL\n"); ++ else ++ debug_printf(state, "pid: %d comm: %s\n", ++ current->pid, current->comm); ++ dump_regs(state, (unsigned *)regs); ++ ++ if (!user_mode(regs)) { ++ struct stackframe frame; ++ frame.fp = regs->ARM_fp; ++ frame.sp = regs->ARM_sp; ++ frame.lr = regs->ARM_lr; ++ frame.pc = regs->ARM_pc; ++ debug_printf(state, ++ " pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n", ++ regs->ARM_pc, regs->ARM_pc, regs->ARM_lr, regs->ARM_lr, ++ regs->ARM_sp, regs->ARM_fp); ++ walk_stackframe(&frame, report_trace, &sts); ++ return; ++ } ++ ++ tail = ((struct frame_tail *) regs->ARM_fp) - 1; ++ while (depth-- && tail && !((unsigned long) tail & 3)) ++ tail = user_backtrace(state, tail); ++} ++ ++static void do_ps(struct fiq_debugger_state *state) ++{ ++ struct task_struct *g; ++ struct task_struct *p; ++ unsigned task_state; ++ static const char stat_nam[] = "RSDTtZX"; ++ ++ debug_printf(state, "pid ppid prio task pc\n"); ++ read_lock(&tasklist_lock); ++ do_each_thread(g, p) { ++ task_state = p->state ? __ffs(p->state) + 1 : 0; ++ debug_printf(state, ++ "%5d %5d %4d ", p->pid, p->parent->pid, p->prio); ++ debug_printf(state, "%-13.13s %c", p->comm, ++ task_state >= sizeof(stat_nam) ? '?' : stat_nam[task_state]); ++ if (task_state == TASK_RUNNING) ++ debug_printf(state, " running\n"); ++ else ++ debug_printf(state, " %08lx\n", thread_saved_pc(p)); ++ } while_each_thread(g, p); ++ read_unlock(&tasklist_lock); ++} ++ ++#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE ++static void begin_syslog_dump(struct fiq_debugger_state *state) ++{ ++ state->syslog_dumping = true; ++} ++ ++static void end_syslog_dump(struct fiq_debugger_state *state) ++{ ++ state->syslog_dumping = false; ++} ++#else ++extern int do_syslog(int type, char __user *bug, int count); ++static void begin_syslog_dump(struct fiq_debugger_state *state) ++{ ++ do_syslog(5 /* clear */, NULL, 0); ++} ++ ++static void end_syslog_dump(struct fiq_debugger_state *state) ++{ ++ char buf[128]; ++ int ret; ++ int idx = 0; ++ ++ while (1) { ++ ret = log_buf_copy(buf, idx, sizeof(buf) - 1); ++ if (ret <= 0) ++ break; ++ buf[ret] = 0; ++ debug_printf(state, "%s", buf); ++ idx += ret; ++ } ++} ++#endif ++ ++static void do_sysrq(struct fiq_debugger_state *state, char rq) ++{ ++ if ((rq == 'g' || rq == 'G') && !fiq_kgdb_enable) { ++ debug_printf(state, "sysrq-g blocked\n"); ++ return; ++ } ++ begin_syslog_dump(state); ++ handle_sysrq(rq); ++ end_syslog_dump(state); ++} ++ ++#ifdef CONFIG_KGDB ++static void do_kgdb(struct fiq_debugger_state *state) ++{ ++ if (!fiq_kgdb_enable) { ++ debug_printf(state, "kgdb through fiq debugger not enabled\n"); ++ return; ++ } ++ ++ debug_printf(state, "enabling console and triggering kgdb\n"); ++ state->console_enable = true; ++ handle_sysrq('g'); ++} ++#endif ++ ++static void debug_schedule_work(struct fiq_debugger_state *state, char *cmd) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&state->work_lock, flags); ++ if (state->work_cmd[0] != '\0') { ++ debug_printf(state, "work command processor busy\n"); ++ spin_unlock_irqrestore(&state->work_lock, flags); ++ return; ++ } ++ ++ strlcpy(state->work_cmd, cmd, sizeof(state->work_cmd)); ++ spin_unlock_irqrestore(&state->work_lock, flags); ++ ++ schedule_work(&state->work); ++} ++ ++static void debug_work(struct work_struct *work) ++{ ++ struct fiq_debugger_state *state; ++ char work_cmd[DEBUG_MAX]; ++ char *cmd; ++ unsigned long flags; ++ ++ state = container_of(work, struct fiq_debugger_state, work); ++ ++ spin_lock_irqsave(&state->work_lock, flags); ++ ++ strlcpy(work_cmd, state->work_cmd, sizeof(work_cmd)); ++ state->work_cmd[0] = '\0'; ++ ++ spin_unlock_irqrestore(&state->work_lock, flags); ++ ++ cmd = work_cmd; ++ if (!strncmp(cmd, "reboot", 6)) { ++ cmd += 6; ++ while (*cmd == ' ') ++ cmd++; ++ if (cmd != '\0') ++ kernel_restart(cmd); ++ else ++ kernel_restart(NULL); ++ } else { ++ debug_printf(state, "unknown work command '%s'\n", work_cmd); ++ } ++} ++ ++/* This function CANNOT be called in FIQ context */ ++static void debug_irq_exec(struct fiq_debugger_state *state, char *cmd) ++{ ++ if (!strcmp(cmd, "ps")) ++ do_ps(state); ++ if (!strcmp(cmd, "sysrq")) ++ do_sysrq(state, 'h'); ++ if (!strncmp(cmd, "sysrq ", 6)) ++ do_sysrq(state, cmd[6]); ++#ifdef CONFIG_KGDB ++ if (!strcmp(cmd, "kgdb")) ++ do_kgdb(state); ++#endif ++ if (!strncmp(cmd, "reboot", 6)) ++ debug_schedule_work(state, cmd); ++} ++ ++static void debug_help(struct fiq_debugger_state *state) ++{ ++ debug_printf(state, "FIQ Debugger commands:\n" ++ " pc PC status\n" ++ " regs Register dump\n" ++ " allregs Extended Register dump\n" ++ " bt Stack trace\n" ++ " reboot [] Reboot with command \n" ++ " reset [] Hard reset with command \n" ++ " irqs Interupt status\n" ++ " kmsg Kernel log\n" ++ " version Kernel version\n"); ++ debug_printf(state, " sleep Allow sleep while in FIQ\n" ++ " nosleep Disable sleep while in FIQ\n" ++ " console Switch terminal to console\n" ++ " cpu Current CPU\n" ++ " cpu Switch to CPU\n"); ++ debug_printf(state, " ps Process list\n" ++ " sysrq sysrq options\n" ++ " sysrq Execute sysrq with \n"); ++#ifdef CONFIG_KGDB ++ debug_printf(state, " kgdb Enter kernel debugger\n"); ++#endif ++} ++ ++static void take_affinity(void *info) ++{ ++ struct fiq_debugger_state *state = info; ++ struct cpumask cpumask; ++ ++ cpumask_clear(&cpumask); ++ cpumask_set_cpu(get_cpu(), &cpumask); ++ ++ irq_set_affinity(state->uart_irq, &cpumask); ++} ++ ++static void switch_cpu(struct fiq_debugger_state *state, int cpu) ++{ ++ if (!debug_have_fiq(state)) ++ smp_call_function_single(cpu, take_affinity, state, false); ++ state->current_cpu = cpu; ++} ++ ++static bool debug_fiq_exec(struct fiq_debugger_state *state, ++ const char *cmd, unsigned *regs, void *svc_sp) ++{ ++ bool signal_helper = false; ++ ++ if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) { ++ debug_help(state); ++ } else if (!strcmp(cmd, "pc")) { ++ debug_printf(state, " pc %08x cpsr %08x mode %s\n", ++ regs[15], regs[16], mode_name(regs[16])); ++ } else if (!strcmp(cmd, "regs")) { ++ dump_regs(state, regs); ++ } else if (!strcmp(cmd, "allregs")) { ++ dump_allregs(state, regs); ++ } else if (!strcmp(cmd, "bt")) { ++ dump_stacktrace(state, (struct pt_regs *)regs, 100, svc_sp); ++ } else if (!strncmp(cmd, "reset", 5)) { ++ cmd += 5; ++ while (*cmd == ' ') ++ cmd++; ++ if (*cmd) { ++ char tmp_cmd[32]; ++ strlcpy(tmp_cmd, cmd, sizeof(tmp_cmd)); ++ machine_restart(tmp_cmd); ++ } else { ++ machine_restart(NULL); ++ } ++ } else if (!strcmp(cmd, "irqs")) { ++ dump_irqs(state); ++ } else if (!strcmp(cmd, "kmsg")) { ++ dump_kernel_log(state); ++ } else if (!strcmp(cmd, "version")) { ++ debug_printf(state, "%s\n", linux_banner); ++ } else if (!strcmp(cmd, "sleep")) { ++ state->no_sleep = false; ++ debug_printf(state, "enabling sleep\n"); ++ } else if (!strcmp(cmd, "nosleep")) { ++ state->no_sleep = true; ++ debug_printf(state, "disabling sleep\n"); ++ } else if (!strcmp(cmd, "console")) { ++ debug_printf(state, "console mode\n"); ++ debug_uart_flush(state); ++ state->console_enable = true; ++ } else if (!strcmp(cmd, "cpu")) { ++ debug_printf(state, "cpu %d\n", state->current_cpu); ++ } else if (!strncmp(cmd, "cpu ", 4)) { ++ unsigned long cpu = 0; ++ if (strict_strtoul(cmd + 4, 10, &cpu) == 0) ++ switch_cpu(state, cpu); ++ else ++ debug_printf(state, "invalid cpu\n"); ++ debug_printf(state, "cpu %d\n", state->current_cpu); ++ } else { ++ if (state->debug_busy) { ++ debug_printf(state, ++ "command processor busy. trying to abort.\n"); ++ state->debug_abort = -1; ++ } else { ++ strcpy(state->debug_cmd, cmd); ++ state->debug_busy = 1; ++ } ++ ++ return true; ++ } ++ if (!state->console_enable) ++ debug_prompt(state); ++ ++ return signal_helper; ++} ++ ++static void sleep_timer_expired(unsigned long data) ++{ ++ struct fiq_debugger_state *state = (struct fiq_debugger_state *)data; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&state->sleep_timer_lock, flags); ++ if (state->uart_enabled && !state->no_sleep) { ++ if (state->debug_enable && !state->console_enable) { ++ state->debug_enable = false; ++ debug_printf_nfiq(state, "suspending fiq debugger\n"); ++ } ++ state->ignore_next_wakeup_irq = true; ++ debug_uart_disable(state); ++ state->uart_enabled = false; ++ enable_wakeup_irq(state); ++ } ++ wake_unlock(&state->debugger_wake_lock); ++ spin_unlock_irqrestore(&state->sleep_timer_lock, flags); ++} ++ ++static void handle_wakeup(struct fiq_debugger_state *state) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&state->sleep_timer_lock, flags); ++ if (state->wakeup_irq >= 0 && state->ignore_next_wakeup_irq) { ++ state->ignore_next_wakeup_irq = false; ++ } else if (!state->uart_enabled) { ++ wake_lock(&state->debugger_wake_lock); ++ debug_uart_enable(state); ++ state->uart_enabled = true; ++ disable_wakeup_irq(state); ++ mod_timer(&state->sleep_timer, jiffies + HZ / 2); ++ } ++ spin_unlock_irqrestore(&state->sleep_timer_lock, flags); ++} ++ ++static irqreturn_t wakeup_irq_handler(int irq, void *dev) ++{ ++ struct fiq_debugger_state *state = dev; ++ ++ if (!state->no_sleep) ++ debug_puts(state, "WAKEUP\n"); ++ handle_wakeup(state); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++static void debug_handle_irq_context(struct fiq_debugger_state *state) ++{ ++ if (!state->no_sleep) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&state->sleep_timer_lock, flags); ++ wake_lock(&state->debugger_wake_lock); ++ mod_timer(&state->sleep_timer, jiffies + HZ * 5); ++ spin_unlock_irqrestore(&state->sleep_timer_lock, flags); ++ } ++#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) ++ if (state->tty) { ++ int i; ++ int count = fiq_debugger_ringbuf_level(state->tty_rbuf); ++ for (i = 0; i < count; i++) { ++ int c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0); ++ tty_insert_flip_char(state->tty, c, TTY_NORMAL); ++ if (!fiq_debugger_ringbuf_consume(state->tty_rbuf, 1)) ++ pr_warn("fiq tty failed to consume byte\n"); ++ } ++ tty_flip_buffer_push(state->tty); ++ } ++#endif ++ if (state->debug_busy) { ++ debug_irq_exec(state, state->debug_cmd); ++ if (!state->console_enable) ++ debug_prompt(state); ++ state->debug_busy = 0; ++ } ++} ++ ++static int debug_getc(struct fiq_debugger_state *state) ++{ ++ return state->pdata->uart_getc(state->pdev); ++} ++ ++static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state, ++ int this_cpu, void *regs, void *svc_sp) ++{ ++ int c; ++ static int last_c; ++ int count = 0; ++ bool signal_helper = false; ++ ++ if (this_cpu != state->current_cpu) { ++ if (state->in_fiq) ++ return false; ++ ++ if (atomic_inc_return(&state->unhandled_fiq_count) != ++ MAX_UNHANDLED_FIQ_COUNT) ++ return false; ++ ++ debug_printf(state, "fiq_debugger: cpu %d not responding, " ++ "reverting to cpu %d\n", state->current_cpu, ++ this_cpu); ++ ++ atomic_set(&state->unhandled_fiq_count, 0); ++ switch_cpu(state, this_cpu); ++ return false; ++ } ++ ++ state->in_fiq = true; ++ ++ while ((c = debug_getc(state)) != FIQ_DEBUGGER_NO_CHAR) { ++ count++; ++ if (!state->debug_enable) { ++ if ((c == 13) || (c == 10)) { ++ state->debug_enable = true; ++ state->debug_count = 0; ++ debug_prompt(state); ++ } ++ } else if (c == FIQ_DEBUGGER_BREAK) { ++ state->console_enable = false; ++ debug_puts(state, "fiq debugger mode\n"); ++ state->debug_count = 0; ++ debug_prompt(state); ++#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE ++ } else if (state->console_enable && state->tty_rbuf) { ++ fiq_debugger_ringbuf_push(state->tty_rbuf, c); ++ signal_helper = true; ++#endif ++ } else if ((c >= ' ') && (c < 127)) { ++ if (state->debug_count < (DEBUG_MAX - 1)) { ++ state->debug_buf[state->debug_count++] = c; ++ debug_putc(state, c); ++ } ++ } else if ((c == 8) || (c == 127)) { ++ if (state->debug_count > 0) { ++ state->debug_count--; ++ debug_putc(state, 8); ++ debug_putc(state, ' '); ++ debug_putc(state, 8); ++ } ++ } else if ((c == 13) || (c == 10)) { ++ if (c == '\r' || (c == '\n' && last_c != '\r')) { ++ debug_putc(state, '\r'); ++ debug_putc(state, '\n'); ++ } ++ if (state->debug_count) { ++ state->debug_buf[state->debug_count] = 0; ++ state->debug_count = 0; ++ signal_helper |= ++ debug_fiq_exec(state, state->debug_buf, ++ regs, svc_sp); ++ } else { ++ debug_prompt(state); ++ } ++ } ++ last_c = c; ++ } ++ if (!state->console_enable) ++ debug_uart_flush(state); ++ if (state->pdata->fiq_ack) ++ state->pdata->fiq_ack(state->pdev, state->fiq); ++ ++ /* poke sleep timer if necessary */ ++ if (state->debug_enable && !state->no_sleep) ++ signal_helper = true; ++ ++ atomic_set(&state->unhandled_fiq_count, 0); ++ state->in_fiq = false; ++ ++ return signal_helper; ++} ++ ++static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp) ++{ ++ struct fiq_debugger_state *state = ++ container_of(h, struct fiq_debugger_state, handler); ++ unsigned int this_cpu = THREAD_INFO(svc_sp)->cpu; ++ bool need_irq; ++ ++ need_irq = debug_handle_uart_interrupt(state, this_cpu, regs, svc_sp); ++ if (need_irq) ++ debug_force_irq(state); ++} ++ ++/* ++ * When not using FIQs, we only use this single interrupt as an entry point. ++ * This just effectively takes over the UART interrupt and does all the work ++ * in this context. ++ */ ++static irqreturn_t debug_uart_irq(int irq, void *dev) ++{ ++ struct fiq_debugger_state *state = dev; ++ bool not_done; ++ ++ handle_wakeup(state); ++ ++ /* handle the debugger irq in regular context */ ++ not_done = debug_handle_uart_interrupt(state, smp_processor_id(), ++ get_irq_regs(), ++ current_thread_info()); ++ if (not_done) ++ debug_handle_irq_context(state); ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * If FIQs are used, not everything can happen in fiq context. ++ * FIQ handler does what it can and then signals this interrupt to finish the ++ * job in irq context. ++ */ ++static irqreturn_t debug_signal_irq(int irq, void *dev) ++{ ++ struct fiq_debugger_state *state = dev; ++ ++ if (state->pdata->force_irq_ack) ++ state->pdata->force_irq_ack(state->pdev, state->signal_irq); ++ ++ debug_handle_irq_context(state); ++ ++ return IRQ_HANDLED; ++} ++ ++static void debug_resume(struct fiq_glue_handler *h) ++{ ++ struct fiq_debugger_state *state = ++ container_of(h, struct fiq_debugger_state, handler); ++ if (state->pdata->uart_resume) ++ state->pdata->uart_resume(state->pdev); ++} ++ ++#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) ++struct tty_driver *debug_console_device(struct console *co, int *index) ++{ ++ *index = co->index; ++ return fiq_tty_driver; ++} ++ ++static void debug_console_write(struct console *co, ++ const char *s, unsigned int count) ++{ ++ struct fiq_debugger_state *state; ++ unsigned long flags; ++ ++ state = container_of(co, struct fiq_debugger_state, console); ++ ++ if (!state->console_enable && !state->syslog_dumping) ++ return; ++ ++ debug_uart_enable(state); ++ spin_lock_irqsave(&state->console_lock, flags); ++ while (count--) { ++ if (*s == '\n') ++ debug_putc(state, '\r'); ++ debug_putc(state, *s++); ++ } ++ debug_uart_flush(state); ++ spin_unlock_irqrestore(&state->console_lock, flags); ++ debug_uart_disable(state); ++} ++ ++static struct console fiq_debugger_console = { ++ .name = "ttyFIQ", ++ .device = debug_console_device, ++ .write = debug_console_write, ++ .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED, ++}; ++ ++int fiq_tty_open(struct tty_struct *tty, struct file *filp) ++{ ++ int line = tty->index; ++ struct fiq_debugger_state **states = tty->driver->driver_state; ++ struct fiq_debugger_state *state = states[line]; ++ if (state->tty_open_count++) ++ return 0; ++ ++ tty->driver_data = state; ++ state->tty = tty; ++ return 0; ++} ++ ++void fiq_tty_close(struct tty_struct *tty, struct file *filp) ++{ ++ struct fiq_debugger_state *state = tty->driver_data; ++ if (--state->tty_open_count) ++ return; ++ state->tty = NULL; ++} ++ ++int fiq_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) ++{ ++ int i; ++ struct fiq_debugger_state *state = tty->driver_data; ++ ++ if (!state->console_enable) ++ return count; ++ ++ debug_uart_enable(state); ++ spin_lock_irq(&state->console_lock); ++ for (i = 0; i < count; i++) ++ debug_putc(state, *buf++); ++ spin_unlock_irq(&state->console_lock); ++ debug_uart_disable(state); ++ ++ return count; ++} ++ ++int fiq_tty_write_room(struct tty_struct *tty) ++{ ++ return 16; ++} ++ ++#ifdef CONFIG_CONSOLE_POLL ++static int fiq_tty_poll_init(struct tty_driver *driver, int line, char *options) ++{ ++ return 0; ++} ++ ++static int fiq_tty_poll_get_char(struct tty_driver *driver, int line) ++{ ++ struct fiq_debugger_state *state = driver->ttys[line]->driver_data; ++ int c = NO_POLL_CHAR; ++ ++ debug_uart_enable(state); ++ if (debug_have_fiq(state)) { ++ int count = fiq_debugger_ringbuf_level(state->tty_rbuf); ++ if (count > 0) { ++ c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0); ++ fiq_debugger_ringbuf_consume(state->tty_rbuf, 1); ++ } ++ } else { ++ c = debug_getc(state); ++ if (c == FIQ_DEBUGGER_NO_CHAR) ++ c = NO_POLL_CHAR; ++ } ++ debug_uart_disable(state); ++ ++ return c; ++} ++ ++static void fiq_tty_poll_put_char(struct tty_driver *driver, int line, char ch) ++{ ++ struct fiq_debugger_state *state = driver->ttys[line]->driver_data; ++ debug_uart_enable(state); ++ debug_putc(state, ch); ++ debug_uart_disable(state); ++} ++#endif ++ ++static const struct tty_operations fiq_tty_driver_ops = { ++ .write = fiq_tty_write, ++ .write_room = fiq_tty_write_room, ++ .open = fiq_tty_open, ++ .close = fiq_tty_close, ++#ifdef CONFIG_CONSOLE_POLL ++ .poll_init = fiq_tty_poll_init, ++ .poll_get_char = fiq_tty_poll_get_char, ++ .poll_put_char = fiq_tty_poll_put_char, ++#endif ++}; ++ ++static int fiq_debugger_tty_init(void) ++{ ++ int ret; ++ struct fiq_debugger_state **states = NULL; ++ ++ states = kzalloc(sizeof(*states) * MAX_FIQ_DEBUGGER_PORTS, GFP_KERNEL); ++ if (!states) { ++ pr_err("Failed to allocate fiq debugger state structres\n"); ++ return -ENOMEM; ++ } ++ ++ fiq_tty_driver = alloc_tty_driver(MAX_FIQ_DEBUGGER_PORTS); ++ if (!fiq_tty_driver) { ++ pr_err("Failed to allocate fiq debugger tty\n"); ++ ret = -ENOMEM; ++ goto err_free_state; ++ } ++ ++ fiq_tty_driver->owner = THIS_MODULE; ++ fiq_tty_driver->driver_name = "fiq-debugger"; ++ fiq_tty_driver->name = "ttyFIQ"; ++ fiq_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; ++ fiq_tty_driver->subtype = SERIAL_TYPE_NORMAL; ++ fiq_tty_driver->init_termios = tty_std_termios; ++ fiq_tty_driver->flags = TTY_DRIVER_REAL_RAW | ++ TTY_DRIVER_DYNAMIC_DEV; ++ fiq_tty_driver->driver_state = states; ++ ++ fiq_tty_driver->init_termios.c_cflag = ++ B115200 | CS8 | CREAD | HUPCL | CLOCAL; ++ fiq_tty_driver->init_termios.c_ispeed = 115200; ++ fiq_tty_driver->init_termios.c_ospeed = 115200; ++ ++ tty_set_operations(fiq_tty_driver, &fiq_tty_driver_ops); ++ ++ ret = tty_register_driver(fiq_tty_driver); ++ if (ret) { ++ pr_err("Failed to register fiq tty: %d\n", ret); ++ goto err_free_tty; ++ } ++ ++ pr_info("Registered FIQ tty driver\n"); ++ return 0; ++ ++err_free_tty: ++ put_tty_driver(fiq_tty_driver); ++ fiq_tty_driver = NULL; ++err_free_state: ++ kfree(states); ++ return ret; ++} ++ ++static int fiq_debugger_tty_init_one(struct fiq_debugger_state *state) ++{ ++ int ret; ++ struct device *tty_dev; ++ struct fiq_debugger_state **states = fiq_tty_driver->driver_state; ++ ++ states[state->pdev->id] = state; ++ ++ state->tty_rbuf = fiq_debugger_ringbuf_alloc(1024); ++ if (!state->tty_rbuf) { ++ pr_err("Failed to allocate fiq debugger ringbuf\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ tty_dev = tty_register_device(fiq_tty_driver, state->pdev->id, ++ &state->pdev->dev); ++ if (IS_ERR(tty_dev)) { ++ pr_err("Failed to register fiq debugger tty device\n"); ++ ret = PTR_ERR(tty_dev); ++ goto err; ++ } ++ ++ device_set_wakeup_capable(tty_dev, 1); ++ ++ pr_info("Registered fiq debugger ttyFIQ%d\n", state->pdev->id); ++ ++ return 0; ++ ++err: ++ fiq_debugger_ringbuf_free(state->tty_rbuf); ++ state->tty_rbuf = NULL; ++ return ret; ++} ++#endif ++ ++static int fiq_debugger_dev_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct fiq_debugger_state *state = platform_get_drvdata(pdev); ++ ++ if (state->pdata->uart_dev_suspend) ++ return state->pdata->uart_dev_suspend(pdev); ++ return 0; ++} ++ ++static int fiq_debugger_dev_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct fiq_debugger_state *state = platform_get_drvdata(pdev); ++ ++ if (state->pdata->uart_dev_resume) ++ return state->pdata->uart_dev_resume(pdev); ++ return 0; ++} ++ ++static int fiq_debugger_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct fiq_debugger_pdata *pdata = dev_get_platdata(&pdev->dev); ++ struct fiq_debugger_state *state; ++ int fiq; ++ int uart_irq; ++ ++ if (pdev->id >= MAX_FIQ_DEBUGGER_PORTS) ++ return -EINVAL; ++ ++ if (!pdata->uart_getc || !pdata->uart_putc) ++ return -EINVAL; ++ if ((pdata->uart_enable && !pdata->uart_disable) || ++ (!pdata->uart_enable && pdata->uart_disable)) ++ return -EINVAL; ++ ++ fiq = platform_get_irq_byname(pdev, "fiq"); ++ uart_irq = platform_get_irq_byname(pdev, "uart_irq"); ++ ++ /* uart_irq mode and fiq mode are mutually exclusive, but one of them ++ * is required */ ++ if ((uart_irq < 0 && fiq < 0) || (uart_irq >= 0 && fiq >= 0)) ++ return -EINVAL; ++ if (fiq >= 0 && !pdata->fiq_enable) ++ return -EINVAL; ++ ++ state = kzalloc(sizeof(*state), GFP_KERNEL); ++ setup_timer(&state->sleep_timer, sleep_timer_expired, ++ (unsigned long)state); ++ state->pdata = pdata; ++ state->pdev = pdev; ++ state->no_sleep = initial_no_sleep; ++ state->debug_enable = initial_debug_enable; ++ state->console_enable = initial_console_enable; ++ ++ state->fiq = fiq; ++ state->uart_irq = uart_irq; ++ state->signal_irq = platform_get_irq_byname(pdev, "signal"); ++ state->wakeup_irq = platform_get_irq_byname(pdev, "wakeup"); ++ ++ INIT_WORK(&state->work, debug_work); ++ spin_lock_init(&state->work_lock); ++ ++ platform_set_drvdata(pdev, state); ++ ++ spin_lock_init(&state->sleep_timer_lock); ++ ++ if (state->wakeup_irq < 0 && debug_have_fiq(state)) ++ state->no_sleep = true; ++ state->ignore_next_wakeup_irq = !state->no_sleep; ++ ++ wake_lock_init(&state->debugger_wake_lock, ++ WAKE_LOCK_SUSPEND, "serial-debug"); ++ ++ state->clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(state->clk)) ++ state->clk = NULL; ++ ++ /* do not call pdata->uart_enable here since uart_init may still ++ * need to do some initialization before uart_enable can work. ++ * So, only try to manage the clock during init. ++ */ ++ if (state->clk) ++ clk_enable(state->clk); ++ ++ if (pdata->uart_init) { ++ ret = pdata->uart_init(pdev); ++ if (ret) ++ goto err_uart_init; ++ } ++ ++ debug_printf_nfiq(state, "\n", ++ state->no_sleep ? "" : "twice "); ++ ++ if (debug_have_fiq(state)) { ++ state->handler.fiq = debug_fiq; ++ state->handler.resume = debug_resume; ++ ret = fiq_glue_register_handler(&state->handler); ++ if (ret) { ++ pr_err("%s: could not install fiq handler\n", __func__); ++ goto err_register_fiq; ++ } ++ ++ pdata->fiq_enable(pdev, state->fiq, 1); ++ } else { ++ ret = request_irq(state->uart_irq, debug_uart_irq, ++ IRQF_NO_SUSPEND, "debug", state); ++ if (ret) { ++ pr_err("%s: could not install irq handler\n", __func__); ++ goto err_register_irq; ++ } ++ ++ /* for irq-only mode, we want this irq to wake us up, if it ++ * can. ++ */ ++ enable_irq_wake(state->uart_irq); ++ } ++ ++ if (state->clk) ++ clk_disable(state->clk); ++ ++ if (state->signal_irq >= 0) { ++ ret = request_irq(state->signal_irq, debug_signal_irq, ++ IRQF_TRIGGER_RISING, "debug-signal", state); ++ if (ret) ++ pr_err("serial_debugger: could not install signal_irq"); ++ } ++ ++ if (state->wakeup_irq >= 0) { ++ ret = request_irq(state->wakeup_irq, wakeup_irq_handler, ++ IRQF_TRIGGER_FALLING | IRQF_DISABLED, ++ "debug-wakeup", state); ++ if (ret) { ++ pr_err("serial_debugger: " ++ "could not install wakeup irq\n"); ++ state->wakeup_irq = -1; ++ } else { ++ ret = enable_irq_wake(state->wakeup_irq); ++ if (ret) { ++ pr_err("serial_debugger: " ++ "could not enable wakeup\n"); ++ state->wakeup_irq_no_set_wake = true; ++ } ++ } ++ } ++ if (state->no_sleep) ++ handle_wakeup(state); ++ ++#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) ++ spin_lock_init(&state->console_lock); ++ state->console = fiq_debugger_console; ++ state->console.index = pdev->id; ++ if (!console_set_on_cmdline) ++ add_preferred_console(state->console.name, ++ state->console.index, NULL); ++ register_console(&state->console); ++ fiq_debugger_tty_init_one(state); ++#endif ++ return 0; ++ ++err_register_irq: ++err_register_fiq: ++ if (pdata->uart_free) ++ pdata->uart_free(pdev); ++err_uart_init: ++ if (state->clk) ++ clk_disable(state->clk); ++ if (state->clk) ++ clk_put(state->clk); ++ wake_lock_destroy(&state->debugger_wake_lock); ++ platform_set_drvdata(pdev, NULL); ++ kfree(state); ++ return ret; ++} ++ ++static const struct dev_pm_ops fiq_debugger_dev_pm_ops = { ++ .suspend = fiq_debugger_dev_suspend, ++ .resume = fiq_debugger_dev_resume, ++}; ++ ++static struct platform_driver fiq_debugger_driver = { ++ .probe = fiq_debugger_probe, ++ .driver = { ++ .name = "fiq_debugger", ++ .pm = &fiq_debugger_dev_pm_ops, ++ }, ++}; ++ ++static int __init fiq_debugger_init(void) ++{ ++ fiq_debugger_tty_init(); ++ return platform_driver_register(&fiq_debugger_driver); ++} ++ ++postcore_initcall(fiq_debugger_init); +diff --git a/arch/arm/common/fiq_debugger_ringbuf.h b/arch/arm/common/fiq_debugger_ringbuf.h +new file mode 100644 +index 00000000..2649b558 +--- /dev/null ++++ b/arch/arm/common/fiq_debugger_ringbuf.h +@@ -0,0 +1,94 @@ ++/* ++ * arch/arm/common/fiq_debugger_ringbuf.c ++ * ++ * simple lockless ringbuffer ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++ ++struct fiq_debugger_ringbuf { ++ int len; ++ int head; ++ int tail; ++ u8 buf[]; ++}; ++ ++ ++static inline struct fiq_debugger_ringbuf *fiq_debugger_ringbuf_alloc(int len) ++{ ++ struct fiq_debugger_ringbuf *rbuf; ++ ++ rbuf = kzalloc(sizeof(*rbuf) + len, GFP_KERNEL); ++ if (rbuf == NULL) ++ return NULL; ++ ++ rbuf->len = len; ++ rbuf->head = 0; ++ rbuf->tail = 0; ++ smp_mb(); ++ ++ return rbuf; ++} ++ ++static inline void fiq_debugger_ringbuf_free(struct fiq_debugger_ringbuf *rbuf) ++{ ++ kfree(rbuf); ++} ++ ++static inline int fiq_debugger_ringbuf_level(struct fiq_debugger_ringbuf *rbuf) ++{ ++ int level = rbuf->head - rbuf->tail; ++ ++ if (level < 0) ++ level = rbuf->len + level; ++ ++ return level; ++} ++ ++static inline int fiq_debugger_ringbuf_room(struct fiq_debugger_ringbuf *rbuf) ++{ ++ return rbuf->len - fiq_debugger_ringbuf_level(rbuf) - 1; ++} ++ ++static inline u8 ++fiq_debugger_ringbuf_peek(struct fiq_debugger_ringbuf *rbuf, int i) ++{ ++ return rbuf->buf[(rbuf->tail + i) % rbuf->len]; ++} ++ ++static inline int ++fiq_debugger_ringbuf_consume(struct fiq_debugger_ringbuf *rbuf, int count) ++{ ++ count = min(count, fiq_debugger_ringbuf_level(rbuf)); ++ ++ rbuf->tail = (rbuf->tail + count) % rbuf->len; ++ smp_mb(); ++ ++ return count; ++} ++ ++static inline int ++fiq_debugger_ringbuf_push(struct fiq_debugger_ringbuf *rbuf, u8 datum) ++{ ++ if (fiq_debugger_ringbuf_room(rbuf) == 0) ++ return 0; ++ ++ rbuf->buf[rbuf->head] = datum; ++ smp_mb(); ++ rbuf->head = (rbuf->head + 1) % rbuf->len; ++ smp_mb(); ++ ++ return 1; ++} +diff --git a/arch/arm/common/fiq_glue.S b/arch/arm/common/fiq_glue.S +new file mode 100644 +index 00000000..9e3455a0 +--- /dev/null ++++ b/arch/arm/common/fiq_glue.S +@@ -0,0 +1,111 @@ ++/* ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++ ++ .text ++ ++ .global fiq_glue_end ++ ++ /* fiq stack: r0-r15,cpsr,spsr of interrupted mode */ ++ ++ENTRY(fiq_glue) ++ /* store pc, cpsr from previous mode */ ++ mrs r12, spsr ++ sub r11, lr, #4 ++ subs r10, #1 ++ bne nested_fiq ++ ++ stmfd sp!, {r11-r12, lr} ++ ++ /* store r8-r14 from previous mode */ ++ sub sp, sp, #(7 * 4) ++ stmia sp, {r8-r14}^ ++ nop ++ ++ /* store r0-r7 from previous mode */ ++ stmfd sp!, {r0-r7} ++ ++ /* setup func(data,regs) arguments */ ++ mov r0, r9 ++ mov r1, sp ++ mov r3, r8 ++ ++ mov r7, sp ++ ++ /* Get sp and lr from non-user modes */ ++ and r4, r12, #MODE_MASK ++ cmp r4, #USR_MODE ++ beq fiq_from_usr_mode ++ ++ mov r7, sp ++ orr r4, r4, #(PSR_I_BIT | PSR_F_BIT) ++ msr cpsr_c, r4 ++ str sp, [r7, #(4 * 13)] ++ str lr, [r7, #(4 * 14)] ++ mrs r5, spsr ++ str r5, [r7, #(4 * 17)] ++ ++ cmp r4, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT) ++ /* use fiq stack if we reenter this mode */ ++ subne sp, r7, #(4 * 3) ++ ++fiq_from_usr_mode: ++ msr cpsr_c, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT) ++ mov r2, sp ++ sub sp, r7, #12 ++ stmfd sp!, {r2, ip, lr} ++ /* call func(data,regs) */ ++ blx r3 ++ ldmfd sp, {r2, ip, lr} ++ mov sp, r2 ++ ++ /* restore/discard saved state */ ++ cmp r4, #USR_MODE ++ beq fiq_from_usr_mode_exit ++ ++ msr cpsr_c, r4 ++ ldr sp, [r7, #(4 * 13)] ++ ldr lr, [r7, #(4 * 14)] ++ msr spsr_cxsf, r5 ++ ++fiq_from_usr_mode_exit: ++ msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) ++ ++ ldmfd sp!, {r0-r7} ++ add sp, sp, #(7 * 4) ++ ldmfd sp!, {r11-r12, lr} ++exit_fiq: ++ msr spsr_cxsf, r12 ++ add r10, #1 ++ movs pc, r11 ++ ++nested_fiq: ++ orr r12, r12, #(PSR_F_BIT) ++ b exit_fiq ++ ++fiq_glue_end: ++ ++ENTRY(fiq_glue_setup) /* func, data, sp */ ++ mrs r3, cpsr ++ msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) ++ movs r8, r0 ++ mov r9, r1 ++ mov sp, r2 ++ moveq r10, #0 ++ movne r10, #1 ++ msr cpsr_c, r3 ++ bx lr ++ +diff --git a/arch/arm/common/fiq_glue_setup.c b/arch/arm/common/fiq_glue_setup.c +new file mode 100644 +index 00000000..4044c7db +--- /dev/null ++++ b/arch/arm/common/fiq_glue_setup.c +@@ -0,0 +1,100 @@ ++/* ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++extern unsigned char fiq_glue, fiq_glue_end; ++extern void fiq_glue_setup(void *func, void *data, void *sp); ++ ++static struct fiq_handler fiq_debbuger_fiq_handler = { ++ .name = "fiq_glue", ++}; ++DEFINE_PER_CPU(void *, fiq_stack); ++static struct fiq_glue_handler *current_handler; ++static DEFINE_MUTEX(fiq_glue_lock); ++ ++static void fiq_glue_setup_helper(void *info) ++{ ++ struct fiq_glue_handler *handler = info; ++ fiq_glue_setup(handler->fiq, handler, ++ __get_cpu_var(fiq_stack) + THREAD_START_SP); ++} ++ ++int fiq_glue_register_handler(struct fiq_glue_handler *handler) ++{ ++ int ret; ++ int cpu; ++ ++ if (!handler || !handler->fiq) ++ return -EINVAL; ++ ++ mutex_lock(&fiq_glue_lock); ++ if (fiq_stack) { ++ ret = -EBUSY; ++ goto err_busy; ++ } ++ ++ for_each_possible_cpu(cpu) { ++ void *stack; ++ stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); ++ if (WARN_ON(!stack)) { ++ ret = -ENOMEM; ++ goto err_alloc_fiq_stack; ++ } ++ per_cpu(fiq_stack, cpu) = stack; ++ } ++ ++ ret = claim_fiq(&fiq_debbuger_fiq_handler); ++ if (WARN_ON(ret)) ++ goto err_claim_fiq; ++ ++ current_handler = handler; ++ on_each_cpu(fiq_glue_setup_helper, handler, true); ++ set_fiq_handler(&fiq_glue, &fiq_glue_end - &fiq_glue); ++ ++ mutex_unlock(&fiq_glue_lock); ++ return 0; ++ ++err_claim_fiq: ++err_alloc_fiq_stack: ++ for_each_possible_cpu(cpu) { ++ __free_pages(per_cpu(fiq_stack, cpu), THREAD_SIZE_ORDER); ++ per_cpu(fiq_stack, cpu) = NULL; ++ } ++err_busy: ++ mutex_unlock(&fiq_glue_lock); ++ return ret; ++} ++ ++/** ++ * fiq_glue_resume - Restore fiqs after suspend or low power idle states ++ * ++ * This must be called before calling local_fiq_enable after returning from a ++ * power state where the fiq mode registers were lost. If a driver provided ++ * a resume hook when it registered the handler it will be called. ++ */ ++ ++void fiq_glue_resume(void) ++{ ++ if (!current_handler) ++ return; ++ fiq_glue_setup(current_handler->fiq, current_handler, ++ __get_cpu_var(fiq_stack) + THREAD_START_SP); ++ if (current_handler->resume) ++ current_handler->resume(current_handler); ++} ++ +diff --git a/arch/arm/configs/aimer39_ak3916_defconfig b/arch/arm/configs/aimer39_ak3916_defconfig +new file mode 100644 +index 00000000..294c522a +--- /dev/null ++++ b/arch/arm/configs/aimer39_ak3916_defconfig +@@ -0,0 +1,1962 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.4.35 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_GENERIC_GPIO=y ++CONFIG_ARCH_USES_GETTIMEOFFSET=y ++CONFIG_KTIME_SCALAR=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++CONFIG_ARCH_HAS_CPUFREQ=y ++CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_VECTORS_BASE=0xffff0000 ++# CONFIG_ARM_PATCH_PHYS_VIRT is not set ++CONFIG_PHYS_OFFSET=0x81000000 ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_HAVE_IRQ_WORK=y ++ ++# ++# General setup ++# ++# CONFIG_EXPERIMENTAL is not set ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++# CONFIG_KERNEL_GZIP is not set ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++CONFIG_KERNEL_LZO=y ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_FHANDLE is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_GENERIC_HARDIRQS=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_SHOW=y ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=16 ++# CONFIG_CGROUPS is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_NAMESPACES is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++# CONFIG_BLK_DEV_INITRD is not set ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_EXPERT=y ++CONFIG_UID16=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_SHMEM is not set ++CONFIG_AIO=y ++CONFIG_EMBEDDED=y ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++# CONFIG_PERF_COUNTERS is not set ++# CONFIG_VM_EVENT_COUNTERS is not set ++# CONFIG_SLUB_DEBUG is not set ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++CONFIG_JUMP_LABEL=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_FORCE_LOAD=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++CONFIG_BLOCK=y ++# CONFIG_LBDAF is not set ++# CONFIG_BLK_DEV_BSG is not set ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++ ++# ++# Partition Types ++# ++# CONFIG_PARTITION_ADVANCED is not set ++CONFIG_MSDOS_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++# CONFIG_IOSCHED_DEADLINE is not set ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_CFQ=y ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="cfq" ++# CONFIG_INLINE_SPIN_TRYLOCK is not set ++# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK is not set ++# CONFIG_INLINE_SPIN_LOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQ is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set ++# CONFIG_INLINE_SPIN_UNLOCK_BH is not set ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_READ_TRYLOCK is not set ++# CONFIG_INLINE_READ_LOCK is not set ++# CONFIG_INLINE_READ_LOCK_BH is not set ++# CONFIG_INLINE_READ_LOCK_IRQ is not set ++# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set ++CONFIG_INLINE_READ_UNLOCK=y ++# CONFIG_INLINE_READ_UNLOCK_BH is not set ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_WRITE_TRYLOCK is not set ++# CONFIG_INLINE_WRITE_LOCK is not set ++# CONFIG_INLINE_WRITE_LOCK_BH is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQ is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set ++CONFIG_INLINE_WRITE_UNLOCK=y ++# CONFIG_INLINE_WRITE_UNLOCK_BH is not set ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set ++# CONFIG_MUTEX_SPIN_ON_OWNER is not set ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_BCMRING is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_CNS3XXX is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_PRIMA2 is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MXS is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_H720X is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP23XX is not set ++# CONFIG_ARCH_IXP2000 is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_KIRKWOOD is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_PICOXCELL is not set ++# CONFIG_ARCH_PNX4008 is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_S5P64X0 is not set ++# CONFIG_ARCH_S5PC100 is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHARK is not set ++# CONFIG_ARCH_U300 is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_NOMADIK is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_VT8500 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARCH_AK39=y ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++ ++# ++# System MMU ++# ++CONFIG_CPU_AK3916=y ++# CONFIG_ARCH_SDK3910 is not set ++CONFIG_ARCH_AIMER39_AK3916=y ++# CONFIG_ARCH_AIMER39_AK3918 is not set ++CONFIG_ASIC_FREQ_VALUE=90000000 ++ ++# ++# Power management ++# ++# CONFIG_AK39_PM is not set ++# CONFIG_AK39_PWM_TIMER is not set ++CONFIG_PLAT_ANYKA=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_ARM926T=y ++CONFIG_CPU_32v5=y ++CONFIG_CPU_ABRT_EV5TJ=y ++CONFIG_CPU_PABRT_LEGACY=y ++CONFIG_CPU_CACHE_VIVT=y ++CONFIG_CPU_COPY_V4WB=y ++CONFIG_CPU_TLB_V4WBI=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++CONFIG_CPU_USE_DOMAINS=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++# CONFIG_ARM_THUMB is not set ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set ++# CONFIG_CPU_CACHE_ROUND_ROBIN is not set ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT=5 ++CONFIG_ARM_NR_BANKS=8 ++# CONFIG_FIQ_DEBUGGER is not set ++ ++# ++# Bus support ++# ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_PCCARD=y ++CONFIG_PCMCIA=y ++ ++# ++# PC-card bridges ++# ++ ++# ++# Kernel Features ++# ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ=100 ++CONFIG_AEABI=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++# CONFIG_HIGHMEM is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=999999 ++# CONFIG_COMPACTION is not set ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_SECCOMP is not set ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y ++ ++# ++# Boot options ++# ++# CONFIG_USE_OF is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_CMDLINE="root=/dev/mtdblock1 ro init=/sbin/init mem=64M console=ttySAK0,115200" ++CONFIG_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_CMDLINE_EXTEND is not set ++# CONFIG_CMDLINE_FORCE is not set ++# CONFIG_XIP_KERNEL is not set ++# CONFIG_AUTO_ZRELADDR is not set ++CONFIG_RAM_BASE=0x80000000 ++CONFIG_VIDEO_RESERVED_MEM_SIZE=0x1000000 ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++# CONFIG_CPU_FREQ is not set ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++# CONFIG_VFP is not set ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_HAVE_AOUT=y ++# CONFIG_BINFMT_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_HAS_WAKELOCK=y ++CONFIG_WAKELOCK=y ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_CLK=y ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_UNIX_DIAG=y ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++CONFIG_IP_PNP_RARP=y ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_IP_MROUTE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++CONFIG_INET_UDP_DIAG=y ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_IPV6 is not set ++CONFIG_ANDROID_PARANOID_NETWORK=y ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_IP_NF_QUEUE is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_PHONET is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++CONFIG_BQL=y ++CONFIG_HAVE_BPF_JIT=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++CONFIG_WIRELESS=y ++CONFIG_WIRELESS_EXT=y ++CONFIG_WEXT_CORE=y ++CONFIG_WEXT_PROC=y ++CONFIG_WEXT_SPY=y ++CONFIG_WEXT_PRIV=y ++CONFIG_CFG80211=y ++# CONFIG_NL80211_TESTMODE is not set ++# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set ++# CONFIG_CFG80211_REG_DEBUG is not set ++CONFIG_CFG80211_DEFAULT_PS=y ++# CONFIG_CFG80211_INTERNAL_REGDB is not set ++CONFIG_CFG80211_WEXT=y ++CONFIG_WIRELESS_EXT_SYSFS=y ++CONFIG_LIB80211=y ++# CONFIG_LIB80211_DEBUG is not set ++CONFIG_CFG80211_ALLOW_RECONNECT=y ++CONFIG_MAC80211=y ++CONFIG_MAC80211_HAS_RC=y ++# CONFIG_MAC80211_RC_PID is not set ++CONFIG_MAC80211_RC_MINSTREL=y ++CONFIG_MAC80211_RC_MINSTREL_HT=y ++CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y ++CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" ++# CONFIG_MAC80211_LEDS is not set ++# CONFIG_MAC80211_DEBUG_MENU is not set ++# CONFIG_WIMAX is not set ++CONFIG_RFKILL=y ++CONFIG_RFKILL_PM=y ++CONFIG_RFKILL_LEDS=y ++# CONFIG_RFKILL_INPUT is not set ++# CONFIG_RFKILL_GPIO is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_SYNC is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++# CONFIG_MTD_AFS_PARTS is not set ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_AK_SPIFLASH=y ++# CONFIG_MTD_NAND_IDS is not set ++# CONFIG_MTD_NAND is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR flash memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++ ++# ++# DRBD disabled because PROC_FS, INET or CONNECTOR not selected ++# ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_ATMEL_PWM is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_SENSORS_AK8975 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085 is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_WL127X_RFKILL is not set ++CONFIG_AK_SERIAL_NUMBER=y ++# CONFIG_AK_MOTOR is not set ++ ++# ++# user space generic gpio controller ++# ++# CONFIG_GPIOS_AKCUSTOM is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++CONFIG_HAVE_IDE=y ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_PROC_FS is not set ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++# CONFIG_SCSI_WAIT_SCAN is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_MII is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_ARCNET is not set ++ ++# ++# CAIF transport drivers ++# ++CONFIG_ETHERNET=y ++# CONFIG_NET_VENDOR_3COM is not set ++# CONFIG_NET_VENDOR_AMD is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_CALXEDA_XGMAC is not set ++# CONFIG_NET_VENDOR_CHELSIO is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++# CONFIG_NET_VENDOR_FUJITSU is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_XIRCOM is not set ++CONFIG_AK_ETHERNET=y ++# CONFIG_PHYLIB is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++# CONFIG_TR is not set ++ ++# ++# USB Network Adapters ++# ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_HSO is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++CONFIG_PCMCIA_RAYCS=y ++# CONFIG_LIBERTAS_THINFIRM is not set ++# CONFIG_ATMEL is not set ++# CONFIG_AT76C50X_USB is not set ++# CONFIG_AIRO_CS is not set ++# CONFIG_USB_ZD1201 is not set ++# CONFIG_RTL8187 is not set ++# CONFIG_MAC80211_HWSIM is not set ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_ATH_COMMON is not set ++# CONFIG_B43 is not set ++# CONFIG_B43LEGACY is not set ++# CONFIG_BCMDHD is not set ++# CONFIG_BRCMFMAC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_LIBERTAS is not set ++# CONFIG_HERMES is not set ++# CONFIG_RT2X00 is not set ++# CONFIG_MWIFIEX is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++# CONFIG_KEYBOARD_ATKBD is not set ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8323 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++CONFIG_KEYBOARD_AKKEY=y ++# CONFIG_KEYBOARD_AK_KEYPAD is not set ++CONFIG_KEYBOARD_ADKEY=y ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++# CONFIG_VT is not set ++# CONFIG_UNIX98_PTYS is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++# CONFIG_DEVKMEM is not set ++ ++# ++# Serial drivers ++# ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX3107 is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_TIMBERDALE is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++CONFIG_SERIAL_AK39_UART=y ++CONFIG_SERIAL_AK39_CONSOLE=y ++# CONFIG_SERIAL_GPIO_UART is not set ++CONFIG_TTY_PRINTK=y ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++ ++# ++# PCMCIA character devices ++# ++# CONFIG_SYNCLINK_CS is not set ++# CONFIG_CARDMAN_4000 is not set ++# CONFIG_CARDMAN_4040 is not set ++# CONFIG_IPWIRELESS is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_RAMOOPS is not set ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++# CONFIG_I2C_CHARDEV is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++CONFIG_I2C_SMBUS=y ++ ++# ++# I2C Algorithms ++# ++CONFIG_I2C_ALGOBIT=y ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_SIMTEC is not set ++CONFIG_I2C_ANYKA=y ++CONFIG_I2C_AK39_HW=y ++# CONFIG_I2C_GPIO_SOFT is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++CONFIG_SPI_BITBANG=y ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_OC_TINY is not set ++# CONFIG_SPI_PXA2XX_PCI is not set ++CONFIG_SPI_ANYKA=y ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++ ++# ++# Enable Device Drivers -> PPS to see the PTP clock options. ++# ++CONFIG_ARCH_REQUIRE_GPIOLIB=y ++CONFIG_GPIOLIB=y ++# CONFIG_DEBUG_GPIO is not set ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_BATTERY_ANDROID is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_BATTERY_AK=y ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++CONFIG_WATCHDOG=y ++CONFIG_WATCHDOG_CORE=y ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++ ++# ++# Watchdog Device Drivers ++# ++# CONFIG_SOFT_WATCHDOG is not set ++# CONFIG_DW_WATCHDOG is not set ++# CONFIG_MAX63XX_WATCHDOG is not set ++CONFIG_AK39_WATCHDOG=y ++ ++# ++# USB-based Watchdog Cards ++# ++# CONFIG_USBPCWATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_CORE is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_STMPE is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_S5M_CORE is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_MC13XXX is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_REGULATOR is not set ++CONFIG_MEDIA_SUPPORT=y ++ ++# ++# Multimedia core support ++# ++CONFIG_VIDEO_DEV=y ++CONFIG_VIDEO_V4L2_COMMON=y ++# CONFIG_DVB_CORE is not set ++CONFIG_VIDEO_MEDIA=y ++ ++# ++# Multimedia drivers ++# ++# CONFIG_RC_CORE is not set ++# CONFIG_MEDIA_ATTACH is not set ++CONFIG_MEDIA_TUNER=y ++# CONFIG_MEDIA_TUNER_CUSTOMISE is not set ++CONFIG_MEDIA_TUNER_SIMPLE=y ++CONFIG_MEDIA_TUNER_TDA8290=y ++CONFIG_MEDIA_TUNER_TDA827X=y ++CONFIG_MEDIA_TUNER_TDA18271=y ++CONFIG_MEDIA_TUNER_TDA9887=y ++CONFIG_MEDIA_TUNER_TEA5767=y ++CONFIG_MEDIA_TUNER_MT20XX=y ++CONFIG_MEDIA_TUNER_XC2028=y ++CONFIG_MEDIA_TUNER_XC5000=y ++CONFIG_MEDIA_TUNER_XC4000=y ++CONFIG_MEDIA_TUNER_MC44S803=y ++CONFIG_VIDEO_V4L2=y ++CONFIG_VIDEOBUF_GEN=y ++CONFIG_VIDEOBUF_DMA_CONTIG=y ++CONFIG_VIDEOBUF2_CORE=y ++CONFIG_VIDEO_CAPTURE_DRIVERS=y ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set ++ ++# ++# Encoders, decoders, sensors and other helper chips ++# ++ ++# ++# Audio decoders, processors and mixers ++# ++# CONFIG_VIDEO_TVAUDIO is not set ++# CONFIG_VIDEO_TDA7432 is not set ++# CONFIG_VIDEO_TDA9840 is not set ++# CONFIG_VIDEO_TEA6415C is not set ++# CONFIG_VIDEO_TEA6420 is not set ++# CONFIG_VIDEO_MSP3400 is not set ++# CONFIG_VIDEO_CS5345 is not set ++# CONFIG_VIDEO_CS53L32A is not set ++# CONFIG_VIDEO_WM8775 is not set ++# CONFIG_VIDEO_WM8739 is not set ++# CONFIG_VIDEO_VP27SMPX is not set ++ ++# ++# RDS decoders ++# ++# CONFIG_VIDEO_SAA6588 is not set ++ ++# ++# Video decoders ++# ++# CONFIG_VIDEO_ADV7180 is not set ++# CONFIG_VIDEO_ADV7183 is not set ++# CONFIG_VIDEO_BT819 is not set ++# CONFIG_VIDEO_BT856 is not set ++# CONFIG_VIDEO_BT866 is not set ++# CONFIG_VIDEO_KS0127 is not set ++# CONFIG_VIDEO_SAA7110 is not set ++# CONFIG_VIDEO_SAA711X is not set ++# CONFIG_VIDEO_SAA7191 is not set ++# CONFIG_VIDEO_TVP514X is not set ++# CONFIG_VIDEO_TVP5150 is not set ++# CONFIG_VIDEO_TVP7002 is not set ++# CONFIG_VIDEO_VPX3220 is not set ++ ++# ++# Video and audio decoders ++# ++# CONFIG_VIDEO_SAA717X is not set ++# CONFIG_VIDEO_CX25840 is not set ++ ++# ++# MPEG video encoders ++# ++# CONFIG_VIDEO_CX2341X is not set ++ ++# ++# Video encoders ++# ++# CONFIG_VIDEO_SAA7127 is not set ++# CONFIG_VIDEO_SAA7185 is not set ++# CONFIG_VIDEO_ADV7170 is not set ++# CONFIG_VIDEO_ADV7175 is not set ++# CONFIG_VIDEO_ADV7343 is not set ++# CONFIG_VIDEO_AK881X is not set ++ ++# ++# Camera sensor devices ++# ++# CONFIG_VIDEO_OV7670 is not set ++# CONFIG_VIDEO_VS6624 is not set ++# CONFIG_VIDEO_MT9V011 is not set ++# CONFIG_VIDEO_TCM825X is not set ++# CONFIG_VIDEO_SR030PC30 is not set ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++# CONFIG_VIDEO_UPD64031A is not set ++# CONFIG_VIDEO_UPD64083 is not set ++ ++# ++# Miscelaneous helper chips ++# ++# CONFIG_VIDEO_THS7303 is not set ++# CONFIG_VIDEO_M52790 is not set ++# CONFIG_V4L_USB_DRIVERS is not set ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_SOC_CAMERA=y ++# CONFIG_SOC_CAMERA_IMX074 is not set ++# CONFIG_SOC_CAMERA_MT9M001 is not set ++# CONFIG_SOC_CAMERA_MT9M111 is not set ++# CONFIG_SOC_CAMERA_MT9T031 is not set ++# CONFIG_SOC_CAMERA_MT9T112 is not set ++# CONFIG_SOC_CAMERA_MT9V022 is not set ++# CONFIG_SOC_CAMERA_RJ54N1 is not set ++# CONFIG_SOC_CAMERA_TW9910 is not set ++# CONFIG_SOC_CAMERA_PLATFORM is not set ++# CONFIG_SOC_CAMERA_OV2640 is not set ++# CONFIG_SOC_CAMERA_OV5642 is not set ++# CONFIG_SOC_CAMERA_OV6650 is not set ++# CONFIG_SOC_CAMERA_OV772X is not set ++# CONFIG_SOC_CAMERA_OV9640 is not set ++# CONFIG_SOC_CAMERA_OV9740 is not set ++CONFIG_LINUX_AKSENSOR=y ++CONFIG_SENSOR_GC0308=y ++CONFIG_SENSOR_OV2643=y ++CONFIG_SENSOR_OV7725=y ++CONFIG_SENSOR_HM1375=y ++CONFIG_SENSOR_OV9712=y ++CONFIG_SENSOR_OV2710=y ++# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set ++# CONFIG_VIDEO_SH_MOBILE_CEU is not set ++CONFIG_VIDEO_AK=y ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_RADIO_ADAPTERS is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DRM is not set ++CONFIG_ION=y ++CONFIG_ION_AK=y ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++# CONFIG_FB is not set ++# CONFIG_EXYNOS_VIDEO is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++CONFIG_SOUND=y ++# CONFIG_SOUND_OSS_CORE is not set ++CONFIG_SND=y ++CONFIG_SND_TIMER=y ++CONFIG_SND_PCM=y ++# CONFIG_SND_SEQUENCER is not set ++# CONFIG_SND_MIXER_OSS is not set ++# CONFIG_SND_PCM_OSS is not set ++# CONFIG_SND_DYNAMIC_MINORS is not set ++# CONFIG_SND_SUPPORT_OLD_API is not set ++# CONFIG_SND_VERBOSE_PROCFS is not set ++# CONFIG_SND_VERBOSE_PRINTK is not set ++# CONFIG_SND_DEBUG is not set ++# CONFIG_SND_RAWMIDI_SEQ is not set ++# CONFIG_SND_OPL3_LIB_SEQ is not set ++# CONFIG_SND_OPL4_LIB_SEQ is not set ++# CONFIG_SND_SBAWE_SEQ is not set ++# CONFIG_SND_EMU10K1_SEQ is not set ++# CONFIG_SND_DRIVERS is not set ++CONFIG_SND_ARM=y ++CONFIG_SND_AK_PCM=y ++CONFIG_CODEC_AK39=y ++CONFIG_SPKHP_SWITCH_AUTO=y ++# CONFIG_SPKHP_SWITCH_MIXER is not set ++# CONFIG_SPKHP_SWITCH_UEVENT is not set ++CONFIG_SUPPORT_AEC=y ++# CONFIG_SND_SPI is not set ++# CONFIG_SND_USB is not set ++CONFIG_SND_PCMCIA=y ++# CONFIG_SND_VXPOCKET is not set ++# CONFIG_SND_PDAUDIOCF is not set ++# CONFIG_SND_SOC is not set ++# CONFIG_SOUND_PRIME is not set ++# CONFIG_HID_SUPPORT is not set ++# CONFIG_USB_ARCH_HAS_OHCI is not set ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++# CONFIG_USB_ARCH_HAS_XHCI is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEVICEFS is not set ++# CONFIG_USB_DEVICE_CLASS is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++CONFIG_USB_ANYKA_HCD=y ++CONFIG_USB_AKOTG_HS_HCD=m ++# CONFIG_USB_AKOTG_DMA is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_RENESAS_USBHS is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_LIBUSUAL is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=500 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_MV_UDC is not set ++# CONFIG_USB_GADGET_AKUDC_PRODUCER is not set ++CONFIG_USB_GADGET_AKUDC=y ++CONFIG_USB_AKUDC=m ++# CONFIG_USB_AKUDC_DEBUG_FS is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_GADGET_DUALSPEED=y ++# CONFIG_USB_ZERO is not set ++# CONFIG_USB_AUDIO is not set ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_FILE_STORAGE is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++ ++# ++# OTG and related infrastructure ++# ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ULPI is not set ++# CONFIG_NOP_USB_XCEIV is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++# CONFIG_MMC_BLOCK_BOUNCE is not set ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++# CONFIG_SDIO_WIFI is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_SDHCI_PXAV3 is not set ++# CONFIG_MMC_SDHCI_PXAV2 is not set ++# CONFIG_MMC_SPI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++CONFIG_MMC_ANYKA=y ++# CONFIG_MEMSTICK is not set ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++ ++# ++# LED drivers ++# ++# CONFIG_LEDS_LM3530 is not set ++# CONFIG_LEDS_GPIO is not set ++# CONFIG_LEDS_LP3944 is not set ++# CONFIG_LEDS_LP5521 is not set ++# CONFIG_LEDS_LP5523 is not set ++# CONFIG_LEDS_PCA955X is not set ++# CONFIG_LEDS_PCA9633 is not set ++# CONFIG_LEDS_DAC124S085 is not set ++# CONFIG_LEDS_BD2802 is not set ++CONFIG_LEDS_AK39=y ++# CONFIG_LEDS_LT3593 is not set ++# CONFIG_LEDS_RENESAS_TPU is not set ++# CONFIG_LEDS_TCA6507 is not set ++# CONFIG_LEDS_OT200 is not set ++CONFIG_LEDS_TRIGGERS=y ++ ++# ++# LED Triggers ++# ++CONFIG_LEDS_TRIGGER_TIMER=y ++# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set ++# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set ++# CONFIG_LEDS_TRIGGER_GPIO is not set ++# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set ++ ++# ++# iptables trigger is under Netfilter config (LED target) ++# ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++CONFIG_RTC_LIB=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_HCTOSYS=y ++CONFIG_RTC_HCTOSYS_DEVICE="rtc0" ++# CONFIG_RTC_DEBUG is not set ++ ++# ++# RTC interfaces ++# ++CONFIG_RTC_INTF_SYSFS=y ++CONFIG_RTC_INTF_PROC=y ++CONFIG_RTC_INTF_DEV=y ++# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set ++# CONFIG_RTC_DRV_TEST is not set ++ ++# ++# I2C RTC drivers ++# ++# CONFIG_RTC_DRV_DS1307 is not set ++# CONFIG_RTC_DRV_DS1374 is not set ++# CONFIG_RTC_DRV_DS1672 is not set ++# CONFIG_RTC_DRV_DS3232 is not set ++# CONFIG_RTC_DRV_MAX6900 is not set ++# CONFIG_RTC_DRV_RS5C372 is not set ++# CONFIG_RTC_DRV_ISL1208 is not set ++# CONFIG_RTC_DRV_ISL12022 is not set ++# CONFIG_RTC_DRV_X1205 is not set ++# CONFIG_RTC_DRV_PCF8563 is not set ++# CONFIG_RTC_DRV_PCF8583 is not set ++# CONFIG_RTC_DRV_M41T80 is not set ++# CONFIG_RTC_DRV_BQ32K is not set ++# CONFIG_RTC_DRV_S35390A is not set ++# CONFIG_RTC_DRV_FM3130 is not set ++# CONFIG_RTC_DRV_RX8581 is not set ++# CONFIG_RTC_DRV_RX8025 is not set ++# CONFIG_RTC_DRV_EM3027 is not set ++# CONFIG_RTC_DRV_RV3029C2 is not set ++ ++# ++# SPI RTC drivers ++# ++# CONFIG_RTC_DRV_M41T93 is not set ++# CONFIG_RTC_DRV_M41T94 is not set ++# CONFIG_RTC_DRV_DS1305 is not set ++# CONFIG_RTC_DRV_DS1390 is not set ++# CONFIG_RTC_DRV_MAX6902 is not set ++# CONFIG_RTC_DRV_R9701 is not set ++# CONFIG_RTC_DRV_RS5C348 is not set ++# CONFIG_RTC_DRV_DS3234 is not set ++# CONFIG_RTC_DRV_PCF2123 is not set ++ ++# ++# Platform RTC drivers ++# ++# CONFIG_RTC_DRV_CMOS is not set ++# CONFIG_RTC_DRV_DS1286 is not set ++# CONFIG_RTC_DRV_DS1511 is not set ++# CONFIG_RTC_DRV_DS1553 is not set ++# CONFIG_RTC_DRV_DS1742 is not set ++# CONFIG_RTC_DRV_STK17TA8 is not set ++# CONFIG_RTC_DRV_M48T86 is not set ++# CONFIG_RTC_DRV_M48T35 is not set ++# CONFIG_RTC_DRV_M48T59 is not set ++# CONFIG_RTC_DRV_MSM6242 is not set ++# CONFIG_RTC_DRV_BQ4802 is not set ++# CONFIG_RTC_DRV_RP5C01 is not set ++# CONFIG_RTC_DRV_V3020 is not set ++ ++# ++# on-CPU RTC drivers ++# ++CONFIG_RTC_DRV_AK=y ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++CONFIG_UIO=y ++# CONFIG_UIO_PDRV is not set ++# CONFIG_UIO_PDRV_GENIRQ is not set ++CONFIG_UIO_VCODEC=y ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_BALLOON is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++CONFIG_CLKDEV_LOOKUP=y ++ ++# ++# Hardware Spinlock drivers ++# ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers (EXPERIMENTAL) ++# ++ ++# ++# Rpmsg drivers (EXPERIMENTAL) ++# ++# CONFIG_VIRT_DRIVERS is not set ++# CONFIG_PM_DEVFREQ is not set ++ ++# ++# File systems ++# ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_FS_POSIX_ACL is not set ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_SYSFS=y ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_YAFFS_FS is not set ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++# CONFIG_JFFS2_FS_WRITEBUFFER is not set ++CONFIG_JFFS2_COMPRESSION_OPTIONS=y ++# CONFIG_JFFS2_ZLIB is not set ++# CONFIG_JFFS2_LZO is not set ++# CONFIG_JFFS2_RTIME is not set ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_JFFS2_CMODE_NONE=y ++# CONFIG_JFFS2_CMODE_PRIORITY is not set ++# CONFIG_JFFS2_CMODE_SIZE is not set ++# CONFIG_JFFS2_CMODE_FAVOURLZO is not set ++# CONFIG_CRAMFS is not set ++CONFIG_SQUASHFS=y ++# CONFIG_SQUASHFS_XATTR is not set ++CONFIG_SQUASHFS_ZLIB=y ++# CONFIG_SQUASHFS_LZO is not set ++# CONFIG_SQUASHFS_XZ is not set ++# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set ++# CONFIG_SQUASHFS_EMBEDDED is not set ++CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_NETWORK_FILESYSTEMS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++ ++# ++# Kernel hacking ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_FRAME_WARN=1024 ++# CONFIG_MAGIC_SYSRQ is not set ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++CONFIG_DEBUG_KERNEL=y ++# CONFIG_DEBUG_SHIRQ is not set ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set ++# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set ++# CONFIG_HARDLOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_TIMER_STATS is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_STATS is not set ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_RT_MUTEX_TESTER is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_STACKTRACE is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_KOBJECT is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_DEBUG_VM is not set ++# CONFIG_DEBUG_WRITECOUNT is not set ++# CONFIG_DEBUG_MEMORY_INIT is not set ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++CONFIG_FRAME_POINTER=y ++# CONFIG_BOOT_PRINTK_DELAY is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++# CONFIG_DEBUG_PAGEALLOC is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_STRICT_DEVMEM is not set ++CONFIG_DEBUG_USER=y ++# CONFIG_DEBUG_RODATA is not set ++# CONFIG_DEBUG_LL is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_HMAC is not set ++ ++# ++# Digest ++# ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++# CONFIG_CRYPTO_HW is not set ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++CONFIG_ZLIB_INFLATE=y ++# CONFIG_XZ_DEC is not set ++# CONFIG_XZ_DEC_BCJ is not set ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_GENERIC_ATOMIC64=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set +diff --git a/arch/arm/configs/aimer39_ak3916_ram_defconfig b/arch/arm/configs/aimer39_ak3916_ram_defconfig +new file mode 100644 +index 00000000..dc6af2ad +--- /dev/null ++++ b/arch/arm/configs/aimer39_ak3916_ram_defconfig +@@ -0,0 +1,1966 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.4.35 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_GENERIC_GPIO=y ++CONFIG_ARCH_USES_GETTIMEOFFSET=y ++CONFIG_KTIME_SCALAR=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++CONFIG_ARCH_HAS_CPUFREQ=y ++CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_VECTORS_BASE=0xffff0000 ++# CONFIG_ARM_PATCH_PHYS_VIRT is not set ++CONFIG_PHYS_OFFSET=0x81000000 ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_HAVE_IRQ_WORK=y ++ ++# ++# General setup ++# ++# CONFIG_EXPERIMENTAL is not set ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_FHANDLE is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_GENERIC_HARDIRQS=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_SHOW=y ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=16 ++# CONFIG_CGROUPS is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_NAMESPACES is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="../ipcamera/rootfs/rootfs.initramfs" ++CONFIG_INITRAMFS_ROOT_UID=0 ++CONFIG_INITRAMFS_ROOT_GID=0 ++CONFIG_RD_GZIP=y ++# CONFIG_RD_BZIP2 is not set ++# CONFIG_RD_LZMA is not set ++# CONFIG_RD_XZ is not set ++# CONFIG_RD_LZO is not set ++CONFIG_INITRAMFS_COMPRESSION_NONE=y ++# CONFIG_INITRAMFS_COMPRESSION_GZIP is not set ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_EXPERT=y ++CONFIG_UID16=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_SHMEM is not set ++CONFIG_AIO=y ++CONFIG_EMBEDDED=y ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++# CONFIG_PERF_COUNTERS is not set ++# CONFIG_VM_EVENT_COUNTERS is not set ++# CONFIG_SLUB_DEBUG is not set ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++CONFIG_JUMP_LABEL=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_FORCE_LOAD=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++CONFIG_BLOCK=y ++# CONFIG_LBDAF is not set ++# CONFIG_BLK_DEV_BSG is not set ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++ ++# ++# Partition Types ++# ++# CONFIG_PARTITION_ADVANCED is not set ++CONFIG_MSDOS_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++# CONFIG_IOSCHED_DEADLINE is not set ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_CFQ=y ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="cfq" ++# CONFIG_INLINE_SPIN_TRYLOCK is not set ++# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK is not set ++# CONFIG_INLINE_SPIN_LOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQ is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set ++# CONFIG_INLINE_SPIN_UNLOCK_BH is not set ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_READ_TRYLOCK is not set ++# CONFIG_INLINE_READ_LOCK is not set ++# CONFIG_INLINE_READ_LOCK_BH is not set ++# CONFIG_INLINE_READ_LOCK_IRQ is not set ++# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set ++CONFIG_INLINE_READ_UNLOCK=y ++# CONFIG_INLINE_READ_UNLOCK_BH is not set ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_WRITE_TRYLOCK is not set ++# CONFIG_INLINE_WRITE_LOCK is not set ++# CONFIG_INLINE_WRITE_LOCK_BH is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQ is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set ++CONFIG_INLINE_WRITE_UNLOCK=y ++# CONFIG_INLINE_WRITE_UNLOCK_BH is not set ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set ++# CONFIG_MUTEX_SPIN_ON_OWNER is not set ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_BCMRING is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_CNS3XXX is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_PRIMA2 is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MXS is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_H720X is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP23XX is not set ++# CONFIG_ARCH_IXP2000 is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_KIRKWOOD is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_PICOXCELL is not set ++# CONFIG_ARCH_PNX4008 is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_S5P64X0 is not set ++# CONFIG_ARCH_S5PC100 is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHARK is not set ++# CONFIG_ARCH_U300 is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_NOMADIK is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_VT8500 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARCH_AK39=y ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++ ++# ++# System MMU ++# ++CONFIG_CPU_AK3916=y ++# CONFIG_ARCH_SDK3910 is not set ++CONFIG_ARCH_AIMER39_AK3916=y ++# CONFIG_ARCH_AIMER39_AK3918 is not set ++CONFIG_ASIC_FREQ_VALUE=90000000 ++ ++# ++# Power management ++# ++# CONFIG_AK39_PM is not set ++# CONFIG_AK39_PWM_TIMER is not set ++CONFIG_PLAT_ANYKA=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_ARM926T=y ++CONFIG_CPU_32v5=y ++CONFIG_CPU_ABRT_EV5TJ=y ++CONFIG_CPU_PABRT_LEGACY=y ++CONFIG_CPU_CACHE_VIVT=y ++CONFIG_CPU_COPY_V4WB=y ++CONFIG_CPU_TLB_V4WBI=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++CONFIG_CPU_USE_DOMAINS=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++# CONFIG_ARM_THUMB is not set ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set ++# CONFIG_CPU_CACHE_ROUND_ROBIN is not set ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT=5 ++CONFIG_ARM_NR_BANKS=8 ++# CONFIG_FIQ_DEBUGGER is not set ++ ++# ++# Bus support ++# ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_PCCARD=y ++CONFIG_PCMCIA=y ++ ++# ++# PC-card bridges ++# ++ ++# ++# Kernel Features ++# ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ=100 ++CONFIG_AEABI=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++# CONFIG_HIGHMEM is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=999999 ++# CONFIG_COMPACTION is not set ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_SECCOMP is not set ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y ++ ++# ++# Boot options ++# ++# CONFIG_USE_OF is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_CMDLINE="mem=64M console=ttySAK0,115200" ++CONFIG_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_CMDLINE_EXTEND is not set ++# CONFIG_CMDLINE_FORCE is not set ++# CONFIG_XIP_KERNEL is not set ++# CONFIG_AUTO_ZRELADDR is not set ++CONFIG_RAM_BASE=0x80000000 ++CONFIG_VIDEO_RESERVED_MEM_SIZE=0x1000000 ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++# CONFIG_CPU_FREQ is not set ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++# CONFIG_VFP is not set ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_HAVE_AOUT=y ++# CONFIG_BINFMT_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_HAS_WAKELOCK=y ++CONFIG_WAKELOCK=y ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_CLK=y ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_UNIX_DIAG=y ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++CONFIG_IP_PNP_RARP=y ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_IP_MROUTE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++CONFIG_INET_UDP_DIAG=y ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_IPV6 is not set ++CONFIG_ANDROID_PARANOID_NETWORK=y ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_IP_NF_QUEUE is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_PHONET is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++CONFIG_BQL=y ++CONFIG_HAVE_BPF_JIT=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++CONFIG_WIRELESS=y ++CONFIG_WIRELESS_EXT=y ++CONFIG_WEXT_CORE=y ++CONFIG_WEXT_PROC=y ++CONFIG_WEXT_SPY=y ++CONFIG_WEXT_PRIV=y ++CONFIG_CFG80211=y ++# CONFIG_NL80211_TESTMODE is not set ++# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set ++# CONFIG_CFG80211_REG_DEBUG is not set ++CONFIG_CFG80211_DEFAULT_PS=y ++# CONFIG_CFG80211_INTERNAL_REGDB is not set ++CONFIG_CFG80211_WEXT=y ++CONFIG_WIRELESS_EXT_SYSFS=y ++CONFIG_LIB80211=y ++# CONFIG_LIB80211_DEBUG is not set ++CONFIG_CFG80211_ALLOW_RECONNECT=y ++CONFIG_MAC80211=y ++CONFIG_MAC80211_HAS_RC=y ++# CONFIG_MAC80211_RC_PID is not set ++CONFIG_MAC80211_RC_MINSTREL=y ++CONFIG_MAC80211_RC_MINSTREL_HT=y ++CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y ++CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" ++# CONFIG_MAC80211_LEDS is not set ++# CONFIG_MAC80211_DEBUG_MENU is not set ++# CONFIG_WIMAX is not set ++CONFIG_RFKILL=y ++CONFIG_RFKILL_PM=y ++CONFIG_RFKILL_LEDS=y ++# CONFIG_RFKILL_INPUT is not set ++# CONFIG_RFKILL_GPIO is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_SYNC is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++# CONFIG_MTD_AFS_PARTS is not set ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_AK_SPIFLASH=y ++# CONFIG_MTD_NAND_IDS is not set ++# CONFIG_MTD_NAND is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR flash memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++ ++# ++# DRBD disabled because PROC_FS, INET or CONNECTOR not selected ++# ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_ATMEL_PWM is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_SENSORS_AK8975 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085 is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_WL127X_RFKILL is not set ++CONFIG_AK_SERIAL_NUMBER=y ++# CONFIG_AK_MOTOR is not set ++ ++# ++# user space generic gpio controller ++# ++# CONFIG_GPIOS_AKCUSTOM is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++CONFIG_HAVE_IDE=y ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_PROC_FS is not set ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++# CONFIG_SCSI_WAIT_SCAN is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_MII is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_ARCNET is not set ++ ++# ++# CAIF transport drivers ++# ++CONFIG_ETHERNET=y ++# CONFIG_NET_VENDOR_3COM is not set ++# CONFIG_NET_VENDOR_AMD is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_CALXEDA_XGMAC is not set ++# CONFIG_NET_VENDOR_CHELSIO is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++# CONFIG_NET_VENDOR_FUJITSU is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_XIRCOM is not set ++CONFIG_AK_ETHERNET=y ++# CONFIG_PHYLIB is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++# CONFIG_TR is not set ++ ++# ++# USB Network Adapters ++# ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_HSO is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++CONFIG_PCMCIA_RAYCS=y ++# CONFIG_LIBERTAS_THINFIRM is not set ++# CONFIG_ATMEL is not set ++# CONFIG_AT76C50X_USB is not set ++# CONFIG_AIRO_CS is not set ++# CONFIG_USB_ZD1201 is not set ++# CONFIG_RTL8187 is not set ++# CONFIG_MAC80211_HWSIM is not set ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_ATH_COMMON is not set ++# CONFIG_B43 is not set ++# CONFIG_B43LEGACY is not set ++# CONFIG_BCMDHD is not set ++# CONFIG_BRCMFMAC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_LIBERTAS is not set ++# CONFIG_HERMES is not set ++# CONFIG_RT2X00 is not set ++# CONFIG_MWIFIEX is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++# CONFIG_KEYBOARD_ATKBD is not set ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8323 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++CONFIG_KEYBOARD_AKKEY=y ++# CONFIG_KEYBOARD_AK_KEYPAD is not set ++CONFIG_KEYBOARD_ADKEY=y ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++# CONFIG_VT is not set ++# CONFIG_UNIX98_PTYS is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++# CONFIG_DEVKMEM is not set ++ ++# ++# Serial drivers ++# ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX3107 is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_TIMBERDALE is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++CONFIG_SERIAL_AK39_UART=y ++CONFIG_SERIAL_AK39_CONSOLE=y ++# CONFIG_SERIAL_GPIO_UART is not set ++CONFIG_TTY_PRINTK=y ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++ ++# ++# PCMCIA character devices ++# ++# CONFIG_SYNCLINK_CS is not set ++# CONFIG_CARDMAN_4000 is not set ++# CONFIG_CARDMAN_4040 is not set ++# CONFIG_IPWIRELESS is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_RAMOOPS is not set ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++# CONFIG_I2C_CHARDEV is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++CONFIG_I2C_SMBUS=y ++ ++# ++# I2C Algorithms ++# ++CONFIG_I2C_ALGOBIT=y ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_SIMTEC is not set ++CONFIG_I2C_ANYKA=y ++CONFIG_I2C_AK39_HW=y ++# CONFIG_I2C_GPIO_SOFT is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++CONFIG_SPI_BITBANG=y ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_OC_TINY is not set ++# CONFIG_SPI_PXA2XX_PCI is not set ++CONFIG_SPI_ANYKA=y ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++ ++# ++# Enable Device Drivers -> PPS to see the PTP clock options. ++# ++CONFIG_ARCH_REQUIRE_GPIOLIB=y ++CONFIG_GPIOLIB=y ++# CONFIG_DEBUG_GPIO is not set ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_BATTERY_ANDROID is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_BATTERY_AK=y ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++CONFIG_WATCHDOG=y ++CONFIG_WATCHDOG_CORE=y ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++ ++# ++# Watchdog Device Drivers ++# ++# CONFIG_SOFT_WATCHDOG is not set ++# CONFIG_DW_WATCHDOG is not set ++# CONFIG_MAX63XX_WATCHDOG is not set ++CONFIG_AK39_WATCHDOG=y ++ ++# ++# USB-based Watchdog Cards ++# ++# CONFIG_USBPCWATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_CORE is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_STMPE is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_S5M_CORE is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_MC13XXX is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_REGULATOR is not set ++CONFIG_MEDIA_SUPPORT=y ++ ++# ++# Multimedia core support ++# ++CONFIG_VIDEO_DEV=y ++CONFIG_VIDEO_V4L2_COMMON=y ++# CONFIG_DVB_CORE is not set ++CONFIG_VIDEO_MEDIA=y ++ ++# ++# Multimedia drivers ++# ++# CONFIG_RC_CORE is not set ++# CONFIG_MEDIA_ATTACH is not set ++CONFIG_MEDIA_TUNER=y ++# CONFIG_MEDIA_TUNER_CUSTOMISE is not set ++CONFIG_MEDIA_TUNER_SIMPLE=y ++CONFIG_MEDIA_TUNER_TDA8290=y ++CONFIG_MEDIA_TUNER_TDA827X=y ++CONFIG_MEDIA_TUNER_TDA18271=y ++CONFIG_MEDIA_TUNER_TDA9887=y ++CONFIG_MEDIA_TUNER_TEA5767=y ++CONFIG_MEDIA_TUNER_MT20XX=y ++CONFIG_MEDIA_TUNER_XC2028=y ++CONFIG_MEDIA_TUNER_XC5000=y ++CONFIG_MEDIA_TUNER_XC4000=y ++CONFIG_MEDIA_TUNER_MC44S803=y ++CONFIG_VIDEO_V4L2=y ++CONFIG_VIDEOBUF_GEN=y ++CONFIG_VIDEOBUF_DMA_CONTIG=y ++CONFIG_VIDEOBUF2_CORE=y ++CONFIG_VIDEO_CAPTURE_DRIVERS=y ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set ++ ++# ++# Encoders, decoders, sensors and other helper chips ++# ++ ++# ++# Audio decoders, processors and mixers ++# ++# CONFIG_VIDEO_TVAUDIO is not set ++# CONFIG_VIDEO_TDA7432 is not set ++# CONFIG_VIDEO_TDA9840 is not set ++# CONFIG_VIDEO_TEA6415C is not set ++# CONFIG_VIDEO_TEA6420 is not set ++# CONFIG_VIDEO_MSP3400 is not set ++# CONFIG_VIDEO_CS5345 is not set ++# CONFIG_VIDEO_CS53L32A is not set ++# CONFIG_VIDEO_WM8775 is not set ++# CONFIG_VIDEO_WM8739 is not set ++# CONFIG_VIDEO_VP27SMPX is not set ++ ++# ++# RDS decoders ++# ++# CONFIG_VIDEO_SAA6588 is not set ++ ++# ++# Video decoders ++# ++# CONFIG_VIDEO_ADV7180 is not set ++# CONFIG_VIDEO_ADV7183 is not set ++# CONFIG_VIDEO_BT819 is not set ++# CONFIG_VIDEO_BT856 is not set ++# CONFIG_VIDEO_BT866 is not set ++# CONFIG_VIDEO_KS0127 is not set ++# CONFIG_VIDEO_SAA7110 is not set ++# CONFIG_VIDEO_SAA711X is not set ++# CONFIG_VIDEO_SAA7191 is not set ++# CONFIG_VIDEO_TVP514X is not set ++# CONFIG_VIDEO_TVP5150 is not set ++# CONFIG_VIDEO_TVP7002 is not set ++# CONFIG_VIDEO_VPX3220 is not set ++ ++# ++# Video and audio decoders ++# ++# CONFIG_VIDEO_SAA717X is not set ++# CONFIG_VIDEO_CX25840 is not set ++ ++# ++# MPEG video encoders ++# ++# CONFIG_VIDEO_CX2341X is not set ++ ++# ++# Video encoders ++# ++# CONFIG_VIDEO_SAA7127 is not set ++# CONFIG_VIDEO_SAA7185 is not set ++# CONFIG_VIDEO_ADV7170 is not set ++# CONFIG_VIDEO_ADV7175 is not set ++# CONFIG_VIDEO_ADV7343 is not set ++# CONFIG_VIDEO_AK881X is not set ++ ++# ++# Camera sensor devices ++# ++# CONFIG_VIDEO_OV7670 is not set ++# CONFIG_VIDEO_VS6624 is not set ++# CONFIG_VIDEO_MT9V011 is not set ++# CONFIG_VIDEO_TCM825X is not set ++# CONFIG_VIDEO_SR030PC30 is not set ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++# CONFIG_VIDEO_UPD64031A is not set ++# CONFIG_VIDEO_UPD64083 is not set ++ ++# ++# Miscelaneous helper chips ++# ++# CONFIG_VIDEO_THS7303 is not set ++# CONFIG_VIDEO_M52790 is not set ++# CONFIG_V4L_USB_DRIVERS is not set ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_SOC_CAMERA=y ++# CONFIG_SOC_CAMERA_IMX074 is not set ++# CONFIG_SOC_CAMERA_MT9M001 is not set ++# CONFIG_SOC_CAMERA_MT9M111 is not set ++# CONFIG_SOC_CAMERA_MT9T031 is not set ++# CONFIG_SOC_CAMERA_MT9T112 is not set ++# CONFIG_SOC_CAMERA_MT9V022 is not set ++# CONFIG_SOC_CAMERA_RJ54N1 is not set ++# CONFIG_SOC_CAMERA_TW9910 is not set ++# CONFIG_SOC_CAMERA_PLATFORM is not set ++# CONFIG_SOC_CAMERA_OV2640 is not set ++# CONFIG_SOC_CAMERA_OV5642 is not set ++# CONFIG_SOC_CAMERA_OV6650 is not set ++# CONFIG_SOC_CAMERA_OV772X is not set ++# CONFIG_SOC_CAMERA_OV9640 is not set ++# CONFIG_SOC_CAMERA_OV9740 is not set ++CONFIG_LINUX_AKSENSOR=y ++CONFIG_SENSOR_GC0308=y ++CONFIG_SENSOR_OV2643=y ++CONFIG_SENSOR_OV7725=y ++CONFIG_SENSOR_HM1375=y ++CONFIG_SENSOR_OV9712=y ++CONFIG_SENSOR_OV2710=y ++# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set ++# CONFIG_VIDEO_SH_MOBILE_CEU is not set ++CONFIG_VIDEO_AK=y ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_RADIO_ADAPTERS is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DRM is not set ++CONFIG_ION=y ++CONFIG_ION_AK=y ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++# CONFIG_FB is not set ++# CONFIG_EXYNOS_VIDEO is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++CONFIG_SOUND=y ++# CONFIG_SOUND_OSS_CORE is not set ++CONFIG_SND=y ++CONFIG_SND_TIMER=y ++CONFIG_SND_PCM=y ++# CONFIG_SND_SEQUENCER is not set ++# CONFIG_SND_MIXER_OSS is not set ++# CONFIG_SND_PCM_OSS is not set ++# CONFIG_SND_DYNAMIC_MINORS is not set ++# CONFIG_SND_SUPPORT_OLD_API is not set ++# CONFIG_SND_VERBOSE_PROCFS is not set ++# CONFIG_SND_VERBOSE_PRINTK is not set ++# CONFIG_SND_DEBUG is not set ++# CONFIG_SND_RAWMIDI_SEQ is not set ++# CONFIG_SND_OPL3_LIB_SEQ is not set ++# CONFIG_SND_OPL4_LIB_SEQ is not set ++# CONFIG_SND_SBAWE_SEQ is not set ++# CONFIG_SND_EMU10K1_SEQ is not set ++# CONFIG_SND_DRIVERS is not set ++CONFIG_SND_ARM=y ++CONFIG_SND_AK_PCM=y ++CONFIG_CODEC_AK39=y ++CONFIG_SPKHP_SWITCH_AUTO=y ++# CONFIG_SPKHP_SWITCH_MIXER is not set ++# CONFIG_SPKHP_SWITCH_UEVENT is not set ++# CONFIG_SUPPORT_AEC is not set ++# CONFIG_SND_SPI is not set ++# CONFIG_SND_USB is not set ++CONFIG_SND_PCMCIA=y ++# CONFIG_SND_VXPOCKET is not set ++# CONFIG_SND_PDAUDIOCF is not set ++# CONFIG_SND_SOC is not set ++# CONFIG_SOUND_PRIME is not set ++# CONFIG_HID_SUPPORT is not set ++# CONFIG_USB_ARCH_HAS_OHCI is not set ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++# CONFIG_USB_ARCH_HAS_XHCI is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEVICEFS is not set ++# CONFIG_USB_DEVICE_CLASS is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++CONFIG_USB_ANYKA_HCD=y ++CONFIG_USB_AKOTG_HS_HCD=m ++# CONFIG_USB_AKOTG_DMA is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_RENESAS_USBHS is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_LIBUSUAL is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=500 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_MV_UDC is not set ++# CONFIG_USB_GADGET_AKUDC_PRODUCER is not set ++CONFIG_USB_GADGET_AKUDC=y ++CONFIG_USB_AKUDC=m ++# CONFIG_USB_AKUDC_DEBUG_FS is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_GADGET_DUALSPEED=y ++# CONFIG_USB_ZERO is not set ++# CONFIG_USB_AUDIO is not set ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_FILE_STORAGE is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++ ++# ++# OTG and related infrastructure ++# ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ULPI is not set ++# CONFIG_NOP_USB_XCEIV is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++# CONFIG_MMC_BLOCK_BOUNCE is not set ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++# CONFIG_SDIO_WIFI is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_SDHCI_PXAV3 is not set ++# CONFIG_MMC_SDHCI_PXAV2 is not set ++# CONFIG_MMC_SPI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++CONFIG_MMC_ANYKA=y ++# CONFIG_MEMSTICK is not set ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++ ++# ++# LED drivers ++# ++# CONFIG_LEDS_LM3530 is not set ++# CONFIG_LEDS_GPIO is not set ++# CONFIG_LEDS_LP3944 is not set ++# CONFIG_LEDS_LP5521 is not set ++# CONFIG_LEDS_LP5523 is not set ++# CONFIG_LEDS_PCA955X is not set ++# CONFIG_LEDS_PCA9633 is not set ++# CONFIG_LEDS_DAC124S085 is not set ++# CONFIG_LEDS_BD2802 is not set ++CONFIG_LEDS_AK39=y ++# CONFIG_LEDS_LT3593 is not set ++# CONFIG_LEDS_RENESAS_TPU is not set ++# CONFIG_LEDS_TCA6507 is not set ++# CONFIG_LEDS_OT200 is not set ++CONFIG_LEDS_TRIGGERS=y ++ ++# ++# LED Triggers ++# ++CONFIG_LEDS_TRIGGER_TIMER=y ++# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set ++# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set ++# CONFIG_LEDS_TRIGGER_GPIO is not set ++# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set ++ ++# ++# iptables trigger is under Netfilter config (LED target) ++# ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++CONFIG_RTC_LIB=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_HCTOSYS=y ++CONFIG_RTC_HCTOSYS_DEVICE="rtc0" ++# CONFIG_RTC_DEBUG is not set ++ ++# ++# RTC interfaces ++# ++CONFIG_RTC_INTF_SYSFS=y ++CONFIG_RTC_INTF_PROC=y ++CONFIG_RTC_INTF_DEV=y ++# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set ++# CONFIG_RTC_DRV_TEST is not set ++ ++# ++# I2C RTC drivers ++# ++# CONFIG_RTC_DRV_DS1307 is not set ++# CONFIG_RTC_DRV_DS1374 is not set ++# CONFIG_RTC_DRV_DS1672 is not set ++# CONFIG_RTC_DRV_DS3232 is not set ++# CONFIG_RTC_DRV_MAX6900 is not set ++# CONFIG_RTC_DRV_RS5C372 is not set ++# CONFIG_RTC_DRV_ISL1208 is not set ++# CONFIG_RTC_DRV_ISL12022 is not set ++# CONFIG_RTC_DRV_X1205 is not set ++# CONFIG_RTC_DRV_PCF8563 is not set ++# CONFIG_RTC_DRV_PCF8583 is not set ++# CONFIG_RTC_DRV_M41T80 is not set ++# CONFIG_RTC_DRV_BQ32K is not set ++# CONFIG_RTC_DRV_S35390A is not set ++# CONFIG_RTC_DRV_FM3130 is not set ++# CONFIG_RTC_DRV_RX8581 is not set ++# CONFIG_RTC_DRV_RX8025 is not set ++# CONFIG_RTC_DRV_EM3027 is not set ++# CONFIG_RTC_DRV_RV3029C2 is not set ++ ++# ++# SPI RTC drivers ++# ++# CONFIG_RTC_DRV_M41T93 is not set ++# CONFIG_RTC_DRV_M41T94 is not set ++# CONFIG_RTC_DRV_DS1305 is not set ++# CONFIG_RTC_DRV_DS1390 is not set ++# CONFIG_RTC_DRV_MAX6902 is not set ++# CONFIG_RTC_DRV_R9701 is not set ++# CONFIG_RTC_DRV_RS5C348 is not set ++# CONFIG_RTC_DRV_DS3234 is not set ++# CONFIG_RTC_DRV_PCF2123 is not set ++ ++# ++# Platform RTC drivers ++# ++# CONFIG_RTC_DRV_CMOS is not set ++# CONFIG_RTC_DRV_DS1286 is not set ++# CONFIG_RTC_DRV_DS1511 is not set ++# CONFIG_RTC_DRV_DS1553 is not set ++# CONFIG_RTC_DRV_DS1742 is not set ++# CONFIG_RTC_DRV_STK17TA8 is not set ++# CONFIG_RTC_DRV_M48T86 is not set ++# CONFIG_RTC_DRV_M48T35 is not set ++# CONFIG_RTC_DRV_M48T59 is not set ++# CONFIG_RTC_DRV_MSM6242 is not set ++# CONFIG_RTC_DRV_BQ4802 is not set ++# CONFIG_RTC_DRV_RP5C01 is not set ++# CONFIG_RTC_DRV_V3020 is not set ++ ++# ++# on-CPU RTC drivers ++# ++CONFIG_RTC_DRV_AK=y ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++CONFIG_UIO=y ++# CONFIG_UIO_PDRV is not set ++# CONFIG_UIO_PDRV_GENIRQ is not set ++CONFIG_UIO_VCODEC=y ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_BALLOON is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++CONFIG_CLKDEV_LOOKUP=y ++ ++# ++# Hardware Spinlock drivers ++# ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers (EXPERIMENTAL) ++# ++ ++# ++# Rpmsg drivers (EXPERIMENTAL) ++# ++# CONFIG_VIRT_DRIVERS is not set ++# CONFIG_PM_DEVFREQ is not set ++ ++# ++# File systems ++# ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_FS_POSIX_ACL is not set ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_SYSFS=y ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_YAFFS_FS is not set ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++# CONFIG_JFFS2_FS_WRITEBUFFER is not set ++CONFIG_JFFS2_COMPRESSION_OPTIONS=y ++# CONFIG_JFFS2_ZLIB is not set ++# CONFIG_JFFS2_LZO is not set ++# CONFIG_JFFS2_RTIME is not set ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_JFFS2_CMODE_NONE=y ++# CONFIG_JFFS2_CMODE_PRIORITY is not set ++# CONFIG_JFFS2_CMODE_SIZE is not set ++# CONFIG_JFFS2_CMODE_FAVOURLZO is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_NETWORK_FILESYSTEMS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++ ++# ++# Kernel hacking ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_FRAME_WARN=1024 ++# CONFIG_MAGIC_SYSRQ is not set ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++CONFIG_DEBUG_KERNEL=y ++# CONFIG_DEBUG_SHIRQ is not set ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set ++# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set ++# CONFIG_HARDLOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_TIMER_STATS is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_STATS is not set ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_RT_MUTEX_TESTER is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_STACKTRACE is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_KOBJECT is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_DEBUG_VM is not set ++# CONFIG_DEBUG_WRITECOUNT is not set ++# CONFIG_DEBUG_MEMORY_INIT is not set ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++CONFIG_FRAME_POINTER=y ++# CONFIG_BOOT_PRINTK_DELAY is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++# CONFIG_DEBUG_PAGEALLOC is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_STRICT_DEVMEM is not set ++CONFIG_DEBUG_USER=y ++# CONFIG_DEBUG_RODATA is not set ++# CONFIG_DEBUG_LL is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_HMAC is not set ++ ++# ++# Digest ++# ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++# CONFIG_CRYPTO_HW is not set ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++CONFIG_ZLIB_INFLATE=y ++# CONFIG_XZ_DEC is not set ++# CONFIG_XZ_DEC_BCJ is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_GENERIC_ATOMIC64=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set +diff --git a/arch/arm/configs/aimer39_ak3918_defconfig b/arch/arm/configs/aimer39_ak3918_defconfig +new file mode 100644 +index 00000000..c767162a +--- /dev/null ++++ b/arch/arm/configs/aimer39_ak3918_defconfig +@@ -0,0 +1,1962 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.4.35 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_GENERIC_GPIO=y ++CONFIG_ARCH_USES_GETTIMEOFFSET=y ++CONFIG_KTIME_SCALAR=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++CONFIG_ARCH_HAS_CPUFREQ=y ++CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_VECTORS_BASE=0xffff0000 ++# CONFIG_ARM_PATCH_PHYS_VIRT is not set ++CONFIG_PHYS_OFFSET=0x81000000 ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_HAVE_IRQ_WORK=y ++ ++# ++# General setup ++# ++# CONFIG_EXPERIMENTAL is not set ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++# CONFIG_KERNEL_GZIP is not set ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++CONFIG_KERNEL_LZO=y ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_FHANDLE is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_GENERIC_HARDIRQS=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_SHOW=y ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=16 ++# CONFIG_CGROUPS is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_NAMESPACES is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++# CONFIG_BLK_DEV_INITRD is not set ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_EXPERT=y ++CONFIG_UID16=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_SHMEM is not set ++CONFIG_AIO=y ++CONFIG_EMBEDDED=y ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++# CONFIG_PERF_COUNTERS is not set ++# CONFIG_VM_EVENT_COUNTERS is not set ++# CONFIG_SLUB_DEBUG is not set ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++CONFIG_JUMP_LABEL=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_FORCE_LOAD=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++CONFIG_BLOCK=y ++# CONFIG_LBDAF is not set ++# CONFIG_BLK_DEV_BSG is not set ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++ ++# ++# Partition Types ++# ++# CONFIG_PARTITION_ADVANCED is not set ++CONFIG_MSDOS_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++# CONFIG_IOSCHED_DEADLINE is not set ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_CFQ=y ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="cfq" ++# CONFIG_INLINE_SPIN_TRYLOCK is not set ++# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK is not set ++# CONFIG_INLINE_SPIN_LOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQ is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set ++# CONFIG_INLINE_SPIN_UNLOCK_BH is not set ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_READ_TRYLOCK is not set ++# CONFIG_INLINE_READ_LOCK is not set ++# CONFIG_INLINE_READ_LOCK_BH is not set ++# CONFIG_INLINE_READ_LOCK_IRQ is not set ++# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set ++CONFIG_INLINE_READ_UNLOCK=y ++# CONFIG_INLINE_READ_UNLOCK_BH is not set ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_WRITE_TRYLOCK is not set ++# CONFIG_INLINE_WRITE_LOCK is not set ++# CONFIG_INLINE_WRITE_LOCK_BH is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQ is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set ++CONFIG_INLINE_WRITE_UNLOCK=y ++# CONFIG_INLINE_WRITE_UNLOCK_BH is not set ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set ++# CONFIG_MUTEX_SPIN_ON_OWNER is not set ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_BCMRING is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_CNS3XXX is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_PRIMA2 is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MXS is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_H720X is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP23XX is not set ++# CONFIG_ARCH_IXP2000 is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_KIRKWOOD is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_PICOXCELL is not set ++# CONFIG_ARCH_PNX4008 is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_S5P64X0 is not set ++# CONFIG_ARCH_S5PC100 is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHARK is not set ++# CONFIG_ARCH_U300 is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_NOMADIK is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_VT8500 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARCH_AK39=y ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++ ++# ++# System MMU ++# ++CONFIG_CPU_AK3918=y ++# CONFIG_ARCH_SDK3910 is not set ++# CONFIG_ARCH_AIMER39_AK3916 is not set ++CONFIG_ARCH_AIMER39_AK3918=y ++CONFIG_ASIC_FREQ_VALUE=90000000 ++ ++# ++# Power management ++# ++# CONFIG_AK39_PM is not set ++# CONFIG_AK39_PWM_TIMER is not set ++CONFIG_PLAT_ANYKA=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_ARM926T=y ++CONFIG_CPU_32v5=y ++CONFIG_CPU_ABRT_EV5TJ=y ++CONFIG_CPU_PABRT_LEGACY=y ++CONFIG_CPU_CACHE_VIVT=y ++CONFIG_CPU_COPY_V4WB=y ++CONFIG_CPU_TLB_V4WBI=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++CONFIG_CPU_USE_DOMAINS=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++# CONFIG_ARM_THUMB is not set ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set ++# CONFIG_CPU_CACHE_ROUND_ROBIN is not set ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT=5 ++CONFIG_ARM_NR_BANKS=8 ++# CONFIG_FIQ_DEBUGGER is not set ++ ++# ++# Bus support ++# ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_PCCARD=y ++CONFIG_PCMCIA=y ++ ++# ++# PC-card bridges ++# ++ ++# ++# Kernel Features ++# ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ=100 ++CONFIG_AEABI=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++# CONFIG_HIGHMEM is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=999999 ++# CONFIG_COMPACTION is not set ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_SECCOMP is not set ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y ++ ++# ++# Boot options ++# ++# CONFIG_USE_OF is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_CMDLINE="root=/dev/mtdblock1 ro init=/sbin/init mem=64M console=ttySAK0,115200" ++CONFIG_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_CMDLINE_EXTEND is not set ++# CONFIG_CMDLINE_FORCE is not set ++# CONFIG_XIP_KERNEL is not set ++# CONFIG_AUTO_ZRELADDR is not set ++CONFIG_RAM_BASE=0x80000000 ++CONFIG_VIDEO_RESERVED_MEM_SIZE=0x1000000 ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++# CONFIG_CPU_FREQ is not set ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++# CONFIG_VFP is not set ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_HAVE_AOUT=y ++# CONFIG_BINFMT_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_HAS_WAKELOCK=y ++CONFIG_WAKELOCK=y ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_CLK=y ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_UNIX_DIAG=y ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++CONFIG_IP_PNP_RARP=y ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_IP_MROUTE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++CONFIG_INET_UDP_DIAG=y ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_IPV6 is not set ++CONFIG_ANDROID_PARANOID_NETWORK=y ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_IP_NF_QUEUE is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_PHONET is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++CONFIG_BQL=y ++CONFIG_HAVE_BPF_JIT=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++CONFIG_WIRELESS=y ++CONFIG_WIRELESS_EXT=y ++CONFIG_WEXT_CORE=y ++CONFIG_WEXT_PROC=y ++CONFIG_WEXT_SPY=y ++CONFIG_WEXT_PRIV=y ++CONFIG_CFG80211=y ++# CONFIG_NL80211_TESTMODE is not set ++# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set ++# CONFIG_CFG80211_REG_DEBUG is not set ++CONFIG_CFG80211_DEFAULT_PS=y ++# CONFIG_CFG80211_INTERNAL_REGDB is not set ++CONFIG_CFG80211_WEXT=y ++CONFIG_WIRELESS_EXT_SYSFS=y ++CONFIG_LIB80211=y ++# CONFIG_LIB80211_DEBUG is not set ++CONFIG_CFG80211_ALLOW_RECONNECT=y ++CONFIG_MAC80211=y ++CONFIG_MAC80211_HAS_RC=y ++# CONFIG_MAC80211_RC_PID is not set ++CONFIG_MAC80211_RC_MINSTREL=y ++CONFIG_MAC80211_RC_MINSTREL_HT=y ++CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y ++CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" ++# CONFIG_MAC80211_LEDS is not set ++# CONFIG_MAC80211_DEBUG_MENU is not set ++# CONFIG_WIMAX is not set ++CONFIG_RFKILL=y ++CONFIG_RFKILL_PM=y ++CONFIG_RFKILL_LEDS=y ++# CONFIG_RFKILL_INPUT is not set ++# CONFIG_RFKILL_GPIO is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_SYNC is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++# CONFIG_MTD_AFS_PARTS is not set ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_AK_SPIFLASH=y ++# CONFIG_MTD_NAND_IDS is not set ++# CONFIG_MTD_NAND is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR flash memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++ ++# ++# DRBD disabled because PROC_FS, INET or CONNECTOR not selected ++# ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_ATMEL_PWM is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_SENSORS_AK8975 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085 is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_WL127X_RFKILL is not set ++CONFIG_AK_SERIAL_NUMBER=y ++CONFIG_AK_MOTOR=y ++ ++# ++# user space generic gpio controller ++# ++CONFIG_GPIOS_AKCUSTOM=y ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++CONFIG_HAVE_IDE=y ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_PROC_FS is not set ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++# CONFIG_SCSI_WAIT_SCAN is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_MII is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_ARCNET is not set ++ ++# ++# CAIF transport drivers ++# ++CONFIG_ETHERNET=y ++# CONFIG_NET_VENDOR_3COM is not set ++# CONFIG_NET_VENDOR_AMD is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_CALXEDA_XGMAC is not set ++# CONFIG_NET_VENDOR_CHELSIO is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++# CONFIG_NET_VENDOR_FUJITSU is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_XIRCOM is not set ++CONFIG_AK_ETHERNET=y ++# CONFIG_PHYLIB is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++# CONFIG_TR is not set ++ ++# ++# USB Network Adapters ++# ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_HSO is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++CONFIG_PCMCIA_RAYCS=y ++# CONFIG_LIBERTAS_THINFIRM is not set ++# CONFIG_ATMEL is not set ++# CONFIG_AT76C50X_USB is not set ++# CONFIG_AIRO_CS is not set ++# CONFIG_USB_ZD1201 is not set ++# CONFIG_RTL8187 is not set ++# CONFIG_MAC80211_HWSIM is not set ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_ATH_COMMON is not set ++# CONFIG_B43 is not set ++# CONFIG_B43LEGACY is not set ++# CONFIG_BCMDHD is not set ++# CONFIG_BRCMFMAC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_LIBERTAS is not set ++# CONFIG_HERMES is not set ++# CONFIG_RT2X00 is not set ++# CONFIG_MWIFIEX is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++# CONFIG_KEYBOARD_ATKBD is not set ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8323 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++CONFIG_KEYBOARD_AKKEY=y ++# CONFIG_KEYBOARD_AK_KEYPAD is not set ++# CONFIG_KEYBOARD_ADKEY is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++# CONFIG_VT is not set ++# CONFIG_UNIX98_PTYS is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++# CONFIG_DEVKMEM is not set ++ ++# ++# Serial drivers ++# ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX3107 is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_TIMBERDALE is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++CONFIG_SERIAL_AK39_UART=y ++CONFIG_SERIAL_AK39_CONSOLE=y ++# CONFIG_SERIAL_GPIO_UART is not set ++CONFIG_TTY_PRINTK=y ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++ ++# ++# PCMCIA character devices ++# ++# CONFIG_SYNCLINK_CS is not set ++# CONFIG_CARDMAN_4000 is not set ++# CONFIG_CARDMAN_4040 is not set ++# CONFIG_IPWIRELESS is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_RAMOOPS is not set ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++# CONFIG_I2C_CHARDEV is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++CONFIG_I2C_SMBUS=y ++ ++# ++# I2C Algorithms ++# ++CONFIG_I2C_ALGOBIT=y ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_SIMTEC is not set ++CONFIG_I2C_ANYKA=y ++CONFIG_I2C_AK39_HW=y ++# CONFIG_I2C_GPIO_SOFT is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++CONFIG_SPI_BITBANG=y ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_OC_TINY is not set ++# CONFIG_SPI_PXA2XX_PCI is not set ++CONFIG_SPI_ANYKA=y ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++ ++# ++# Enable Device Drivers -> PPS to see the PTP clock options. ++# ++CONFIG_ARCH_REQUIRE_GPIOLIB=y ++CONFIG_GPIOLIB=y ++# CONFIG_DEBUG_GPIO is not set ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_BATTERY_ANDROID is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_SMB347 is not set ++# CONFIG_BATTERY_AK is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++CONFIG_WATCHDOG=y ++CONFIG_WATCHDOG_CORE=y ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++ ++# ++# Watchdog Device Drivers ++# ++# CONFIG_SOFT_WATCHDOG is not set ++# CONFIG_DW_WATCHDOG is not set ++# CONFIG_MAX63XX_WATCHDOG is not set ++CONFIG_AK39_WATCHDOG=y ++ ++# ++# USB-based Watchdog Cards ++# ++# CONFIG_USBPCWATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_CORE is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_STMPE is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_S5M_CORE is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_MC13XXX is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_REGULATOR is not set ++CONFIG_MEDIA_SUPPORT=y ++ ++# ++# Multimedia core support ++# ++CONFIG_VIDEO_DEV=y ++CONFIG_VIDEO_V4L2_COMMON=y ++# CONFIG_DVB_CORE is not set ++CONFIG_VIDEO_MEDIA=y ++ ++# ++# Multimedia drivers ++# ++# CONFIG_RC_CORE is not set ++# CONFIG_MEDIA_ATTACH is not set ++CONFIG_MEDIA_TUNER=y ++# CONFIG_MEDIA_TUNER_CUSTOMISE is not set ++CONFIG_MEDIA_TUNER_SIMPLE=y ++CONFIG_MEDIA_TUNER_TDA8290=y ++CONFIG_MEDIA_TUNER_TDA827X=y ++CONFIG_MEDIA_TUNER_TDA18271=y ++CONFIG_MEDIA_TUNER_TDA9887=y ++CONFIG_MEDIA_TUNER_TEA5767=y ++CONFIG_MEDIA_TUNER_MT20XX=y ++CONFIG_MEDIA_TUNER_XC2028=y ++CONFIG_MEDIA_TUNER_XC5000=y ++CONFIG_MEDIA_TUNER_XC4000=y ++CONFIG_MEDIA_TUNER_MC44S803=y ++CONFIG_VIDEO_V4L2=y ++CONFIG_VIDEOBUF_GEN=y ++CONFIG_VIDEOBUF_DMA_CONTIG=y ++CONFIG_VIDEOBUF2_CORE=y ++CONFIG_VIDEO_CAPTURE_DRIVERS=y ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set ++ ++# ++# Encoders, decoders, sensors and other helper chips ++# ++ ++# ++# Audio decoders, processors and mixers ++# ++# CONFIG_VIDEO_TVAUDIO is not set ++# CONFIG_VIDEO_TDA7432 is not set ++# CONFIG_VIDEO_TDA9840 is not set ++# CONFIG_VIDEO_TEA6415C is not set ++# CONFIG_VIDEO_TEA6420 is not set ++# CONFIG_VIDEO_MSP3400 is not set ++# CONFIG_VIDEO_CS5345 is not set ++# CONFIG_VIDEO_CS53L32A is not set ++# CONFIG_VIDEO_WM8775 is not set ++# CONFIG_VIDEO_WM8739 is not set ++# CONFIG_VIDEO_VP27SMPX is not set ++ ++# ++# RDS decoders ++# ++# CONFIG_VIDEO_SAA6588 is not set ++ ++# ++# Video decoders ++# ++# CONFIG_VIDEO_ADV7180 is not set ++# CONFIG_VIDEO_ADV7183 is not set ++# CONFIG_VIDEO_BT819 is not set ++# CONFIG_VIDEO_BT856 is not set ++# CONFIG_VIDEO_BT866 is not set ++# CONFIG_VIDEO_KS0127 is not set ++# CONFIG_VIDEO_SAA7110 is not set ++# CONFIG_VIDEO_SAA711X is not set ++# CONFIG_VIDEO_SAA7191 is not set ++# CONFIG_VIDEO_TVP514X is not set ++# CONFIG_VIDEO_TVP5150 is not set ++# CONFIG_VIDEO_TVP7002 is not set ++# CONFIG_VIDEO_VPX3220 is not set ++ ++# ++# Video and audio decoders ++# ++# CONFIG_VIDEO_SAA717X is not set ++# CONFIG_VIDEO_CX25840 is not set ++ ++# ++# MPEG video encoders ++# ++# CONFIG_VIDEO_CX2341X is not set ++ ++# ++# Video encoders ++# ++# CONFIG_VIDEO_SAA7127 is not set ++# CONFIG_VIDEO_SAA7185 is not set ++# CONFIG_VIDEO_ADV7170 is not set ++# CONFIG_VIDEO_ADV7175 is not set ++# CONFIG_VIDEO_ADV7343 is not set ++# CONFIG_VIDEO_AK881X is not set ++ ++# ++# Camera sensor devices ++# ++# CONFIG_VIDEO_OV7670 is not set ++# CONFIG_VIDEO_VS6624 is not set ++# CONFIG_VIDEO_MT9V011 is not set ++# CONFIG_VIDEO_TCM825X is not set ++# CONFIG_VIDEO_SR030PC30 is not set ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++# CONFIG_VIDEO_UPD64031A is not set ++# CONFIG_VIDEO_UPD64083 is not set ++ ++# ++# Miscelaneous helper chips ++# ++# CONFIG_VIDEO_THS7303 is not set ++# CONFIG_VIDEO_M52790 is not set ++# CONFIG_V4L_USB_DRIVERS is not set ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_SOC_CAMERA=y ++# CONFIG_SOC_CAMERA_IMX074 is not set ++# CONFIG_SOC_CAMERA_MT9M001 is not set ++# CONFIG_SOC_CAMERA_MT9M111 is not set ++# CONFIG_SOC_CAMERA_MT9T031 is not set ++# CONFIG_SOC_CAMERA_MT9T112 is not set ++# CONFIG_SOC_CAMERA_MT9V022 is not set ++# CONFIG_SOC_CAMERA_RJ54N1 is not set ++# CONFIG_SOC_CAMERA_TW9910 is not set ++# CONFIG_SOC_CAMERA_PLATFORM is not set ++# CONFIG_SOC_CAMERA_OV2640 is not set ++# CONFIG_SOC_CAMERA_OV5642 is not set ++# CONFIG_SOC_CAMERA_OV6650 is not set ++# CONFIG_SOC_CAMERA_OV772X is not set ++# CONFIG_SOC_CAMERA_OV9640 is not set ++# CONFIG_SOC_CAMERA_OV9740 is not set ++CONFIG_LINUX_AKSENSOR=y ++CONFIG_SENSOR_GC0308=y ++CONFIG_SENSOR_OV2643=y ++CONFIG_SENSOR_OV7725=y ++CONFIG_SENSOR_HM1375=y ++CONFIG_SENSOR_OV9712=y ++CONFIG_SENSOR_OV2710=y ++# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set ++# CONFIG_VIDEO_SH_MOBILE_CEU is not set ++CONFIG_VIDEO_AK=y ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_RADIO_ADAPTERS is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DRM is not set ++CONFIG_ION=y ++CONFIG_ION_AK=y ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++# CONFIG_FB is not set ++# CONFIG_EXYNOS_VIDEO is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++CONFIG_SOUND=y ++# CONFIG_SOUND_OSS_CORE is not set ++CONFIG_SND=y ++CONFIG_SND_TIMER=y ++CONFIG_SND_PCM=y ++# CONFIG_SND_SEQUENCER is not set ++# CONFIG_SND_MIXER_OSS is not set ++# CONFIG_SND_PCM_OSS is not set ++# CONFIG_SND_DYNAMIC_MINORS is not set ++# CONFIG_SND_SUPPORT_OLD_API is not set ++# CONFIG_SND_VERBOSE_PROCFS is not set ++# CONFIG_SND_VERBOSE_PRINTK is not set ++# CONFIG_SND_DEBUG is not set ++# CONFIG_SND_RAWMIDI_SEQ is not set ++# CONFIG_SND_OPL3_LIB_SEQ is not set ++# CONFIG_SND_OPL4_LIB_SEQ is not set ++# CONFIG_SND_SBAWE_SEQ is not set ++# CONFIG_SND_EMU10K1_SEQ is not set ++# CONFIG_SND_DRIVERS is not set ++CONFIG_SND_ARM=y ++CONFIG_SND_AK_PCM=y ++CONFIG_CODEC_AK39=y ++CONFIG_SPKHP_SWITCH_AUTO=y ++# CONFIG_SPKHP_SWITCH_MIXER is not set ++# CONFIG_SPKHP_SWITCH_UEVENT is not set ++CONFIG_SUPPORT_AEC=y ++# CONFIG_SND_SPI is not set ++# CONFIG_SND_USB is not set ++CONFIG_SND_PCMCIA=y ++# CONFIG_SND_VXPOCKET is not set ++# CONFIG_SND_PDAUDIOCF is not set ++# CONFIG_SND_SOC is not set ++# CONFIG_SOUND_PRIME is not set ++# CONFIG_HID_SUPPORT is not set ++# CONFIG_USB_ARCH_HAS_OHCI is not set ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++# CONFIG_USB_ARCH_HAS_XHCI is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEVICEFS is not set ++# CONFIG_USB_DEVICE_CLASS is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++CONFIG_USB_ANYKA_HCD=y ++CONFIG_USB_AKOTG_HS_HCD=m ++# CONFIG_USB_AKOTG_DMA is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_RENESAS_USBHS is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_LIBUSUAL is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=500 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_MV_UDC is not set ++# CONFIG_USB_GADGET_AKUDC_PRODUCER is not set ++CONFIG_USB_GADGET_AKUDC=y ++CONFIG_USB_AKUDC=m ++# CONFIG_USB_AKUDC_DEBUG_FS is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_GADGET_DUALSPEED=y ++# CONFIG_USB_ZERO is not set ++# CONFIG_USB_AUDIO is not set ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_FILE_STORAGE is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++ ++# ++# OTG and related infrastructure ++# ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ULPI is not set ++# CONFIG_NOP_USB_XCEIV is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++# CONFIG_MMC_BLOCK_BOUNCE is not set ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++# CONFIG_SDIO_WIFI is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_SDHCI_PXAV3 is not set ++# CONFIG_MMC_SDHCI_PXAV2 is not set ++# CONFIG_MMC_SPI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++CONFIG_MMC_ANYKA=y ++# CONFIG_MEMSTICK is not set ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++ ++# ++# LED drivers ++# ++# CONFIG_LEDS_LM3530 is not set ++# CONFIG_LEDS_GPIO is not set ++# CONFIG_LEDS_LP3944 is not set ++# CONFIG_LEDS_LP5521 is not set ++# CONFIG_LEDS_LP5523 is not set ++# CONFIG_LEDS_PCA955X is not set ++# CONFIG_LEDS_PCA9633 is not set ++# CONFIG_LEDS_DAC124S085 is not set ++# CONFIG_LEDS_BD2802 is not set ++CONFIG_LEDS_AK39=y ++# CONFIG_LEDS_LT3593 is not set ++# CONFIG_LEDS_RENESAS_TPU is not set ++# CONFIG_LEDS_TCA6507 is not set ++# CONFIG_LEDS_OT200 is not set ++CONFIG_LEDS_TRIGGERS=y ++ ++# ++# LED Triggers ++# ++CONFIG_LEDS_TRIGGER_TIMER=y ++# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set ++# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set ++# CONFIG_LEDS_TRIGGER_GPIO is not set ++# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set ++ ++# ++# iptables trigger is under Netfilter config (LED target) ++# ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++CONFIG_RTC_LIB=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_HCTOSYS=y ++CONFIG_RTC_HCTOSYS_DEVICE="rtc0" ++# CONFIG_RTC_DEBUG is not set ++ ++# ++# RTC interfaces ++# ++CONFIG_RTC_INTF_SYSFS=y ++CONFIG_RTC_INTF_PROC=y ++CONFIG_RTC_INTF_DEV=y ++# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set ++# CONFIG_RTC_DRV_TEST is not set ++ ++# ++# I2C RTC drivers ++# ++# CONFIG_RTC_DRV_DS1307 is not set ++# CONFIG_RTC_DRV_DS1374 is not set ++# CONFIG_RTC_DRV_DS1672 is not set ++# CONFIG_RTC_DRV_DS3232 is not set ++# CONFIG_RTC_DRV_MAX6900 is not set ++# CONFIG_RTC_DRV_RS5C372 is not set ++# CONFIG_RTC_DRV_ISL1208 is not set ++# CONFIG_RTC_DRV_ISL12022 is not set ++# CONFIG_RTC_DRV_X1205 is not set ++# CONFIG_RTC_DRV_PCF8563 is not set ++# CONFIG_RTC_DRV_PCF8583 is not set ++# CONFIG_RTC_DRV_M41T80 is not set ++# CONFIG_RTC_DRV_BQ32K is not set ++# CONFIG_RTC_DRV_S35390A is not set ++# CONFIG_RTC_DRV_FM3130 is not set ++# CONFIG_RTC_DRV_RX8581 is not set ++# CONFIG_RTC_DRV_RX8025 is not set ++# CONFIG_RTC_DRV_EM3027 is not set ++# CONFIG_RTC_DRV_RV3029C2 is not set ++ ++# ++# SPI RTC drivers ++# ++# CONFIG_RTC_DRV_M41T93 is not set ++# CONFIG_RTC_DRV_M41T94 is not set ++# CONFIG_RTC_DRV_DS1305 is not set ++# CONFIG_RTC_DRV_DS1390 is not set ++# CONFIG_RTC_DRV_MAX6902 is not set ++# CONFIG_RTC_DRV_R9701 is not set ++# CONFIG_RTC_DRV_RS5C348 is not set ++# CONFIG_RTC_DRV_DS3234 is not set ++# CONFIG_RTC_DRV_PCF2123 is not set ++ ++# ++# Platform RTC drivers ++# ++# CONFIG_RTC_DRV_CMOS is not set ++# CONFIG_RTC_DRV_DS1286 is not set ++# CONFIG_RTC_DRV_DS1511 is not set ++# CONFIG_RTC_DRV_DS1553 is not set ++# CONFIG_RTC_DRV_DS1742 is not set ++# CONFIG_RTC_DRV_STK17TA8 is not set ++# CONFIG_RTC_DRV_M48T86 is not set ++# CONFIG_RTC_DRV_M48T35 is not set ++# CONFIG_RTC_DRV_M48T59 is not set ++# CONFIG_RTC_DRV_MSM6242 is not set ++# CONFIG_RTC_DRV_BQ4802 is not set ++# CONFIG_RTC_DRV_RP5C01 is not set ++# CONFIG_RTC_DRV_V3020 is not set ++ ++# ++# on-CPU RTC drivers ++# ++CONFIG_RTC_DRV_AK=y ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++CONFIG_UIO=y ++# CONFIG_UIO_PDRV is not set ++# CONFIG_UIO_PDRV_GENIRQ is not set ++CONFIG_UIO_VCODEC=y ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_BALLOON is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++CONFIG_CLKDEV_LOOKUP=y ++ ++# ++# Hardware Spinlock drivers ++# ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers (EXPERIMENTAL) ++# ++ ++# ++# Rpmsg drivers (EXPERIMENTAL) ++# ++# CONFIG_VIRT_DRIVERS is not set ++# CONFIG_PM_DEVFREQ is not set ++ ++# ++# File systems ++# ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_FS_POSIX_ACL is not set ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_SYSFS=y ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_YAFFS_FS is not set ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++# CONFIG_JFFS2_FS_WRITEBUFFER is not set ++CONFIG_JFFS2_COMPRESSION_OPTIONS=y ++# CONFIG_JFFS2_ZLIB is not set ++# CONFIG_JFFS2_LZO is not set ++# CONFIG_JFFS2_RTIME is not set ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_JFFS2_CMODE_NONE=y ++# CONFIG_JFFS2_CMODE_PRIORITY is not set ++# CONFIG_JFFS2_CMODE_SIZE is not set ++# CONFIG_JFFS2_CMODE_FAVOURLZO is not set ++# CONFIG_CRAMFS is not set ++CONFIG_SQUASHFS=y ++# CONFIG_SQUASHFS_XATTR is not set ++CONFIG_SQUASHFS_ZLIB=y ++# CONFIG_SQUASHFS_LZO is not set ++# CONFIG_SQUASHFS_XZ is not set ++# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set ++# CONFIG_SQUASHFS_EMBEDDED is not set ++CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_NETWORK_FILESYSTEMS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++ ++# ++# Kernel hacking ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_FRAME_WARN=1024 ++# CONFIG_MAGIC_SYSRQ is not set ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++CONFIG_DEBUG_KERNEL=y ++# CONFIG_DEBUG_SHIRQ is not set ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set ++# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set ++# CONFIG_HARDLOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_TIMER_STATS is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_STATS is not set ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_RT_MUTEX_TESTER is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_STACKTRACE is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_KOBJECT is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_DEBUG_VM is not set ++# CONFIG_DEBUG_WRITECOUNT is not set ++# CONFIG_DEBUG_MEMORY_INIT is not set ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++CONFIG_FRAME_POINTER=y ++# CONFIG_BOOT_PRINTK_DELAY is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++# CONFIG_DEBUG_PAGEALLOC is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_STRICT_DEVMEM is not set ++CONFIG_DEBUG_USER=y ++# CONFIG_DEBUG_RODATA is not set ++# CONFIG_DEBUG_LL is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_HMAC is not set ++ ++# ++# Digest ++# ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++# CONFIG_CRYPTO_HW is not set ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++CONFIG_ZLIB_INFLATE=y ++# CONFIG_XZ_DEC is not set ++# CONFIG_XZ_DEC_BCJ is not set ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_GENERIC_ATOMIC64=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set +diff --git a/arch/arm/configs/ak39_micro_defconfig b/arch/arm/configs/ak39_micro_defconfig +new file mode 100644 +index 00000000..aab60f4b +--- /dev/null ++++ b/arch/arm/configs/ak39_micro_defconfig +@@ -0,0 +1,1272 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.4.35 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_GENERIC_GPIO=y ++CONFIG_ARCH_USES_GETTIMEOFFSET=y ++CONFIG_KTIME_SCALAR=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++CONFIG_ARCH_HAS_CPUFREQ=y ++CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_VECTORS_BASE=0xffff0000 ++# CONFIG_ARM_PATCH_PHYS_VIRT is not set ++CONFIG_PHYS_OFFSET=0x80000000 ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_HAVE_IRQ_WORK=y ++CONFIG_IRQ_WORK=y ++ ++# ++# General setup ++# ++# CONFIG_EXPERIMENTAL is not set ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_FHANDLE=y ++# CONFIG_TASKSTATS is not set ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_GENERIC_HARDIRQS=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_SHOW=y ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=16 ++# CONFIG_CGROUPS is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="../targetfs/rootfs/rootfs.initramfs" ++CONFIG_INITRAMFS_ROOT_UID=0 ++CONFIG_INITRAMFS_ROOT_GID=0 ++CONFIG_RD_GZIP=y ++# CONFIG_RD_BZIP2 is not set ++# CONFIG_RD_LZMA is not set ++# CONFIG_RD_XZ is not set ++# CONFIG_RD_LZO is not set ++CONFIG_INITRAMFS_COMPRESSION_NONE=y ++# CONFIG_INITRAMFS_COMPRESSION_GZIP is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_EXPERT=y ++CONFIG_UID16=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_EMBEDDED=y ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++CONFIG_PERF_EVENTS=y ++# CONFIG_PERF_COUNTERS is not set ++# CONFIG_DEBUG_PERF_USE_VMALLOC is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++CONFIG_JUMP_LABEL=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++ ++# ++# GCOV-based kernel profiling ++# ++# CONFIG_GCOV_KERNEL is not set ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_FORCE_LOAD=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++CONFIG_BLK_DEV_BSGLIB=y ++CONFIG_BLK_DEV_INTEGRITY=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_SYSV68_PARTITION is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++# CONFIG_DEFAULT_DEADLINE is not set ++CONFIG_DEFAULT_CFQ=y ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="cfq" ++# CONFIG_INLINE_SPIN_TRYLOCK is not set ++# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK is not set ++# CONFIG_INLINE_SPIN_LOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQ is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set ++# CONFIG_INLINE_SPIN_UNLOCK_BH is not set ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_READ_TRYLOCK is not set ++# CONFIG_INLINE_READ_LOCK is not set ++# CONFIG_INLINE_READ_LOCK_BH is not set ++# CONFIG_INLINE_READ_LOCK_IRQ is not set ++# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set ++CONFIG_INLINE_READ_UNLOCK=y ++# CONFIG_INLINE_READ_UNLOCK_BH is not set ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_WRITE_TRYLOCK is not set ++# CONFIG_INLINE_WRITE_LOCK is not set ++# CONFIG_INLINE_WRITE_LOCK_BH is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQ is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set ++CONFIG_INLINE_WRITE_UNLOCK=y ++# CONFIG_INLINE_WRITE_UNLOCK_BH is not set ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set ++# CONFIG_MUTEX_SPIN_ON_OWNER is not set ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_BCMRING is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_CNS3XXX is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_PRIMA2 is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MXS is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_H720X is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP23XX is not set ++# CONFIG_ARCH_IXP2000 is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_KIRKWOOD is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_PICOXCELL is not set ++# CONFIG_ARCH_PNX4008 is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_S5P64X0 is not set ++# CONFIG_ARCH_S5PC100 is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHARK is not set ++# CONFIG_ARCH_U300 is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_NOMADIK is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_VT8500 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARCH_AK39=y ++ ++# ++# System MMU ++# ++CONFIG_CPU_AK3910=y ++CONFIG_ARCH_SDK3910=y ++ ++# ++# Power management ++# ++CONFIG_PLAT_ANYKA=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_ARM926T=y ++CONFIG_CPU_32v5=y ++CONFIG_CPU_ABRT_EV5TJ=y ++CONFIG_CPU_PABRT_LEGACY=y ++CONFIG_CPU_CACHE_VIVT=y ++CONFIG_CPU_COPY_V4WB=y ++CONFIG_CPU_TLB_V4WBI=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++CONFIG_CPU_USE_DOMAINS=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set ++# CONFIG_CPU_CACHE_ROUND_ROBIN is not set ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT=5 ++CONFIG_ARM_NR_BANKS=8 ++# CONFIG_FIQ_DEBUGGER is not set ++ ++# ++# Bus support ++# ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_PCCARD=y ++CONFIG_PCMCIA=y ++ ++# ++# PC-card bridges ++# ++ ++# ++# Kernel Features ++# ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ=100 ++CONFIG_AEABI=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++# CONFIG_HIGHMEM is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=999999 ++# CONFIG_COMPACTION is not set ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_SECCOMP is not set ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y ++ ++# ++# Boot options ++# ++# CONFIG_USE_OF is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_CMDLINE="mem=256M console=ttySAK0,115200" ++CONFIG_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_CMDLINE_EXTEND is not set ++# CONFIG_CMDLINE_FORCE is not set ++# CONFIG_XIP_KERNEL is not set ++# CONFIG_AUTO_ZRELADDR is not set ++CONFIG_RAM_BASE=0x80000000 ++CONFIG_VIDEO_RESERVED_MEM_SIZE=0x0 ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++# CONFIG_CPU_FREQ is not set ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_HAVE_AOUT=y ++# CONFIG_BINFMT_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_HAS_WAKELOCK=y ++CONFIG_WAKELOCK=y ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_CLK=y ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_UNIX_DIAG=y ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++CONFIG_NET_KEY=y ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++CONFIG_IP_PNP_RARP=y ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_IP_MROUTE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++CONFIG_INET_UDP_DIAG=y ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_IPV6 is not set ++CONFIG_ANDROID_PARANOID_NETWORK=y ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_IP_NF_QUEUE is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_PHONET is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++CONFIG_BQL=y ++CONFIG_HAVE_BPF_JIT=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++CONFIG_WIRELESS=y ++CONFIG_WEXT_CORE=y ++CONFIG_WEXT_PROC=y ++CONFIG_CFG80211=y ++# CONFIG_NL80211_TESTMODE is not set ++# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set ++# CONFIG_CFG80211_REG_DEBUG is not set ++CONFIG_CFG80211_DEFAULT_PS=y ++# CONFIG_CFG80211_DEBUGFS is not set ++# CONFIG_CFG80211_INTERNAL_REGDB is not set ++CONFIG_CFG80211_WEXT=y ++CONFIG_WIRELESS_EXT_SYSFS=y ++CONFIG_LIB80211=y ++# CONFIG_LIB80211_DEBUG is not set ++CONFIG_CFG80211_ALLOW_RECONNECT=y ++CONFIG_MAC80211=y ++CONFIG_MAC80211_HAS_RC=y ++# CONFIG_MAC80211_RC_PID is not set ++CONFIG_MAC80211_RC_MINSTREL=y ++CONFIG_MAC80211_RC_MINSTREL_HT=y ++CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y ++CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" ++# CONFIG_MAC80211_DEBUGFS is not set ++# CONFIG_MAC80211_DEBUG_MENU is not set ++# CONFIG_WIMAX is not set ++CONFIG_RFKILL=y ++CONFIG_RFKILL_PM=y ++# CONFIG_RFKILL_INPUT is not set ++# CONFIG_RFKILL_GPIO is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++CONFIG_SYNC=y ++CONFIG_SW_SYNC=y ++# CONFIG_SW_SYNC_USER is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++# CONFIG_MTD_AFS_PARTS is not set ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++# CONFIG_MTD_CHAR is not set ++# CONFIG_MTD_BLKDEVS is not set ++# CONFIG_MTD_BLOCK is not set ++# CONFIG_MTD_BLOCK_RO is not set ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++# CONFIG_MTD_NAND_IDS is not set ++# CONFIG_MTD_NAND is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR flash memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++ ++# ++# DRBD disabled because PROC_FS, INET or CONNECTOR not selected ++# ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_ATMEL_PWM is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_WL127X_RFKILL is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_93CX6 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++ ++# ++# Altera FPGA firmware download module ++# ++CONFIG_HAVE_IDE=y ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++# CONFIG_SCSI is not set ++# CONFIG_SCSI_DMA is not set ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_NETDEVICES is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++# CONFIG_INPUT_EVDEV is not set ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_TIMBERDALE is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++CONFIG_SERIAL_AK39_UART=y ++CONFIG_SERIAL_AK39_CONSOLE=y ++CONFIG_TTY_PRINTK=y ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++ ++# ++# PCMCIA character devices ++# ++# CONFIG_SYNCLINK_CS is not set ++# CONFIG_CARDMAN_4000 is not set ++# CONFIG_CARDMAN_4040 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_RAMOOPS is not set ++# CONFIG_I2C is not set ++# CONFIG_SPI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++ ++# ++# Enable Device Drivers -> PPS to see the PTP clock options. ++# ++CONFIG_ARCH_REQUIRE_GPIOLIB=y ++CONFIG_GPIOLIB=y ++# CONFIG_DEBUG_GPIO is not set ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++ ++# ++# I2C GPIO expanders: ++# ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_REGULATOR is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DRM is not set ++# CONFIG_ION is not set ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++# CONFIG_FB is not set ++# CONFIG_EXYNOS_VIDEO is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_SOUND is not set ++# CONFIG_HID_SUPPORT is not set ++# CONFIG_USB_ARCH_HAS_OHCI is not set ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++# CONFIG_USB_ARCH_HAS_XHCI is not set ++# CONFIG_USB_SUPPORT is not set ++# CONFIG_MMC is not set ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_BALLOON is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++CONFIG_CLKDEV_LOOKUP=y ++ ++# ++# Hardware Spinlock drivers ++# ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers (EXPERIMENTAL) ++# ++ ++# ++# Rpmsg drivers (EXPERIMENTAL) ++# ++# CONFIG_VIRT_DRIVERS is not set ++# CONFIG_PM_DEVFREQ is not set ++ ++# ++# File systems ++# ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++CONFIG_EXT2_FS_POSIX_ACL=y ++CONFIG_EXT2_FS_SECURITY=y ++CONFIG_EXT2_FS_XIP=y ++CONFIG_EXT3_FS=y ++CONFIG_EXT3_DEFAULTS_TO_ORDERED=y ++CONFIG_EXT3_FS_XATTR=y ++CONFIG_EXT3_FS_POSIX_ACL=y ++CONFIG_EXT3_FS_SECURITY=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_XATTR=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_FS_XIP=y ++CONFIG_JBD=y ++# CONFIG_JBD_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_EXPORTFS=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++CONFIG_GENERIC_ACL=y ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_TMPFS_XATTR=y ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_JFFS2_FS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_NETWORK_FILESYSTEMS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++CONFIG_NLS_CODEPAGE_936=y ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++CONFIG_NLS_UTF8=y ++ ++# ++# Kernel hacking ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_FRAME_WARN=1024 ++# CONFIG_MAGIC_SYSRQ is not set ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++CONFIG_DEBUG_FS=y ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++CONFIG_DEBUG_KERNEL=y ++# CONFIG_DEBUG_SHIRQ is not set ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set ++# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set ++# CONFIG_HARDLOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_TIMER_STATS is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_RT_MUTEX_TESTER is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_KOBJECT is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_DEBUG_VM is not set ++# CONFIG_DEBUG_WRITECOUNT is not set ++# CONFIG_DEBUG_MEMORY_INIT is not set ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++CONFIG_FRAME_POINTER=y ++# CONFIG_BOOT_PRINTK_DELAY is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_LKDTM is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++# CONFIG_DEBUG_PAGEALLOC is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++# CONFIG_DYNAMIC_DEBUG is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_STRICT_DEVMEM is not set ++CONFIG_DEBUG_USER=y ++# CONFIG_DEBUG_RODATA is not set ++# CONFIG_DEBUG_LL is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_HMAC is not set ++ ++# ++# Digest ++# ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++CONFIG_CRC_T10DIF=y ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_GENERIC_ATOMIC64=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set +diff --git a/arch/arm/configs/ak39_producer_defconfig b/arch/arm/configs/ak39_producer_defconfig +new file mode 100644 +index 00000000..fa47ebd7 +--- /dev/null ++++ b/arch/arm/configs/ak39_producer_defconfig +@@ -0,0 +1,1028 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.4.35 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_GENERIC_GPIO=y ++CONFIG_ARCH_USES_GETTIMEOFFSET=y ++CONFIG_KTIME_SCALAR=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++CONFIG_ARCH_HAS_CPUFREQ=y ++CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_VECTORS_BASE=0xffff0000 ++# CONFIG_ARM_PATCH_PHYS_VIRT is not set ++CONFIG_PHYS_OFFSET=0x81a00000 ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_HAVE_IRQ_WORK=y ++ ++# ++# General setup ++# ++# CONFIG_EXPERIMENTAL is not set ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_FHANDLE is not set ++CONFIG_HAVE_GENERIC_HARDIRQS=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_SHOW=y ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=16 ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_NAMESPACES is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="../../librootfs/rootfs" ++CONFIG_INITRAMFS_ROOT_UID=0 ++CONFIG_INITRAMFS_ROOT_GID=0 ++CONFIG_RD_GZIP=y ++# CONFIG_RD_BZIP2 is not set ++# CONFIG_RD_LZMA is not set ++# CONFIG_RD_XZ is not set ++# CONFIG_RD_LZO is not set ++# CONFIG_INITRAMFS_COMPRESSION_NONE is not set ++CONFIG_INITRAMFS_COMPRESSION_GZIP=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_EXPERT=y ++CONFIG_UID16=y ++# CONFIG_SYSCTL_SYSCALL is not set ++# CONFIG_KALLSYMS is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++# CONFIG_EPOLL is not set ++# CONFIG_SIGNALFD is not set ++# CONFIG_TIMERFD is not set ++# CONFIG_EVENTFD is not set ++# CONFIG_SHMEM is not set ++# CONFIG_AIO is not set ++CONFIG_EMBEDDED=y ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++# CONFIG_PERF_COUNTERS is not set ++# CONFIG_VM_EVENT_COUNTERS is not set ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++CONFIG_JUMP_LABEL=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_FORCE_LOAD=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++CONFIG_BLOCK=y ++# CONFIG_LBDAF is not set ++# CONFIG_BLK_DEV_BSG is not set ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++ ++# ++# Partition Types ++# ++# CONFIG_PARTITION_ADVANCED is not set ++CONFIG_MSDOS_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++# CONFIG_IOSCHED_DEADLINE is not set ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_CFQ=y ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="cfq" ++# CONFIG_INLINE_SPIN_TRYLOCK is not set ++# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK is not set ++# CONFIG_INLINE_SPIN_LOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQ is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set ++# CONFIG_INLINE_SPIN_UNLOCK_BH is not set ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_READ_TRYLOCK is not set ++# CONFIG_INLINE_READ_LOCK is not set ++# CONFIG_INLINE_READ_LOCK_BH is not set ++# CONFIG_INLINE_READ_LOCK_IRQ is not set ++# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set ++CONFIG_INLINE_READ_UNLOCK=y ++# CONFIG_INLINE_READ_UNLOCK_BH is not set ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_WRITE_TRYLOCK is not set ++# CONFIG_INLINE_WRITE_LOCK is not set ++# CONFIG_INLINE_WRITE_LOCK_BH is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQ is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set ++CONFIG_INLINE_WRITE_UNLOCK=y ++# CONFIG_INLINE_WRITE_UNLOCK_BH is not set ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set ++# CONFIG_MUTEX_SPIN_ON_OWNER is not set ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_BCMRING is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_CNS3XXX is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_PRIMA2 is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MXS is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_H720X is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP23XX is not set ++# CONFIG_ARCH_IXP2000 is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_KIRKWOOD is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_PICOXCELL is not set ++# CONFIG_ARCH_PNX4008 is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_S5P64X0 is not set ++# CONFIG_ARCH_S5PC100 is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHARK is not set ++# CONFIG_ARCH_U300 is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_NOMADIK is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_VT8500 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARCH_AK39=y ++ ++# ++# System MMU ++# ++CONFIG_CPU_AK3910=y ++CONFIG_ARCH_SDK3910=y ++# CONFIG_ARCH_AIMER39_AK3916 is not set ++CONFIG_ASIC_FREQ_VALUE=90000000 ++ ++# ++# Power management ++# ++# CONFIG_AK39_PM is not set ++# CONFIG_AK39_PWM is not set ++CONFIG_PLAT_ANYKA=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_ARM926T=y ++CONFIG_CPU_32v5=y ++CONFIG_CPU_ABRT_EV5TJ=y ++CONFIG_CPU_PABRT_LEGACY=y ++CONFIG_CPU_CACHE_VIVT=y ++CONFIG_CPU_COPY_V4WB=y ++CONFIG_CPU_TLB_V4WBI=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++CONFIG_CPU_USE_DOMAINS=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++# CONFIG_ARM_THUMB is not set ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set ++# CONFIG_CPU_CACHE_ROUND_ROBIN is not set ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT=5 ++CONFIG_ARM_NR_BANKS=8 ++# CONFIG_FIQ_DEBUGGER is not set ++ ++# ++# Bus support ++# ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ=100 ++CONFIG_AEABI=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++# CONFIG_HIGHMEM is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=999999 ++# CONFIG_COMPACTION is not set ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_SECCOMP is not set ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y ++ ++# ++# Boot options ++# ++# CONFIG_USE_OF is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_CMDLINE="mem=64M console=ttySAK0,115200 download=1" ++CONFIG_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_CMDLINE_EXTEND is not set ++# CONFIG_CMDLINE_FORCE is not set ++# CONFIG_XIP_KERNEL is not set ++# CONFIG_AUTO_ZRELADDR is not set ++CONFIG_RAM_BASE=0x80000000 ++CONFIG_VIDEO_RESERVED_MEM_SIZE=0x1a00000 ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++# CONFIG_CPU_FREQ is not set ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++# CONFIG_VFP is not set ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_HAVE_AOUT=y ++# CONFIG_BINFMT_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_HAS_WAKELOCK=y ++CONFIG_WAKELOCK=y ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_CLK=y ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++# CONFIG_NET is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++CONFIG_SYNC=y ++CONFIG_SW_SYNC=y ++# CONFIG_SW_SYNC_USER is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++# CONFIG_MTD_AFS_PARTS is not set ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_AK_SPIFLASH=y ++# CONFIG_MTD_NAND_IDS is not set ++# CONFIG_MTD_NAND is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR flash memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++ ++# ++# DRBD disabled because PROC_FS, INET or CONNECTOR not selected ++# ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_MG_DISK is not set ++ ++# ++# Misc devices ++# ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_ATMEL_PWM is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++ ++# ++# Altera FPGA firmware download module ++# ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++# CONFIG_SCSI is not set ++# CONFIG_SCSI_DMA is not set ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++ ++# ++# Input device support ++# ++# CONFIG_INPUT is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++# CONFIG_VT is not set ++# CONFIG_UNIX98_PTYS is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX3107 is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_TIMBERDALE is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++CONFIG_SERIAL_AK39_UART=y ++CONFIG_SERIAL_AK39_CONSOLE=y ++# CONFIG_TTY_PRINTK is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_RAMOOPS is not set ++# CONFIG_I2C is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++CONFIG_SPI_BITBANG=y ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_OC_TINY is not set ++# CONFIG_SPI_PXA2XX_PCI is not set ++CONFIG_SPI_ANYKA=y ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++ ++# ++# Enable Device Drivers -> PPS to see the PTP clock options. ++# ++CONFIG_ARCH_REQUIRE_GPIOLIB=y ++CONFIG_GPIOLIB=y ++# CONFIG_DEBUG_GPIO is not set ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++ ++# ++# I2C GPIO expanders: ++# ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_STMPE is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_MC13XXX is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_REGULATOR is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DRM is not set ++# CONFIG_ION is not set ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++# CONFIG_FB is not set ++# CONFIG_EXYNOS_VIDEO is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_SOUND is not set ++# CONFIG_USB_ARCH_HAS_OHCI is not set ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++# CONFIG_USB_ARCH_HAS_XHCI is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++# CONFIG_USB is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_USB_GADGET_AKUDC_PRODUCER=y ++CONFIG_USB_AKUDC_PRODUCER=y ++# CONFIG_USB_GADGET_AKUDC is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++CONFIG_USB_GADGET_DUALSPEED=y ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_FILE_STORAGE=m ++# CONFIG_USB_FILE_STORAGE_TEST is not set ++# CONFIG_USB_MASS_STORAGE is not set ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++ ++# ++# OTG and related infrastructure ++# ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ULPI is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_MMC is not set ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_BALLOON is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++CONFIG_CLKDEV_LOOKUP=y ++ ++# ++# Hardware Spinlock drivers ++# ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers (EXPERIMENTAL) ++# ++ ++# ++# Rpmsg drivers (EXPERIMENTAL) ++# ++# CONFIG_VIRT_DRIVERS is not set ++# CONFIG_PM_DEVFREQ is not set ++ ++# ++# File systems ++# ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_FS_POSIX_ACL is not set ++# CONFIG_FILE_LOCKING is not set ++# CONFIG_FSNOTIFY is not set ++# CONFIG_DNOTIFY is not set ++# CONFIG_INOTIFY_USER is not set ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++# CONFIG_MSDOS_FS is not set ++# CONFIG_VFAT_FS is not set ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_SYSFS=y ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_YAFFS_FS is not set ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++CONFIG_JFFS2_COMPRESSION_OPTIONS=y ++# CONFIG_JFFS2_ZLIB is not set ++# CONFIG_JFFS2_LZO is not set ++# CONFIG_JFFS2_RTIME is not set ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_JFFS2_CMODE_NONE=y ++# CONFIG_JFFS2_CMODE_PRIORITY is not set ++# CONFIG_JFFS2_CMODE_SIZE is not set ++# CONFIG_JFFS2_CMODE_FAVOURLZO is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="utf8" ++# CONFIG_NLS_CODEPAGE_437 is not set ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++# CONFIG_NLS_ISO8859_1 is not set ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++CONFIG_NLS_UTF8=y ++ ++# ++# Kernel hacking ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_FRAME_WARN=1024 ++# CONFIG_MAGIC_SYSRQ is not set ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++CONFIG_DEBUG_KERNEL=y ++# CONFIG_DEBUG_SHIRQ is not set ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set ++# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set ++# CONFIG_HARDLOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_TIMER_STATS is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_STATS is not set ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_RT_MUTEX_TESTER is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_STACKTRACE is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_KOBJECT is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_DEBUG_VM is not set ++# CONFIG_DEBUG_WRITECOUNT is not set ++# CONFIG_DEBUG_MEMORY_INIT is not set ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++CONFIG_FRAME_POINTER=y ++# CONFIG_BOOT_PRINTK_DELAY is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++# CONFIG_DEBUG_PAGEALLOC is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_STRICT_DEVMEM is not set ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_RODATA is not set ++# CONFIG_DEBUG_LL is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++# CONFIG_CRYPTO is not set ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++CONFIG_CRC_T10DIF=y ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++CONFIG_ZLIB_INFLATE=y ++# CONFIG_XZ_DEC is not set ++# CONFIG_XZ_DEC_BCJ is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y ++CONFIG_GENERIC_ATOMIC64=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set +diff --git a/arch/arm/configs/ak39_sdk3910_defconfig b/arch/arm/configs/ak39_sdk3910_defconfig +new file mode 100644 +index 00000000..2c051bc0 +--- /dev/null ++++ b/arch/arm/configs/ak39_sdk3910_defconfig +@@ -0,0 +1,1920 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.4.35 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_GENERIC_GPIO=y ++CONFIG_ARCH_USES_GETTIMEOFFSET=y ++CONFIG_KTIME_SCALAR=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++CONFIG_ARCH_HAS_CPUFREQ=y ++CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_VECTORS_BASE=0xffff0000 ++# CONFIG_ARM_PATCH_PHYS_VIRT is not set ++CONFIG_PHYS_OFFSET=0x81000000 ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_HAVE_IRQ_WORK=y ++CONFIG_IRQ_WORK=y ++ ++# ++# General setup ++# ++# CONFIG_EXPERIMENTAL is not set ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_FHANDLE=y ++# CONFIG_TASKSTATS is not set ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_GENERIC_HARDIRQS=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_SHOW=y ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=16 ++# CONFIG_CGROUPS is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="../targetfs/rootfs/rootfs.initramfs" ++CONFIG_INITRAMFS_ROOT_UID=0 ++CONFIG_INITRAMFS_ROOT_GID=0 ++CONFIG_RD_GZIP=y ++# CONFIG_RD_BZIP2 is not set ++# CONFIG_RD_LZMA is not set ++# CONFIG_RD_XZ is not set ++# CONFIG_RD_LZO is not set ++CONFIG_INITRAMFS_COMPRESSION_NONE=y ++# CONFIG_INITRAMFS_COMPRESSION_GZIP is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_EXPERT=y ++CONFIG_UID16=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_EMBEDDED=y ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++CONFIG_PERF_EVENTS=y ++# CONFIG_PERF_COUNTERS is not set ++# CONFIG_DEBUG_PERF_USE_VMALLOC is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++CONFIG_JUMP_LABEL=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++ ++# ++# GCOV-based kernel profiling ++# ++# CONFIG_GCOV_KERNEL is not set ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_FORCE_LOAD=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++CONFIG_BLK_DEV_BSGLIB=y ++CONFIG_BLK_DEV_INTEGRITY=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_SYSV68_PARTITION is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++# CONFIG_DEFAULT_DEADLINE is not set ++CONFIG_DEFAULT_CFQ=y ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="cfq" ++# CONFIG_INLINE_SPIN_TRYLOCK is not set ++# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK is not set ++# CONFIG_INLINE_SPIN_LOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQ is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set ++# CONFIG_INLINE_SPIN_UNLOCK_BH is not set ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_READ_TRYLOCK is not set ++# CONFIG_INLINE_READ_LOCK is not set ++# CONFIG_INLINE_READ_LOCK_BH is not set ++# CONFIG_INLINE_READ_LOCK_IRQ is not set ++# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set ++CONFIG_INLINE_READ_UNLOCK=y ++# CONFIG_INLINE_READ_UNLOCK_BH is not set ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_WRITE_TRYLOCK is not set ++# CONFIG_INLINE_WRITE_LOCK is not set ++# CONFIG_INLINE_WRITE_LOCK_BH is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQ is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set ++CONFIG_INLINE_WRITE_UNLOCK=y ++# CONFIG_INLINE_WRITE_UNLOCK_BH is not set ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set ++# CONFIG_MUTEX_SPIN_ON_OWNER is not set ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_BCMRING is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_CNS3XXX is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_PRIMA2 is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MXS is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_H720X is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP23XX is not set ++# CONFIG_ARCH_IXP2000 is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_KIRKWOOD is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_PICOXCELL is not set ++# CONFIG_ARCH_PNX4008 is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_S5P64X0 is not set ++# CONFIG_ARCH_S5PC100 is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHARK is not set ++# CONFIG_ARCH_U300 is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_NOMADIK is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_VT8500 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARCH_AK39=y ++# CONFIG_GPIO_PCA953X is not set ++ ++# ++# System MMU ++# ++CONFIG_CPU_AK3910=y ++CONFIG_ARCH_SDK3910=y ++# CONFIG_ARCH_AIMER39_AK3916 is not set ++CONFIG_ASIC_FREQ_VALUE=90000000 ++ ++# ++# Power management ++# ++# CONFIG_AK39_PM is not set ++# CONFIG_AK39_PWM is not set ++CONFIG_PLAT_ANYKA=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_ARM926T=y ++CONFIG_CPU_32v5=y ++CONFIG_CPU_ABRT_EV5TJ=y ++CONFIG_CPU_PABRT_LEGACY=y ++CONFIG_CPU_CACHE_VIVT=y ++CONFIG_CPU_COPY_V4WB=y ++CONFIG_CPU_TLB_V4WBI=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++CONFIG_CPU_USE_DOMAINS=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set ++# CONFIG_CPU_CACHE_ROUND_ROBIN is not set ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT=5 ++CONFIG_ARM_NR_BANKS=8 ++# CONFIG_FIQ_DEBUGGER is not set ++ ++# ++# Bus support ++# ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_PCCARD=y ++CONFIG_PCMCIA=y ++ ++# ++# PC-card bridges ++# ++ ++# ++# Kernel Features ++# ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ=100 ++CONFIG_AEABI=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++# CONFIG_HIGHMEM is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=999999 ++# CONFIG_COMPACTION is not set ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_SECCOMP is not set ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y ++ ++# ++# Boot options ++# ++# CONFIG_USE_OF is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_CMDLINE="mem=64M console=ttySAK0,115200" ++CONFIG_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_CMDLINE_EXTEND is not set ++# CONFIG_CMDLINE_FORCE is not set ++# CONFIG_XIP_KERNEL is not set ++# CONFIG_AUTO_ZRELADDR is not set ++CONFIG_RAM_BASE=0x80000000 ++CONFIG_VIDEO_RESERVED_MEM_SIZE=0x1000000 ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++# CONFIG_CPU_FREQ is not set ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_HAVE_AOUT=y ++# CONFIG_BINFMT_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_HAS_WAKELOCK=y ++CONFIG_WAKELOCK=y ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_CLK=y ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_UNIX_DIAG=y ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++CONFIG_NET_KEY=y ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++CONFIG_IP_PNP_RARP=y ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_IP_MROUTE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++CONFIG_INET_UDP_DIAG=y ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_IPV6 is not set ++CONFIG_ANDROID_PARANOID_NETWORK=y ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_IP_NF_QUEUE is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_PHONET is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++CONFIG_BQL=y ++CONFIG_HAVE_BPF_JIT=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++CONFIG_WIRELESS=y ++CONFIG_WIRELESS_EXT=y ++CONFIG_WEXT_CORE=y ++CONFIG_WEXT_PROC=y ++CONFIG_WEXT_SPY=y ++CONFIG_WEXT_PRIV=y ++CONFIG_CFG80211=y ++# CONFIG_NL80211_TESTMODE is not set ++# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set ++# CONFIG_CFG80211_REG_DEBUG is not set ++CONFIG_CFG80211_DEFAULT_PS=y ++# CONFIG_CFG80211_DEBUGFS is not set ++# CONFIG_CFG80211_INTERNAL_REGDB is not set ++CONFIG_CFG80211_WEXT=y ++CONFIG_WIRELESS_EXT_SYSFS=y ++CONFIG_LIB80211=y ++# CONFIG_LIB80211_DEBUG is not set ++CONFIG_CFG80211_ALLOW_RECONNECT=y ++CONFIG_MAC80211=y ++CONFIG_MAC80211_HAS_RC=y ++# CONFIG_MAC80211_RC_PID is not set ++CONFIG_MAC80211_RC_MINSTREL=y ++CONFIG_MAC80211_RC_MINSTREL_HT=y ++CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y ++CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" ++# CONFIG_MAC80211_LEDS is not set ++# CONFIG_MAC80211_DEBUGFS is not set ++# CONFIG_MAC80211_DEBUG_MENU is not set ++# CONFIG_WIMAX is not set ++CONFIG_RFKILL=y ++CONFIG_RFKILL_PM=y ++CONFIG_RFKILL_LEDS=y ++# CONFIG_RFKILL_INPUT is not set ++# CONFIG_RFKILL_GPIO is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++CONFIG_DMA_SHARED_BUFFER=y ++CONFIG_SYNC=y ++CONFIG_SW_SYNC=y ++# CONFIG_SW_SYNC_USER is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++# CONFIG_MTD_AFS_PARTS is not set ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_AK_SPIFLASH=y ++# CONFIG_MTD_NAND_IDS is not set ++# CONFIG_MTD_NAND is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR flash memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++ ++# ++# DRBD disabled because PROC_FS, INET or CONNECTOR not selected ++# ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_ATMEL_PWM is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_SENSORS_AK8975 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085 is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_WL127X_RFKILL is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++CONFIG_HAVE_IDE=y ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_PROC_FS is not set ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++CONFIG_SCSI_WAIT_SCAN=m ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_MII is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_ARCNET is not set ++ ++# ++# CAIF transport drivers ++# ++CONFIG_ETHERNET=y ++# CONFIG_NET_VENDOR_3COM is not set ++# CONFIG_NET_VENDOR_AMD is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_CALXEDA_XGMAC is not set ++# CONFIG_NET_VENDOR_CHELSIO is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++# CONFIG_NET_VENDOR_FUJITSU is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_XIRCOM is not set ++CONFIG_AK_ETHERNET=y ++# CONFIG_PHYLIB is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++# CONFIG_TR is not set ++ ++# ++# USB Network Adapters ++# ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_HSO is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++CONFIG_PCMCIA_RAYCS=y ++# CONFIG_LIBERTAS_THINFIRM is not set ++# CONFIG_ATMEL is not set ++# CONFIG_AT76C50X_USB is not set ++# CONFIG_AIRO_CS is not set ++# CONFIG_USB_ZD1201 is not set ++# CONFIG_RTL8187 is not set ++# CONFIG_MAC80211_HWSIM is not set ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_ATH_COMMON is not set ++# CONFIG_B43 is not set ++# CONFIG_B43LEGACY is not set ++# CONFIG_BCMDHD is not set ++# CONFIG_BRCMFMAC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_LIBERTAS is not set ++# CONFIG_HERMES is not set ++# CONFIG_RT2X00 is not set ++# CONFIG_MWIFIEX is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++CONFIG_WAN=y ++# CONFIG_HDLC is not set ++# CONFIG_DLCI is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++# CONFIG_INPUT_EVDEV is not set ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX3107 is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_TIMBERDALE is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++CONFIG_SERIAL_AK39_UART=y ++CONFIG_SERIAL_AK39_CONSOLE=y ++CONFIG_TTY_PRINTK=y ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++ ++# ++# PCMCIA character devices ++# ++# CONFIG_SYNCLINK_CS is not set ++# CONFIG_CARDMAN_4000 is not set ++# CONFIG_CARDMAN_4040 is not set ++# CONFIG_IPWIRELESS is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_RAMOOPS is not set ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++# CONFIG_I2C_CHARDEV is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++CONFIG_I2C_SMBUS=y ++ ++# ++# I2C Algorithms ++# ++CONFIG_I2C_ALGOBIT=y ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_SIMTEC is not set ++CONFIG_I2C_ANYKA=y ++CONFIG_I2C_AK39_HW=y ++# CONFIG_I2C_GPIO_SOFT is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++CONFIG_SPI_BITBANG=y ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_OC_TINY is not set ++# CONFIG_SPI_PXA2XX_PCI is not set ++CONFIG_SPI_ANYKA=y ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++ ++# ++# Enable Device Drivers -> PPS to see the PTP clock options. ++# ++CONFIG_ARCH_REQUIRE_GPIOLIB=y ++CONFIG_GPIOLIB=y ++# CONFIG_DEBUG_GPIO is not set ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_CORE is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_STMPE is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_S5M_CORE is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_MC13XXX is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_REGULATOR is not set ++CONFIG_MEDIA_SUPPORT=y ++ ++# ++# Multimedia core support ++# ++CONFIG_VIDEO_DEV=y ++CONFIG_VIDEO_V4L2_COMMON=y ++# CONFIG_DVB_CORE is not set ++CONFIG_VIDEO_MEDIA=y ++ ++# ++# Multimedia drivers ++# ++# CONFIG_RC_CORE is not set ++# CONFIG_MEDIA_ATTACH is not set ++CONFIG_MEDIA_TUNER=y ++# CONFIG_MEDIA_TUNER_CUSTOMISE is not set ++CONFIG_MEDIA_TUNER_SIMPLE=y ++CONFIG_MEDIA_TUNER_TDA8290=y ++CONFIG_MEDIA_TUNER_TDA827X=y ++CONFIG_MEDIA_TUNER_TDA18271=y ++CONFIG_MEDIA_TUNER_TDA9887=y ++CONFIG_MEDIA_TUNER_TEA5767=y ++CONFIG_MEDIA_TUNER_MT20XX=y ++CONFIG_MEDIA_TUNER_XC2028=y ++CONFIG_MEDIA_TUNER_XC5000=y ++CONFIG_MEDIA_TUNER_XC4000=y ++CONFIG_MEDIA_TUNER_MC44S803=y ++CONFIG_VIDEO_V4L2=y ++CONFIG_VIDEOBUF_GEN=y ++CONFIG_VIDEOBUF_DMA_CONTIG=y ++CONFIG_VIDEOBUF2_CORE=y ++CONFIG_VIDEO_CAPTURE_DRIVERS=y ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set ++ ++# ++# Encoders, decoders, sensors and other helper chips ++# ++ ++# ++# Audio decoders, processors and mixers ++# ++# CONFIG_VIDEO_TVAUDIO is not set ++# CONFIG_VIDEO_TDA7432 is not set ++# CONFIG_VIDEO_TDA9840 is not set ++# CONFIG_VIDEO_TEA6415C is not set ++# CONFIG_VIDEO_TEA6420 is not set ++# CONFIG_VIDEO_MSP3400 is not set ++# CONFIG_VIDEO_CS5345 is not set ++# CONFIG_VIDEO_CS53L32A is not set ++# CONFIG_VIDEO_WM8775 is not set ++# CONFIG_VIDEO_WM8739 is not set ++# CONFIG_VIDEO_VP27SMPX is not set ++ ++# ++# RDS decoders ++# ++# CONFIG_VIDEO_SAA6588 is not set ++ ++# ++# Video decoders ++# ++# CONFIG_VIDEO_ADV7180 is not set ++# CONFIG_VIDEO_ADV7183 is not set ++# CONFIG_VIDEO_BT819 is not set ++# CONFIG_VIDEO_BT856 is not set ++# CONFIG_VIDEO_BT866 is not set ++# CONFIG_VIDEO_KS0127 is not set ++# CONFIG_VIDEO_SAA7110 is not set ++# CONFIG_VIDEO_SAA711X is not set ++# CONFIG_VIDEO_SAA7191 is not set ++# CONFIG_VIDEO_TVP514X is not set ++# CONFIG_VIDEO_TVP5150 is not set ++# CONFIG_VIDEO_TVP7002 is not set ++# CONFIG_VIDEO_VPX3220 is not set ++ ++# ++# Video and audio decoders ++# ++# CONFIG_VIDEO_SAA717X is not set ++# CONFIG_VIDEO_CX25840 is not set ++ ++# ++# MPEG video encoders ++# ++# CONFIG_VIDEO_CX2341X is not set ++ ++# ++# Video encoders ++# ++# CONFIG_VIDEO_SAA7127 is not set ++# CONFIG_VIDEO_SAA7185 is not set ++# CONFIG_VIDEO_ADV7170 is not set ++# CONFIG_VIDEO_ADV7175 is not set ++# CONFIG_VIDEO_ADV7343 is not set ++# CONFIG_VIDEO_AK881X is not set ++ ++# ++# Camera sensor devices ++# ++# CONFIG_VIDEO_OV7670 is not set ++# CONFIG_VIDEO_VS6624 is not set ++# CONFIG_VIDEO_MT9V011 is not set ++# CONFIG_VIDEO_TCM825X is not set ++# CONFIG_VIDEO_SR030PC30 is not set ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++# CONFIG_VIDEO_UPD64031A is not set ++# CONFIG_VIDEO_UPD64083 is not set ++ ++# ++# Miscelaneous helper chips ++# ++# CONFIG_VIDEO_THS7303 is not set ++# CONFIG_VIDEO_M52790 is not set ++# CONFIG_V4L_USB_DRIVERS is not set ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_SOC_CAMERA=y ++# CONFIG_SOC_CAMERA_IMX074 is not set ++# CONFIG_SOC_CAMERA_MT9M001 is not set ++# CONFIG_SOC_CAMERA_MT9M111 is not set ++# CONFIG_SOC_CAMERA_MT9T031 is not set ++# CONFIG_SOC_CAMERA_MT9T112 is not set ++# CONFIG_SOC_CAMERA_MT9V022 is not set ++# CONFIG_SOC_CAMERA_RJ54N1 is not set ++# CONFIG_SOC_CAMERA_TW9910 is not set ++# CONFIG_SOC_CAMERA_PLATFORM is not set ++# CONFIG_SOC_CAMERA_OV2640 is not set ++# CONFIG_SOC_CAMERA_OV5642 is not set ++# CONFIG_SOC_CAMERA_OV6650 is not set ++# CONFIG_SOC_CAMERA_OV772X is not set ++# CONFIG_SOC_CAMERA_OV9640 is not set ++# CONFIG_SOC_CAMERA_OV9740 is not set ++CONFIG_LINUX_AKSENSOR=y ++CONFIG_SENSOR_GC0308=y ++CONFIG_SENSOR_OV2643=y ++# CONFIG_SENSOR_OV7725 is not set ++# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set ++# CONFIG_VIDEO_SH_MOBILE_CEU is not set ++CONFIG_VIDEO_AK=y ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_RADIO_ADAPTERS is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DRM is not set ++CONFIG_ION=y ++CONFIG_ION_AK=y ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++# CONFIG_FB is not set ++# CONFIG_EXYNOS_VIDEO is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++CONFIG_SOUND=y ++# CONFIG_SOUND_OSS_CORE is not set ++CONFIG_SND=y ++CONFIG_SND_TIMER=y ++CONFIG_SND_PCM=y ++# CONFIG_SND_SEQUENCER is not set ++# CONFIG_SND_MIXER_OSS is not set ++# CONFIG_SND_PCM_OSS is not set ++# CONFIG_SND_DYNAMIC_MINORS is not set ++CONFIG_SND_SUPPORT_OLD_API=y ++CONFIG_SND_VERBOSE_PROCFS=y ++# CONFIG_SND_VERBOSE_PRINTK is not set ++# CONFIG_SND_DEBUG is not set ++# CONFIG_SND_RAWMIDI_SEQ is not set ++# CONFIG_SND_OPL3_LIB_SEQ is not set ++# CONFIG_SND_OPL4_LIB_SEQ is not set ++# CONFIG_SND_SBAWE_SEQ is not set ++# CONFIG_SND_EMU10K1_SEQ is not set ++CONFIG_SND_DRIVERS=y ++# CONFIG_SND_DUMMY is not set ++# CONFIG_SND_ALOOP is not set ++# CONFIG_SND_MTPAV is not set ++# CONFIG_SND_SERIAL_U16550 is not set ++# CONFIG_SND_MPU401 is not set ++CONFIG_SND_ARM=y ++CONFIG_SND_AK_PCM=y ++CONFIG_CODEC_AK39=y ++CONFIG_SPKHP_SWITCH_AUTO=y ++# CONFIG_SPKHP_SWITCH_MIXER is not set ++# CONFIG_SPKHP_SWITCH_UEVENT is not set ++CONFIG_SND_SPI=y ++CONFIG_SND_USB=y ++# CONFIG_SND_USB_AUDIO is not set ++# CONFIG_SND_USB_UA101 is not set ++# CONFIG_SND_USB_CAIAQ is not set ++# CONFIG_SND_USB_6FIRE is not set ++CONFIG_SND_PCMCIA=y ++# CONFIG_SND_VXPOCKET is not set ++# CONFIG_SND_PDAUDIOCF is not set ++# CONFIG_SND_SOC is not set ++# CONFIG_SOUND_PRIME is not set ++# CONFIG_HID_SUPPORT is not set ++# CONFIG_USB_ARCH_HAS_OHCI is not set ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++# CONFIG_USB_ARCH_HAS_XHCI is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEVICEFS is not set ++# CONFIG_USB_DEVICE_CLASS is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++CONFIG_USB_ANYKA_HCD=y ++CONFIG_USB_AKOTG_HS_HCD=m ++# CONFIG_USB_AKOTG_DMA is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_RENESAS_USBHS is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_LIBUSUAL is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++# CONFIG_USB_GADGET_DEBUG_FS is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_MV_UDC is not set ++# CONFIG_USB_GADGET_AKUDC_PRODUCER is not set ++CONFIG_USB_GADGET_AKUDC=y ++CONFIG_USB_AKUDC=m ++# CONFIG_USB_AKUDC_DEBUG_FS is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_GADGET_DUALSPEED=y ++# CONFIG_USB_ZERO is not set ++# CONFIG_USB_AUDIO is not set ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++CONFIG_USB_FILE_STORAGE=m ++# CONFIG_USB_FILE_STORAGE_TEST is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++ ++# ++# OTG and related infrastructure ++# ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ULPI is not set ++# CONFIG_NOP_USB_XCEIV is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++# CONFIG_MMC_BLOCK_BOUNCE is not set ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_SDHCI_PXAV3 is not set ++# CONFIG_MMC_SDHCI_PXAV2 is not set ++# CONFIG_MMC_SPI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++CONFIG_MMC_ANYKA=y ++# CONFIG_MEMSTICK is not set ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++ ++# ++# LED drivers ++# ++# CONFIG_LEDS_LM3530 is not set ++# CONFIG_LEDS_GPIO is not set ++# CONFIG_LEDS_LP3944 is not set ++# CONFIG_LEDS_LP5521 is not set ++# CONFIG_LEDS_LP5523 is not set ++# CONFIG_LEDS_PCA955X is not set ++# CONFIG_LEDS_PCA9633 is not set ++# CONFIG_LEDS_DAC124S085 is not set ++# CONFIG_LEDS_BD2802 is not set ++CONFIG_LEDS_AK39=y ++# CONFIG_LEDS_LT3593 is not set ++# CONFIG_LEDS_RENESAS_TPU is not set ++# CONFIG_LEDS_TCA6507 is not set ++# CONFIG_LEDS_OT200 is not set ++CONFIG_LEDS_TRIGGERS=y ++ ++# ++# LED Triggers ++# ++CONFIG_LEDS_TRIGGER_TIMER=y ++# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set ++# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set ++# CONFIG_LEDS_TRIGGER_GPIO is not set ++# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set ++ ++# ++# iptables trigger is under Netfilter config (LED target) ++# ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++CONFIG_UIO=y ++# CONFIG_UIO_PDRV is not set ++# CONFIG_UIO_PDRV_GENIRQ is not set ++CONFIG_UIO_VCODEC=y ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_BALLOON is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++CONFIG_CLKDEV_LOOKUP=y ++ ++# ++# Hardware Spinlock drivers ++# ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers (EXPERIMENTAL) ++# ++ ++# ++# Rpmsg drivers (EXPERIMENTAL) ++# ++# CONFIG_VIRT_DRIVERS is not set ++# CONFIG_PM_DEVFREQ is not set ++ ++# ++# File systems ++# ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++CONFIG_EXT2_FS_POSIX_ACL=y ++CONFIG_EXT2_FS_SECURITY=y ++CONFIG_EXT2_FS_XIP=y ++CONFIG_EXT3_FS=y ++CONFIG_EXT3_DEFAULTS_TO_ORDERED=y ++CONFIG_EXT3_FS_XATTR=y ++CONFIG_EXT3_FS_POSIX_ACL=y ++CONFIG_EXT3_FS_SECURITY=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_XATTR=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_FS_XIP=y ++CONFIG_JBD=y ++# CONFIG_JBD_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_EXPORTFS=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++CONFIG_GENERIC_ACL=y ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_TMPFS_XATTR=y ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_YAFFS_FS is not set ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++CONFIG_JFFS2_COMPRESSION_OPTIONS=y ++# CONFIG_JFFS2_ZLIB is not set ++# CONFIG_JFFS2_LZO is not set ++# CONFIG_JFFS2_RTIME is not set ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_JFFS2_CMODE_NONE=y ++# CONFIG_JFFS2_CMODE_PRIORITY is not set ++# CONFIG_JFFS2_CMODE_SIZE is not set ++# CONFIG_JFFS2_CMODE_FAVOURLZO is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_NETWORK_FILESYSTEMS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++CONFIG_NLS_CODEPAGE_936=y ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++CONFIG_NLS_UTF8=y ++ ++# ++# Kernel hacking ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_FRAME_WARN=1024 ++# CONFIG_MAGIC_SYSRQ is not set ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++CONFIG_DEBUG_FS=y ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++CONFIG_DEBUG_KERNEL=y ++# CONFIG_DEBUG_SHIRQ is not set ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_HARDLOCKUP_DETECTOR_NMI is not set ++# CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU is not set ++# CONFIG_HARDLOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_TIMER_STATS is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_RT_MUTEX_TESTER is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_KOBJECT is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_DEBUG_VM is not set ++# CONFIG_DEBUG_WRITECOUNT is not set ++# CONFIG_DEBUG_MEMORY_INIT is not set ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++CONFIG_FRAME_POINTER=y ++# CONFIG_BOOT_PRINTK_DELAY is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_LKDTM is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++# CONFIG_DEBUG_PAGEALLOC is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++# CONFIG_DYNAMIC_DEBUG is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_STRICT_DEVMEM is not set ++CONFIG_DEBUG_USER=y ++# CONFIG_DEBUG_RODATA is not set ++# CONFIG_DEBUG_LL is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_HMAC is not set ++ ++# ++# Digest ++# ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++CONFIG_CRC_T10DIF=y ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_GENERIC_ATOMIC64=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set +diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h +index 42dec04f..d0366c1e 100644 +--- a/arch/arm/include/asm/cacheflush.h ++++ b/arch/arm/include/asm/cacheflush.h +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + + #define CACHE_COLOUR(vaddr) ((vaddr & (SHMLBA - 1)) >> PAGE_SHIFT) + +diff --git a/arch/arm/include/asm/fiq_debugger.h b/arch/arm/include/asm/fiq_debugger.h +new file mode 100644 +index 00000000..4d274883 +--- /dev/null ++++ b/arch/arm/include/asm/fiq_debugger.h +@@ -0,0 +1,64 @@ ++/* ++ * arch/arm/include/asm/fiq_debugger.h ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * Author: Colin Cross ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_ ++#define _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_ ++ ++#include ++ ++#define FIQ_DEBUGGER_NO_CHAR NO_POLL_CHAR ++#define FIQ_DEBUGGER_BREAK 0x00ff0100 ++ ++#define FIQ_DEBUGGER_FIQ_IRQ_NAME "fiq" ++#define FIQ_DEBUGGER_SIGNAL_IRQ_NAME "signal" ++#define FIQ_DEBUGGER_WAKEUP_IRQ_NAME "wakeup" ++ ++/** ++ * struct fiq_debugger_pdata - fiq debugger platform data ++ * @uart_resume: used to restore uart state right before enabling ++ * the fiq. ++ * @uart_enable: Do the work necessary to communicate with the uart ++ * hw (enable clocks, etc.). This must be ref-counted. ++ * @uart_disable: Do the work necessary to disable the uart hw ++ * (disable clocks, etc.). This must be ref-counted. ++ * @uart_dev_suspend: called during PM suspend, generally not needed ++ * for real fiq mode debugger. ++ * @uart_dev_resume: called during PM resume, generally not needed ++ * for real fiq mode debugger. ++ */ ++struct fiq_debugger_pdata { ++ int (*uart_init)(struct platform_device *pdev); ++ void (*uart_free)(struct platform_device *pdev); ++ int (*uart_resume)(struct platform_device *pdev); ++ int (*uart_getc)(struct platform_device *pdev); ++ void (*uart_putc)(struct platform_device *pdev, unsigned int c); ++ void (*uart_flush)(struct platform_device *pdev); ++ void (*uart_enable)(struct platform_device *pdev); ++ void (*uart_disable)(struct platform_device *pdev); ++ ++ int (*uart_dev_suspend)(struct platform_device *pdev); ++ int (*uart_dev_resume)(struct platform_device *pdev); ++ ++ void (*fiq_enable)(struct platform_device *pdev, unsigned int fiq, ++ bool enable); ++ void (*fiq_ack)(struct platform_device *pdev, unsigned int fiq); ++ ++ void (*force_irq)(struct platform_device *pdev, unsigned int irq); ++ void (*force_irq_ack)(struct platform_device *pdev, unsigned int irq); ++}; ++ ++#endif +diff --git a/arch/arm/include/asm/fiq_glue.h b/arch/arm/include/asm/fiq_glue.h +new file mode 100644 +index 00000000..d54c29db +--- /dev/null ++++ b/arch/arm/include/asm/fiq_glue.h +@@ -0,0 +1,30 @@ ++/* ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __ASM_FIQ_GLUE_H ++#define __ASM_FIQ_GLUE_H ++ ++struct fiq_glue_handler { ++ void (*fiq)(struct fiq_glue_handler *h, void *regs, void *svc_sp); ++ void (*resume)(struct fiq_glue_handler *h); ++}; ++ ++int fiq_glue_register_handler(struct fiq_glue_handler *handler); ++ ++#ifdef CONFIG_FIQ_GLUE ++void fiq_glue_resume(void); ++#else ++static inline void fiq_glue_resume(void) {} ++#endif ++ ++#endif +diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h +index 436e60b2..2740c2a2 100644 +--- a/arch/arm/include/asm/hardirq.h ++++ b/arch/arm/include/asm/hardirq.h +@@ -5,7 +5,7 @@ + #include + #include + +-#define NR_IPI 5 ++#define NR_IPI 6 + + typedef struct { + unsigned int __softirq_pending; +diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h +index c4c87bc1..bd2c6a53 100644 +--- a/arch/arm/include/asm/hardware/cache-l2x0.h ++++ b/arch/arm/include/asm/hardware/cache-l2x0.h +@@ -66,6 +66,7 @@ + #define L2X0_STNDBY_MODE_EN (1 << 0) + + /* Registers shifts and masks */ ++#define L2X0_CACHE_ID_REV_MASK (0x3f) + #define L2X0_CACHE_ID_PART_MASK (0xf << 6) + #define L2X0_CACHE_ID_PART_L210 (1 << 6) + #define L2X0_CACHE_ID_PART_L310 (3 << 6) +@@ -102,6 +103,8 @@ + + #define L2X0_ADDR_FILTER_EN 1 + ++#define REV_PL310_R2P0 4 ++ + #ifndef __ASSEMBLY__ + extern void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask); + #if defined(CONFIG_CACHE_L2X0) && defined(CONFIG_OF) +diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h +index 7ecd793b..dcf74d71 100644 +--- a/arch/arm/include/asm/hardware/coresight.h ++++ b/arch/arm/include/asm/hardware/coresight.h +@@ -17,15 +17,23 @@ + #define TRACER_ACCESSED_BIT 0 + #define TRACER_RUNNING_BIT 1 + #define TRACER_CYCLE_ACC_BIT 2 ++#define TRACER_TRACE_DATA_BIT 3 ++#define TRACER_TIMESTAMP_BIT 4 ++#define TRACER_BRANCHOUTPUT_BIT 5 ++#define TRACER_RETURN_STACK_BIT 6 + #define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT) + #define TRACER_RUNNING BIT(TRACER_RUNNING_BIT) + #define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT) ++#define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT) ++#define TRACER_TIMESTAMP BIT(TRACER_TIMESTAMP_BIT) ++#define TRACER_BRANCHOUTPUT BIT(TRACER_BRANCHOUTPUT_BIT) ++#define TRACER_RETURN_STACK BIT(TRACER_RETURN_STACK_BIT) + + #define TRACER_TIMEOUT 10000 + +-#define etm_writel(t, v, x) \ +- (__raw_writel((v), (t)->etm_regs + (x))) +-#define etm_readl(t, x) (__raw_readl((t)->etm_regs + (x))) ++#define etm_writel(t, id, v, x) \ ++ (__raw_writel((v), (t)->etm_regs[(id)] + (x))) ++#define etm_readl(t, id, x) (__raw_readl((t)->etm_regs[(id)] + (x))) + + /* CoreSight Management Registers */ + #define CSMR_LOCKACCESS 0xfb0 +@@ -43,7 +51,7 @@ + #define ETMCTRL_POWERDOWN 1 + #define ETMCTRL_PROGRAM (1 << 10) + #define ETMCTRL_PORTSEL (1 << 11) +-#define ETMCTRL_DO_CONTEXTID (3 << 14) ++#define ETMCTRL_CONTEXTIDSIZE(x) (((x) & 3) << 14) + #define ETMCTRL_PORTMASK1 (7 << 4) + #define ETMCTRL_PORTMASK2 (1 << 21) + #define ETMCTRL_PORTMASK (ETMCTRL_PORTMASK1 | ETMCTRL_PORTMASK2) +@@ -55,9 +63,12 @@ + #define ETMCTRL_DATA_DO_BOTH (ETMCTRL_DATA_DO_DATA | ETMCTRL_DATA_DO_ADDR) + #define ETMCTRL_BRANCH_OUTPUT (1 << 8) + #define ETMCTRL_CYCLEACCURATE (1 << 12) ++#define ETMCTRL_TIMESTAMP_EN (1 << 28) ++#define ETMCTRL_RETURN_STACK_EN (1 << 29) + + /* ETM configuration code register */ + #define ETMR_CONFCODE (0x04) ++#define ETMCCR_ETMIDR_PRESENT BIT(31) + + /* ETM trace start/stop resource control register */ + #define ETMR_TRACESSCTRL (0x18) +@@ -113,10 +124,25 @@ + #define ETMR_TRACEENCTRL 0x24 + #define ETMTE_INCLEXCL BIT(24) + #define ETMR_TRACEENEVT 0x20 +-#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \ +- ETMCTRL_DATA_DO_ADDR | \ +- ETMCTRL_BRANCH_OUTPUT | \ +- ETMCTRL_DO_CONTEXTID) ++ ++#define ETMR_VIEWDATAEVT 0x30 ++#define ETMR_VIEWDATACTRL1 0x34 ++#define ETMR_VIEWDATACTRL2 0x38 ++#define ETMR_VIEWDATACTRL3 0x3c ++#define ETMVDC3_EXCLONLY BIT(16) ++ ++#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT) ++ ++#define ETMR_ID 0x1e4 ++#define ETMIDR_VERSION(x) (((x) >> 4) & 0xff) ++#define ETMIDR_VERSION_3_1 0x21 ++#define ETMIDR_VERSION_PFT_1_0 0x30 ++ ++#define ETMR_CCE 0x1e8 ++#define ETMCCER_RETURN_STACK_IMPLEMENTED BIT(23) ++#define ETMCCER_TIMESTAMPING_IMPLEMENTED BIT(22) ++ ++#define ETMR_TRACEIDR 0x200 + + /* ETM management registers, "ETM Architecture", 3.5.24 */ + #define ETMMR_OSLAR 0x300 +@@ -140,14 +166,16 @@ + #define ETBFF_TRIGIN BIT(8) + #define ETBFF_TRIGEVT BIT(9) + #define ETBFF_TRIGFL BIT(10) ++#define ETBFF_STOPFL BIT(12) + + #define etb_writel(t, v, x) \ + (__raw_writel((v), (t)->etb_regs + (x))) + #define etb_readl(t, x) (__raw_readl((t)->etb_regs + (x))) + +-#define etm_lock(t) do { etm_writel((t), 0, CSMR_LOCKACCESS); } while (0) +-#define etm_unlock(t) \ +- do { etm_writel((t), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0) ++#define etm_lock(t, id) \ ++ do { etm_writel((t), (id), 0, CSMR_LOCKACCESS); } while (0) ++#define etm_unlock(t, id) \ ++ do { etm_writel((t), (id), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0) + + #define etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0) + #define etb_unlock(t) \ +diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h +index 35c21c37..3e0857a6 100644 +--- a/arch/arm/include/asm/irq.h ++++ b/arch/arm/include/asm/irq.h +@@ -30,6 +30,9 @@ extern void asm_do_IRQ(unsigned int, struct pt_regs *); + void handle_IRQ(unsigned int, struct pt_regs *); + void init_IRQ(void); + ++void arch_trigger_all_cpu_backtrace(void); ++#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace ++ + #endif + + #endif +diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h +new file mode 100644 +index 00000000..bca864ac +--- /dev/null ++++ b/arch/arm/include/asm/mach/mmc.h +@@ -0,0 +1,28 @@ ++/* ++ * arch/arm/include/asm/mach/mmc.h ++ */ ++#ifndef ASMARM_MACH_MMC_H ++#define ASMARM_MACH_MMC_H ++ ++#include ++#include ++#include ++ ++struct embedded_sdio_data { ++ struct sdio_cis cis; ++ struct sdio_cccr cccr; ++ struct sdio_embedded_func *funcs; ++ int num_funcs; ++}; ++ ++struct mmc_platform_data { ++ unsigned int ocr_mask; /* available voltages */ ++ int built_in; /* built-in device flag */ ++ int card_present; /* card detect state */ ++ u32 (*translate_vdd)(struct device *, unsigned int); ++ unsigned int (*status)(struct device *); ++ struct embedded_sdio_data *embedded_sdio; ++ int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); ++}; ++ ++#endif +diff --git a/arch/arm/include/asm/mmu.h b/arch/arm/include/asm/mmu.h +index b8e580a2..14965658 100644 +--- a/arch/arm/include/asm/mmu.h ++++ b/arch/arm/include/asm/mmu.h +@@ -34,11 +34,4 @@ typedef struct { + + #endif + +-/* +- * switch_mm() may do a full cache flush over the context switch, +- * so enable interrupts over the context switch to avoid high +- * latency. +- */ +-#define __ARCH_WANT_INTERRUPTS_ON_CTXSW +- + #endif +diff --git a/arch/arm/include/asm/mmu_context.h b/arch/arm/include/asm/mmu_context.h +index a0b3cac0..0306bc64 100644 +--- a/arch/arm/include/asm/mmu_context.h ++++ b/arch/arm/include/asm/mmu_context.h +@@ -43,45 +43,104 @@ void __check_kvm_seq(struct mm_struct *mm); + #define ASID_FIRST_VERSION (1 << ASID_BITS) + + extern unsigned int cpu_last_asid; +-#ifdef CONFIG_SMP +-DECLARE_PER_CPU(struct mm_struct *, current_mm); +-#endif + + void __init_new_context(struct task_struct *tsk, struct mm_struct *mm); + void __new_context(struct mm_struct *mm); ++void cpu_set_reserved_ttbr0(void); + +-static inline void check_context(struct mm_struct *mm) ++static inline void switch_new_context(struct mm_struct *mm) + { +- /* +- * This code is executed with interrupts enabled. Therefore, +- * mm->context.id cannot be updated to the latest ASID version +- * on a different CPU (and condition below not triggered) +- * without first getting an IPI to reset the context. The +- * alternative is to take a read_lock on mm->context.id_lock +- * (after changing its type to rwlock_t). +- */ +- if (unlikely((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) +- __new_context(mm); ++ unsigned long flags; ++ ++ __new_context(mm); ++ ++ local_irq_save(flags); ++ cpu_switch_mm(mm->pgd, mm); ++ local_irq_restore(flags); ++} + ++static inline void check_and_switch_context(struct mm_struct *mm, ++ struct task_struct *tsk) ++{ + if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq)) + __check_kvm_seq(mm); ++ ++ /* ++ * Required during context switch to avoid speculative page table ++ * walking with the wrong TTBR. ++ */ ++ cpu_set_reserved_ttbr0(); ++ ++ if (!((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) ++ /* ++ * The ASID is from the current generation, just switch to the ++ * new pgd. This condition is only true for calls from ++ * context_switch() and interrupts are already disabled. ++ */ ++ cpu_switch_mm(mm->pgd, mm); ++ else if (irqs_disabled()) ++ /* ++ * Defer the new ASID allocation until after the context ++ * switch critical region since __new_context() cannot be ++ * called with interrupts disabled (it sends IPIs). ++ */ ++ set_ti_thread_flag(task_thread_info(tsk), TIF_SWITCH_MM); ++ else ++ /* ++ * That is a direct call to switch_mm() or activate_mm() with ++ * interrupts enabled and a new context. ++ */ ++ switch_new_context(mm); + } + + #define init_new_context(tsk,mm) (__init_new_context(tsk,mm),0) + +-#else +- +-static inline void check_context(struct mm_struct *mm) ++#define finish_arch_post_lock_switch \ ++ finish_arch_post_lock_switch ++static inline void finish_arch_post_lock_switch(void) + { ++ if (test_and_clear_thread_flag(TIF_SWITCH_MM)) ++ switch_new_context(current->mm); ++} ++ ++#else /* !CONFIG_CPU_HAS_ASID */ ++ + #ifdef CONFIG_MMU ++ ++static inline void check_and_switch_context(struct mm_struct *mm, ++ struct task_struct *tsk) ++{ + if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq)) + __check_kvm_seq(mm); +-#endif ++ ++ if (irqs_disabled()) ++ /* ++ * cpu_switch_mm() needs to flush the VIVT caches. To avoid ++ * high interrupt latencies, defer the call and continue ++ * running with the old mm. Since we only support UP systems ++ * on non-ASID CPUs, the old mm will remain valid until the ++ * finish_arch_post_lock_switch() call. ++ */ ++ set_ti_thread_flag(task_thread_info(tsk), TIF_SWITCH_MM); ++ else ++ cpu_switch_mm(mm->pgd, mm); + } + ++#define finish_arch_post_lock_switch \ ++ finish_arch_post_lock_switch ++static inline void finish_arch_post_lock_switch(void) ++{ ++ if (test_and_clear_thread_flag(TIF_SWITCH_MM)) { ++ struct mm_struct *mm = current->mm; ++ cpu_switch_mm(mm->pgd, mm); ++ } ++} ++ ++#endif /* CONFIG_MMU */ ++ + #define init_new_context(tsk,mm) 0 + +-#endif ++#endif /* CONFIG_CPU_HAS_ASID */ + + #define destroy_context(mm) do { } while(0) + +@@ -119,12 +178,7 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, + __flush_icache_all(); + #endif + if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next) { +-#ifdef CONFIG_SMP +- struct mm_struct **crt_mm = &per_cpu(current_mm, cpu); +- *crt_mm = next; +-#endif +- check_context(next); +- cpu_switch_mm(next->pgd, next); ++ check_and_switch_context(next, tsk); + if (cache_is_vivt()) + cpumask_clear_cpu(cpu, mm_cpumask(prev)); + } +diff --git a/arch/arm/include/asm/rodata.h b/arch/arm/include/asm/rodata.h +new file mode 100644 +index 00000000..8c8add87 +--- /dev/null ++++ b/arch/arm/include/asm/rodata.h +@@ -0,0 +1,32 @@ ++/* ++ * arch/arm/include/asm/rodata.h ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * Author: Colin Cross ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef _ASMARM_RODATA_H ++#define _ASMARM_RODATA_H ++ ++#ifndef __ASSEMBLY__ ++ ++#ifdef CONFIG_DEBUG_RODATA ++ ++int set_memory_rw(unsigned long virt, int numpages); ++int set_memory_ro(unsigned long virt, int numpages); ++ ++void mark_rodata_ro(void); ++void set_kernel_text_rw(void); ++void set_kernel_text_ro(void); ++#else ++static inline void set_kernel_text_rw(void) { } ++static inline void set_kernel_text_ro(void) { } ++#endif ++ ++#endif ++ ++#endif +diff --git a/arch/arm/include/asm/sched_clock.h b/arch/arm/include/asm/sched_clock.h +index e3f75726..05b8e82e 100644 +--- a/arch/arm/include/asm/sched_clock.h ++++ b/arch/arm/include/asm/sched_clock.h +@@ -10,5 +10,7 @@ + + extern void sched_clock_postinit(void); + extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate); ++extern void setup_sched_clock_needs_suspend(u32 (*read)(void), int bits, ++ unsigned long rate); + + #endif +diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h +index ae292932..7f74b59f 100644 +--- a/arch/arm/include/asm/smp.h ++++ b/arch/arm/include/asm/smp.h +@@ -93,4 +93,6 @@ extern void platform_cpu_enable(unsigned int cpu); + extern void arch_send_call_function_single_ipi(int cpu); + extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); + ++extern void smp_send_all_cpu_backtrace(void); ++ + #endif /* ifndef __ASM_ARM_SMP_H */ +diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h +index 0f04d845..68388eb4 100644 +--- a/arch/arm/include/asm/thread_info.h ++++ b/arch/arm/include/asm/thread_info.h +@@ -153,6 +153,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, + #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ + #define TIF_RESTORE_SIGMASK 20 + #define TIF_SECCOMP 21 ++#define TIF_SWITCH_MM 22 /* deferred switch_mm */ + + #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) + #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) +diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c +index 36d20bd5..c5fb6c9f 100644 +--- a/arch/arm/kernel/etm.c ++++ b/arch/arm/kernel/etm.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -37,26 +38,37 @@ MODULE_AUTHOR("Alexander Shishkin"); + struct tracectx { + unsigned int etb_bufsz; + void __iomem *etb_regs; +- void __iomem *etm_regs; ++ void __iomem **etm_regs; ++ int etm_regs_count; + unsigned long flags; + int ncmppairs; + int etm_portsz; ++ int etm_contextid_size; ++ u32 etb_fc; ++ unsigned long range_start; ++ unsigned long range_end; ++ unsigned long data_range_start; ++ unsigned long data_range_end; ++ bool dump_initial_etb; + struct device *dev; + struct clk *emu_clk; + struct mutex mutex; + }; + +-static struct tracectx tracer; ++static struct tracectx tracer = { ++ .range_start = (unsigned long)_stext, ++ .range_end = (unsigned long)_etext, ++}; + + static inline bool trace_isrunning(struct tracectx *t) + { + return !!(t->flags & TRACER_RUNNING); + } + +-static int etm_setup_address_range(struct tracectx *t, int n, ++static int etm_setup_address_range(struct tracectx *t, int id, int n, + unsigned long start, unsigned long end, int exclude, int data) + { +- u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \ ++ u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY | + ETMAAT_NOVALCMP; + + if (n < 1 || n > t->ncmppairs) +@@ -72,95 +84,185 @@ static int etm_setup_address_range(struct tracectx *t, int n, + flags |= ETMAAT_IEXEC; + + /* first comparator for the range */ +- etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2)); +- etm_writel(t, start, ETMR_COMP_VAL(n * 2)); ++ etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2)); ++ etm_writel(t, id, start, ETMR_COMP_VAL(n * 2)); + + /* second comparator is right next to it */ +- etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); +- etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1)); +- +- flags = exclude ? ETMTE_INCLEXCL : 0; +- etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL); ++ etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); ++ etm_writel(t, id, end, ETMR_COMP_VAL(n * 2 + 1)); ++ ++ if (data) { ++ flags = exclude ? ETMVDC3_EXCLONLY : 0; ++ if (exclude) ++ n += 8; ++ etm_writel(t, id, flags | BIT(n), ETMR_VIEWDATACTRL3); ++ } else { ++ flags = exclude ? ETMTE_INCLEXCL : 0; ++ etm_writel(t, id, flags | (1 << n), ETMR_TRACEENCTRL); ++ } + + return 0; + } + +-static int trace_start(struct tracectx *t) ++static int trace_start_etm(struct tracectx *t, int id) + { + u32 v; + unsigned long timeout = TRACER_TIMEOUT; + +- etb_unlock(t); +- +- etb_writel(t, 0, ETBR_FORMATTERCTRL); +- etb_writel(t, 1, ETBR_CTRL); +- +- etb_lock(t); +- +- /* configure etm */ + v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz); ++ v |= ETMCTRL_CONTEXTIDSIZE(t->etm_contextid_size); + + if (t->flags & TRACER_CYCLE_ACC) + v |= ETMCTRL_CYCLEACCURATE; + +- etm_unlock(t); ++ if (t->flags & TRACER_BRANCHOUTPUT) ++ v |= ETMCTRL_BRANCH_OUTPUT; ++ ++ if (t->flags & TRACER_TRACE_DATA) ++ v |= ETMCTRL_DATA_DO_ADDR; ++ ++ if (t->flags & TRACER_TIMESTAMP) ++ v |= ETMCTRL_TIMESTAMP_EN; ++ ++ if (t->flags & TRACER_RETURN_STACK) ++ v |= ETMCTRL_RETURN_STACK_EN; + +- etm_writel(t, v, ETMR_CTRL); ++ etm_unlock(t, id); + +- while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) ++ etm_writel(t, id, v, ETMR_CTRL); ++ ++ while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) + ; + if (!timeout) { + dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); +- etm_lock(t); ++ etm_lock(t, id); + return -EFAULT; + } + +- etm_setup_address_range(t, 1, (unsigned long)_stext, +- (unsigned long)_etext, 0, 0); +- etm_writel(t, 0, ETMR_TRACEENCTRL2); +- etm_writel(t, 0, ETMR_TRACESSCTRL); +- etm_writel(t, 0x6f, ETMR_TRACEENEVT); ++ if (t->range_start || t->range_end) ++ etm_setup_address_range(t, id, 1, ++ t->range_start, t->range_end, 0, 0); ++ else ++ etm_writel(t, id, ETMTE_INCLEXCL, ETMR_TRACEENCTRL); ++ ++ etm_writel(t, id, 0, ETMR_TRACEENCTRL2); ++ etm_writel(t, id, 0, ETMR_TRACESSCTRL); ++ etm_writel(t, id, 0x6f, ETMR_TRACEENEVT); ++ ++ etm_writel(t, id, 0, ETMR_VIEWDATACTRL1); ++ etm_writel(t, id, 0, ETMR_VIEWDATACTRL2); ++ ++ if (t->data_range_start || t->data_range_end) ++ etm_setup_address_range(t, id, 2, t->data_range_start, ++ t->data_range_end, 0, 1); ++ else ++ etm_writel(t, id, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3); ++ ++ etm_writel(t, id, 0x6f, ETMR_VIEWDATAEVT); + + v &= ~ETMCTRL_PROGRAM; + v |= ETMCTRL_PORTSEL; + +- etm_writel(t, v, ETMR_CTRL); ++ etm_writel(t, id, v, ETMR_CTRL); + + timeout = TRACER_TIMEOUT; +- while (etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) ++ while (etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) + ; + if (!timeout) { + dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n"); +- etm_lock(t); ++ etm_lock(t, id); + return -EFAULT; + } + +- etm_lock(t); ++ etm_lock(t, id); ++ return 0; ++} ++ ++static int trace_start(struct tracectx *t) ++{ ++ int ret; ++ int id; ++ u32 etb_fc = t->etb_fc; ++ ++ etb_unlock(t); ++ ++ t->dump_initial_etb = false; ++ etb_writel(t, 0, ETBR_WRITEADDR); ++ etb_writel(t, etb_fc, ETBR_FORMATTERCTRL); ++ etb_writel(t, 1, ETBR_CTRL); ++ ++ etb_lock(t); ++ ++ /* configure etm(s) */ ++ for (id = 0; id < t->etm_regs_count; id++) { ++ ret = trace_start_etm(t, id); ++ if (ret) ++ return ret; ++ } + + t->flags |= TRACER_RUNNING; + + return 0; + } + +-static int trace_stop(struct tracectx *t) ++static int trace_stop_etm(struct tracectx *t, int id) + { + unsigned long timeout = TRACER_TIMEOUT; + +- etm_unlock(t); ++ etm_unlock(t, id); + +- etm_writel(t, 0x440, ETMR_CTRL); +- while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) ++ etm_writel(t, id, 0x440, ETMR_CTRL); ++ while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) + ; + if (!timeout) { +- dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); +- etm_lock(t); ++ dev_err(t->dev, ++ "etm%d: Waiting for progbit to assert timed out\n", ++ id); ++ etm_lock(t, id); + return -EFAULT; + } + +- etm_lock(t); ++ etm_lock(t, id); ++ return 0; ++} ++ ++static int trace_power_down_etm(struct tracectx *t, int id) ++{ ++ unsigned long timeout = TRACER_TIMEOUT; ++ etm_unlock(t, id); ++ while (!(etm_readl(t, id, ETMR_STATUS) & ETMST_PROGBIT) && --timeout) ++ ; ++ if (!timeout) { ++ dev_err(t->dev, "etm%d: Waiting for status progbit to assert timed out\n", ++ id); ++ etm_lock(t, id); ++ return -EFAULT; ++ } ++ ++ etm_writel(t, id, 0x441, ETMR_CTRL); ++ ++ etm_lock(t, id); ++ return 0; ++} ++ ++static int trace_stop(struct tracectx *t) ++{ ++ int id; ++ unsigned long timeout = TRACER_TIMEOUT; ++ u32 etb_fc = t->etb_fc; ++ ++ for (id = 0; id < t->etm_regs_count; id++) ++ trace_stop_etm(t, id); ++ ++ for (id = 0; id < t->etm_regs_count; id++) ++ trace_power_down_etm(t, id); + + etb_unlock(t); +- etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); ++ if (etb_fc) { ++ etb_fc |= ETBFF_STOPFL; ++ etb_writel(t, t->etb_fc, ETBR_FORMATTERCTRL); ++ } ++ etb_writel(t, etb_fc | ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); + + timeout = TRACER_TIMEOUT; + while (etb_readl(t, ETBR_FORMATTERCTRL) & +@@ -185,24 +287,15 @@ static int trace_stop(struct tracectx *t) + static int etb_getdatalen(struct tracectx *t) + { + u32 v; +- int rp, wp; ++ int wp; + + v = etb_readl(t, ETBR_STATUS); + + if (v & 1) + return t->etb_bufsz; + +- rp = etb_readl(t, ETBR_READADDR); + wp = etb_readl(t, ETBR_WRITEADDR); +- +- if (rp > wp) { +- etb_writel(t, 0, ETBR_READADDR); +- etb_writel(t, 0, ETBR_WRITEADDR); +- +- return 0; +- } +- +- return wp - rp; ++ return wp; + } + + /* sysrq+v will always stop the running trace and leave it at that */ +@@ -235,21 +328,18 @@ static void etm_dump(void) + printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM))); + printk(KERN_INFO "\n--- ETB buffer end ---\n"); + +- /* deassert the overflow bit */ +- etb_writel(t, 1, ETBR_CTRL); +- etb_writel(t, 0, ETBR_CTRL); +- +- etb_writel(t, 0, ETBR_TRIGGERCOUNT); +- etb_writel(t, 0, ETBR_READADDR); +- etb_writel(t, 0, ETBR_WRITEADDR); +- + etb_lock(t); + } + + static void sysrq_etm_dump(int key) + { ++ if (!mutex_trylock(&tracer.mutex)) { ++ printk(KERN_INFO "Tracing hardware busy\n"); ++ return; ++ } + dev_dbg(tracer.dev, "Dumping ETB buffer\n"); + etm_dump(); ++ mutex_unlock(&tracer.mutex); + } + + static struct sysrq_key_op sysrq_etm_op = { +@@ -276,6 +366,10 @@ static ssize_t etb_read(struct file *file, char __user *data, + struct tracectx *t = file->private_data; + u32 first = 0; + u32 *buf; ++ int wpos; ++ int skip; ++ long wlength; ++ loff_t pos = *ppos; + + mutex_lock(&t->mutex); + +@@ -287,31 +381,39 @@ static ssize_t etb_read(struct file *file, char __user *data, + etb_unlock(t); + + total = etb_getdatalen(t); ++ if (total == 0 && t->dump_initial_etb) ++ total = t->etb_bufsz; + if (total == t->etb_bufsz) + first = etb_readl(t, ETBR_WRITEADDR); + ++ if (pos > total * 4) { ++ skip = 0; ++ wpos = total; ++ } else { ++ skip = (int)pos % 4; ++ wpos = (int)pos / 4; ++ } ++ total -= wpos; ++ first = (first + wpos) % t->etb_bufsz; ++ + etb_writel(t, first, ETBR_READADDR); + +- length = min(total * 4, (int)len); +- buf = vmalloc(length); ++ wlength = min(total, DIV_ROUND_UP(skip + (int)len, 4)); ++ length = min(total * 4 - skip, (int)len); ++ buf = vmalloc(wlength * 4); + +- dev_dbg(t->dev, "ETB buffer length: %d\n", total); ++ dev_dbg(t->dev, "ETB read %ld bytes to %lld from %ld words at %d\n", ++ length, pos, wlength, first); ++ dev_dbg(t->dev, "ETB buffer length: %d\n", total + wpos); + dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS)); +- for (i = 0; i < length / 4; i++) ++ for (i = 0; i < wlength; i++) + buf[i] = etb_readl(t, ETBR_READMEM); + +- /* the only way to deassert overflow bit in ETB status is this */ +- etb_writel(t, 1, ETBR_CTRL); +- etb_writel(t, 0, ETBR_CTRL); +- +- etb_writel(t, 0, ETBR_WRITEADDR); +- etb_writel(t, 0, ETBR_READADDR); +- etb_writel(t, 0, ETBR_TRIGGERCOUNT); +- + etb_lock(t); + +- length -= copy_to_user(data, buf, length); ++ length -= copy_to_user(data, (u8 *)buf + skip, length); + vfree(buf); ++ *ppos = pos + length; + + out: + mutex_unlock(&t->mutex); +@@ -348,28 +450,17 @@ static int __devinit etb_probe(struct amba_device *dev, const struct amba_id *id + if (ret) + goto out; + ++ mutex_lock(&t->mutex); + t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); + if (!t->etb_regs) { + ret = -ENOMEM; + goto out_release; + } + ++ t->dev = &dev->dev; ++ t->dump_initial_etb = true; + amba_set_drvdata(dev, t); + +- etb_miscdev.parent = &dev->dev; +- +- ret = misc_register(&etb_miscdev); +- if (ret) +- goto out_unmap; +- +- t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); +- if (IS_ERR(t->emu_clk)) { +- dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); +- return -EFAULT; +- } +- +- clk_enable(t->emu_clk); +- + etb_unlock(t); + t->etb_bufsz = etb_readl(t, ETBR_DEPTH); + dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz); +@@ -378,6 +469,20 @@ static int __devinit etb_probe(struct amba_device *dev, const struct amba_id *id + etb_writel(t, 0, ETBR_CTRL); + etb_writel(t, 0x1000, ETBR_FORMATTERCTRL); + etb_lock(t); ++ mutex_unlock(&t->mutex); ++ ++ etb_miscdev.parent = &dev->dev; ++ ++ ret = misc_register(&etb_miscdev); ++ if (ret) ++ goto out_unmap; ++ ++ /* Get optional clock. Currently used to select clock source on omap3 */ ++ t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); ++ if (IS_ERR(t->emu_clk)) ++ dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); ++ else ++ clk_enable(t->emu_clk); + + dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n"); + +@@ -385,10 +490,13 @@ out: + return ret; + + out_unmap: ++ mutex_lock(&t->mutex); + amba_set_drvdata(dev, NULL); + iounmap(t->etb_regs); ++ t->etb_regs = NULL; + + out_release: ++ mutex_unlock(&t->mutex); + amba_release_regions(dev); + + return ret; +@@ -403,8 +511,10 @@ static int etb_remove(struct amba_device *dev) + iounmap(t->etb_regs); + t->etb_regs = NULL; + +- clk_disable(t->emu_clk); +- clk_put(t->emu_clk); ++ if (!IS_ERR(t->emu_clk)) { ++ clk_disable(t->emu_clk); ++ clk_put(t->emu_clk); ++ } + + amba_release_regions(dev); + +@@ -448,7 +558,10 @@ static ssize_t trace_running_store(struct kobject *kobj, + return -EINVAL; + + mutex_lock(&tracer.mutex); +- ret = value ? trace_start(&tracer) : trace_stop(&tracer); ++ if (!tracer.etb_regs) ++ ret = -ENODEV; ++ else ++ ret = value ? trace_start(&tracer) : trace_stop(&tracer); + mutex_unlock(&tracer.mutex); + + return ret ? : n; +@@ -463,36 +576,50 @@ static ssize_t trace_info_show(struct kobject *kobj, + { + u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st; + int datalen; ++ int id; ++ int ret; + +- etb_unlock(&tracer); +- datalen = etb_getdatalen(&tracer); +- etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); +- etb_ra = etb_readl(&tracer, ETBR_READADDR); +- etb_st = etb_readl(&tracer, ETBR_STATUS); +- etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); +- etb_lock(&tracer); +- +- etm_unlock(&tracer); +- etm_ctrl = etm_readl(&tracer, ETMR_CTRL); +- etm_st = etm_readl(&tracer, ETMR_STATUS); +- etm_lock(&tracer); ++ mutex_lock(&tracer.mutex); ++ if (tracer.etb_regs) { ++ etb_unlock(&tracer); ++ datalen = etb_getdatalen(&tracer); ++ etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); ++ etb_ra = etb_readl(&tracer, ETBR_READADDR); ++ etb_st = etb_readl(&tracer, ETBR_STATUS); ++ etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); ++ etb_lock(&tracer); ++ } else { ++ etb_wa = etb_ra = etb_st = etb_fc = ~0; ++ datalen = -1; ++ } + +- return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" ++ ret = sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" + "ETBR_WRITEADDR:\t%08x\n" + "ETBR_READADDR:\t%08x\n" + "ETBR_STATUS:\t%08x\n" +- "ETBR_FORMATTERCTRL:\t%08x\n" +- "ETMR_CTRL:\t%08x\n" +- "ETMR_STATUS:\t%08x\n", ++ "ETBR_FORMATTERCTRL:\t%08x\n", + datalen, + tracer.ncmppairs, + etb_wa, + etb_ra, + etb_st, +- etb_fc, ++ etb_fc ++ ); ++ ++ for (id = 0; id < tracer.etm_regs_count; id++) { ++ etm_unlock(&tracer, id); ++ etm_ctrl = etm_readl(&tracer, id, ETMR_CTRL); ++ etm_st = etm_readl(&tracer, id, ETMR_STATUS); ++ etm_lock(&tracer, id); ++ ret += sprintf(buf + ret, "ETMR_CTRL:\t%08x\n" ++ "ETMR_STATUS:\t%08x\n", + etm_ctrl, + etm_st + ); ++ } ++ mutex_unlock(&tracer.mutex); ++ ++ return ret; + } + + static struct kobj_attribute trace_info_attr = +@@ -531,42 +658,260 @@ static ssize_t trace_mode_store(struct kobject *kobj, + static struct kobj_attribute trace_mode_attr = + __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store); + ++static ssize_t trace_contextid_size_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ /* 0: No context id tracing, 1: One byte, 2: Two bytes, 3: Four bytes */ ++ return sprintf(buf, "%d\n", (1 << tracer.etm_contextid_size) >> 1); ++} ++ ++static ssize_t trace_contextid_size_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ unsigned int contextid_size; ++ ++ if (sscanf(buf, "%u", &contextid_size) != 1) ++ return -EINVAL; ++ ++ if (contextid_size == 3 || contextid_size > 4) ++ return -EINVAL; ++ ++ mutex_lock(&tracer.mutex); ++ tracer.etm_contextid_size = fls(contextid_size); ++ mutex_unlock(&tracer.mutex); ++ ++ return n; ++} ++ ++static struct kobj_attribute trace_contextid_size_attr = ++ __ATTR(trace_contextid_size, 0644, ++ trace_contextid_size_show, trace_contextid_size_store); ++ ++static ssize_t trace_branch_output_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_BRANCHOUTPUT)); ++} ++ ++static ssize_t trace_branch_output_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ unsigned int branch_output; ++ ++ if (sscanf(buf, "%u", &branch_output) != 1) ++ return -EINVAL; ++ ++ mutex_lock(&tracer.mutex); ++ if (branch_output) { ++ tracer.flags |= TRACER_BRANCHOUTPUT; ++ /* Branch broadcasting is incompatible with the return stack */ ++ tracer.flags &= ~TRACER_RETURN_STACK; ++ } else { ++ tracer.flags &= ~TRACER_BRANCHOUTPUT; ++ } ++ mutex_unlock(&tracer.mutex); ++ ++ return n; ++} ++ ++static struct kobj_attribute trace_branch_output_attr = ++ __ATTR(trace_branch_output, 0644, ++ trace_branch_output_show, trace_branch_output_store); ++ ++static ssize_t trace_return_stack_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_RETURN_STACK)); ++} ++ ++static ssize_t trace_return_stack_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ unsigned int return_stack; ++ ++ if (sscanf(buf, "%u", &return_stack) != 1) ++ return -EINVAL; ++ ++ mutex_lock(&tracer.mutex); ++ if (return_stack) { ++ tracer.flags |= TRACER_RETURN_STACK; ++ /* Return stack is incompatible with branch broadcasting */ ++ tracer.flags &= ~TRACER_BRANCHOUTPUT; ++ } else { ++ tracer.flags &= ~TRACER_RETURN_STACK; ++ } ++ mutex_unlock(&tracer.mutex); ++ ++ return n; ++} ++ ++static struct kobj_attribute trace_return_stack_attr = ++ __ATTR(trace_return_stack, 0644, ++ trace_return_stack_show, trace_return_stack_store); ++ ++static ssize_t trace_timestamp_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_TIMESTAMP)); ++} ++ ++static ssize_t trace_timestamp_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ unsigned int timestamp; ++ ++ if (sscanf(buf, "%u", ×tamp) != 1) ++ return -EINVAL; ++ ++ mutex_lock(&tracer.mutex); ++ if (timestamp) ++ tracer.flags |= TRACER_TIMESTAMP; ++ else ++ tracer.flags &= ~TRACER_TIMESTAMP; ++ mutex_unlock(&tracer.mutex); ++ ++ return n; ++} ++ ++static struct kobj_attribute trace_timestamp_attr = ++ __ATTR(trace_timestamp, 0644, ++ trace_timestamp_show, trace_timestamp_store); ++ ++static ssize_t trace_range_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%08lx %08lx\n", ++ tracer.range_start, tracer.range_end); ++} ++ ++static ssize_t trace_range_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ unsigned long range_start, range_end; ++ ++ if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2) ++ return -EINVAL; ++ ++ mutex_lock(&tracer.mutex); ++ tracer.range_start = range_start; ++ tracer.range_end = range_end; ++ mutex_unlock(&tracer.mutex); ++ ++ return n; ++} ++ ++ ++static struct kobj_attribute trace_range_attr = ++ __ATTR(trace_range, 0644, trace_range_show, trace_range_store); ++ ++static ssize_t trace_data_range_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ unsigned long range_start; ++ u64 range_end; ++ mutex_lock(&tracer.mutex); ++ range_start = tracer.data_range_start; ++ range_end = tracer.data_range_end; ++ if (!range_end && (tracer.flags & TRACER_TRACE_DATA)) ++ range_end = 0x100000000ULL; ++ mutex_unlock(&tracer.mutex); ++ return sprintf(buf, "%08lx %08llx\n", range_start, range_end); ++} ++ ++static ssize_t trace_data_range_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ unsigned long range_start; ++ u64 range_end; ++ ++ if (sscanf(buf, "%lx %llx", &range_start, &range_end) != 2) ++ return -EINVAL; ++ ++ mutex_lock(&tracer.mutex); ++ tracer.data_range_start = range_start; ++ tracer.data_range_end = (unsigned long)range_end; ++ if (range_end) ++ tracer.flags |= TRACER_TRACE_DATA; ++ else ++ tracer.flags &= ~TRACER_TRACE_DATA; ++ mutex_unlock(&tracer.mutex); ++ ++ return n; ++} ++ ++ ++static struct kobj_attribute trace_data_range_attr = ++ __ATTR(trace_data_range, 0644, ++ trace_data_range_show, trace_data_range_store); ++ + static int __devinit etm_probe(struct amba_device *dev, const struct amba_id *id) + { + struct tracectx *t = &tracer; + int ret = 0; ++ void __iomem **new_regs; ++ int new_count; ++ u32 etmccr; ++ u32 etmidr; ++ u32 etmccer = 0; ++ u8 etm_version = 0; ++ ++ mutex_lock(&t->mutex); ++ new_count = t->etm_regs_count + 1; ++ new_regs = krealloc(t->etm_regs, ++ sizeof(t->etm_regs[0]) * new_count, GFP_KERNEL); + +- if (t->etm_regs) { +- dev_dbg(&dev->dev, "ETM already initialized\n"); +- ret = -EBUSY; ++ if (!new_regs) { ++ dev_dbg(&dev->dev, "Failed to allocate ETM register array\n"); ++ ret = -ENOMEM; + goto out; + } ++ t->etm_regs = new_regs; + + ret = amba_request_regions(dev, NULL); + if (ret) + goto out; + +- t->etm_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); +- if (!t->etm_regs) { ++ t->etm_regs[t->etm_regs_count] = ++ ioremap_nocache(dev->res.start, resource_size(&dev->res)); ++ if (!t->etm_regs[t->etm_regs_count]) { + ret = -ENOMEM; + goto out_release; + } + +- amba_set_drvdata(dev, t); ++ amba_set_drvdata(dev, t->etm_regs[t->etm_regs_count]); + +- mutex_init(&t->mutex); +- t->dev = &dev->dev; +- t->flags = TRACER_CYCLE_ACC; ++ t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA | TRACER_BRANCHOUTPUT; + t->etm_portsz = 1; ++ t->etm_contextid_size = 3; + +- etm_unlock(t); +- (void)etm_readl(t, ETMMR_PDSR); ++ etm_unlock(t, t->etm_regs_count); ++ (void)etm_readl(t, t->etm_regs_count, ETMMR_PDSR); + /* dummy first read */ +- (void)etm_readl(&tracer, ETMMR_OSSRR); +- +- t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf; +- etm_writel(t, 0x440, ETMR_CTRL); +- etm_lock(t); ++ (void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR); ++ ++ etmccr = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE); ++ t->ncmppairs = etmccr & 0xf; ++ if (etmccr & ETMCCR_ETMIDR_PRESENT) { ++ etmidr = etm_readl(t, t->etm_regs_count, ETMR_ID); ++ etm_version = ETMIDR_VERSION(etmidr); ++ if (etm_version >= ETMIDR_VERSION_3_1) ++ etmccer = etm_readl(t, t->etm_regs_count, ETMR_CCE); ++ } ++ etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL); ++ etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR); ++ etm_lock(t, t->etm_regs_count); + + ret = sysfs_create_file(&dev->dev.kobj, + &trace_running_attr.attr); +@@ -582,36 +927,101 @@ static int __devinit etm_probe(struct amba_device *dev, const struct amba_id *id + if (ret) + dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n"); + +- dev_dbg(t->dev, "ETM AMBA driver initialized.\n"); ++ ret = sysfs_create_file(&dev->dev.kobj, ++ &trace_contextid_size_attr.attr); ++ if (ret) ++ dev_dbg(&dev->dev, ++ "Failed to create trace_contextid_size in sysfs\n"); ++ ++ ret = sysfs_create_file(&dev->dev.kobj, ++ &trace_branch_output_attr.attr); ++ if (ret) ++ dev_dbg(&dev->dev, ++ "Failed to create trace_branch_output in sysfs\n"); ++ ++ if (etmccer & ETMCCER_RETURN_STACK_IMPLEMENTED) { ++ ret = sysfs_create_file(&dev->dev.kobj, ++ &trace_return_stack_attr.attr); ++ if (ret) ++ dev_dbg(&dev->dev, ++ "Failed to create trace_return_stack in sysfs\n"); ++ } ++ ++ if (etmccer & ETMCCER_TIMESTAMPING_IMPLEMENTED) { ++ ret = sysfs_create_file(&dev->dev.kobj, ++ &trace_timestamp_attr.attr); ++ if (ret) ++ dev_dbg(&dev->dev, ++ "Failed to create trace_timestamp in sysfs\n"); ++ } ++ ++ ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr); ++ if (ret) ++ dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n"); ++ ++ if (etm_version < ETMIDR_VERSION_PFT_1_0) { ++ ret = sysfs_create_file(&dev->dev.kobj, ++ &trace_data_range_attr.attr); ++ if (ret) ++ dev_dbg(&dev->dev, ++ "Failed to create trace_data_range in sysfs\n"); ++ } else { ++ tracer.flags &= ~TRACER_TRACE_DATA; ++ } ++ ++ dev_dbg(&dev->dev, "ETM AMBA driver initialized.\n"); ++ ++ /* Enable formatter if there are multiple trace sources */ ++ if (new_count > 1) ++ t->etb_fc = ETBFF_ENFCONT | ETBFF_ENFTC; ++ ++ t->etm_regs_count = new_count; + + out: ++ mutex_unlock(&t->mutex); + return ret; + + out_unmap: + amba_set_drvdata(dev, NULL); +- iounmap(t->etm_regs); ++ iounmap(t->etm_regs[t->etm_regs_count]); + + out_release: + amba_release_regions(dev); + ++ mutex_unlock(&t->mutex); + return ret; + } + + static int etm_remove(struct amba_device *dev) + { +- struct tracectx *t = amba_get_drvdata(dev); ++ int i; ++ struct tracectx *t = &tracer; ++ void __iomem *etm_regs = amba_get_drvdata(dev); ++ ++ sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr); ++ sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr); ++ sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr); ++ sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr); ++ sysfs_remove_file(&dev->dev.kobj, &trace_data_range_attr.attr); + + amba_set_drvdata(dev, NULL); + +- iounmap(t->etm_regs); +- t->etm_regs = NULL; ++ mutex_lock(&t->mutex); ++ for (i = 0; i < t->etm_regs_count; i++) ++ if (t->etm_regs[i] == etm_regs) ++ break; ++ for (; i < t->etm_regs_count - 1; i++) ++ t->etm_regs[i] = t->etm_regs[i + 1]; ++ t->etm_regs_count--; ++ if (!t->etm_regs_count) { ++ kfree(t->etm_regs); ++ t->etm_regs = NULL; ++ } ++ mutex_unlock(&t->mutex); + ++ iounmap(etm_regs); + amba_release_regions(dev); + +- sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr); +- sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr); +- sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr); +- + return 0; + } + +@@ -620,6 +1030,10 @@ static struct amba_id etm_ids[] = { + .id = 0x0003b921, + .mask = 0x0007ffff, + }, ++ { ++ .id = 0x0003b950, ++ .mask = 0x0007ffff, ++ }, + { 0, 0 }, + }; + +@@ -637,6 +1051,8 @@ static int __init etm_init(void) + { + int retval; + ++ mutex_init(&tracer.mutex); ++ + retval = amba_driver_register(&etb_driver); + if (retval) { + printk(KERN_ERR "Failed to register etb\n"); +diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c +index df0bf0c8..6a740a93 100644 +--- a/arch/arm/kernel/ftrace.c ++++ b/arch/arm/kernel/ftrace.c +@@ -13,6 +13,7 @@ + */ + + #include ++#include + #include + + #include +@@ -63,6 +64,20 @@ static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) + } + #endif + ++int ftrace_arch_code_modify_prepare(void) ++{ ++ set_kernel_text_rw(); ++ set_all_modules_text_rw(); ++ return 0; ++} ++ ++int ftrace_arch_code_modify_post_process(void) ++{ ++ set_all_modules_text_ro(); ++ set_kernel_text_ro(); ++ return 0; ++} ++ + static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) + { + return arm_gen_branch_link(pc, addr); +@@ -179,19 +194,20 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, + old = *parent; + *parent = return_hooker; + +- err = ftrace_push_return_trace(old, self_addr, &trace.depth, +- frame_pointer); +- if (err == -EBUSY) { +- *parent = old; +- return; +- } +- + trace.func = self_addr; ++ trace.depth = current->curr_ret_stack + 1; + + /* Only trace if the calling function expects to */ + if (!ftrace_graph_entry(&trace)) { +- current->curr_ret_stack--; + *parent = old; ++ return; ++ } ++ ++ err = ftrace_push_return_trace(old, self_addr, &trace.depth, ++ frame_pointer); ++ if (err == -EBUSY) { ++ *parent = old; ++ return; + } + } + +diff --git a/arch/arm/kernel/leds.c b/arch/arm/kernel/leds.c +index 1911dae1..2050399e 100644 +--- a/arch/arm/kernel/leds.c ++++ b/arch/arm/kernel/leds.c +@@ -10,6 +10,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + +@@ -103,6 +105,25 @@ static struct syscore_ops leds_syscore_ops = { + .resume = leds_resume, + }; + ++static int leds_idle_notifier(struct notifier_block *nb, unsigned long val, ++ void *data) ++{ ++ switch (val) { ++ case IDLE_START: ++ leds_event(led_idle_start); ++ break; ++ case IDLE_END: ++ leds_event(led_idle_end); ++ break; ++ } ++ ++ return 0; ++} ++ ++static struct notifier_block leds_idle_nb = { ++ .notifier_call = leds_idle_notifier, ++}; ++ + static int __init leds_init(void) + { + int ret; +@@ -111,8 +132,11 @@ static int __init leds_init(void) + ret = device_register(&leds_device); + if (ret == 0) + ret = device_create_file(&leds_device, &dev_attr_event); +- if (ret == 0) ++ if (ret == 0) { + register_syscore_ops(&leds_syscore_ops); ++ idle_notifier_register(&leds_idle_nb); ++ } ++ + return ret; + } + +diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c +index 48f36246..ff6c55df 100644 +--- a/arch/arm/kernel/process.c ++++ b/arch/arm/kernel/process.c +@@ -31,9 +31,9 @@ + #include + #include + #include ++#include + + #include +-#include + #include + #include + #include +@@ -60,6 +60,18 @@ extern void setup_mm_for_reboot(void); + + static volatile int hlt_counter; + ++#ifdef CONFIG_SMP ++void arch_trigger_all_cpu_backtrace(void) ++{ ++ smp_send_all_cpu_backtrace(); ++} ++#else ++void arch_trigger_all_cpu_backtrace(void) ++{ ++ dump_stack(); ++} ++#endif ++ + void disable_hlt(void) + { + hlt_counter++; +@@ -92,6 +104,31 @@ __setup("hlt", hlt_setup); + extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); + typedef void (*phys_reset_t)(unsigned long); + ++#ifdef CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART ++void arm_machine_flush_console(void) ++{ ++ printk("\n"); ++ pr_emerg("Restarting %s\n", linux_banner); ++ if (console_trylock()) { ++ console_unlock(); ++ return; ++ } ++ ++ mdelay(50); ++ ++ local_irq_disable(); ++ if (!console_trylock()) ++ pr_emerg("arm_restart: Console was locked! Busting\n"); ++ else ++ pr_emerg("arm_restart: Console was locked!\n"); ++ console_unlock(); ++} ++#else ++void arm_machine_flush_console(void) ++{ ++} ++#endif ++ + /* + * A temporary stack to use for CPU reset. This is static so that we + * don't clobber it with the identity mapping. When running with this +@@ -207,9 +244,9 @@ void cpu_idle(void) + + /* endless idle loop with no priority at all */ + while (1) { ++ idle_notifier_call_chain(IDLE_START); + tick_nohz_idle_enter(); + rcu_idle_enter(); +- leds_event(led_idle_start); + while (!need_resched()) { + #ifdef CONFIG_HOTPLUG_CPU + if (cpu_is_offline(smp_processor_id())) +@@ -240,9 +277,9 @@ void cpu_idle(void) + } else + local_irq_enable(); + } +- leds_event(led_idle_end); + rcu_idle_exit(); + tick_nohz_idle_exit(); ++ idle_notifier_call_chain(IDLE_END); + schedule_preempt_disabled(); + } + } +@@ -260,6 +297,15 @@ __setup("reboot=", reboot_setup); + void machine_shutdown(void) + { + #ifdef CONFIG_SMP ++ /* ++ * Disable preemption so we're guaranteed to ++ * run to power off or reboot and prevent ++ * the possibility of switching to another ++ * thread that might wind up blocking on ++ * one of the stopped CPUs. ++ */ ++ preempt_disable(); ++ + smp_send_stop(); + #endif + } +@@ -282,6 +328,10 @@ void machine_restart(char *cmd) + { + machine_shutdown(); + ++ /* Flush the console to make sure all the relevant messages make it ++ * out to the console drivers */ ++ arm_machine_flush_console(); ++ + arm_pm_restart(reboot_mode, cmd); + + /* Give a grace period for failure to restart of 1s */ +@@ -293,6 +343,77 @@ void machine_restart(char *cmd) + while (1); + } + ++/* ++ * dump a block of kernel memory from around the given address ++ */ ++static void show_data(unsigned long addr, int nbytes, const char *name) ++{ ++ int i, j; ++ int nlines; ++ u32 *p; ++ ++ /* ++ * don't attempt to dump non-kernel addresses or ++ * values that are probably just small negative numbers ++ */ ++ if (addr < PAGE_OFFSET || addr > -256UL) ++ return; ++ ++ printk("\n%s: %#lx:\n", name, addr); ++ ++ /* ++ * round address down to a 32 bit boundary ++ * and always dump a multiple of 32 bytes ++ */ ++ p = (u32 *)(addr & ~(sizeof(u32) - 1)); ++ nbytes += (addr & (sizeof(u32) - 1)); ++ nlines = (nbytes + 31) / 32; ++ ++ ++ for (i = 0; i < nlines; i++) { ++ /* ++ * just display low 16 bits of address to keep ++ * each line of the dump < 80 characters ++ */ ++ printk("%04lx ", (unsigned long)p & 0xffff); ++ for (j = 0; j < 8; j++) { ++ u32 data; ++ if (probe_kernel_address(p, data)) { ++ printk(" ********"); ++ } else { ++ printk(" %08x", data); ++ } ++ ++p; ++ } ++ printk("\n"); ++ } ++} ++ ++static void show_extra_register_data(struct pt_regs *regs, int nbytes) ++{ ++ mm_segment_t fs; ++ ++ fs = get_fs(); ++ set_fs(KERNEL_DS); ++ show_data(regs->ARM_pc - nbytes, nbytes * 2, "PC"); ++ show_data(regs->ARM_lr - nbytes, nbytes * 2, "LR"); ++ show_data(regs->ARM_sp - nbytes, nbytes * 2, "SP"); ++ show_data(regs->ARM_ip - nbytes, nbytes * 2, "IP"); ++ show_data(regs->ARM_fp - nbytes, nbytes * 2, "FP"); ++ show_data(regs->ARM_r0 - nbytes, nbytes * 2, "R0"); ++ show_data(regs->ARM_r1 - nbytes, nbytes * 2, "R1"); ++ show_data(regs->ARM_r2 - nbytes, nbytes * 2, "R2"); ++ show_data(regs->ARM_r3 - nbytes, nbytes * 2, "R3"); ++ show_data(regs->ARM_r4 - nbytes, nbytes * 2, "R4"); ++ show_data(regs->ARM_r5 - nbytes, nbytes * 2, "R5"); ++ show_data(regs->ARM_r6 - nbytes, nbytes * 2, "R6"); ++ show_data(regs->ARM_r7 - nbytes, nbytes * 2, "R7"); ++ show_data(regs->ARM_r8 - nbytes, nbytes * 2, "R8"); ++ show_data(regs->ARM_r9 - nbytes, nbytes * 2, "R9"); ++ show_data(regs->ARM_r10 - nbytes, nbytes * 2, "R10"); ++ set_fs(fs); ++} ++ + void __show_regs(struct pt_regs *regs) + { + unsigned long flags; +@@ -352,6 +473,8 @@ void __show_regs(struct pt_regs *regs) + printk("Control: %08x%s\n", ctrl, buf); + } + #endif ++ ++ show_extra_register_data(regs, 128); + } + + void show_regs(struct pt_regs * regs) +diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c +index 6bbf936b..63bc22c8 100644 +--- a/arch/arm/kernel/sched_clock.c ++++ b/arch/arm/kernel/sched_clock.c +@@ -21,6 +21,8 @@ struct clock_data { + u32 epoch_cyc_copy; + u32 mult; + u32 shift; ++ bool suspended; ++ bool needs_suspend; + }; + + static void sched_clock_poll(unsigned long wrap_ticks); +@@ -49,6 +51,9 @@ static unsigned long long cyc_to_sched_clock(u32 cyc, u32 mask) + u64 epoch_ns; + u32 epoch_cyc; + ++ if (cd.suspended) ++ return cd.epoch_ns; ++ + /* + * Load the epoch_cyc and epoch_ns atomically. We do this by + * ensuring that we always write epoch_cyc, epoch_ns and +@@ -98,6 +103,13 @@ static void sched_clock_poll(unsigned long wrap_ticks) + update_sched_clock(); + } + ++void __init setup_sched_clock_needs_suspend(u32 (*read)(void), int bits, ++ unsigned long rate) ++{ ++ setup_sched_clock(read, bits, rate); ++ cd.needs_suspend = true; ++} ++ + void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) + { + unsigned long r, w; +@@ -169,11 +181,23 @@ void __init sched_clock_postinit(void) + static int sched_clock_suspend(void) + { + sched_clock_poll(sched_clock_timer.data); ++ if (cd.needs_suspend) ++ cd.suspended = true; + return 0; + } + ++static void sched_clock_resume(void) ++{ ++ if (cd.needs_suspend) { ++ cd.epoch_cyc = read_sched_clock(); ++ cd.epoch_cyc_copy = cd.epoch_cyc; ++ cd.suspended = false; ++ } ++} ++ + static struct syscore_ops sched_clock_ops = { + .suspend = sched_clock_suspend, ++ .resume = sched_clock_resume, + }; + + static int __init sched_clock_syscore_init(void) +diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c +index ebfac782..5df79774 100644 +--- a/arch/arm/kernel/setup.c ++++ b/arch/arm/kernel/setup.c +@@ -575,6 +575,21 @@ static int __init early_mem(char *p) + size = memparse(p, &endp); + if (*endp == '@') + start = memparse(endp + 1, NULL); ++ ++#ifdef CONFIG_VIDEO_RESERVED_MEM_SIZE ++ /* ++ * Workaround for AK39xx H.264 decoder limitation which requires continous ++ * physical RAM which do NOT cross 32MB boundary (Decoder IP requirement). ++ * ++ * To avoid confusing developers, developer still pass something like ++ * mem=REAL_RAM_SIZE in command line. But we must skip memory reserved for ++ * H.264 decoder since they are NOT handled by normal kernel VM. They are ++ * manipulated by pmem-like drivers. ++ */ ++ if (meminfo.nr_banks == 0) { ++ size -= CONFIG_VIDEO_RESERVED_MEM_SIZE; ++ } ++#endif + + arm_add_memory(start, size); + +diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c +index d68d1b69..eb3a2912 100644 +--- a/arch/arm/kernel/signal.c ++++ b/arch/arm/kernel/signal.c +@@ -642,7 +642,7 @@ static void do_signal(struct pt_regs *regs, int syscall) + } + } + +- if (try_to_freeze()) ++ if (try_to_freeze_nowarn()) + goto no_signal; + + /* +diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c +index 8f14a1b8..22ad00ad 100644 +--- a/arch/arm/kernel/smp.c ++++ b/arch/arm/kernel/smp.c +@@ -56,6 +56,7 @@ enum ipi_msg_type { + IPI_CALL_FUNC, + IPI_CALL_FUNC_SINGLE, + IPI_CPU_STOP, ++ IPI_CPU_BACKTRACE, + }; + + static DECLARE_COMPLETION(cpu_running); +@@ -389,6 +390,7 @@ static const char *ipi_types[NR_IPI] = { + S(IPI_CALL_FUNC, "Function call interrupts"), + S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"), + S(IPI_CPU_STOP, "CPU stop interrupts"), ++ S(IPI_CPU_BACKTRACE, "CPU backtrace"), + }; + + void show_ipi_list(struct seq_file *p, int prec) +@@ -520,6 +522,58 @@ static void ipi_cpu_stop(unsigned int cpu) + cpu_relax(); + } + ++static cpumask_t backtrace_mask; ++static DEFINE_RAW_SPINLOCK(backtrace_lock); ++ ++/* "in progress" flag of arch_trigger_all_cpu_backtrace */ ++static unsigned long backtrace_flag; ++ ++void smp_send_all_cpu_backtrace(void) ++{ ++ unsigned int this_cpu = smp_processor_id(); ++ int i; ++ ++ if (test_and_set_bit(0, &backtrace_flag)) ++ /* ++ * If there is already a trigger_all_cpu_backtrace() in progress ++ * (backtrace_flag == 1), don't output double cpu dump infos. ++ */ ++ return; ++ ++ cpumask_copy(&backtrace_mask, cpu_online_mask); ++ cpu_clear(this_cpu, backtrace_mask); ++ ++ pr_info("Backtrace for cpu %d (current):\n", this_cpu); ++ dump_stack(); ++ ++ pr_info("\nsending IPI to all other CPUs:\n"); ++ smp_cross_call(&backtrace_mask, IPI_CPU_BACKTRACE); ++ ++ /* Wait for up to 10 seconds for all other CPUs to do the backtrace */ ++ for (i = 0; i < 10 * 1000; i++) { ++ if (cpumask_empty(&backtrace_mask)) ++ break; ++ mdelay(1); ++ } ++ ++ clear_bit(0, &backtrace_flag); ++ smp_mb__after_clear_bit(); ++} ++ ++/* ++ * ipi_cpu_backtrace - handle IPI from smp_send_all_cpu_backtrace() ++ */ ++static void ipi_cpu_backtrace(unsigned int cpu, struct pt_regs *regs) ++{ ++ if (cpu_isset(cpu, backtrace_mask)) { ++ raw_spin_lock(&backtrace_lock); ++ pr_warning("IPI backtrace for cpu %d\n", cpu); ++ show_regs(regs); ++ raw_spin_unlock(&backtrace_lock); ++ cpu_clear(cpu, backtrace_mask); ++ } ++} ++ + /* + * Main handler for inter-processor interrupts + */ +@@ -565,6 +619,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs) + irq_exit(); + break; + ++ case IPI_CPU_BACKTRACE: ++ ipi_cpu_backtrace(cpu, regs); ++ break; ++ + default: + printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n", + cpu, ipinr); +diff --git a/arch/arm/mach-ak39/Kconfig b/arch/arm/mach-ak39/Kconfig +new file mode 100644 +index 00000000..79decd10 +--- /dev/null ++++ b/arch/arm/mach-ak39/Kconfig +@@ -0,0 +1,73 @@ ++# linux/arch/arm/mach-ak39/Kconfig ++# ++# Copyright 2013 Anaka Microelectronics ++# ++# Licensed under GPLv2 ++ ++# define CPU TYPE FLAGS ++config CPU_AK3910 ++ bool ++config CPU_AK3916 ++ bool ++config CPU_AK3918 ++ bool ++ ++# Machine support ++choice ++ prompt "ANYKA AK39xx boards" ++ default ARCH_SDK3910 ++ depends on ARCH_AK39 ++ ++config ARCH_SDK3910 ++ bool "SDK3910" ++ depends on ARCH_AK39 ++ select CPU_AK3910 ++ help ++ Say Y here if you are using the SDK3910(Athena v2 borad) on chip AK3910 ++ ++config ARCH_AIMER39_AK3916 ++ bool "Aimer39 AK3916 board" ++ depends on ARCH_AK39 ++ select CPU_AK3916 ++ help ++ Say Y here if you are using the Aimer39_AK3916_MB_v1.0.0 board on chip AK3916 ++ ++config ARCH_AIMER39_AK3918 ++ bool "Aimer39 AK3918 board" ++ depends on ARCH_AK39 ++ select CPU_AK3918 ++ help ++ Say Y here if you are using the Aimer39_AK3918_MB_v1.0.0 board on chip AK3918 ++ ++endchoice ++ ++# aisc cpufreq set ++config ASIC_FREQ_VALUE ++ int "Config asic frequency(if asic>100MHz asic=vclk; else asic=vclk/2)" ++ range 45000000 120000000 ++ default 90000000 ++ depends on ARCH_AK39 ++ help ++ config ak39xx asic frequency when the kernel uncompress. ++ note: vclk be equal to even asic. ++ ++# power managerment ++comment "Power management" ++config AK39_PM ++ bool "ak39 Power Management support" ++ depends on PM && ARCH_AK39 ++ help ++ Say Y here if you want support power management for AK39. ++ ++config AK39_CPUFREQ ++ bool "ak39 Frequency scaling support" ++ depends on CPU_FREQ && ARCH_AK39 ++ help ++ Say Y here, CPU Frequency scaling support for AK39 SoC CPUs. ++ ++# chip extra controller lib ++config AK39_PWM_TIMER ++ bool "ak39 PWM/Timer Control support" ++ depends on ARCH_AK39 ++ help ++ Say Y here if you want support pwm control. +diff --git a/arch/arm/mach-ak39/Makefile b/arch/arm/mach-ak39/Makefile +new file mode 100644 +index 00000000..91413177 +--- /dev/null ++++ b/arch/arm/mach-ak39/Makefile +@@ -0,0 +1,29 @@ ++#linux/arch/arm/mach-ak39/Makefile ++# ++# Copyright 2013 Anyka Microelectronics ++# ++# Licensed under GPLv2 ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Core support for all Anyka SoCs ++obj-y += cpu.o time.o irq.o clock.o \ ++ l2cache.o devices.o \ ++ ak39-gpio.o gpio.o \ ++ adc.o \ ++ reset.o reboot.o \ ++ ++ ++# chip extra interface ++obj-$(CONFIG_AK39_PWM_TIMER) += pwm_timer.o ++ ++# Power Management support ++obj-$(CONFIG_AK39_PM) += pm.o ++ ++# Machin support ++obj-$(CONFIG_ARCH_SDK3910) += mach-sdk3910.o ++obj-$(CONFIG_ARCH_AIMER39_AK3916) += mach-aimer39_ak3916.o ++obj-$(CONFIG_ARCH_AIMER39_AK3918) += mach-aimer39_ak3918.o +diff --git a/arch/arm/mach-ak39/Makefile.boot b/arch/arm/mach-ak39/Makefile.boot +new file mode 100644 +index 00000000..f2e0fa5e +--- /dev/null ++++ b/arch/arm/mach-ak39/Makefile.boot +@@ -0,0 +1,11 @@ ++ifdef CONFIG_VIDEO_RESERVED_MEM_SIZE ++ __ZRELADDR := $(shell /bin/bash -c 'printf "0x%08x" \ ++ $$[$(CONFIG_RAM_BASE) + $(CONFIG_VIDEO_RESERVED_MEM_SIZE) + 0x8000]') ++__PARAMS_PHYS := $(shell /bin/bash -c 'printf "0x%08x" \ ++ $$[$(CONFIG_RAM_BASE) + $(CONFIG_VIDEO_RESERVED_MEM_SIZE) + 0x100]') ++ zreladdr-y := $(__ZRELADDR) ++params_phys-y := $(__PARAMS_PHYS) ++else ++ zreladdr-y := 0x80008000 ++params_phys-y := 0x80000100 ++endif +diff --git a/arch/arm/mach-ak39/adc.c b/arch/arm/mach-ak39/adc.c +new file mode 100644 +index 00000000..aa778fd9 +--- /dev/null ++++ b/arch/arm/mach-ak39/adc.c +@@ -0,0 +1,186 @@ ++/* ++ * ak_adc.c - ak ADC1 operation API ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static spinlock_t adc1_lock; ++ ++/** ++ * @BRIEF config saradc clk ++ * @AUTHOR Gao Wangsheng ++ * @DATE 2013-4-24 ++ * @PARAM div SARADC CLK = 12Mhz/(div+1); ++ * @PARAM div value always 2 ++ * @RETURN ++ * @NOTE: ++ */ ++static void adc1_clk_cfg(unsigned long div) ++{ ++ unsigned long saradc_chief_driven; ++ unsigned long saradc_module_driven; ++ ++ //reserve the driven value ++ saradc_chief_driven = ((REG32(SAR_IF_CFG_REG)) & (0x1<<0)); ++ saradc_module_driven = ((REG32(SAR_IF_CFG_REG)) & (0x7<<5)); ++ ++ //disable module chief driven by sar adc clk ++ REG32(SAR_IF_CFG_REG) &= (~(1<<0)); ++ ++ //disable Ain0_sampling Ain1_sampling Bat_sampling ++ REG32(SAR_IF_CFG_REG) &= (~(0x7<<5)); ++ ++ //close sar adc clk ++ REG32(AD_DA_CLK1_REG) &= (~(0x1<<3)); ++ ++ //cofig div ++ REG32(AD_DA_CLK1_REG) &= (~0x7); ++ REG32(AD_DA_CLK1_REG) |= (div & 0x7); ++ ++ //open sar adc clk ++ REG32(AD_DA_CLK1_REG) |= (0x1<<3); ++ ++ //get back the driven cfg ++ REG32(SAR_IF_CFG_REG) |= (saradc_chief_driven | saradc_module_driven); ++} ++ ++static void power_on_adc1(void) ++{ ++ REG32(AD_DA_CLK1_REG) &= (~(1 << 31)); ++} ++ ++static void power_off_adc1(void) ++{ ++ REG32(AD_DA_CLK1_REG) |= (1 << 31); ++} ++ ++static void enable_adc1_channel(int channel) ++{ ++ REG32(SAR_IF_CFG_REG) &= ~(1 << 0); ++ REG32(SAR_IF_CFG_REG) |= (1 << (channel + 5)); ++ REG32(SAR_IF_CFG_REG) |= (1 << 0); ++} ++ ++static void disable_adc1_channel(int channel) ++{ ++ REG32(SAR_IF_CFG_REG) &= ~(1 << 0); ++ REG32(SAR_IF_CFG_REG) &= ~(1 << (channel + 5)); ++ REG32(SAR_IF_CFG_REG) |= (1 << 0); ++} ++ ++/** ++ * @brief: Read AD0/AD1/BAT voltage ++ * @author: Zhongjunchao ++ * @data: 2011-7-20 ++ * ++ * @Warning: Please don`t use this function in IRQ handle routine! ++ */ ++unsigned long adc1_read_channel(int channel) ++{ ++ int count; ++ unsigned long val = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&adc1_lock, flags); ++ ++ power_on_adc1(); ++ enable_adc1_channel(channel); ++ mdelay(1); ++ ++ val = ((REG32(SAR_IF_SMP_DAT_REG) >> (channel * 10)) & 0x3ff); ++ if (channel == AK_ADC1_BAT) { ++ for(count = 0; count < 4; count++) { ++ mdelay(1); ++ val += ((REG32(SAR_IF_SMP_DAT_REG) >> (channel * 10)) & 0x3ff); ++ } ++ val /= 5; ++ } ++ val = (val * AK_AVCC) >> 10; ++ ++ disable_adc1_channel(channel); ++ power_off_adc1(); ++ ++ spin_unlock_irqrestore(&adc1_lock, flags); ++ ++ return val; ++} ++ ++/** ++ * @brief: ADC1 initialization ++ * @author: Zhongjunchao ++ * @data: 2011-7-20 ++ * @modify: 2013-4-27 ++ * ++ * @note: This function will init ADC1 clock and sample rate, and then enable ++ * ADC1 but not power on. Any device use ADC1 please use the read value API. ++ * We use some default value, if ADC1 can`t work make be will change it. ++ * Please use this function in machine init. ++ */ ++int adc1_init(void) ++{ ++ unsigned long samplerate; ++ unsigned long adc1_clk, clkdiv; ++ unsigned long spl_cycle, spl_hold, spl_wait; ++ ++ spin_lock_init(&adc1_lock); ++ ++ /* reset adc1 */ ++ REG32(RESET_CTRL_REG) &= (~(1<<30)); ++ ++ /* config adc1 clk, default 1.5MHz(BAT use only in 1.5MHz) */ ++ clkdiv = ADC1_MAIN_CLK/ADC1_DEFAULT_CLK - 1; ++ clkdiv &= 0x7; ++ adc1_clk_cfg(clkdiv); ++ ++ /* release reset */ ++ REG32(RESET_CTRL_REG) |= (1<<30); ++ ++ /* disable all sample */ ++ REG32(SAR_IF_CFG_REG) &= ( ~( (1<<0) | (0x7<<5) ) ); ++ ++ /* clear all adc1 interrupt state */ ++ REG32(SAR_IF_INT_STATUS_REG) = 0x0; ++ ++ /* mask all adc1 interrupt */ ++ REG32(SAR_IF_CFG_REG) &= (~ (0xf<<1)); ++ ++ /* power on adc1 */ ++ power_on_adc1(); ++ ++ //select AVCC ++ REG32(SAR_ADC_CFG_REG) &= (~((1<<16)|(1<<22))); ++ ++ /* one channel one time, default samplerate is 5000 */ ++ adc1_clk = ADC1_MAIN_CLK / (clkdiv + 1); ++ samplerate = DEFAULT_SAMPLE; ++ spl_cycle = adc1_clk / samplerate; ++ spl_wait = 1; ++ spl_hold = spl_wait + 16 + 1; ++ ++ REG32(SAR_IF_CFG_REG) &= (~(0xff << 14)); ++ REG32(SAR_IF_CFG_REG) |= (spl_wait << 14); ++ ++ REG32(SAR_TIMING_CFG_REG) = 0; ++ REG32(SAR_TIMING_CFG_REG) = spl_cycle | (spl_hold << 16) ; ++ ++ /* spl_cnt config, what is it ? */ ++ REG32(SAR_IF_CFG_REG) &= (~(0x7 << 8)); ++ REG32(SAR_IF_CFG_REG) |= (1 << 8); ++ ++ /* disable the bat div radio */ ++ REG32(SAR_ADC_CFG_REG) &= ~(1 << 1) ; ++ ++ /* we don`t know who will use ADC1, so we close it */ ++ power_off_adc1(); ++ ++ return 0; ++} +diff --git a/arch/arm/mach-ak39/ak39-gpio.c b/arch/arm/mach-ak39/ak39-gpio.c +new file mode 100755 +index 00000000..401235b0 +--- /dev/null ++++ b/arch/arm/mach-ak39/ak39-gpio.c +@@ -0,0 +1,839 @@ ++/* ++ * arch/arm/mach-ak39/gpio.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++#include ++#include ++#include ++ ++//share pin config fore module in AK39xx ++struct gpio_sharepin_cfg share_cfg_module[] = { ++ {ePIN_AS_OPCLK, SHARE_CFG1, (0x3<<2), (1<<2), 0, 0, 0, 0}, ++ {ePIN_AS_JTAG, SHARE_CFG1, (0x3f<<16)|(1<<1), (0x15<<16)|(1<<1), 0, 0, 0, 0}, ++ {ePIN_AS_RTCK, SHARE_CFG1, (0x3<<22), (1<<22), 0, 0, 0, 0}, ++ {ePIN_AS_I2S, SHARE_CFG13, ((0xf<<8)|(1<<13)), ((0xf<<8)|(1<<13)), 0, 0, (0x3<<4), (0x3<<4)}, ++ {ePIN_AS_PWM1, SHARE_CFG1, ((0x3<<16)|(1<<4)), ((0x3<<16)|(1<<4)), 0, 0, 0, 0}, ++ {ePIN_AS_PWM2, SHARE_CFG1, (0x3<<18), (0x3<<18), 0, 0, 0, 0}, ++ {ePIN_AS_PWM3, SHARE_CFG1, ((0x3<<20)|(1<<6)), ((0x3<<20)|(1<<6)), 0, 0, 0, 0}, ++ {ePIN_AS_PWM4, SHARE_CFG1, ((0x3<<22)|(1<<7)), ((0x3<<22)|(1<<7)), 0, 0, 0, 0}, ++#if defined(CONFIG_CPU_AK3910) ++ {ePIN_AS_PWM5, SHARE_CFG13, (0x3<<2), (0x3<<2), 0, 0, (0x3<<4), (0x1<<4)}, ++ {ePIN_AS_SPI1, SHARE_CFG3, 0, 0, 0, 0, ((0x3<<23)|(1<<21)|(1<<19)|(0x3)),((0x1<<23)|(1<<21)|(1<<19)|(0x3))}, ++ {ePIN_AS_SPI2, SHARE_CFG3, 0, 0, 0, 0, ((0x3<<23)|(1<<21)|(1<<19)|(0xf<<2)),((0x3<<23)|(1<<21)|(1<<19)|(0xa<<2))}, ++#elif defined(CONFIG_CPU_AK3916) || defined(CONFIG_CPU_AK3918) ++ {ePIN_AS_PWM5, SHARE_CFG13, ((1<<12)|(0x3<<2)), ((1<<12)|(0x3<<2)), 0, 0, (0x3<<4), (0x1<<4)}, ++ {ePIN_AS_SPI1, SHARE_CFG3, 0, 0, 0, 0, ((0x3<<23)|(1<<21)|(1<<19)|(0x3)),((0x1<<23)|(1<<21)|(1<<19)|(0x3))}, ++ {ePIN_AS_SPI2, SHARE_CFG3, 0, 0, 0, 0, ((0x3<<23)|(1<<21)|(1<<19)|(0xf<<2)),((0x3<<23)|(1<<21)|(1<<19)|(0xa<<2))}, ++#endif ++ {ePIN_AS_UART1, SHARE_CFG1, (0x3<<14), (0x3<<14), 0, 0, 0, 0}, ++ {ePIN_AS_UART2, SHARE_CFG1, (0xff<<16), (0xaa<<16), 0, 0, 0, 0}, ++ {ePIN_AS_CAMERA, SHARE_CFG2, 0, 0, (0xf), (0x0), 0, 0}, ++ {ePIN_AS_SDIO, SHARE_CFG3, 0, 0, 0, 0, (0xff<<16), (0x57<<16)}, ++ {ePIN_AS_MCI, SHARE_CFG3, 0, 0, 0, 0, (0xf<<6), (0xf<<6)}, ++ {ePIN_AS_MCI_8LINE, SHARE_CFG3, 0, 0, 0, 0, (0x3ff<<6), (0x15f<<6)}, ++ {ePIN_AS_MAC, SHARE_CFG12, (0x3<<2), (1<<2), (0x1fff<<4), (0x1fff<<4), 0, 0}, ++ {ePIN_AS_I2C, SHARE_CFG3, 0, 0, 0, 0, (0x3<<25), (0x3<<25)}, ++ {ePIN_AS_IRDA, SHARE_CFG3, 0, 0, 0, 0, (0x3<<2), (0x1<<2)}, ++ {ePIN_AS_DUMMY, EXIT_CFG, 0, 0, 0, 0, 0, 0} ++}; ++ ++struct gpio_pupd_cfg pupd_cfg_info[] = { ++ //pin, index, register, up/down ++ {AK_GPIO_0, 0, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_1, 19, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_2, 20, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_3, 1, PUPD_CFG1, PULLDOWN}, ++ {AK_GPIO_4, 21, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_5, 22, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_6, 23, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_7, 24, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_8, 4, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_9, 5, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_10, 7, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_11, 8, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_12, 10, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_13, 11, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_14, 14, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_15, 15, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_16, 16, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_17, 17, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_18, 18, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_19, 20, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_20, 21, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_21, 22, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_22, 23, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_23, 25, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_24, 26, PUPD_CFG2, PULLDOWN}, ++ {AK_GPIO_25, 0, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_26, 1, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_27, 20, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_28, 21, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_29, 2, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_30, 3, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_31, 4, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_32, 5, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_33, 6, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_34, 7, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_35, 8, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_36, 9, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_37, 10, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_38, 11, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_39, 12, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_40, 13, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_41, 14, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_42, 15, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_43, 16, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_44, 17, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_45, 18, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_46, 19, PUPD_CFG3, PULLUP}, ++ {AK_GPIO_47, 2, PUPD_CFG1, PULLDOWN}, ++ {AK_GPIO_48, 3, PUPD_CFG1, PULLDOWN}, ++ {AK_GPIO_50, 5, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_51, 6, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_52, 7, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_53, 8, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_54, 9, PUPD_CFG1, PULLDOWN}, ++ {AK_GPIO_55, 10, PUPD_CFG1, PULLDOWN}, ++ {AK_GPIO_56, 11, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_57, 12, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_58, 13, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_59, 14, PUPD_CFG1, PULLDOWN}, ++ {AK_GPIO_60, 15, PUPD_CFG1, PULLDOWN}, ++ {AK_GPIO_61, 16, PUPD_CFG1, PULLDOWN}, ++ {AK_GPIO_62, 17, PUPD_CFG1, PULLUP}, ++ {AK_GPIO_63, 18, PUPD_CFG1, PULLUP}, ++}; ++ ++//this used to clr in gpio chare pin cfg1 ++struct sharepin_as_gpio sharepin_cfg_gpio1[] = { ++ {0, 0, 0, AS_GPIO_CFG_BIT1}, ++ {3, 3, 1, AS_GPIO_CFG_BIT1}, ++ {47, 47, 2, AS_GPIO_CFG_BIT2}, ++ {48, 48, 4, AS_GPIO_CFG_BIT1}, ++ {50, 50, 6, AS_GPIO_CFG_BIT1}, ++ {51, 51, 7, AS_GPIO_CFG_BIT1}, ++ {52, 52, 8, AS_GPIO_CFG_BIT1}, ++ {53, 53, 9, AS_GPIO_CFG_BIT1}, ++ {54, 54, 10, AS_GPIO_CFG_BIT1}, ++ {55, 55, 11, AS_GPIO_CFG_BIT1}, ++ {56, 56, 12, AS_GPIO_CFG_BIT1}, ++ {57, 57, 13, AS_GPIO_CFG_BIT1}, ++ {1, 1, 14, AS_GPIO_CFG_BIT1}, ++ {2, 2, 15, AS_GPIO_CFG_BIT1}, ++ {4, 4, 16, AS_GPIO_CFG_BIT2}, ++ {5, 5, 18, AS_GPIO_CFG_BIT2}, ++ {6, 6, 20, AS_GPIO_CFG_BIT2}, ++ {7, 7, 22, AS_GPIO_CFG_BIT2}, ++}; ++ ++//this used to clr in gpio chare pin cfg2 ++struct sharepin_as_gpio sharepin_cfg_gpio3[] = { ++ {25, 25, 0, AS_GPIO_CFG_BIT1}, ++ {26, 26, 1, AS_GPIO_CFG_BIT1}, ++ {29, 29, 2, AS_GPIO_CFG_BIT2}, ++ {30, 30, 4, AS_GPIO_CFG_BIT2}, ++ {31, 31, 6, AS_GPIO_CFG_BIT1}, ++ {32, 32, 7, AS_GPIO_CFG_BIT1}, ++ {33, 33, 8, AS_GPIO_CFG_BIT1}, ++ {34, 36, 9, AS_GPIO_CFG_BIT1}, ++ {37, 38, 10, AS_GPIO_CFG_BIT2}, ++ {39, 39, 12, AS_GPIO_CFG_BIT2}, ++ {40, 40, 14, AS_GPIO_CFG_BIT2}, ++ {41, 41, 16, AS_GPIO_CFG_BIT1}, ++ {42, 42, 17, AS_GPIO_CFG_BIT1}, ++ {43, 43, 18, AS_GPIO_CFG_BIT2}, ++ {44, 44, 20, AS_GPIO_CFG_BIT2}, ++ {45, 46, 22, AS_GPIO_CFG_BIT2}, ++ {27, 27, 25, AS_GPIO_CFG_BIT1}, ++ {28, 28, 26, AS_GPIO_CFG_BIT1}, ++}; ++ ++#define INVALID_WK_BIT 0xff ++struct t_gpio_wakeup_cfg gpio_wakeup_cfg[] = { ++ //gpio_start gpio_end start_bit ++ {AK_GPIO_0, AK_GPIO_7, 0}, ++ {AK_GPIO_12, AK_GPIO_14, 8}, ++ {AK_GPIO_22, AK_GPIO_30, 11}, ++ {AK_GPIO_39, AK_GPIO_44, 16}, ++ {AK_GPIO_47, AK_GPIO_55, 22}, ++ {AK_GPIO_57, AK_GPIO_57, 31}, ++}; ++ ++ ++unsigned int ak3910_invalid_gpio[] = { ++ AK_GPIO_37, AK_GPIO_38, AK_GPIO_39, AK_GPIO_40, AK_GPIO_56, ++ AK_GPIO_58, AK_GPIO_59, AK_GPIO_60, AK_GPIO_61, AK_GPIO_62, ++ AK_GPIO_63, ++}; ++ ++unsigned int ak3916_invalid_gpio[] = { ++}; ++ ++static unsigned char get_bit_by_pin_wk(unsigned char pin) ++{ ++ int i, n; ++ n = ARRAY_SIZE(gpio_wakeup_cfg); ++ ++ for (i=0; i= gpio_wakeup_cfg[i].gpio_start && pin <= gpio_wakeup_cfg[i].gpio_end) ++ return gpio_wakeup_cfg[i].start_bit + (pin - gpio_wakeup_cfg[i].gpio_start); ++ } ++ ++ return INVALID_WK_BIT; ++} ++ ++/* when the specific bit is set to 0, the wake-up GPIO is rising triggered ++ * when the specific bit is set to 1, the wake-up GPIO is falling triggered ++ */ ++void ak_gpio_wakeup_pol(unsigned int pin, unsigned char pol) ++{ ++ unsigned char bit = get_bit_by_pin_wk(pin); ++ unsigned int val; ++ ++ if (bit == INVALID_WK_BIT) { ++ panic("this pin %u doesn't support wakeup function\n", pin); ++ return; ++ } ++ ++ val = REG32(AK_WGPIO_POLARITY); ++ val &= ~(1 << bit); ++ val |= (pol << bit); ++ REG32(AK_WGPIO_POLARITY) = val; ++} ++EXPORT_SYMBOL(ak_gpio_wakeup_pol); ++ ++int ak_gpio_wakeup(unsigned int pin, unsigned char enable) ++{ ++ unsigned char bit = get_bit_by_pin_wk(pin); ++ unsigned int val; ++ ++ if (bit == INVALID_WK_BIT) { ++ panic("this pin %d doesn't support wakeup function\n", pin); ++ return -1; ++ } ++ //clear wake gpio status ++ val = REG32(AK_WGPIO_CLEAR); ++ val |= (1 << bit); ++ REG32(AK_WGPIO_CLEAR) = val; ++ val &= ~(1 << bit); ++ REG32(AK_WGPIO_CLEAR) = val; ++ ++ val = REG32(AK_WGPIO_ENABLE); ++ if (enable == AK_WAKEUP_ENABLE) { ++ val |= (1 << bit); ++ } else if (enable == AK_WAKEUP_DISABLE) { ++ val &= ~(1 << bit); ++ } else ++ panic("wrong enable value in ak_gpio_wakeup\n"); ++ REG32(AK_WGPIO_ENABLE) = val; ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_gpio_wakeup); ++ ++/* ++ * @brief set gpio pin group as specified module used ++ * @param[in] PinCfg enum data. the specified module ++ */ ++void ak_group_config(T_GPIO_SHAREPIN_CFG mod_name) ++{ ++ unsigned long i, flags, val = 0; ++ ++ if(ePIN_AS_GPIO == mod_name) { ++ //set all pin as gpio except uart0 ++ local_irq_save(flags); ++ __raw_writel(0xc000, AK_SHAREPIN_CON1); ++ __raw_writel(0xf, AK_SHAREPIN_CON2); ++ __raw_writel(0x0, AK_SHAREPIN_CON3); ++ local_irq_restore(flags); ++ return; ++ } ++ ++ for(i = 0; ; i++) { ++ if(ePIN_AS_DUMMY == share_cfg_module[i].func_module) ++ break; ++ ++ if(mod_name == share_cfg_module[i].func_module) { ++ //set pull attribute for module ++ g_ak39_setgroup_attribute(mod_name); ++ ++ local_irq_save(flags); ++ switch(share_cfg_module[i].share_config) { ++ case SHARE_CFG1: //set share pin cfg reg1 ++ val = __raw_readl(AK_SHAREPIN_CON1); ++ val &= ~(share_cfg_module[i].reg1_bit_mask); ++ val |= (share_cfg_module[i].reg1_bit_value); ++ __raw_writel(val, AK_SHAREPIN_CON1); ++ break; ++ ++ case SHARE_CFG2: //set share pin cfg reg2 ++ val = __raw_readl(AK_SHAREPIN_CON2); ++ val &= ~(share_cfg_module[i].reg2_bit_mask); ++ val |= (share_cfg_module[i].reg2_bit_value); ++ __raw_writel(val, AK_SHAREPIN_CON2); ++ break; ++ ++ case SHARE_CFG3: //set share pin cfg reg3 ++ val = __raw_readl(AK_SHAREPIN_CON3); ++ val &= ~(share_cfg_module[i].reg3_bit_mask); ++ val |= (share_cfg_module[i].reg3_bit_value); ++ __raw_writel(val, AK_SHAREPIN_CON3); ++ break; ++ ++ case SHARE_CFG12: ++ val = __raw_readl(AK_SHAREPIN_CON1); ++ val &= ~(share_cfg_module[i].reg1_bit_mask); ++ val |= (share_cfg_module[i].reg1_bit_value); ++ __raw_writel(val, AK_SHAREPIN_CON1); ++ ++ val = __raw_readl(AK_SHAREPIN_CON2); ++ val &= ~(share_cfg_module[i].reg2_bit_mask); ++ val |= (share_cfg_module[i].reg2_bit_value); ++ __raw_writel(val, AK_SHAREPIN_CON2); ++ break; ++ case SHARE_CFG13: ++ val = __raw_readl(AK_SHAREPIN_CON1); ++ val &= ~(share_cfg_module[i].reg1_bit_mask); ++ val |= (share_cfg_module[i].reg1_bit_value); ++ __raw_writel(val, AK_SHAREPIN_CON1); ++ ++ val = __raw_readl(AK_SHAREPIN_CON3); ++ val &= ~(share_cfg_module[i].reg3_bit_mask); ++ val |= (share_cfg_module[i].reg3_bit_value); ++ __raw_writel(val, AK_SHAREPIN_CON3); ++ break; ++ ++ case SHARE_CFG23: ++ val = __raw_readl(AK_SHAREPIN_CON2); ++ val &= ~(share_cfg_module[i].reg2_bit_mask); ++ val |= (share_cfg_module[i].reg2_bit_value); ++ __raw_writel(val, AK_SHAREPIN_CON2); ++ ++ val = __raw_readl(AK_SHAREPIN_CON3); ++ val &= ~(share_cfg_module[i].reg3_bit_mask); ++ val |= (share_cfg_module[i].reg3_bit_value); ++ __raw_writel(val, AK_SHAREPIN_CON3); ++ break; ++ ++ case SHARE_CFG123: ++ val = __raw_readl(AK_SHAREPIN_CON1); ++ val &= ~(share_cfg_module[i].reg1_bit_mask); ++ val |= (share_cfg_module[i].reg1_bit_value); ++ __raw_writel(val, AK_SHAREPIN_CON1); ++ ++ val = __raw_readl(AK_SHAREPIN_CON2); ++ val &= ~(share_cfg_module[i].reg2_bit_mask); ++ val |= (share_cfg_module[i].reg2_bit_value); ++ __raw_writel(val, AK_SHAREPIN_CON2); ++ ++ val = __raw_readl(AK_SHAREPIN_CON3); ++ val &= ~(share_cfg_module[i].reg3_bit_mask); ++ val |= (share_cfg_module[i].reg3_bit_value); ++ __raw_writel(val, AK_SHAREPIN_CON3); ++ break; ++ ++ default: ++ break; ++ } ++ local_irq_restore(flags); ++ return ; ++ } ++ } ++ return ; ++} ++EXPORT_SYMBOL(ak_group_config); ++ ++static unsigned char gpio_assert_legal(unsigned long pin) ++{ ++ int i, len; ++ unsigned int *gpio_legal; ++ ++ if ((pin < 0) || (pin > GPIO_UPLIMIT)) { ++ return AK_FALSE; ++ } ++ ++#if defined(CONFIG_CPU_AK3910) ++ len = ARRAY_SIZE(ak3910_invalid_gpio); ++ gpio_legal = ak3910_invalid_gpio; ++#elif defined(CONFIG_CPU_AK3916) || defined(CONFIG_CPU_AK3918) ++ len = ARRAY_SIZE(ak3916_invalid_gpio); ++ gpio_legal = ak3916_invalid_gpio; ++#endif ++ ++ for(i = 0; i < len; i++) { ++ if(gpio_legal[i] == pin) ++ return AK_FALSE; ++ } ++ return AK_TRUE; ++} ++ ++ ++/** ++ * @brief set gpio share pin as gpio ++ * @param pin [in] gpio pin ID ++ */ ++int g_ak39_setpin_as_gpio(unsigned int pin) ++{ ++ int i, bit = 0; ++ unsigned long flags; ++ ++ //check param ++ if(!gpio_assert_legal(pin)) { ++ panic("Error, Invalid gpio %u configuration!\n", pin); ++ return -1; ++ } ++ ++ /* reserved uart0 confige, but provide the error info */ ++ if(AK_GPIO_1 == pin || AK_GPIO_2 == pin) { ++ REG32(AK_SHAREPIN_CON1) |= (0x3 << 14); ++ return -1; ++ } ++ ++ //loop to find the correct bits to clr in share ping cfg1 ++ for(i = 0; i < ARRAY_SIZE(sharepin_cfg_gpio1); i++){ ++ if((pin >= sharepin_cfg_gpio1[i].gpio_start) ++ && (pin <= sharepin_cfg_gpio1[i].gpio_end)) ++ { ++ local_irq_save(flags); ++ ++ bit = sharepin_cfg_gpio1[i].index; ++ if (sharepin_cfg_gpio1[i].flag == AS_GPIO_CFG_BIT1) ++ REG32(AK_SHAREPIN_CON1) &= ~(1 << bit); ++ else if (sharepin_cfg_gpio1[i].flag == AS_GPIO_CFG_BIT2) ++ REG32(AK_SHAREPIN_CON1) &= ~(0x3 << bit); ++ ++ local_irq_restore(flags); ++ return 0; ++ } ++ } ++ ++ //find the correct bits to clr in share ping cfg2 ++ if ((pin >= AK_GPIO_8) && (pin <= AK_GPIO_24)) { ++ local_irq_save(flags); ++ if (pin <= AK_GPIO_11) ++ REG32(AK_SHAREPIN_CON2) |= (1<<(pin-8)); ++ else ++ REG32(AK_SHAREPIN_CON2) &= ~(1<<(pin-8)); ++ local_irq_restore(flags); ++ return 0; ++ } ++ ++ //loop to find the correct bits to set in share ping cfg3 ++ for(i = 0; i < ARRAY_SIZE(sharepin_cfg_gpio3); i++){ ++ if((pin >= sharepin_cfg_gpio3[i].gpio_start) ++ && (pin <= sharepin_cfg_gpio3[i].gpio_end)) ++ { ++ local_irq_save(flags); ++ ++ bit = sharepin_cfg_gpio3[i].index; ++ if (sharepin_cfg_gpio3[i].flag == AS_GPIO_CFG_BIT1) ++ REG32(AK_SHAREPIN_CON3) &= ~(1 << bit); ++ else if (sharepin_cfg_gpio3[i].flag == AS_GPIO_CFG_BIT2) ++ REG32(AK_SHAREPIN_CON3) &= ~(0x3 << bit); ++ ++ local_irq_restore(flags); ++ return 0; ++ } ++ } ++ return 0; ++} ++ ++/* ++ enable: 1:enable pullup 0:disable pullup function ++ if the pin is attached pullup and pulldown resistor, then writing 1 to enable ++ pullup, 0 to enable pulldown, if you want to disable pullup/pulldown, then ++ disable the PE parameter ++*/ ++int g_ak39_gpio_pullup(unsigned int pin, unsigned char enable) ++{ ++ void __iomem *base = AK_PPU_PPD_BASE(pin); ++ unsigned long flags; ++ int i; ++ ++ if(!gpio_assert_legal(pin)) { ++ panic("Error, Invalid gpio %u configuration!\n", pin); ++ return -1; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(pupd_cfg_info); i++) { ++ if (pin == pupd_cfg_info[i].pin) { ++ ++ if (pupd_cfg_info[i].pupd_type == PULLDOWN) ++ panic("Invalid GPIO[%d] pullup config.\n", pin); ++ ++ switch(pupd_cfg_info[i].pupd_cfg) { ++ case PUPD_CFG1: ++ base = AK_PPU_PPD1; ++ break; ++ case PUPD_CFG2: ++ base = AK_PPU_PPD2; ++ break; ++ case PUPD_CFG3: ++ base = AK_PPU_PPD3; ++ break; ++ } ++ ++ local_irq_save(flags); ++ if (enable == AK_PULLUP_ENABLE) ++ REG32(base) &= ~(1 << pupd_cfg_info[i].index); ++ else if (enable == AK_PULLUP_DISABLE) ++ REG32(base) |= (1 << pupd_cfg_info[i].index); ++ local_irq_restore(flags); ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++//1.enable pulldown 0.disable pulldown ++ int g_ak39_gpio_pulldown(unsigned int pin, unsigned char enable) ++{ ++ void __iomem *base = AK_PPU_PPD_BASE(pin); ++ unsigned long flags; ++ int i; ++ ++ if(!gpio_assert_legal(pin)) { ++ panic("Error, Invalid gpio %u configuration!\n", pin); ++ return -1; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(pupd_cfg_info); i++) { ++ if (pin == pupd_cfg_info[i].pin) { ++ ++ if (pupd_cfg_info[i].pupd_type == PULLUP) ++ panic("Invalid GPIO[%d] pulldown config.\n", pin); ++ ++ switch(pupd_cfg_info[i].pupd_cfg) { ++ case PUPD_CFG1: ++ base = AK_PPU_PPD1; ++ break; ++ case PUPD_CFG2: ++ base = AK_PPU_PPD2; ++ break; ++ case PUPD_CFG3: ++ base = AK_PPU_PPD3; ++ break; ++ } ++ ++ local_irq_save(flags); ++ if (enable == AK_PULLDOWN_ENABLE) ++ REG32(base) &= ~(1 << pupd_cfg_info[i].index); ++ else if (enable == AK_PULLDOWN_DISABLE) ++ REG32(base) |= (1 << pupd_cfg_info[i].index); ++ local_irq_restore(flags); ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++void g_ak39_setgroup_attribute(T_GPIO_SHAREPIN_CFG mod_name) ++{ ++ unsigned long pin, start_pin = 0, end_pin = 0; ++ ++ switch (mod_name) { ++ case ePIN_AS_MCI: ++ start_pin = 31, end_pin = 36; ++ for (pin = start_pin; pin <= end_pin; pin++) ++ { ++ g_ak39_gpio_pullup(pin, AK_TRUE); ++ } ++ break; ++ ++ case ePIN_AS_MCI_8LINE: ++ start_pin = 31, end_pin = 40; ++ for (pin = start_pin; pin <= end_pin; pin++) ++ { ++ g_ak39_gpio_pullup(pin, AK_TRUE); ++ } ++ break; ++ ++ case ePIN_AS_SDIO: ++ start_pin = 41, end_pin = 46; ++ for (pin = start_pin; pin <= end_pin; pin++) ++ { ++ g_ak39_gpio_pullup(pin, AK_TRUE); ++ } ++ break; ++ ++ case ePIN_AS_SPI1: ++ /* spi_clk #spi_cs */ ++ start_pin = 25, end_pin = 26; ++ for (pin = start_pin; pin <= end_pin; pin++) ++ { ++ g_ak39_gpio_pullup(pin, AK_TRUE); ++ } ++ ++ start_pin = 43, end_pin = 46; ++ for (pin = start_pin; pin <= end_pin; pin++) ++ { ++ g_ak39_gpio_pullup(pin, AK_TRUE); ++ } ++ ++ break; ++ ++ case ePIN_AS_SPI2: ++ /* spi_clk #spi_cs */ ++ start_pin = 29, end_pin = 30; ++ for (pin = start_pin; pin <= end_pin; pin++) ++ { ++ g_ak39_gpio_pullup(pin, AK_TRUE); ++ } ++ ++ start_pin = 43, end_pin = 46; ++ for (pin = start_pin; pin <= end_pin; pin++) ++ { ++ g_ak39_gpio_pullup(pin, AK_TRUE); ++ } ++ break; ++ ++ case ePIN_AS_I2S: ++ start_pin = 52, end_pin = 53; ++ for (pin = start_pin; pin <= end_pin; pin++) ++ { ++ g_ak39_gpio_pullup(pin, AK_FALSE); ++ } ++ g_ak39_gpio_pullup(AK_GPIO_57, AK_FALSE); ++ g_ak39_gpio_pulldown(AK_GPIO_55, AK_FALSE); ++ g_ak39_gpio_pullup(AK_GPIO_55, AK_FALSE); ++ break; ++ ++ case ePIN_AS_UART1: ++ start_pin = 1, end_pin = 2; ++ for (pin = start_pin; pin <= end_pin; pin++) ++ { ++ g_ak39_gpio_pullup(pin, AK_TRUE); ++ } ++ break; ++ ++ case ePIN_AS_UART2: ++ start_pin = 4, end_pin = 7; ++ for (pin = start_pin; pin <= end_pin; pin++) ++ { ++ g_ak39_gpio_pullup(pin, AK_TRUE); ++ } ++ break; ++ ++ case ePIN_AS_I2C: ++ start_pin = 27, end_pin = 28; ++ for (pin = start_pin; pin <= end_pin; pin++) ++ { ++ g_ak39_gpio_pullup(pin, AK_FALSE); ++ } ++ break; ++ default: ++ break; ++ } ++} ++ ++/* ++ * configuration gpio pin ++ * 0: corresponding port is input mode ++ * 1: corresponding port is output mode ++ */ ++int g_ak39_gpio_cfgpin(unsigned int pin, unsigned int to) ++{ ++ void __iomem *base = AK_GPIO_DIR_BASE(pin); ++ unsigned int offset = ((pin) & 31); ++ unsigned long flags; ++ ++ if(!gpio_assert_legal(pin)) { ++ panic("Error, Invalid gpio %d configuration!\n", pin); ++ return -1; ++ } ++ ++ /* gpio[49] can't set output mode for ak39xx*/ ++ if ((to == AK_GPIO_DIR_OUTPUT)&&(pin == AK_GPIO_49)) { ++ panic("Error, gpio %d isn't config output mode\n", pin); ++ return -1; ++ } ++ ++ local_irq_save(flags); ++ if (AK_GPIO_DIR_INPUT == to) ++ REG32(base) &= ~(1 << offset); ++ else if (AK_GPIO_DIR_OUTPUT == to) ++ REG32(base) |= (1 << offset); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++/* hold the real-time output value from GPIO[x] */ ++int g_ak39_gpio_setpin(unsigned int pin, unsigned int to) ++{ ++ void __iomem *base = AK_GPIO_OUT_BASE(pin); ++ unsigned int offset = ((pin) & 31); ++ unsigned long flags; ++ ++ if(!gpio_assert_legal(pin)) { ++ panic("Error, Invalid gpio %d configuration!\n", pin); ++ return -1; ++ } ++ ++ /* gpio[49] can't set output level for ak39xx*/ ++ if (pin == AK_GPIO_49) { ++ panic("Error, gpio %d isn't config outpu level\n", pin); ++ return -1; ++ } ++ ++ local_irq_save(flags); ++ if (AK_GPIO_OUT_LOW == to) ++ REG32(base) &= ~(1 << offset); ++ else if (AK_GPIO_OUT_HIGH == to) ++ REG32(base) |= (1 << offset); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++ ++/* hold the real-time input value of GPIO[x] */ ++ int g_ak39_gpio_getpin(unsigned int pin) ++{ ++ void __iomem *base = AK_GPIO_IN_BASE(pin); ++ unsigned int offset = ((pin) & 31); ++ ++ if(!gpio_assert_legal(pin)) { ++ panic("Error, read invalid gpio %d status!\n", pin); ++ return -1; ++ } ++ return ((__raw_readl(base) & (1 << offset)) == (1 << offset)); ++} ++ ++ ++/* ++ * enalbe/disable the interrupt function of GPIO[X] ++ * 1: interrupt function of corresponding port is enable ++ * 0: interrupt function of corresponding port is disable ++ */ ++int g_ak39_gpio_inten(unsigned int pin, unsigned int enable) ++{ ++ void __iomem *base = AK_GPIO_INTEN_BASE(pin); ++ unsigned int offset = ((pin) & 31); ++ unsigned long flags; ++ ++ if(!gpio_assert_legal(pin)) { ++ panic("Error, invalid gpio %d!\n", pin); ++ return -1; ++ } ++ ++ local_irq_save(flags); ++ if (AK_GPIO_INT_ENABLE == enable) ++ REG32(base) |= (1 << offset); ++ else if (AK_GPIO_INT_DISABLE == enable) ++ REG32(base) &= ~(1 << offset); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++ ++/* ++ * interrupt polarity selection ++ * 0: the input interrupt polarity of GPIO[X] is active high ++ * 1: the input interrupt polarity of GPIO[X] is active low ++ */ ++int g_ak39_gpio_intpol(unsigned int pin, unsigned int level) ++{ ++ void __iomem *base = AK_GPIO_INTPOL_BASE(pin); ++ unsigned int offset = ((pin) & 31); ++ unsigned long flags; ++ ++ if(!gpio_assert_legal(pin)) { ++ panic("Error, invalid gpio %d!\n", pin); ++ return -1; ++ } ++ ++ local_irq_save(flags); ++ if (AK_GPIO_INT_HIGHLEVEL == level) ++ REG32(base) &= ~(1 << offset); ++ else if (AK_GPIO_INT_LOWLEVEL == level) ++ REG32(base) |= (1 << offset); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++int reg_set_mutli_bit(void __iomem *reg, unsigned int value, int bit, int index) ++{ ++ unsigned long con, flags; ++ ++ if (bit <= 0) ++ return -1; ++ ++ local_irq_save(flags); ++ ++ con = __raw_readl(reg); ++ con &= ~(((1 << bit) - 1) << index); ++ con |= (value << index); ++ __raw_writel(con, reg); ++ ++ local_irq_restore(flags); ++ return 0; ++} ++EXPORT_SYMBOL(reg_set_mutli_bit); ++ ++int g_ak39_gpio_to_irq(unsigned int pin) ++{ ++ if(!gpio_assert_legal(pin)) { ++ panic("Error, invalid gpio %d!\n", pin); ++ return -1; ++ } ++ return (IRQ_GPIO_0 + (pin - AK_GPIO_0)); ++} ++ ++int g_ak39_irq_to_gpio(unsigned int irq) ++{ ++ return (AK_GPIO_0 + (irq - IRQ_GPIO_0)); ++} ++ ++ ++#if 0 ++static const char *ak39_gpio_list[AK_GPIO_MAX]; ++ ++int ak_gpio_request(unsigned long gpio, const char *label) ++{ ++ if (gpio > GPIO_UPLIMIT) ++ return -EINVAL; ++ ++ if (ak39_gpio_list[gpio]) ++ return -EBUSY; ++ ++ if (label) ++ ak39_gpio_list[gpio] = label; ++ else ++ ak39_gpio_list[gpio] = "busy"; ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_gpio_request); ++ ++void ak_gpio_free(unsigned long gpio) ++{ ++ BUG_ON(!ak39_gpio_list[gpio]); ++ ++ ak39_gpio_list[gpio] = NULL; ++} ++EXPORT_SYMBOL(ak_gpio_free); ++ ++#else ++ ++int ak_gpio_request(unsigned long gpio, const char *label) ++{ ++ return 0; ++} ++EXPORT_SYMBOL(ak_gpio_request); ++ ++void ak_gpio_free(unsigned long gpio) ++{ ++} ++EXPORT_SYMBOL(ak_gpio_free); ++ ++#endif ++ +diff --git a/arch/arm/mach-ak39/clock.c b/arch/arm/mach-ak39/clock.c +new file mode 100755 +index 00000000..6fbb4215 +--- /dev/null ++++ b/arch/arm/mach-ak39/clock.c +@@ -0,0 +1,932 @@ ++/* ++ * linux/arch/arm/mach-ak39/clock.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++ ++/* clock information */ ++ ++static LIST_HEAD(clocks); ++ ++/* We originally used an mutex here, but some contexts (see resume) ++ * are calling functions such as clk_set_parent() with IRQs disabled ++ * causing an BUG to be triggered. ++ */ ++DEFINE_SPINLOCK(clocks_lock); ++ ++/* enable and disable calls for use with the clk struct */ ++ ++static int clk_null_enable(struct clk *clk, int enable) ++{ ++ return 0; ++} ++ ++int clk_enable(struct clk *clk) ++{ ++ unsigned long flags; ++ ++ if (IS_ERR(clk) || clk == NULL) ++ return -EINVAL; ++ ++ clk_enable(clk->parent); ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ if ((clk->usage++) == 0) ++ (clk->enable)(clk, 1); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ return 0; ++} ++ ++void clk_disable(struct clk *clk) ++{ ++ unsigned long flags; ++ ++ if (IS_ERR(clk) || clk == NULL) ++ return; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ if ((--clk->usage) == 0) ++ (clk->enable)(clk, 0); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ clk_disable(clk->parent); ++} ++ ++ ++unsigned long clk_get_rate(struct clk *clk) ++{ ++ if (IS_ERR(clk)) ++ return 0; ++ ++ if (clk->rate != 0) ++ return clk->rate; ++ ++ if (clk->ops != NULL && clk->ops->get_rate != NULL) ++ return (clk->ops->get_rate)(clk); ++ ++ if (clk->parent != NULL) ++ return clk_get_rate(clk->parent); ++ ++ return clk->rate; ++} ++ ++long clk_round_rate(struct clk *clk, unsigned long rate) ++{ ++ if (!IS_ERR(clk) && clk->ops && clk->ops->round_rate) ++ return (clk->ops->round_rate)(clk, rate); ++ ++ return rate; ++} ++ ++int clk_set_rate(struct clk *clk, unsigned long rate) ++{ ++ int ret; ++ ++ if (IS_ERR(clk)) ++ return -EINVAL; ++ ++ /* We do not default just do a clk->rate = rate as ++ * the clock may have been made this way by choice. ++ */ ++ ++ WARN_ON(clk->ops == NULL); ++ WARN_ON(clk->ops && clk->ops->set_rate == NULL); ++ ++ if (clk->ops == NULL || clk->ops->set_rate == NULL) ++ return -EINVAL; ++ ++ spin_lock(&clocks_lock); ++ ret = (clk->ops->set_rate)(clk, rate); ++ spin_unlock(&clocks_lock); ++ ++ return ret; ++} ++ ++struct clk *clk_get_parent(struct clk *clk) ++{ ++ return clk->parent; ++} ++ ++int clk_set_parent(struct clk *clk, struct clk *parent) ++{ ++ int ret = 0; ++ ++ if (IS_ERR(clk)) ++ return -EINVAL; ++ ++ spin_lock(&clocks_lock); ++ ++ if (clk->ops && clk->ops->set_parent) ++ ret = (clk->ops->set_parent)(clk, parent); ++ ++ spin_unlock(&clocks_lock); ++ ++ return ret; ++} ++ ++EXPORT_SYMBOL(clk_enable); ++EXPORT_SYMBOL(clk_disable); ++EXPORT_SYMBOL(clk_get_rate); ++EXPORT_SYMBOL(clk_round_rate); ++EXPORT_SYMBOL(clk_set_rate); ++EXPORT_SYMBOL(clk_get_parent); ++EXPORT_SYMBOL(clk_set_parent); ++/* base clocks */ ++ ++#if 0 ++static u32 __power2(u32 x) ++{ ++ u32 s = 1; ++ ++ while (x--) ++ s <<= 1; ++ ++ return s; ++} ++#endif ++ ++static int clk_default_setrate(struct clk *clk, unsigned long rate) ++{ ++ clk->rate = rate; ++ return 0; ++} ++ ++static int inline ak39xx_gate(void __iomem *reg, ++ struct clk *clk, int enable) ++{ ++ unsigned int ctrlbit = clk->ctrlbit; ++ u32 con; ++ ++ con = __raw_readl(reg); ++ ++ if (enable) ++ con &= ~ctrlbit; ++ else ++ con |= ctrlbit; ++ ++ __raw_writel(con, reg); ++ return 0; ++} ++ ++static int ak39xx_12M_enable(struct clk *clk, int enable) ++{ ++ return ak39xx_gate(CLOCK_PERI_PLL_CTRL1, clk, enable); ++} ++ ++static int ak39xx_25M_enable(struct clk *clk, int enable) ++{ ++ unsigned int ctrlbit = clk->ctrlbit; ++ u32 con; ++ ++ con = __raw_readl(CLOCK_PERI_PLL_CTRL1); ++ ++ if (enable) ++ con |= (ctrlbit|AK_CLKCON_CLK_25M_IN); ++ else ++ con &= ~(ctrlbit|AK_CLKCON_CLK_25M_IN); ++ ++ __raw_writel(con, CLOCK_PERI_PLL_CTRL1); ++ return 0; ++} ++ ++struct clk clk_xtal_12M = { ++ .name = "xtal_12M", ++ .usage = 0, ++ .rate = 12 * MHz, ++ .parent = NULL, ++ .enable = ak39xx_12M_enable, ++ .ctrlbit = AK_CLKCON_CLK_12M, ++}; ++ ++struct clk clk_xtal_25M = { ++ .name = "xtal_25M", ++ .rate = 25 * MHz, ++ .parent = NULL, ++ .enable = ak39xx_25M_enable, ++ .ctrlbit = AK_CLKCON_CLK_25M, ++}; ++ ++struct clk clk_xtal_32K = { ++ .name = "xtal_32K", ++ .id = -1, ++ .rate = 32768, ++ .parent = NULL, ++}; ++ ++ ++struct clk clk_cpu_pll = { ++ .name = "cpu_pll", ++ .usage = 0, ++ .rate = 0, ++ .parent = NULL, ++}; ++ ++struct clk clk_asic_pll = { ++ .name = "asic_pll", ++ .usage = 0, ++ .rate = 0, ++ .parent = NULL, ++}; ++ ++struct clk clk_peri_pll = { ++ .name = "peri_pll", ++ .usage = 0, ++ .rate = 0, ++ .parent = NULL, ++}; ++ ++struct clk clk_cpu = { ++ .name = "cpu_pll", ++ .usage = 0, ++ .rate = 0, ++ .parent = &clk_cpu_pll, ++}; ++ ++/* AHB clock maybe from cpu pll, also maybe vclk[0] */ ++struct clk clk_ahb = { ++ .name = "ahb_clk", ++ .usage = 0, ++ .rate = 0, ++ .parent = &clk_cpu_pll, ++}; ++ ++struct clk clk_mem = { ++ .name = "mem_clk", ++ .usage = 0, ++ .rate = 0, ++ .parent = &clk_cpu_pll, ++}; ++ ++ ++struct clk clk_vclk = { ++ .name = "vclk", ++ .usage = 0, ++ .rate = 0, ++ .parent = &clk_asic_pll, ++}; ++ ++struct clk clk_asic = { ++ .name = "asic_clk", ++ .usage = 0, ++ .rate = 0, ++ .parent = &clk_vclk, ++}; ++ ++ ++static int ak39xx_opclk_ctrl(struct clk *clk, int enable) ++{ ++ return ak39xx_gate(CLOCK_PERI_PLL_CTRL2, clk, enable); ++} ++ ++struct clk clk_opclk = { ++ .name = "clk_opclk", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_opclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_MAC, ++}; ++ ++static int ak39xx_mclk_ctrl(struct clk *clk, int enable) ++{ ++ return ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); ++} ++ ++static int ak39xx_video_ctrl(struct clk *clk, int enable) ++{ ++ return ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); ++} ++ ++static int ak39xx_camera_ctrl(struct clk *clk, int enable) ++{ ++ return ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); ++} ++ ++static int ak39xx_asicclk_ctrl(struct clk *clk, int enable) ++{ ++ return ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); ++} ++ ++static int ak39xx_clk_dac(struct clk *clk, int enable) ++{ ++ u32 con; ++ ++ ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); ++ ++ con = __raw_readl(CLOCK_ADC2_DAC_CTRL); ++ ++ if (enable) ++ con |= AK_CLKCON_CLK_DAC; ++ else ++ con &= ~AK_CLKCON_CLK_DAC; ++ ++ __raw_writel(con, CLOCK_ADC2_DAC_CTRL); ++ return 0; ++} ++ ++static int ak39xx_hclk_dac(struct clk *clk, int enable) ++{ ++ u32 con; ++ ++ ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); ++ ++ con = __raw_readl(CLOCK_ADC2_DAC_HS_CTRL); ++ ++ if (enable) ++ con |= AK_CLKCON_HCLK_DAC; ++ else ++ con &= ~AK_CLKCON_HCLK_DAC; ++ ++ __raw_writel(con, CLOCK_ADC2_DAC_HS_CTRL); ++ return 0; ++} ++ ++static int ak39xx_clk_adc1(struct clk *clk, int enable) ++{ ++ u32 con; ++ ++ ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); ++ ++ con = __raw_readl(CLOCK_ADC2_DAC_CTRL); ++ ++ if (enable) ++ con |= AK_CLKCON_CLK_ADC1; ++ else ++ con &= ~AK_CLKCON_CLK_ADC1; ++ ++ __raw_writel(con, CLOCK_ADC2_DAC_CTRL); ++ return 0; ++} ++ ++static int ak39xx_clk_adc2(struct clk *clk, int enable) ++{ ++ u32 con; ++ ++ ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); ++ ++ con = __raw_readl(CLOCK_ADC2_DAC_HS_CTRL); ++ ++ if (enable) ++ con |= AK_CLKCON_CLK_ADC2; ++ else ++ con &= ~AK_CLKCON_CLK_ADC2; ++ ++ __raw_writel(con, CLOCK_ADC2_DAC_HS_CTRL); ++ return 0; ++} ++ ++static int ak39xx_hclk_adc2(struct clk *clk, int enable) ++{ ++ u32 con; ++ ++ ak39xx_gate(CLOCK_GATE_CTRL1, clk, enable); ++ ++ con = __raw_readl(CLOCK_ADC2_DAC_HS_CTRL); ++ ++ if (enable) ++ con |= AK_CLKCON_HCLK_ADC2; ++ else ++ con &= ~AK_CLKCON_HCLK_ADC2; ++ ++ __raw_writel(con, CLOCK_ADC2_DAC_HS_CTRL); ++ return 0; ++} ++ ++static int ak39xx_sensor_ctrl(struct clk *clk, int enable) ++{ ++ unsigned int ctrlbit = clk->ctrlbit; ++ u32 con; ++ ++ con = __raw_readl(CLOCK_PERI_PLL_CTRL2); ++ ++ if (enable) ++ con |= ctrlbit; ++ else ++ con &= ~ctrlbit; ++ ++ __raw_writel(con, CLOCK_PERI_PLL_CTRL2); ++ return 0; ++} ++ ++static struct clk init_clocks[] = { ++ { ++ .name = "dram", ++ .usage = 0, ++ .parent = &clk_cpu, ++ .enable = ak39xx_mclk_ctrl, ++ .ctrlbit = AK_CLKCON_MCLK_DRAM, ++ }, { ++ .name = "video", ++ .usage = 0, ++ .parent = &clk_vclk, ++ .enable = ak39xx_video_ctrl, ++ .ctrlbit = AK_CLKCON_VCLK2_VIDEO, ++ }, { ++ .name = "camera", ++ .usage = 0, ++ .parent = &clk_vclk, ++ .enable = ak39xx_camera_ctrl, ++ .ctrlbit = AK_CLKCON_VCLK1_CAMERA, ++ }, { ++ .name = "usb-host", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_USB, ++ }, { ++ .name = "usb-slave", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_USB, ++ }, { ++ .name = "encryption", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_ENCRY, ++ }, { ++ .name = "mac", ++ .usage = 0, ++ .parent = &clk_opclk, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_MAC, ++ }, { ++ .name = "gpio", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_GPIO, ++ }, { ++ .name = "IrDA", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_IRDA, ++ }, { ++ .name = "i2c", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_I2C, ++ }, { ++ .name = "l2mem", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_L2MEM, ++ }, { ++ .name = "uart1", ++ .devname = "ak39xx-uart.1", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_UART2, ++ }, { ++ .name = "uart0", ++ .devname = "ak39xx-uart.0", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_UART1, ++ }, { ++ .name = "spi2", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_SPI2, ++ }, { ++ .name = "spi1", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_SPI1, ++ }, { ++ .name = "dac_clk", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_clk_dac, ++ .ctrlbit = AK_CLKCON_ASICCLK_DAC, ++ }, { ++ .name = "dac_hclk", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_hclk_dac, ++ .ctrlbit = AK_CLKCON_ASICCLK_DAC, ++ }, { ++ .name = "adc1_clk", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_clk_adc1, ++ .ctrlbit = AK_CLKCON_ASICCLK_ADC, ++ }, { ++ .name = "adc2_clk", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_clk_adc2, ++ .ctrlbit = AK_CLKCON_ASICCLK_ADC, ++ }, { ++ .name = "adc2_hclk", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_hclk_adc2, ++ .ctrlbit = AK_CLKCON_ASICCLK_ADC, ++ ++ }, { ++ .name = "sdio", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_SDIO, ++ }, { ++ .name = "mci", ++ .usage = 0, ++ .parent = &clk_asic, ++ .enable = ak39xx_asicclk_ctrl, ++ .ctrlbit = AK_CLKCON_ASICCLK_MCI, ++ }, { ++ .name = "sensor", ++ .usage = 0, ++ .parent = &clk_peri_pll, ++ .enable = ak39xx_sensor_ctrl, ++ .ctrlbit = AK_CLKCON_SCLK_CIS, ++ }, ++}; ++ ++ ++/** ++ * ak39xx_register_clock() - register a clock ++ * @clk: The clock to register ++ * ++ * Add the specified clock to the list of clocks known by the system. ++ */ ++int ak39xx_register_clock(struct clk *clk) ++{ ++ if (clk->enable == NULL) ++ clk->enable = clk_null_enable; ++ ++ /* fill up the clk_lookup structure and register it*/ ++ clk->lookup.dev_id = clk->devname; ++ clk->lookup.con_id = clk->name; ++ clk->lookup.clk = clk; ++ clkdev_add(&clk->lookup); ++ ++ return 0; ++} ++ ++#if 0 ++/** ++ * ak39xx_register_clocks() - register an array of clock pointers ++ * @clks: Pointer to an array of struct clk pointers ++ * @nr_clks: The number of clocks in the @clks array. ++ * ++ * Call ak39xx_register_clock() for all the clock pointers contained ++ * in the @clks list. Returns the number of failures. ++ */ ++int ak39xx_register_clocks(struct clk **clks, int nr_clks) ++{ ++ int fails = 0; ++ ++ for (; nr_clks > 0; nr_clks--, clks++) { ++ if (ak39xx_register_clock(*clks) < 0) { ++ struct clk *clk = *clks; ++ printk(KERN_ERR "%s: failed to register %p: %s\n", ++ __func__, clk, clk->name); ++ fails++; ++ } ++ } ++ ++ return fails; ++} ++#endif ++ ++/** ++ * s3c_register_clocks() - register an array of clocks ++ * @clkp: Pointer to the first clock in the array. ++ * @nr_clks: Number of clocks to register. ++ * ++ * Call s3c24xx_register_clock() on the @clkp array given, printing an ++ * error if it fails to register the clock (unlikely). ++ */ ++void __init ak39xx_register_clocks(struct clk *clkp, int nr_clks) ++{ ++ int ret; ++ ++ for (; nr_clks > 0; nr_clks--, clkp++) { ++ ret = ak39xx_register_clock(clkp); ++ if (ret < 0) { ++ printk(KERN_ERR "Failed to register clock %s (%d)\n", ++ clkp->name, ret); ++ } ++ } ++} ++ ++ ++/* initialise the clock system */ ++ ++T_cpu_mode ak_get_cpu_mode(void) ++{ ++ unsigned long regval = __raw_readl(CLOCK_CPU_PLL_CTRL); ++ ++ if ((regval & AK39_CLK_CPU3X_MODE) && (~(regval & AK39_CLK_CPU2X_MODE))) ++ return CPU_MODE_CPU3X; ++ else if ((~(regval & AK39_CLK_CPU3X_MODE)) && (regval & AK39_CLK_CPU2X_MODE)) ++ return CPU_MODE_CPU2X; ++ else if ((~(regval & AK39_CLK_CPU3X_MODE)) && (~(regval & AK39_CLK_CPU2X_MODE))) ++ return CPU_MODE_NORMAL; ++} ++EXPORT_SYMBOL(ak_get_cpu_mode); ++ ++bool ak_cpu_is_normal_mode(void) ++{ ++ return ak_get_cpu_mode() == CPU_MODE_NORMAL; ++} ++EXPORT_SYMBOL(ak_cpu_is_normal_mode); ++ ++bool ak_cpu_is_2x_mode(void) ++{ ++ return ak_get_cpu_mode() == CPU_MODE_CPU2X; ++} ++EXPORT_SYMBOL(ak_cpu_is_2x_mode); ++ ++bool ak_cpu_is_3x_mode(void) ++{ ++ return ak_get_cpu_mode() == CPU_MODE_CPU3X; ++} ++EXPORT_SYMBOL(ak_cpu_is_3x_mode); ++ ++unsigned long ak_get_cpu_pll_clk(void) ++{ ++ unsigned long pll_m, pll_n, pll_od; ++ unsigned long cpu_pll_clk; ++ unsigned long regval; ++ ++ regval = __raw_readl(CLOCK_CPU_PLL_CTRL); ++ pll_od = (regval & (0x3 << 12)) >> 12; ++ pll_n = (regval & (0xf << 8)) >> 8; ++ pll_m = regval & 0xff; ++ ++ //cpu_pll_clk = 12 * pll_m /(pll_n * __power2(pll_od)); // clk unit: MHz ++ cpu_pll_clk = 12 * pll_m /(pll_n * (1 << pll_od)); // clk unit: MHz ++ if ((pll_od >= 1) && ((pll_n >= 2) && (pll_n <= 6)) ++ && ((pll_m >= 84) && (pll_m <= 254))) ++ ++ return cpu_pll_clk * MHz; ++ ++ panic("cpu pll clk: %ld(Mhz) is unusable\n", cpu_pll_clk); ++} ++EXPORT_SYMBOL(ak_get_cpu_pll_clk); ++ ++unsigned long ak_get_asic_pll_clk(void) ++{ ++ unsigned long pll_m, pll_n, pll_od; ++ unsigned long asic_pll_clk; ++ unsigned long regval; ++ ++ regval = __raw_readl(CLOCK_ASIC_PLL_CTRL); ++ pll_od = (regval & (0x3 << 12)) >> 12; ++ pll_n = (regval & (0xf << 8)) >> 8; ++ pll_m = regval & 0xff; ++ ++ asic_pll_clk = (12 * pll_m)/(pll_n * (1 << pll_od)); // clk unit: MHz ++ ++ if ((pll_od >= 1) && ((pll_n >= 2) && (pll_n <= 6)) ++ && ((pll_m >= 84) && (pll_m <= 254))) ++ ++ return asic_pll_clk * MHz; ++ ++ panic("asic pll clk: %ld(Mhz) is unusable\n", asic_pll_clk); ++} ++EXPORT_SYMBOL(ak_get_asic_pll_clk); ++ ++ ++unsigned long ak_get_peri_pll_clk(void) ++{ ++ unsigned long pll_m, pll_n, pll_od; ++ unsigned long peri_pll_clk; ++ unsigned long regval; ++ ++ regval = __raw_readl(CLOCK_PERI_PLL_CTRL1); ++ pll_od = (regval & (0x3 << 12)) >> 12; ++ pll_n = (regval & (0xf << 8)) >> 8; ++ pll_m = regval & 0xff; ++ ++ peri_pll_clk = (12 * pll_m)/(pll_n * (1 << pll_od)); // clk unit: MHz ++ if ((pll_od >= 1) && ((pll_n >= 2) && (pll_n <= 6)) ++ && ((pll_m >= 84) && (pll_m <= 254))) ++ ++ return peri_pll_clk * MHz; ++ ++ panic("peri pll clk: %ld(Mhz) is unusable\n", peri_pll_clk); ++} ++EXPORT_SYMBOL(ak_get_peri_pll_clk); ++ ++ ++static unsigned long ak_get_cpu_hclk(void) ++{ ++ unsigned long regval; ++ unsigned long div; ++ ++ regval = __raw_readl(CLOCK_CPU_PLL_CTRL); ++ div = (regval & (0x7 << 17)) >> 17; ++ ++ if (div == 0) ++ return ak_get_cpu_pll_clk() >> 1; ++ ++ return ak_get_cpu_pll_clk() >> div; ++} ++ ++static unsigned long ak_get_cpu_dclk(void) ++{ ++ unsigned long regval; ++ unsigned long div; ++ ++ regval = __raw_readl(CLOCK_CPU_PLL_CTRL); ++ div = (regval & (0x7 << 20)) >> 20; ++ ++ if (div == 0) ++ return ak_get_cpu_pll_clk() >> 1; ++ ++ return ak_get_cpu_pll_clk() >> div; ++} ++ ++unsigned long ak_get_cpu_clk(void) ++{ ++ if (ak_cpu_is_normal_mode()) ++ return ak_get_cpu_hclk(); ++ ++ return ak_get_cpu_pll_clk(); ++} ++EXPORT_SYMBOL(ak_get_cpu_clk); ++ ++unsigned long ak_get_ahb_clk(void) ++{ ++ if (ak_cpu_is_3x_mode()) ++ return ak_get_cpu_pll_clk()/3; ++ ++ return ak_get_cpu_hclk(); ++} ++EXPORT_SYMBOL(ak_get_ahb_clk); ++ ++unsigned long ak_get_mem_clk(void) ++{ ++ if (ak_cpu_is_3x_mode()) ++ return ak_get_cpu_pll_clk()/3; ++ ++ return ak_get_cpu_dclk(); ++} ++EXPORT_SYMBOL(ak_get_mem_clk); ++ ++unsigned long ak_get_vclk(void) ++{ ++ unsigned long regval; ++ unsigned long div; ++ ++ regval = __raw_readl(CLOCK_ASIC_PLL_CTRL); ++ div = (regval & (0x7 << 17)) >> 17; ++ if (div == 0) ++ return ak_get_asic_pll_clk() >> 1; ++ ++ return ak_get_asic_pll_clk() >> div; ++} ++EXPORT_SYMBOL(ak_get_vclk); ++ ++unsigned long ak_get_asic_clk(void) ++{ ++ unsigned long regval; ++ unsigned long div; ++ ++ regval = __raw_readl(CLOCK_ASIC_PLL_CTRL); ++ div = regval & (1 << 24); ++ if (div == 0) ++ return ak_get_vclk(); ++ ++ return ak_get_vclk() >> 1; ++} ++EXPORT_SYMBOL(ak_get_asic_clk); ++ ++void aisc_freq_set(void) ++{ ++ unsigned long asicclk, asicclk_pll; ++ unsigned long div_od, div_n, div_m; ++ unsigned long uartdiv; ++ ++ asicclk = CONFIG_ASIC_FREQ_VALUE; ++ div_od = 2; ++ div_n = 2; ++ if ((div_n < 2) || (div_n > 6) ++ || (div_od < 1) || (div_od > 3)) ++ panic("Asic frequency parameter Error"); ++ ++ if (asicclk > 100*MHz) { ++ /* asic_pll = 2vclk = 2asic */ ++ asicclk_pll = (asicclk/MHz) << 1; ++ div_m = (asicclk_pll*(div_n * (1 << div_od)))/12; ++ uartdiv = asicclk/115200-1; ++ /* set asic frequency */ ++ REG32(AK_VA_SYSCTRL + 0x08) = ((1 << 23)|(1 <<17)|(div_od << 12)|(div_n << 8)|(div_m)); ++ } else { ++ /* asic_pll = 2vclk = 4asic */ ++ asicclk_pll = (asicclk/MHz) << 2; ++ div_m = (asicclk_pll*(div_n * (1 << div_od)))/12; ++ uartdiv = asicclk/115200-1; ++ /* set asic frequency */ ++ REG32(AK_VA_SYSCTRL + 0x08) = ((1 << 24)|(1 << 23)|(1 <<17)|(div_od << 12)|(div_n << 8)|(div_m)); ++ } ++ ++ /* enable asic freq change valid */ ++ REG32(AK_VA_SYSCTRL + 0x04) |= (1 << 28); ++ /* set uart baudrate */ ++ REG32(AK_VA_UART + 0x0) = ((3<<28)|(3<<21)|(uartdiv)); ++} ++EXPORT_SYMBOL(aisc_freq_set); ++ ++/***** end extern call for comm drivers compatible *****/ ++ ++/* initalise all the clocks */ ++static int __init ak39xx_init_clocks(void) ++{ ++ clk_default_setrate(&clk_cpu_pll, ak_get_cpu_pll_clk()); ++ clk_default_setrate(&clk_asic_pll, ak_get_asic_pll_clk()); ++ clk_default_setrate(&clk_peri_pll, ak_get_peri_pll_clk()); ++ ++ clk_default_setrate(&clk_cpu, ak_get_cpu_clk()); ++ clk_default_setrate(&clk_ahb, ak_get_ahb_clk()); ++ clk_default_setrate(&clk_mem, ak_get_mem_clk()); ++ clk_default_setrate(&clk_vclk, ak_get_vclk()); ++ clk_default_setrate(&clk_asic, ak_get_asic_clk()); ++ ++ printk("AK39 clocks: CPU %ldMHz, MEM %ldMHz, ASIC %ldMHz\n", ++ clk_cpu.rate/MHz, clk_mem.rate/MHz, clk_asic.rate/MHz); ++ ++ /* register clocks */ ++ if (ak39xx_register_clock(&clk_xtal_12M) < 0) ++ printk(KERN_ERR "failed to register 12M xtal\n"); ++ ++ if (ak39xx_register_clock(&clk_xtal_25M) < 0) ++ printk(KERN_ERR "failed to register 25M xtal\n"); ++ ++ if (ak39xx_register_clock(&clk_xtal_32K) < 0) ++ printk(KERN_ERR "failed to register 32K xtal\n"); ++ ++ if (ak39xx_register_clock(&clk_cpu_pll) < 0) ++ printk(KERN_ERR "failed to register cpu pll clk\n"); ++ ++ if (ak39xx_register_clock(&clk_asic_pll) < 0) ++ printk(KERN_ERR "failed to register asic pll clk\n"); ++ ++ if (ak39xx_register_clock(&clk_peri_pll) < 0) ++ printk(KERN_ERR "failed to register peri pll clk\n"); ++ ++ if (ak39xx_register_clock(&clk_cpu) < 0) ++ printk(KERN_ERR "failed to register cpu clk\n"); ++ ++ if (ak39xx_register_clock(&clk_ahb) < 0) ++ printk(KERN_ERR "failed to register AHB clk\n"); ++ ++ if (ak39xx_register_clock(&clk_mem) < 0) ++ printk(KERN_ERR "failed to register memory clk\n"); ++ ++ if (ak39xx_register_clock(&clk_vclk) < 0) ++ printk(KERN_ERR "failed to register vclk\n"); ++ ++ if (ak39xx_register_clock(&clk_asic) < 0) ++ printk(KERN_ERR "failed to register asic clk\n"); ++ ++ if (ak39xx_register_clock(&clk_opclk) < 0) ++ printk(KERN_ERR "failed to register opclk\n"); ++ ++ ak39xx_register_clocks(init_clocks, ARRAY_SIZE(init_clocks)); ++ ++#if 0 ++ printk("ak39xx_init_clocks: clk gate reg=0x%p\n", REG32(CLOCK_GATE_CTRL1)); ++ /* ++ * Enable L2buf clock by default, Disable all other clocks. ++ * uart0 clock has been open in uncompreess.h ++ */ ++ int i; ++ for (i = 0; i < ARRAY_SIZE(init_clocks); i++) { ++ if (strcmp(init_clocks[i].name, "uart0") == 0) { ++ clk_enable(&init_clocks[i]); ++ } ++ } ++#endif ++ ++ return 0; ++} ++ ++arch_initcall(ak39xx_init_clocks); ++ +diff --git a/arch/arm/mach-ak39/cpu.c b/arch/arm/mach-ak39/cpu.c +new file mode 100755 +index 00000000..57d0a109 +--- /dev/null ++++ b/arch/arm/mach-ak39/cpu.c +@@ -0,0 +1,67 @@ ++/* ++ * init cpu freq, clock ++ * ++ * report cpu id ++ */ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define AK_CPU_ID (AK_VA_SYSCTRL + 0x00) ++ ++#if defined(CONFIG_CPU_AK3910) ++#define AKCPU_VALUE 0x20120100 ++#define AKCPU_TYPE "AK3910" ++#elif defined(CONFIG_CPU_AK3916) ++#define AKCPU_VALUE 0x20120100 ++#define AKCPU_TYPE "AK3916" ++#elif defined(CONFIG_CPU_AK3918) ++#define AKCPU_VALUE 0x20120100 ++#define AKCPU_TYPE "AK3918" ++#else ++#error AK39xx Board NOT supported ++#endif ++ ++ ++#define IODESC_ENT(x) \ ++{ \ ++ .virtual = (unsigned long)AK_VA_##x, \ ++ .pfn = __phys_to_pfn(AK_PA_##x), \ ++ .length = AK_SZ_##x, \ ++ .type = MT_DEVICE \ ++} ++ ++static struct map_desc ak39_iodesc[] __initdata = { ++ IODESC_ENT(SYSCTRL), ++ IODESC_ENT(CAMERA), ++ IODESC_ENT(VENCODE), ++ IODESC_ENT(SUBCTRL), ++ IODESC_ENT(MAC), ++ IODESC_ENT(REGRAM), ++ IODESC_ENT(L2MEM), ++}; ++ ++void __init ak39_map_io(void) ++{ ++ unsigned long regval = 0x0; ++ ++ /* initialise the io descriptors we need for initialisation */ ++ iotable_init(ak39_iodesc, ARRAY_SIZE(ak39_iodesc)); ++ ++ regval = __raw_readl(AK_CPU_ID); ++ if (regval == AKCPU_VALUE) ++ printk("ANYKA CPU %s (ID 0x%lx)\n", AKCPU_TYPE, regval); ++ else ++ panic("Unknown ANYKA CPU ID: 0x%lx\n", regval); ++ ++ /* need to change asic freq is here, Because higher asic freq was affected usb function, ++ * I don't know essential reason of the problem ++ */ ++ aisc_freq_set(); ++} ++ +diff --git a/arch/arm/mach-ak39/cpu.h b/arch/arm/mach-ak39/cpu.h +new file mode 100644 +index 00000000..1968ab0f +--- /dev/null ++++ b/arch/arm/mach-ak39/cpu.h +@@ -0,0 +1,4 @@ ++ ++extern struct sys_timer ak39_timer; ++ ++void __init ak39_map_io(void); +diff --git a/arch/arm/mach-ak39/cpufreq.c b/arch/arm/mach-ak39/cpufreq.c +new file mode 100755 +index 00000000..95849dc5 +--- /dev/null ++++ b/arch/arm/mach-ak39/cpufreq.c +@@ -0,0 +1,773 @@ ++/* arch/arm/mach-ak39/cpufreq.c ++ * ++ * AK98 CPUfreq Support ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if 0 ++#include ++#include ++ ++#define SIZE_1K 0x00000400 /* 1K */ ++extern atomic_t suspend_flags; ++ ++static struct cpufreq_freqs freqs; ++static T_OPERATION_MODE current_mode; ++static int previous_mode_flag; ++static int cpufreq_in_ddr2 = 1; ++static unsigned int clkdiv = 1000000; ++ ++typedef enum { ++ CPU_LOW_MODE = 0, ++ CPU_NORMAL_MODE, ++ CPU_NORMAL_2XMODE, ++} T_cpufreq_mode; ++ ++#if 1 ++struct cpufreq_mode_clkdiv cpufreq_divs[] = { ++ //mode_name pll_sel clk168_div cpu_div mem_div asic_div low_clock is_3x (cpu,mem,asic[MHz]) ++ {LOW_MODE_CLOCK_0, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ ++ {LOW_MODE_CLOCK_1, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ ++ {LOW_MODE_CLOCK_2, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ ++ {LOW_MODE_CLOCK_3, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ ++ {LOW_MODE_CLOCK_4, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ ++ {LOW_MODE_CLOCK_5, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ ++ {LOW_MODE_CLOCK_6, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ ++ {LOW_MODE_CLOCK_7, 0x2d, 0, 0, 2, 4, 1, 0}, /* (90,180,90) */ ++ ++ {NORMAL_MODE_CLOCK_0, 0x2d, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ ++ {NORMAL_MODE_CLOCK_1, 0x2d, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ ++ {NORMAL_MODE_CLOCK_2, 0x2d, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ ++ {NORMAL_MODE_CLOCK_3, 0x2d, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ ++ {NORMAL_MODE_CLOCK_4, 0x2d, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ ++ {NORMAL_MODE_CLOCK_5, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ ++ {NORMAL_MODE_CLOCK_6, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ ++ {NORMAL_MODE_CLOCK_7, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ ++ ++ {VIDEO_MODE_CLOCK_0, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ ++ {VIDEO_MODE_CLOCK_1, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ ++ {VIDEO_MODE_CLOCK_2, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ ++ {VIDEO_MODE_CLOCK_3, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ ++ {VIDEO_MODE_CLOCK_4, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ ++ {VIDEO_MODE_CLOCK_5, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ ++ {VIDEO_MODE_CLOCK_6, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ ++ {VIDEO_MODE_CLOCK_7, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ ++ {LOWBATTERY_MODE_CLOCK, 0x2d, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ ++ {USBPLUG_MODE_CLOCK, 0x2d, 0, 1, 2, 4, 0, 0}, /* (360,180,90) */ ++ ++}; ++#else ++struct cpufreq_mode_clkdiv cpufreq_divs[] = { ++ //mode_name pll_sel clk168_div cpu_div mem_div asic_div low_clock is_3x (cpu,mem,asic[MHz]) ++ {LOW_MODE_CLOCK_0, 0x14, 0, 0, 2, 4, 1, 0}, /* (65,130,65) */ ++ {LOW_MODE_CLOCK_1, 0x14, 0, 0, 2, 4, 0, 0}, /* (65,130,65) */ ++ {LOW_MODE_CLOCK_2, 0x14, 0, 1, 2, 4, 0, 0}, /* (130,130,65) */ ++ {LOW_MODE_CLOCK_3, 0x14, 0, 0, 2, 2, 0, 0}, /* (130,130,65) */ ++ {LOW_MODE_CLOCK_4, 0x28, 0, 0, 2, 4, 0, 0}, /* (170,170,85) */ ++ {LOW_MODE_CLOCK_5, 0x2D, 0, 0, 2, 4, 0, 0}, /* (180,180,90) */ ++ {LOW_MODE_CLOCK_6, 0x37, 0, 0, 2, 4, 0, 0}, /* (200,200,100) */ ++ {LOW_MODE_CLOCK_7, 0x37, 0, 1, 2, 4, 0, 0}, /* (400,200,100) */ ++ ++ {NORMAL_MODE_CLOCK_0, 0x37, 0, 0, 2, 4, 0, 0}, /* (200,200,100) */ ++ {NORMAL_MODE_CLOCK_1, 0x37, 0, 0, 2, 4, 0, 0}, /* (200,200,100) */ ++ {NORMAL_MODE_CLOCK_2, 0x37, 0, 0, 2, 4, 0, 0}, /* (200,200,100) */ ++ {NORMAL_MODE_CLOCK_3, 0x37, 0, 0, 2, 4, 0, 0}, /* (200,200,100) */ ++ {NORMAL_MODE_CLOCK_4, 0x37, 0, 0, 2, 4, 0, 0}, /* (200,200,100) */ ++ {NORMAL_MODE_CLOCK_5, 0x37, 0, 1, 2, 4, 0, 0}, /* (400,200,100) */ ++ {NORMAL_MODE_CLOCK_6, 0x37, 0, 1, 2, 4, 0, 0}, /* (400,200,100) */ ++ {NORMAL_MODE_CLOCK_7, 0x37, 0, 1, 2, 4, 0, 0}, /* (400,200,100) */ ++ ++ {VIDEO_MODE_CLOCK_0, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ ++ {VIDEO_MODE_CLOCK_1, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ ++ {VIDEO_MODE_CLOCK_2, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ ++ {VIDEO_MODE_CLOCK_3, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ ++ {VIDEO_MODE_CLOCK_4, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ ++ {VIDEO_MODE_CLOCK_5, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ ++ {VIDEO_MODE_CLOCK_6, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ ++ {VIDEO_MODE_CLOCK_7, 0x36, 0, 1, 0, 0, 0, 1}, /* (396,132,132) */ ++}; ++#endif ++ ++static T_OPERATION_MODE prev_suspend_mode; ++#define SUSPEND_NORMAL_MODE NORMAL_MODE_CLOCK_2 ++ ++static int get_cpufreq_mode_clkdiv(T_OPERATION_MODE mode, struct cpufreq_mode_clkdiv *clkdiv) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(cpufreq_divs); i++) { ++ if (cpufreq_divs[i].mode_name == mode) { ++ *clkdiv = cpufreq_divs[i]; ++ return 0; ++ } ++ } ++ return -1; ++} ++ ++static unsigned int calcue_power(unsigned int num) ++{ ++ unsigned int i; ++ ++ if(num < 0) ++ return -1; ++ if((num == 2)||(num == 0)) ++ return 0; ++ ++ for(i = 0; num % 2 == 0; i++){ ++ num /= 2; ++ } ++ if(num > 2) ++ return -1; ++ ++ return i; ++} ++ ++static int current_is_lowmode(struct cpufreq_mode_clkdiv *cpufreq) ++{ ++ if (freqs.old_cpufreq.low_clock == 1) ++ return 1; ++ else ++ return 0; ++} ++ ++static int new_is_lowmode(struct cpufreq_mode_clkdiv *cpufreq) ++{ ++ if ((cpufreq->low_clock == 1)) ++ return 1; ++ else ++ return 0; ++} ++ ++static int new_is_2x_mode(struct cpufreq_mode_clkdiv *cpufreq) ++{ ++ if ((cpufreq->cpu_div == 1)) ++ return 1; ++ else ++ return 0; ++} ++ ++static void config_asicclk_parameter(struct cpufreq_mode_clkdiv *cpufreq, ++ unsigned long *ratio) ++{ ++ unsigned long clk_ratio = *ratio; ++ unsigned int asicdiv; ++ ++ asicdiv = calcue_power(cpufreq->asic_div); ++ if( asicdiv < 0) ++ printk("Error, calcue asic_div."); ++ ++ clk_ratio &= ~(0x7 << 6); ++ clk_ratio |= ((asicdiv << 6) | CLOCK_ASIC_MEM_ENA); ++ ++ *ratio = clk_ratio; ++} ++ ++static void config_clock_parameter(struct cpufreq_mode_clkdiv *cpufreq, ++ unsigned long *ratio) ++{ ++ unsigned long clk_ratio = *ratio; ++ unsigned int memdiv, asicdiv; ++ ++ memdiv = calcue_power(cpufreq->mem_div); ++ if( memdiv < 0) ++ printk("Error, calcue mem_div."); ++ asicdiv = calcue_power(cpufreq->asic_div); ++ if( asicdiv < 0) ++ printk("Error, calcue asic_div."); ++ ++ clk_ratio &= ~((1 << 28)|(1 << 22)|(0xF << 17)|(1 << 15)|(0x7 << 9)|(0x7 << 6)|(0x3F << 0)); ++ ++ clk_ratio |= (cpufreq->is_3x << 28); //is 3x ? ++ clk_ratio |= (cpufreq->clk168_div << 17); //clk168 div ++ clk_ratio |= (cpufreq->cpu_div << 15); //cpu clk = mem clk or cpu clk = asic clk ++ clk_ratio |= (cpufreq->low_clock << 22); //cpu clk = asic clk ++ clk_ratio |= (memdiv << 9)|(asicdiv << 6); //mem div and asic div ++ clk_ratio |= (cpufreq->pll_sel << 0); //pll_sel ++ clk_ratio |= PLL_CHANGE_ENA; ++ ++ *ratio = clk_ratio; ++} ++ ++/* ++ * *function: enter L2 modify register parameters of clock for change sys clcok ++ * */ ++void L2_LINK(freqchange) L2FUNC_NAME(freqchange)(unsigned long param1, ++ unsigned long param2,unsigned long param3,unsigned long param4) ++{ ++ DISABLE_CACHE_MMU(); ++ ++ // check this bit and unitil both are empty ++ while(!((REG32(PHY_RAM_CFG_REG4) & (FIFO_R_EMPTY | FIFO_CMD_EMPTY)) == (FIFO_R_EMPTY | FIFO_CMD_EMPTY))) ++ ; ++ ++ // setup periodic of refresh interval and disable auto-refresh ++ REG32(PHY_RAM_CFG_REG4) = REFRESH_PERIOD_INTERVAL; ++ ++ DDR2_ENTER_POWERDOWN(); ++ // after send enter self - refresh, delay stable clock at least more than 2 tck ++ PM_DELAY(0x4); ++ ++ //disable ram clock ++ REG32(PHY_CLOCK_CTRL_REG) |= (1<<10); ++ ++ // other mode change to normal mode ++ //cpu clock from other mode to normal ++ REG32(PHY_CLOCK_DIV_REG) &= ~((1 << 28)|(1 << 22)); ++ PM_DELAY(0x100); ++ ++ //set clock div and check pll[12] ++ REG32(PHY_CLOCK_DIV_REG) = param1; ++ while (REG32(PHY_CLOCK_DIV_REG) & PLL_CHANGE_ENA); ++ PM_DELAY(0x10); ++ ++ //enable ram clock ++ REG32(PHY_CLOCK_CTRL_REG) &= ~(1<<10); ++ // new clock stable at least more than 1tck,here is ignore because follow has few instruction. ++ ++ // softreset ddr2 memory controller ++ REG32(0x0800000c) |= (0x1 << 26); ++ PM_DELAY(0x10); ++ REG32(0x0800000c) &= ~(0x1 << 26); ++ ++ // re-init ram controller ++ REG32(0x2000e05c) = 0x00000200; // bypass DCC ++ REG32(0x2000e078) = 0x43020100; // initial sstl = 00(12ma), tsel = 10(150ohm) ++ REG32(0x2000e000) = 0x00004e90; // 32 bit bus width ++ ++ // exit precharge power-down mode before delay at least 1 tck ++ PM_DELAY(10); ++ DDR2_EXIT_POWERDOWN(); ++ ++ // load mr, reset dll and delay for 200 tck, and set odt high ++ REG32(PHY_RAM_CPU_CMD) = 0x02800532; ++ // send nop, delay for 200 tck for dll reset, ++ PM_DELAY(0x10); ++ ++ // load mr, clean reset dll and remain odt low in ddr2 memory ++ REG32(PHY_RAM_CPU_CMD) = 0x1a800432; ++ ++ //enable dll and wate for dll stable in ram controller ++ REG32(0x2000e020) = 0x00000003; ++ while (!(REG32(0x2000e020) & (1 << 2))); ++ ++ // open auto-referesh ++ // default as mclk = 120mhz for calc tck=8.3ns, trefi=7.7us ++ REG32(0x2000e00c) = (0x39f<<1)|0x1; ++ ++ //calibration sart and wait for finish ++ REG32(0x2000e024) = ((param2>>0x7)<<10)|0x1; ++ while (!(REG32(0x2000e024) & (1 << 1))); ++ ++ ENABLE_CACHE_MMU(); ++} ++ ++/* ++ *function: change pll clock, include mem clock,asic clock and cpu clock. ++ */ ++static void cpufreq_change_clocks(struct cpufreq_mode_clkdiv *cpufreq) ++{ ++ unsigned long ratio; ++ void *addr; ++ unsigned long phy_addr; ++ ++ addr = kzalloc(512, GFP_KERNEL | GFP_ATOMIC); ++ if (addr == NULL) ++ return ; ++ phy_addr = virt_to_phys(addr); ++ ++ ratio = REG32(CLOCK_DIV_REG); ++ config_clock_parameter(cpufreq, &ratio); ++ ++ SPECIFIC_L2BUF_EXEC(freqchange, ratio, phy_addr,0,0); ++ ++ kfree(addr); ++} ++ ++/* ++ *function: according to needed new clocks and determine branch of cpufreq ++ * @cpufreq: structure include mode name and clock div ++ * note: the mode enter L2 cpufreq ++ */ ++static int l2_cpu_freq_change(struct cpufreq_mode_clkdiv *cpufreq) ++{ ++ int error; ++ ++ error = usermodehelper_disable(); ++ if (error) ++ goto Finish; ++ ++ // freeze process and kernel task. ++ error = cpufreq_freeze_processes(); ++ if (error) ++ goto freeze; ++ ++ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); ++ cpufreq_change_clocks(cpufreq); ++ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); ++ ++ thaw_processes(); ++ usermodehelper_enable(); ++ ++ return 0; ++ ++freeze: ++ thaw_processes(); ++Finish: ++ usermodehelper_enable(); ++ return error; ++} ++ ++static void ddr2_transfrom_clock_mode(T_cpufreq_mode state) ++{ ++ unsigned long ratio; ++ ++ ratio = REG32(CLOCK_DIV_REG); ++ ratio &= ~(1 << 22); ++ switch (state) { ++ case CPU_LOW_MODE: ++ ratio |= (1 << 22); //|(1 << 14); ++ ratio &= ~(1 << 15); ++ break; ++ case CPU_NORMAL_MODE: ++ ratio &= ~(1 << 15); ++ break; ++ case CPU_NORMAL_2XMODE: ++ ratio |= (1 << 15); ++ break; ++ default: ++ printk("CPUFREQ: need mode is error.\n"); ++ break; ++ } ++ REG32(CLOCK_DIV_REG) = ratio; ++} ++ ++/* ++ *function: according to needed new clocks and determine branch of cpufreq ++ * @cpufreq: structure include mode name and clock div ++ * note: the mode cpufreq in ddr2 ++ */ ++static int ddr2_cpu_freq_change(struct cpufreq_mode_clkdiv *cpufreq) ++{ ++ unsigned long ratio, flags; ++ unsigned long cpuid = 0; ++ ++ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); ++ local_irq_save(flags); ++ ++ // check if cpu clock from other mode to normal and cutover to normal ++ if (current_is_lowmode(cpufreq)) { ++ ddr2_transfrom_clock_mode(CPU_NORMAL_MODE); ++ //cpuid = REG32(CPU_CHIP_ID); // used as delay ++ udelay(10); ++ } ++ ++ // change asic clock ++ if (freqs.old_cpufreq.asic_clk != freqs.new_cpufreq.asic_clk) { ++ ratio = REG32(CLOCK_DIV_REG); ++ config_asicclk_parameter(cpufreq, &ratio); ++ ++ REG32(CLOCK_DIV_REG) = ratio; ++ while (REG32(CLOCK_DIV_REG) & CLOCK_ASIC_MEM_ENA); ++ } ++ ++ // change cpu clock ++ if (freqs.old_cpufreq.cpu_clk != freqs.new_cpufreq.cpu_clk) { ++ if (new_is_2x_mode(cpufreq)) ++ ddr2_transfrom_clock_mode(CPU_NORMAL_2XMODE); ++ else if (!new_is_2x_mode(cpufreq)) { ++ if (new_is_lowmode(cpufreq)) ++ ddr2_transfrom_clock_mode(CPU_LOW_MODE); ++ else if (!current_is_lowmode(cpufreq)) ++ ddr2_transfrom_clock_mode(CPU_NORMAL_MODE); ++ } ++ udelay(10); ++ } ++ ++ local_irq_restore(flags); ++ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); ++ ++ return 0; ++} ++ ++static int cpu_freq_change(struct cpufreq_mode_clkdiv *cpufreq) ++{ ++ int error; ++ static int l2_cpufreq_flag = 0; ++ ++ if (freqs.old_cpufreq.pll_sel == freqs.new_cpufreq.pll_sel) { ++ if (current_is_lowmode(cpufreq) && l2_cpufreq_flag) { ++ error = l2_cpu_freq_change(cpufreq); ++ if (error) ++ goto exit_cpufreq; ++ ++ cpufreq_in_ddr2 = 0; ++ } else { ++ error = ddr2_cpu_freq_change(cpufreq); ++ if (error < 0) ++ goto exit_cpufreq; ++ ++ cpufreq_in_ddr2 = 1; ++ l2_cpufreq_flag = 0; ++ } ++ } else { ++ error = l2_cpu_freq_change(cpufreq); ++ if (error) ++ goto exit_cpufreq; ++ ++ cpufreq_in_ddr2 = 0; ++ l2_cpufreq_flag = 1; ++ } ++ ++ return 0; ++ ++exit_cpufreq: ++ return error; ++} ++ ++/* ++ * change from low mode to normal mode for suspend (low mode --> normal mode) ++ */ ++void cpu_freq_suspend_check(void) ++{ ++ struct cpufreq_mode_clkdiv cpufreq; ++ unsigned long ratio; ++ ++ // change to normal mode ++ if (freqs.old_cpufreq.low_clock == 1) { ++ if (cpufreq_in_ddr2) { ++ ddr2_transfrom_clock_mode(CPU_NORMAL_MODE); ++ udelay(10); ++ } else { ++ // save low mode before suspend change normal ++ prev_suspend_mode = current_mode; ++ ++ if(!get_cpufreq_mode_clkdiv(SUSPEND_NORMAL_MODE, &cpufreq)){ ++ cpufreq_change_clocks(&cpufreq); ++ } ++ } ++ } ++} ++EXPORT_SYMBOL(cpu_freq_suspend_check); ++ ++/* ++ * restore low mode after resume (normal mode --> low mode) ++ */ ++void cpu_freq_resume_check(void) ++{ ++ struct cpufreq_mode_clkdiv cpufreq; ++ unsigned long ratio; ++ ++ // chang to low mode ++ if (freqs.old_cpufreq.low_clock == 1) { ++ if (cpufreq_in_ddr2) { ++ ddr2_transfrom_clock_mode(CPU_LOW_MODE); ++ udelay(10); ++ } else { ++ if(!get_cpufreq_mode_clkdiv(prev_suspend_mode, &cpufreq)){ ++ cpufreq_change_clocks(&cpufreq); ++ } ++ } ++ } ++} ++EXPORT_SYMBOL(cpu_freq_resume_check); ++ ++static void info_clock_value(void) ++{ ++ printk("Cpufreq: system clocks(unit:MHz)[cpu,mem,asic] from (%d,%d,%d) to (%d,%d,%d)\n", ++ freqs.old_cpufreq.cpu_clk/clkdiv, freqs.old_cpufreq.mem_clk/clkdiv, ++ freqs.old_cpufreq.asic_clk/clkdiv, ak98_get_cpu_clk()/clkdiv, ++ ak98_get_mem_clk()/clkdiv, ak98_get_asic_clk()/clkdiv); ++} ++ ++static int clock_need_changing(void) ++{ ++ if((freqs.new_cpufreq.cpu_clk == freqs.old_cpufreq.cpu_clk)&& ++ (freqs.new_cpufreq.mem_clk == freqs.old_cpufreq.mem_clk)&& ++ (freqs.new_cpufreq.asic_clk == freqs.old_cpufreq.asic_clk)) ++ return 0; ++ else ++ return 1; ++} ++ ++static unsigned int get_asic_clk(struct cpufreq_mode_clkdiv *cpufreq) ++{ ++ unsigned int asicclk; ++ ++ if(cpufreq->is_3x) ++ asicclk = ((PLL_CLK_MIN+(cpufreq->pll_sel*4))/(cpufreq->clk168_div+1))/3; ++ else ++ asicclk = ((PLL_CLK_MIN+(cpufreq->pll_sel*4))/(cpufreq->clk168_div+1))/cpufreq->asic_div; ++ ++ return asicclk; ++} ++ ++static unsigned int get_mem_clk(struct cpufreq_mode_clkdiv *cpufreq) ++{ ++ unsigned int memclk; ++ ++ if(cpufreq->is_3x) ++ memclk = ((PLL_CLK_MIN+(cpufreq->pll_sel*4))/(cpufreq->clk168_div+1))/3; ++ else ++ memclk = ((PLL_CLK_MIN+(cpufreq->pll_sel*4))/(cpufreq->clk168_div+1))/cpufreq->mem_div; ++ ++ return memclk; ++} ++ ++static unsigned int get_cpu_clk(struct cpufreq_mode_clkdiv *cpufreq) ++{ ++ unsigned int cpuclk; ++ ++ if(cpufreq->is_3x) { ++ cpuclk = (PLL_CLK_MIN+(cpufreq->pll_sel*4))/(cpufreq->clk168_div+1); ++ } else { ++ if(cpufreq->cpu_div) { ++ cpuclk = (PLL_CLK_MIN+(cpufreq->pll_sel*4))/(cpufreq->clk168_div+1); ++ } else { ++ if(cpufreq->low_clock) ++ cpuclk = get_asic_clk(cpufreq); ++ else ++ cpuclk = get_mem_clk(cpufreq); ++ } ++ } ++ ++ return cpuclk; ++} ++ ++static void update_current_clock(void) ++{ ++ freqs.old_cpufreq.cpu_clk = freqs.new_cpufreq.cpu_clk; ++ freqs.old_cpufreq.mem_clk = freqs.new_cpufreq.mem_clk; ++ freqs.old_cpufreq.asic_clk = freqs.new_cpufreq.asic_clk; ++ freqs.old = freqs.new; ++ freqs.old_cpufreq.pll_sel = freqs.new_cpufreq.pll_sel; ++ freqs.old_cpufreq.low_clock = freqs.new_cpufreq.low_clock; ++} ++ ++static void get_newmode_clock(struct cpufreq_mode_clkdiv *cpufreq) ++{ ++ freqs.new_cpufreq.cpu_clk = get_cpu_clk(cpufreq)*clkdiv; ++ freqs.new_cpufreq.mem_clk = get_mem_clk(cpufreq)*clkdiv; ++ freqs.new_cpufreq.asic_clk = get_asic_clk(cpufreq)*clkdiv; ++ freqs.new = freqs.new_cpufreq.cpu_clk; ++ freqs.new_cpufreq.pll_sel = cpufreq->pll_sel; ++ freqs.new_cpufreq.low_clock = cpufreq->low_clock; ++} ++ ++void update_pre_mode(void) ++{ ++ // assign to save old mode ++ previous_mode_flag = freqs.old_cpufreq.low_clock; ++} ++ ++unsigned int get_pll_sel(T_OPERATION_MODE state) ++{ ++ int i, len; ++ ++ len = ARRAY_SIZE(cpufreq_divs); ++ for (i = 0; i < len; i++) { ++ if (state == cpufreq_divs[i].mode_name) ++ break; ++ } ++ if (likely(i < len)) ++ return cpufreq_divs[i].pll_sel; ++ ++ return -1; ++} ++EXPORT_SYMBOL(get_pll_sel); ++ ++int current_mode_is_low_mode(void) ++{ ++ return freqs.old_cpufreq.low_clock; ++} ++EXPORT_SYMBOL(current_mode_is_low_mode); ++ ++/* ++ *function: get system boot's mode ++ */ ++T_OPERATION_MODE get_current_mode(void) ++{ ++ return current_mode; ++} ++EXPORT_SYMBOL(get_current_mode); ++ ++/* function: enter change cpufreq ++ * @state: requested mode name ++ * return: ++ * -1: if system init mode is not surpport. ++ * 0: if cpufreq change successful. ++ */ ++int request_cpufreq_enter(T_OPERATION_MODE state) ++{ ++ int i, len; ++ int error; ++ ++ // check if request suspending, prevent cpufreq when suspending ++ if (atomic_read(&suspend_flags)) ++ return 0; ++ ++ if (state == current_mode) { ++ //printk("requset new mode equal to current mode.\n"); ++ return 0; ++ } ++ ++ len = ARRAY_SIZE(cpufreq_divs); ++ for (i = 0; i < len; i++) { ++ if (state == cpufreq_divs[i].mode_name) ++ break; ++ } ++ if (likely(i < len)) { ++ get_newmode_clock(&cpufreq_divs[i]); ++ update_pre_mode(); ++ ++ if (!clock_need_changing()) { ++ //printk("Cpufreq: new mode clocks equal to old mode clocks, exit changing.\n"); ++ return 0; ++ } ++ ++ error = cpu_freq_change(&cpufreq_divs[i]); ++ if (error) { ++ printk("CPUFREQ: request new mode changed fail. working mode is current mode.\n\n"); ++ goto cpufreq_err; ++ } ++ } else { ++ printk("CPUFREQ: requset new mode is not surpport.\n"); ++ goto cpufreq_err; ++ } ++ ++ info_clock_value(); ++ update_current_clock(); ++ current_mode = state; ++ ++ return 0; ++ ++cpufreq_err: ++ return -1; ++} ++EXPORT_SYMBOL(request_cpufreq_enter); ++ ++/* ++ * function: get system boot's mode ++ */ ++static int get_init_mode(void) ++{ ++ int i; ++ unsigned int pllclk, clk168, cpuclk, memclk, asicclk; ++ unsigned int pllsel, clk168div, cpudiv, memdiv, asicdiv, lowclock; ++ ++ pllclk = ak98_get_pll_clk()/clkdiv; ++ clk168 = ak98_get_clk168m_clk()/clkdiv; ++ cpuclk = freqs.old_cpufreq.cpu_clk/clkdiv; ++ memclk = freqs.old_cpufreq.mem_clk/clkdiv; ++ asicclk = freqs.old_cpufreq.asic_clk/clkdiv; ++ pllsel = ((pllclk - PLL_CLK_MIN)/4) & 0x3f; ++ ++ if(pllclk == clk168) ++ clk168div = 0; ++ else ++ clk168div = pllclk/clk168; ++ ++ //system is 3x mode ++ if((cpuclk / memclk == 3)&&(cpuclk / asicclk == 3)&& ++ (cpuclk % memclk == 0)&&(cpuclk % asicclk == 0)) { ++ for(i = 0; i < ARRAY_SIZE(cpufreq_divs); i++) { ++ if((cpufreq_divs[i].is_3x == 1) && ++ (cpufreq_divs[i].clk168_div == clk168div) && ++ (cpufreq_divs[i].pll_sel == pllsel)){ ++ ++ current_mode = cpufreq_divs[i].mode_name; ++ freqs.old_cpufreq.low_clock = cpufreq_divs[i].low_clock; ++ return 0; ++ } ++ } ++ return -1; ++ } ++ ++ //system is normal mode ++ if(cpuclk == clk168) { ++ lowclock = 0; ++ cpudiv = 1; ++ } else if(cpuclk == memclk) { ++ lowclock = 0; ++ cpudiv = 0; ++ } else if(cpuclk == asicclk) { ++ lowclock = 1; ++ cpudiv = 0; ++ } ++ memdiv = clk168/memclk; ++ asicdiv = clk168/asicclk; ++ ++ for(i = 0; i < ARRAY_SIZE(cpufreq_divs); i++){ ++ if((cpufreq_divs[i].cpu_div == cpudiv) && ++ (cpufreq_divs[i].low_clock == lowclock)&& ++ (cpufreq_divs[i].mem_div == memdiv) && ++ (cpufreq_divs[i].asic_div == asicdiv) && ++ (cpufreq_divs[i].pll_sel == pllsel) && ++ (cpufreq_divs[i].clk168_div == clk168div)){ ++ ++ current_mode = cpufreq_divs[i].mode_name; ++ freqs.old_cpufreq.low_clock = cpufreq_divs[i].low_clock; ++ return 0; ++ } ++ } ++ return -1; ++} ++ ++static void cpufreq_operation_init(void) ++{ ++ int i, error; ++ unsigned int tmp; ++ ++ freqs.old_cpufreq.cpu_clk = ak98_get_cpu_clk(); ++ freqs.old_cpufreq.mem_clk = ak98_get_mem_clk(); ++ freqs.old_cpufreq.asic_clk = ak98_get_asic_clk(); ++ freqs.old = freqs.old_cpufreq.cpu_clk; ++ ++ freqs.flags = 0; ++ ++ tmp = cpufreq_divs[0].pll_sel; ++ for(i = 1; i < ARRAY_SIZE(cpufreq_divs); i++){ ++ if(cpufreq_divs[i].pll_sel > tmp) ++ tmp = cpufreq_divs[i].pll_sel; ++ } ++ freqs.old_cpufreq.pll_sel = tmp; ++ ++ error = get_init_mode(); ++ if(error < 0) ++ current_mode = error; ++ ++ return; ++} ++ ++/* ak98_cpufreq_init ++ * ++ * Attach the cpu frequence scaling functions. This should be called ++ * from the board specific initialisation if the board supports ++ * it. ++*/ ++int __init ak98_cpufreq_init(void) ++{ ++ printk("AK98 cpu frequence change support, (c) 2011 ANYAK\n"); ++ cpufreq_operation_init(); ++ ++ return 0; ++} ++module_init(ak98_cpufreq_init); ++ ++#endif ++ +diff --git a/arch/arm/mach-ak39/devices.c b/arch/arm/mach-ak39/devices.c +new file mode 100755 +index 00000000..4469bf97 +--- /dev/null ++++ b/arch/arm/mach-ak39/devices.c +@@ -0,0 +1,405 @@ ++/* linux/arch/arm/mach-ak39/devices.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct platform_device ak39_uart0_device = { ++ .name = "ak39-uart", ++ .id = 0, ++}; ++EXPORT_SYMBOL(ak39_uart0_device); ++ ++struct platform_device ak39_uart1_device = { ++ .name = "ak39-uart", ++ .id = 1, ++}; ++EXPORT_SYMBOL(ak39_uart1_device); ++ ++struct platform_device ak39_gpio_uart_device = { ++ .name = "gpio-uart", ++ .id = 0, ++}; ++EXPORT_SYMBOL(ak39_gpio_uart_device); ++ ++ ++/* MCI platform data */ ++static struct resource ak39_mmc_resource[] = { ++ [0] = { ++ .start = 0x20100000, ++ .end = 0x20100000 + 0x43, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_MCI, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++struct platform_device ak39_mmc_device = { ++ .name = "ak_mci", ++ .id = -1, ++ .num_resources = ARRAY_SIZE(ak39_mmc_resource), ++ .resource = ak39_mmc_resource, ++}; ++EXPORT_SYMBOL(ak39_mmc_device); ++ ++/* SDIO platform data */ ++static struct resource ak39_sdio_resource[] = { ++ [0] = { ++ .start = 0x20108000, ++ .end = 0x20108000 + 0x43, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_SDIO, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++struct platform_device ak39_sdio_device = { ++ .name = "ak_sdio", ++ .id = -1, ++ .num_resources = ARRAY_SIZE(ak39_sdio_resource), ++ .resource = ak39_sdio_resource, ++}; ++EXPORT_SYMBOL(ak39_sdio_device); ++ ++/* I2C */ ++#if defined(CONFIG_I2C_AK39_HW) ++struct gpio_info i2c_gpios[] = { ++ { ++ .pin = AK_GPIO_27, ++ .pulldown = -1, ++ .pullup = AK_PULLUP_DISABLE, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .value = AK_GPIO_OUT_HIGH, ++ .int_pol = -1, ++ }, ++ { ++ .pin = AK_GPIO_28, ++ .pulldown = -1, ++ .pullup = AK_PULLUP_DISABLE, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .value = AK_GPIO_OUT_HIGH, ++ .int_pol = -1, ++ ++ }, ++}; ++ ++static struct ak39_platform_i2c ak39_default_i2c_data = { ++ .flags = 0, ++ .bus_num = 0, ++ .slave_addr = 0x10, ++ .frequency = 100*1000, ++ .sda_delay = 100, ++ .gpios = i2c_gpios, ++ .npins = ARRAY_SIZE(i2c_gpios), ++}; ++ ++static struct resource ak39_i2c_resource[] = { ++ [0] = { ++ .start = 0x20150000, ++ .end = 0x20150000+SZ_256, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_I2C, ++ .end = IRQ_I2C, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++struct platform_device ak39_i2c_device = { ++ .name = "i2c-ak39", ++ .id = -1, ++ .dev = { ++ .platform_data = &ak39_default_i2c_data, ++ }, ++ .num_resources = ARRAY_SIZE(ak39_i2c_resource), ++ .resource = ak39_i2c_resource, ++}; ++EXPORT_SYMBOL(ak39_i2c_device); ++ ++#elif defined(CONFIG_I2C_GPIO_SOFT) ++struct i2c_gpio_platform_data ak39_i2c_data={ ++ .sda_pin = INVALID_GPIO, ++ .scl_pin = INVALID_GPIO, ++ .udelay = 10, ++ .timeout = 200 ++}; ++ ++struct platform_device ak39_i2c_device = { ++ .name = "i2c-gpio", ++ .id = -1, ++ .dev = { ++ .platform_data = &ak39_i2c_data, ++ }, ++}; ++EXPORT_SYMBOL(ak39_i2c_device); ++#else ++struct platform_device ak39_i2c_device = { ++ .name = "i2c", ++ .id = -1, ++}; ++EXPORT_SYMBOL(ak39_i2c_device); ++#endif ++ ++ ++/* USB udc device data */ ++static struct resource usb_otg_udc_resource[] = { ++ [0] = { ++ .start = 0x20200000, ++ .end = 0x202003ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .name = "usb mcu irq", ++ .start = IRQ_USBOTG_MCU, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ .name = "usb dma irq", ++ .start = IRQ_USBOTG_DMA, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++struct platform_device ak39_usb_udc_device = { ++ .name = "ak-hsudc", ++ .id = -1, ++ .num_resources = ARRAY_SIZE(usb_otg_udc_resource), ++ .resource = usb_otg_udc_resource, ++}; ++EXPORT_SYMBOL(ak39_usb_udc_device); ++ ++/* USB otg host data */ ++static struct resource usb_otg_hcd_resource[] = { ++ [0] = { ++ .start = 0x20200000, ++ .end = 0x202003ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .name = "usb mcu irq", ++ .start = IRQ_USBOTG_MCU, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ .name = "usb dma irq", ++ .start = IRQ_USBOTG_DMA, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++struct platform_device ak39_usb_otg_hcd_device = { ++ .name = "usb-host", ++ .id = -1, ++ .num_resources = ARRAY_SIZE(usb_otg_hcd_resource), ++ .resource = usb_otg_hcd_resource, ++}; ++EXPORT_SYMBOL(ak39_usb_otg_hcd_device); ++ ++/* MAC */ ++static struct resource ak39_mac_resource[] = { ++ [0] = { ++ .start = 0x20300000, ++ .end = 0x20301fff, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .name = "mac irq", ++ .start = IRQ_MAC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++struct platform_device ak39_mac_device = { ++ .name = "ak_ethernet", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(ak39_mac_resource), ++ .resource = ak39_mac_resource, ++}; ++EXPORT_SYMBOL(ak39_mac_device); ++ ++/* AK39 SPI device platform data */ ++static struct resource ak39_spi1_resource[] = { ++ [0] = { ++ .start = 0x20120000, ++ .end = 0x20120027, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_SPI1, ++ .end = IRQ_SPI1, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++struct platform_device ak39_spi1_device = { ++ .name = "ak-spi", ++ .id = -1, ++ .num_resources = ARRAY_SIZE(ak39_spi1_resource), ++ .resource = ak39_spi1_resource, ++}; ++EXPORT_SYMBOL(ak39_spi1_device); ++ ++ ++/* Camera interface resource */ ++static struct resource ak39_camera_resource[] = { ++ [0] = { ++ .start = 0x20000000, ++ .end = 0x20000030, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .name = "camera if irq", ++ .start = IRQ_CAMERA, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct ak_camera_pdata ak39_camera_info = { ++ .mclk = 24, ++}; ++ ++/* camera interface */ ++struct platform_device ak39_camera_interface = { ++ .name = "ak_camera", ++ .id = 39, ++ .num_resources = ARRAY_SIZE(ak39_camera_resource), ++ .resource = ak39_camera_resource, ++ .dev = { ++ .platform_data = &ak39_camera_info, ++ } ++}; ++ ++EXPORT_SYMBOL(ak39_camera_interface); ++ ++static u64 snd_dma_mask = DMA_BIT_MASK(32); ++ ++struct platform_device ak39_pcm_device = { ++ .name = "snd_akpcm", ++ .id = 0, ++ .dev = { ++ .dma_mask = &snd_dma_mask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ }, ++}; ++EXPORT_SYMBOL(ak39_pcm_device); ++ ++ ++static struct ion_platform_data ak39_ion_pdata = { ++ .nr = 1, ++ .heaps = { ++ { ++ .type = ION_HEAP_TYPE_CARVEOUT, ++ .id = 1, ++ .name = "Reserved phys Memory", ++ .base = CONFIG_RAM_BASE, ++ .size = CONFIG_VIDEO_RESERVED_MEM_SIZE, /* the first reserved size */ ++ .align = PAGE_SIZE, ++ }, ++ } ++}; ++ ++struct platform_device ak39_ion_device = { ++ .name = "ion-ak", ++ .id = -1, ++ .dev = { ++ .platform_data = &ak39_ion_pdata, ++ }, ++}; ++EXPORT_SYMBOL(ak39_ion_device); ++ ++struct platform_device ak39_led_pdev = { ++ .name = "ak_led", ++ .id = -1, ++}; ++EXPORT_SYMBOL(ak39_led_pdev); ++ ++struct platform_device akfha_char_device = { ++ .name = "ak-fhachar", ++ .id = -1, ++ .dev = { ++ .platform_data = NULL, ++ }, ++}; ++EXPORT_SYMBOL(akfha_char_device); ++ ++struct platform_device ak39_gpio_keys_device = { ++ .name = "akgpio-keys", ++ .id = -1, ++}; ++EXPORT_SYMBOL(ak39_gpio_keys_device); ++ ++/* battery_power supply */ ++struct platform_device ak39_battery_power = { ++ .name = "battery", ++ .id = -1, ++}; ++EXPORT_SYMBOL(ak39_battery_power); ++ ++static struct resource ak39mmx_resources[] = { ++ { ++ .name = "video-base", ++ .start = 0x20020000, ++ .end = 0x2002042f, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct uio_info akmmx_uioinfo = { ++ .name = "video_codec", ++ .version = "0.1.0", ++#ifdef CONFIG_UIODMA ++ .use_dma = true, ++#endif ++ .irq = UIO_IRQ_CUSTOM, ++}; ++ ++struct platform_device ak39_mmx_device = { ++ .name = "uio_vcodec", ++ .id = 0, ++ .dev = { ++ .platform_data = &akmmx_uioinfo, ++ }, ++ .num_resources = ARRAY_SIZE(ak39mmx_resources), ++ .resource = ak39mmx_resources, ++}; ++EXPORT_SYMBOL(ak39_mmx_device); ++ ++struct platform_device ak39_rtc_device = { ++ .name = "ak-rtc", ++ .id = -1, ++}; ++EXPORT_SYMBOL(ak39_rtc_device); ++ ++struct platform_device ak39_motor0_device = { ++ .name = "ak-motor", ++ .id = 0, ++}; ++EXPORT_SYMBOL(ak39_motor0_device); ++ ++struct platform_device ak39_motor1_device = { ++ .name = "ak-motor", ++ .id = 1, ++}; ++EXPORT_SYMBOL(ak39_motor1_device); ++ ++ ++ +diff --git a/arch/arm/mach-ak39/gpio.c b/arch/arm/mach-ak39/gpio.c +new file mode 100755 +index 00000000..ab11fa53 +--- /dev/null ++++ b/arch/arm/mach-ak39/gpio.c +@@ -0,0 +1,314 @@ ++/** ++* @file arch/arm/mach-ak39/gpio.c ++* @brief dispatch the call of GPIO API ++* Copyright C 2011 Anyka CO.,LTD ++* 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. ++* @author zhou wenyong ++* @date 2011-08-11 ++* @note 2010-10-21 created by caolianming ++* @note 2011-06-14 move this file of old version to the ak39-gpio.c ++* -- unify the GPIO API ++* @note 2010-08-11 add more comments and change some identifiers' name ++*/ ++#include ++#include ++#include ++ ++/* ++ Look up table in which we look up the implementation function of GPIO API by ++ gpio pin ++*/ ++static struct gpio_api_lut ak39_gpio_api_lut[] = ++{ ++ { ++ .pin_start = AK_GPIO_MIN, ++ .pin_end = AK_GPIO_MAX, ++ .setpin_as_gpio = g_ak39_setpin_as_gpio, ++ .gpio_pullup = g_ak39_gpio_pullup, ++ .gpio_pulldown = g_ak39_gpio_pulldown, ++ .gpio_dircfg = g_ak39_gpio_cfgpin, ++ .gpio_intcfg = g_ak39_gpio_inten, ++ .gpio_set_intpol= g_ak39_gpio_intpol, ++ .gpio_setpin = g_ak39_gpio_setpin, ++ .gpio_getpin = g_ak39_gpio_getpin, ++ .gpio_to_irq = g_ak39_gpio_to_irq, ++ .irq_to_gpio = g_ak39_irq_to_gpio, ++ }, ++}; ++ ++#define GPIO_API_LUT ak39_gpio_api_lut ++/* ++ Look up the table to get the implementation function of corresponding ++ GPIO API ++*/ ++#define GET_GPIO_FUNC(pfun, pin, name)\ ++ do{\ ++ int _i_name;\ ++ pfun=NULL;\ ++ for (_i_name=0; _i_name= GPIO_API_LUT[_i_name].pin_start && pin <= GPIO_API_LUT[_i_name].pin_end)\ ++ { pfun = GPIO_API_LUT[_i_name].name; break;}\ ++ }while(0); ++ ++ ++ ++/** ++* @brief set pin as GPIO port ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] pin ++* @return int ++*/ ++int ak_setpin_as_gpio(unsigned int pin) ++{ ++ int (*pfunc)(unsigned int); ++ GET_GPIO_FUNC(pfunc, pin, setpin_as_gpio); ++ if (pfunc) ++ return pfunc(pin); ++ return 0; ++} ++EXPORT_SYMBOL(ak_setpin_as_gpio); ++ ++ ++/** ++* @brief configure GPIO PULLUP function ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] pin ++* @param[in] enable ++* @return int ++*/ ++int ak_gpio_pullup(unsigned int pin, unsigned char enable) ++{ ++ int (*pfunc)(unsigned int, unsigned char); ++ GET_GPIO_FUNC(pfunc, pin, gpio_pullup); ++ if (pfunc) ++ return pfunc(pin, enable); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_gpio_pullup); ++ ++/** ++* @brief configure GPIO PULLDOWN function ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] pin ++* @param[in] enable ++* @return int ++*/ ++int ak_gpio_pulldown(unsigned int pin, unsigned char enable) ++{ ++ int (*pfunc)(unsigned int, unsigned char); ++ GET_GPIO_FUNC(pfunc, pin, gpio_pulldown); ++ if (pfunc) ++ return pfunc(pin, enable); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_gpio_pulldown); ++ ++ ++/** ++* @brief configure GPIO direction ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] pin ++* @param[in] direction ++* @return int ++*/ ++int ak_gpio_dircfg(unsigned int pin, unsigned int direction) ++{ ++ int (*pfunc)(unsigned int, unsigned int); ++ GET_GPIO_FUNC(pfunc, pin, gpio_dircfg); ++ if (pfunc) ++ return pfunc(pin, direction); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_gpio_dircfg); ++/** ++* @brief old version of ak_gpio_dircfg ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] pin ++* @param[in] to ++* @return int ++*/ ++int ak_gpio_cfgpin(unsigned int pin, unsigned int to) ++{ ++ return ak_gpio_dircfg(pin, to); ++} ++EXPORT_SYMBOL(ak_gpio_cfgpin); ++ ++ ++/** ++* @brief enable/disable GPIO interrupt ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] pin ++* @param[in] enable ++* @return int ++*/ ++int ak_gpio_intcfg(unsigned int pin, unsigned int enable) ++{ ++ int (*pfunc)(unsigned int, unsigned int); ++ GET_GPIO_FUNC(pfunc, pin, gpio_intcfg); ++ if (pfunc) ++ return pfunc(pin, enable); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_gpio_intcfg); ++/** ++* @brief old version of ak_gpio_intcfg ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] pin ++* @param[in] enable ++* @return int ++*/ ++int ak_gpio_inten(unsigned int pin, unsigned int enable) ++{ ++ return ak_gpio_intcfg(pin, enable); ++} ++EXPORT_SYMBOL(ak_gpio_inten); ++ ++ ++/** ++* @brief configure GPIO interrupt polarity ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] pin ++* @param[in] level ++* @return int ++*/ ++int ak_gpio_set_intpol(unsigned int pin, unsigned int level) ++{ ++ int (*pfunc)(unsigned int, unsigned int); ++ GET_GPIO_FUNC(pfunc, pin, gpio_set_intpol); ++ if (pfunc) ++ return pfunc(pin, level); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_gpio_set_intpol); ++/** ++* @brief old version of ak_gpio_set_intpol ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] pin ++* @param[in] level ++* @return int ++*/ ++int ak_gpio_intpol(unsigned int pin, unsigned int level) ++{ ++ return ak_gpio_set_intpol(pin, level); ++} ++EXPORT_SYMBOL(ak_gpio_intpol); ++ ++ ++/** ++* @brief configure GPIO output state ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] pin ++* @param[in] to ++* @return int ++*/ ++int ak_gpio_setpin(unsigned int pin, unsigned int to) ++{ ++ int (*pfunc)(unsigned int, unsigned int); ++ GET_GPIO_FUNC(pfunc, pin, gpio_setpin); ++ if (pfunc) ++ return pfunc(pin, to); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_gpio_setpin); ++ ++/** ++* @brief read GPIO input state ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] pin ++* @return int ++*/ ++ int ak_gpio_getpin(unsigned int pin) ++{ ++ int (*pfunc)(unsigned int); ++ GET_GPIO_FUNC(pfunc, pin, gpio_getpin); ++ if (pfunc) ++ return pfunc(pin); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_gpio_getpin); ++ ++ ++/** ++* @brief get corresponding irq by pin ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] pin ++* @return int ++*/ ++ int ak_gpio_to_irq(unsigned int pin) ++{ ++ int (*pfunc)(unsigned int); ++ GET_GPIO_FUNC(pfunc, pin, gpio_to_irq); ++ if (pfunc) ++ return pfunc(pin); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_gpio_to_irq); ++ ++/** ++* @brief get corresponding gpio pin by irq ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[in] irq ++* @return int ++*/ ++ int ak_irq_to_gpio(unsigned int irq) ++{ ++ if ( irq >= IRQ_GPIO_0 && irq<= NR_IRQS - 1) ++ return AK_GPIO_0 + (irq-IRQ_GPIO_0); ++ else ++ panic("wrong irq number %u passed to ak_irq_to_gpio.\n", irq); ++ ++ return -1; ++} ++EXPORT_SYMBOL(ak_irq_to_gpio); ++ ++ ++/** ++* @brief configure GPIO followed properties specified in info ++* @author zhou wenyong ++* @date 2011-08-11 ++* @param[out] *info ++* @return void ++*/ ++void ak_gpio_set(const struct gpio_info *info) ++{ ++ if ( ! (info->pin >= AK_GPIO_MIN && info->pin <= AK_GPIO_MAX )) ++ return ; ++ ++ ak_setpin_as_gpio(info->pin); ++ if (info->dir == AK_GPIO_DIR_OUTPUT || info->dir == AK_GPIO_DIR_INPUT) ++ ak_gpio_dircfg(info->pin, info->dir); ++ if (info->pullup == AK_PULLUP_ENABLE || info->pullup == AK_PULLUP_DISABLE) ++ ak_gpio_pullup(info->pin, info->pullup); ++ if (info->pulldown == AK_PULLDOWN_ENABLE || info->pulldown == AK_PULLDOWN_DISABLE) ++ ak_gpio_pulldown(info->pin, info->pulldown); ++ if (info->value == AK_GPIO_OUT_HIGH || info->value == AK_GPIO_OUT_LOW) ++ ak_gpio_setpin(info->pin, info->value); ++ if (info->int_pol == AK_GPIO_INT_LOWLEVEL || info->int_pol == AK_GPIO_INT_HIGHLEVEL) ++ ak_gpio_set_intpol(info->pin, info->int_pol); ++} ++EXPORT_SYMBOL(ak_gpio_set); ++ +diff --git a/arch/arm/mach-ak39/include/mach/adc.h b/arch/arm/mach-ak39/include/mach/adc.h +new file mode 100755 +index 00000000..7549d81e +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/adc.h +@@ -0,0 +1,40 @@ ++/* ++ * ak_adc.h ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef __AK_ADC_H_ ++#define __AK_ADC_H_ ++ ++#include ++ ++#define RESET_CTRL_REG (AK_VA_SYSCTRL + 0x20) ++#define AD_DA_CLK1_REG (AK_VA_SYSCTRL + 0x0C) ++#define SAR_ADC_CFG_REG (AK_VA_SYSCTRL + 0x98) ++#define SAR_IF_CFG_REG (AK_VA_SYSCTRL + 0x5C) ++#define SAR_TIMING_CFG_REG (AK_VA_SYSCTRL + 0x60) ++#define SAR_THRESHOLD_REG (AK_VA_SYSCTRL + 0x64) ++#define SAR_IF_SMP_DAT_REG (AK_VA_SYSCTRL + 0x68) ++#define SAR_IF_INT_STATUS_REG (AK_VA_SYSCTRL + 0x6C) ++ ++ ++#define ADC1_MAIN_CLK (12000000) ++#define ADC1_DEFAULT_CLK (1500000) /* FIXME: bat work in 1.5MHz */ ++#define DEFAULT_SAMPLE (1000) /* FIXME: somebody need change it */ ++#define AK_AVCC (3300) /* AVCC always 3.3V ? */ ++ ++#define AK_ADC1_AD0 (0) /* FIXME: AD0=AIN0=BAT ? AD1=AIM1 ? AD2=AIN2 ? */ ++#define AK_ADC1_AD1 (1) ++#define AK_ADC1_BAT (2) ++ ++int adc1_init(void); ++unsigned long adc1_read_channel(int channel); ++ ++#define adc1_read_bat() adc1_read_channel(AK_ADC1_AD0) ++#define adc1_read_ad5() adc1_read_channel(AK_ADC1_AD1) /* is it right ? */ ++ ++#endif +diff --git a/arch/arm/mach-ak39/include/mach/ak_codec.h b/arch/arm/mach-ak39/include/mach/ak_codec.h +new file mode 100644 +index 00000000..21ba20b6 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/ak_codec.h +@@ -0,0 +1,164 @@ ++#ifndef AK39_CODEC_H ++#define AK39_CODEC_H ++ ++#include ++#include ++#include ++ ++ ++//pAddress0800 0x08000000-0x080000FF ++#define CPUPLLCHN_CLOCK_CTRL_REG 0x0004 ++ ++#define CLOCK_CTRL_REG 0x000C ++#define HIGHSPEED_CLOCK_CTRL_REG 0x0010 ++#define CLOCK_GATE_CTRL_REG 0x001c ++#define SOFT_RST_CTRL_REG 0x0020 ++#define MULTIPLE_FUN_CTRL_REG1 0x0058 ++#define FADEOUT_CTRL_REG 0x0070 ++ ++#define ADC1_CONF_REG1 0x0098 ++ ++#define ANALOG_CTRL_REG1 0x009c ++#define ANALOG_CTRL_REG2 0x00A0 ++#define ANALOG_CTRL_REG3 0x00A4 ++ ++//pAddress2011 0x2011000-0x20118008 ++#define DAC_CONFIG_REG 0x0000 ++#define I2S_CONFIG_REG 0x0004 ++#define CPU_DATA_REG 0x0008 ++ ++#define ADC2_CONFIG_REG 0x8000 ++#define ADC2_DATA_REG 0x8004 ++ ++//ADC2_CONFIG_REG(0x20118000) ++#define WORD_LENGTH_MASK (0XF << 8) ++#define I2S_EN (1 << 4) ++#define CH_POLARITY_SEL (1 << 3) ++#define HOST_RD_INT_EN (1 << 2) ++#define ADC2MODE_L2_EN (1 << 1) ++#define ADC2_CTRL_EN (1 <<0) ++ ++//CLOCK_CTRL_REG(0x08000004) ++#define ASIC_FREQ_ADJ (1 << 28) ++ ++//CLOCK_CTRL_REG(0x0800000C) ++#define DAC_DIV_VLD (1 << 29) ++#define DAC_CLK_EN (1 << 28) ++#define MASK_DAC_DIV_FRAC (0xFFF << 12) ++#define DAC_DIV_FRAC(val) (((val)&0xFFF) << 12) ++#define MASK_DAC_DIV_INT (0xFF << 4) ++#define DAC_DIV_INT(val) (((val)&0xFF) << 4) ++ ++//HIGHSPEED_CLOCK_CTRL_REG(0x0800 0010) ++#define ADC2_HSDIV_VLD (1 << 29) ++#define ADC2_HCLK_EN (1 << 28) ++#define MASK_ADC2_HCLK_DIV (0x3F << 20) ++#define ADC2_HCLK_DIV(val) (((val)&0x3F)<<20) ++#define DAC_HSDIV_VLD (1 << 19) ++#define DAC_HCLK_EN (1 << 18) ++#define MASK_DAC_HCLK_DIV (0xFF << 10) ++#define DAC_HCLK_DIV(val) (((val)&0xFF)<<10) ++#define ADC2_DIV_VLD (1 << 9) ++#define ADC2_CLK_EN (1 << 8) ++#define MASK_ADC2_DIV (0x3F << 0) ++#define ADC2_DIV(val) ((val)&0x3F) ++ ++ ++//SOFT_RST_CTRL_REG(0x08000020) ++#define DAC_SOFT_RST ((1 << 26)|(1 << 28)) ++#define ADC2_SOFT_RST ((1 << 27)|(1 << 29)) ++ ++ ++//MULTIPLE_FUN_CTRL_REG1(0x08000058) ++#define IN_DAAD_EN (1 << 25) //ENABLE INTERNAL DAC ADC via i2s ++ ++//FADEOUT_CTRL_REG(0x0080 0070) ++#define OSR_MASK (0x7 << 0) ++#define OSR(value) (((value)&0x7) << 0) ++#define ADC2_OSR_BIT 31 ++#define DAC_FILTER_EN (1<<3) ++ ++//ADC_CONF_REG1(0x0080 0098) ++#define SEL_VREF (1<< 23) ++ ++//ANALOG_CTRL_REG1(0x0080 009c) ++#define PD2_HP (1 << 24) ++#define VCM3_SEL (1 << 23) ++#define MASK_HP_GAIN (0x1F << 18) ++#define HP_GAIN(val) (((val)&0x1F)<<18) ++#define PRE_EN1 (1 << 17) ++#define PRE_EN2 (1 << 16) ++#define PD1_HP (1 << 15) ++#define RST_DAC (1 << 11) ++#define PD_OP (1 << 10) ++#define PD_CK (1 << 9) ++#define PD_VCM3 (1 << 3) ++#define PL_VCM2 (1 << 2) //pull down to ground. ++#define PD_VCM2 (1 << 1) // power off ++#define PD_REF (1 << 0) ++ ++//ANALOG_CTRL_REG2(0x0080 00A0) ++#define VDD_MIC_SEL (1 << 25) ++#define PD_S2D (1 << 22) ++#define ADC_LIM (1 << 21) ++#define PD_MICP (1 << 23) ++#define PD_MICN (1 << 24) ++#define PD_ADC2 (1 << 1) ++#define PL_VCM3 (1 << 0) ++ ++//ANALOG_CTRL_REG3(0x0080 00A4) ++ ++//DAC_CONFIG_REG(0x2011000) ++#define ARM_INT (1 << 3) //ARM interrupt enable ++#define MUTE (1 << 2) // repeat to sent the Last data to DAC ++#define FORMAT (1 << 4) // 1 is used memeory saving format. ++#define L2_EN (1 << 1) ++#define DAC_CTRL_EN (1 << 0) ++ ++//I2S_CONFIG_REG(0x20110004) ++#define LR_CLK (1 << 6) ++#define POLARITY_SEL (1 << 5) ++#define I2S_CONFIG_WORDLENGTH_MASK (0x1F << 0) ++ ++/////////////HP_IN ADC23_IN ++#define SOURCE_DAC (0b001) ++#define SOURCE_LINEIN (0b010) ++#define SOURCE_MIC (0b100) ++#define SIGNAL_SRC_MUTE 0 ++#define SIGNAL_SRC_MAX (SOURCE_DAC|SOURCE_LINEIN|SOURCE_MIC) ++ ++#define SOURCE_DAC_MASK (0b001) ++#define SOURCE_LINEIN_MASK (0b010) ++#define SOURCE_MIC_MASK (0b100) ++#define SOURCE_MIXED_ALL_MASK (SOURCE_DAC_MASK|SOURCE_LINEIN_MASK|SOURCE_MIC_MASK) ++ ++ ++#define HEADPHONE_GAIN_MIN 0 ++#define HEADPHONE_GAIN_MAX 5 ++#define LINEIN_GAIN_MIN 0 ++#define LINEIN_GAIN_MAX 15 ++#define MIC_GAIN_MIN 0 ++#define MIC_GAIN_MAX 7 ++ ++ ++#define PLAYMODE_AUTO_SWITCH (0) /*hp & sp switch*/ ++#define PLAYMODE_HP (1) ++#define PLAYMODE_SPEAKER (2) ++ ++/** ++ * platform data of the ak39 pcm driver ++ */ ++struct ak39_codec_platform_data ++{ ++ struct gpio_info hpdet_gpio; /* gpio for headphone detecting */ ++ struct gpio_info spk_down_gpio; /* gpio for shutdown speaker */ ++ struct gpio_info hpmute_gpio; /* gpio for headphone de-pipa */ ++ int hp_on_value; /* the gpio value when headphone is pulg */ ++ int hpdet_irq; /* the irq of heaphone detecting */ ++ int bIsHPmuteUsed; /* does this board has headphone de-pipa hardware or not */ ++ int hp_mute_enable_value; /* the gpio value when enable headphone de-pipa hardware */ ++ int bIsMetalfixed; /* the ak37 SoC is metal fixed version or not */ ++ int boutput_only; /* use only 0: hp & sp, 1: hp only, 2:sp only*/ ++}; ++ ++#endif +diff --git a/arch/arm/mach-ak39/include/mach/clock.h b/arch/arm/mach-ak39/include/mach/clock.h +new file mode 100755 +index 00000000..7ecd310c +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/clock.h +@@ -0,0 +1,154 @@ ++/* ++ * arch/arm/mach-ak39/include/mach/clock.h ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++*/ ++ ++#ifndef _ANYKA_CLK_H_ ++#define _ANYKA_CLK_H_ ++ ++#include ++ ++#define CLOCK_CPU_PLL_CTRL (AK_VA_SYSCTRL + 0x04) ++#define CLOCK_ASIC_PLL_CTRL (AK_VA_SYSCTRL + 0x08) ++#define CLOCK_ADC2_DAC_CTRL (AK_VA_SYSCTRL + 0x0C) ++#define CLOCK_ADC2_DAC_HS_CTRL (AK_VA_SYSCTRL + 0x10) ++#define CLOCK_PERI_PLL_CTRL1 (AK_VA_SYSCTRL + 0x14) ++#define CLOCK_PERI_PLL_CTRL2 (AK_VA_SYSCTRL + 0x18) ++#define CLOCK_GATE_CTRL1 (AK_VA_SYSCTRL + 0x1C) ++#define CLOCK_SOFT_RESET (AK_VA_SYSCTRL + 0x20) ++ ++/* clock gate control register bit */ ++#define AK_CLKCON_MCLK_DRAM (1 << 24) ++#define AK_CLKCON_VCLK2_VIDEO (1 << 20) ++#define AK_CLKCON_VCLK1_CAMERA (1 << 19) ++#define AK_CLKCON_ASICCLK_USB (1 << 15) ++#define AK_CLKCON_ASICCLK_ENCRY (1 << 14) ++#define AK_CLKCON_ASICCLK_MAC (1 << 13) ++#define AK_CLKCON_ASICCLK_GPIO (1 << 12) ++#define AK_CLKCON_ASICCLK_IRDA (1 << 11) ++#define AK_CLKCON_ASICCLK_I2C (1 << 10) ++#define AK_CLKCON_ASICCLK_L2MEM (1 << 9) ++#define AK_CLKCON_ASICCLK_UART2 (1 << 8) ++#define AK_CLKCON_ASICCLK_UART1 (1 << 7) ++#define AK_CLKCON_ASICCLK_SPI2 (1 << 6) ++#define AK_CLKCON_ASICCLK_SPI1 (1 << 5) ++#define AK_CLKCON_ASICCLK_DAC (1 << 4) ++#define AK_CLKCON_ASICCLK_ADC (1 << 3) ++#define AK_CLKCON_ASICCLK_SDIO (1 << 2) ++#define AK_CLKCON_ASICCLK_MCI (1 << 1) ++ ++/* ADC2/DAC clock control register */ ++#define AK_CLKCON_CLK_DAC (1 << 28) ++#define AK_CLKCON_CLK_ADC1 (1 << 3) ++ ++/* ADC2/DAC high speed clock control register */ ++#define AK_CLKCON_HCLK_ADC2 (1 << 28) ++#define AK_CLKCON_HCLK_DAC (1 << 18) ++#define AK_CLKCON_CLK_ADC2 (1 << 8) ++ ++ ++/* PERI PLL channel clock control register1 */ ++//1: peri pll 0: external 12MHz ++#define AK_CLKCON_CLK_PHY_SEL (1 << 19) ++//1: peri pll 0: external 25MHz ++#define AK_CLKCON_CLK_MAC_SEL (1 << 18) ++#define AK_CLKCON_CLK_12M (1 << 17) ++#define AK_CLKCON_CLK_25M (1 << 16) ++#define AK_CLKCON_CLK_25M_IN (1 << 14) ++ ++ ++/* PERI PLL channel clock control register2 */ ++/* 1: positive clk 0: negative clk */ ++#define AK_CLKCON_PCLK_CIS (1 << 20) //camera ++#define AK_CLKCON_SCLK_CIS (1 << 18) //sensor ++#define AK_CLKCON_CLK_OPCLK (1 << 8) //MAC ++ ++ ++/** ++ * struct clk_ops - standard clock operations ++ * @set_rate: set the clock rate, see clk_set_rate(). ++ * @get_rate: get the clock rate, see clk_get_rate(). ++ * @round_rate: round a given clock rate, see clk_round_rate(). ++ * @set_parent: set the clock's parent, see clk_set_parent(). ++ * ++ * Group the common clock implementations together so that we ++ * don't have to keep setting the same fields again. We leave ++ * enable in struct clk. ++ * ++ * Adding an extra layer of indirection into the process should ++ * not be a problem as it is unlikely these operations are going ++ * to need to be called quickly. ++ */ ++struct clk_ops { ++ int (*set_rate)(struct clk *c, unsigned long rate); ++ unsigned long (*get_rate)(struct clk *c); ++ unsigned long (*round_rate)(struct clk *c, unsigned long rate); ++ int (*set_parent)(struct clk *c, struct clk *parent); ++}; ++ ++struct clk { ++ struct list_head list; ++ struct module *owner; ++ struct clk *parent; ++ const char *name; ++ const char *devname; ++ int id; ++ int usage; ++ unsigned long rate; ++ unsigned long ctrlbit; ++ ++ struct clk_lookup lookup; ++ struct clk_ops *ops; ++ int (*enable)(struct clk *, int enable); ++#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) ++ struct dentry *dent; /* For visible tree hierarchy */ ++#endif ++}; ++ ++ ++/* core clock support */ ++extern struct clk clk_xtal_12M; ++extern struct clk clk_xtal_25M; ++extern struct clk clk_xtal_32K; ++extern struct clk clk_pll; ++extern struct clk clk_cpu; ++extern struct clk clk_ahb; ++extern struct clk clk_mem; ++extern struct clk clk_vclk; ++extern struct clk clk_asic; ++ ++ ++/* other clocks which may be registered by board support */ ++ ++typedef enum { ++ CPU_MODE_NORMAL, ++ CPU_MODE_CPU2X, ++ CPU_MODE_CPU3X, ++} T_cpu_mode; ++ ++#define AK39_CLK_CPU3X_MODE (1 << 26) ++#define AK39_CLK_CPU2X_MODE (1 << 24) ++ ++ ++#define MHz 1000000UL ++ ++T_cpu_mode ak_get_cpu_mode(void); ++bool ak_cpu_is_3x_mode(void); ++bool ak_cpu_is_2x_mode(void); ++bool ak_cpu_is_normal_mode(void); ++ ++unsigned long ak_get_cpu_pll_clk(void); ++unsigned long ak_get_asic_pll_clk(void); ++unsigned long ak_get_peri_pll_clk(void); ++unsigned long ak_get_pll_clk(void); ++unsigned long ak_get_cpu_clk(void); ++unsigned long ak_get_ahb_clk(void); ++unsigned long ak_get_mem_clk(void); ++unsigned long ak_get_vclk(void); ++unsigned long ak_get_asic_clk(void); ++void aisc_freq_set(void); ++ ++#endif +diff --git a/arch/arm/mach-ak39/include/mach/cpufreq.h b/arch/arm/mach-ak39/include/mach/cpufreq.h +new file mode 100755 +index 00000000..e1a6ccc4 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/cpufreq.h +@@ -0,0 +1,33 @@ ++#ifndef __CPUFREQ_H__ ++#define __CPUFREQ_H__ ++ ++#if 0 ++#include ++#include ++ ++#define MEM_CLK_DIV 0x7 ++#define ASIC_CLK_DIV 0x7 ++ ++#define PLL_CLK_MIN 180 ++#define PLL_CLK_MAX (PLL_CLK_MIN + 4*(0x3F)) ++ ++//#define CPUFREQ_DEBUG ++ ++/* clock divider register */ ++#define PLL_CHANGE_ENA (1 << 12) ++#define CLOCK_ASIC_MEM_ENA (1 << 14) ++ ++struct cpufreq_mode_clkdiv { ++ T_OPERATION_MODE mode_name; ++ unsigned int pll_sel; ++ unsigned int clk168_div; ++ unsigned int cpu_div; ++ unsigned int mem_div; ++ unsigned int asic_div; ++ unsigned int low_clock; ++ unsigned int is_3x; ++}; ++ ++#endif ++ ++#endif /* end __CPUFREQ_H__ */ +diff --git a/arch/arm/mach-ak39/include/mach/devices.h b/arch/arm/mach-ak39/include/mach/devices.h +new file mode 100755 +index 00000000..c892c714 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/devices.h +@@ -0,0 +1,35 @@ ++#ifndef _MACH_DEVICES_H ++#define _MACH_DEVICES_H ++ ++ ++extern struct platform_device ak39_uart0_device; ++extern struct platform_device ak39_uart1_device; ++extern struct platform_device ak39_gpio_uart_device; ++ ++extern struct platform_device ak39_mmc_device; ++extern struct platform_device ak39_sdio_device; ++ ++extern struct platform_device ak39_i2c_device; ++ ++extern struct platform_device ak39_usb_udc_device; ++extern struct platform_device ak39_usb_otg_hcd_device; ++extern struct platform_device ak39_mac_device; ++ ++extern struct platform_device ak39_spi1_device; ++extern struct platform_device ak39_camera_interface; ++extern struct platform_device ak39_pcm_device; ++extern struct platform_device ak39_mmx_device; ++ ++extern struct platform_device ak39_ion_device; ++extern struct platform_device ak39_led_pdev; ++extern struct platform_device ak39_gpio_keys_device; ++extern struct platform_device ak39_battery_power; ++extern struct platform_device ak39_rtc_device; ++ ++extern struct platform_device akfha_char_device; ++ ++extern struct platform_device ak39_motor0_device; ++extern struct platform_device ak39_motor1_device; ++ ++#endif /* endif _MACH_DEVICES_H */ ++ +diff --git a/arch/arm/mach-ak39/include/mach/entry-macro.S b/arch/arm/mach-ak39/include/mach/entry-macro.S +new file mode 100755 +index 00000000..279c7aab +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/entry-macro.S +@@ -0,0 +1,144 @@ ++/* ++ * include/asm-arm/arch-ak39/entry-macro.S ++ * ++ * Low-level IRQ helper macros for AK39-based platforms ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++*/ ++ ++#include ++#include ++ ++#define AK39_IRQ_INTMASK (AK_VA_SYSCTRL + 0x24) ++#define AK39_FIQ_INTMASK (AK_VA_SYSCTRL + 0x28) ++#define AK39_INT_STATUS (AK_VA_SYSCTRL + 0x4C) ++ ++ .macro get_irqnr_preamble, base, tmp ++ .endm ++ ++ .macro arch_ret_to_user, tmp1, tmp2 ++ .endm ++ ++ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp ++ ++ ldr \base, =AK39_INT_STATUS ++ ldr \irqstat, [\base] @ get interrupts status ++ teq \irqstat, #0x0 ++ beq 1002f ++ ++ ldr \base, =AK39_IRQ_INTMASK @ get interrupts mask ++ ldr \base, [\base] ++ and \irqstat, \irqstat, \base ++ ++ tst \irqstat, #(1< ++#include ++#include "map.h" ++ ++ ++#define AK_WAKEUP_ENABLE 1 ++#define AK_WAKEUP_DISABLE 0 ++#define AK_FALLING_TRIGGERED 1 ++#define AK_RISING_TRIGGERED 0 ++ ++#define AK_GPIO_DIR_OUTPUT 1 ++#define AK_GPIO_DIR_INPUT 0 ++#define AK_GPIO_INT_DISABLE 0 ++#define AK_GPIO_INT_ENABLE 1 ++#define AK_GPIO_INT_LOWLEVEL 0 ++#define AK_GPIO_INT_HIGHLEVEL 1 ++ ++#define AK_GPIO_OUT_LOW 0 ++#define AK_GPIO_OUT_HIGH 1 ++ ++#define AK_PULLUP_DISABLE 0 ++#define AK_PULLUP_ENABLE 1 ++#define AK_PULLDOWN_DISABLE 0 ++#define AK_PULLDOWN_ENABLE 1 ++ ++#define GPIO_PIN_MODE_GPIO 0 ++#define GPIO_PIN_MODE_INT 1 ++ ++#define ATTR_FIXED_1 1 ++#define ATTR_FIXED_0 0 ++#define PIN_ATTE_LINE 6 ++ ++#undef END_FLAG ++#define END_FLAG 0xff ++#define INVALID_GPIO (-1) ++ ++ ++#define AK_FALSE 0 ++#define AK_TRUE 1 ++#undef AK_NULL ++#define AK_NULL ((void *)(0)) ++ ++ ++/**************** gpio offsets ************************/ ++#define AK_GPIO_GROUP1 (32*0) ++#define AK_GPIO_GROUP2 (32*1) ++ ++#define AK_GPIO_GROUP1_NO(offset) ( AK_GPIO_GROUP1 + (offset)) ++#define AK_GPIO_GROUP2_NO(offset) ( AK_GPIO_GROUP2 + (offset)) ++ ++#define AK_GPIO_0 AK_GPIO_GROUP1_NO(0) ++#define AK_GPIO_1 AK_GPIO_GROUP1_NO(1) ++#define AK_GPIO_2 AK_GPIO_GROUP1_NO(2) ++#define AK_GPIO_3 AK_GPIO_GROUP1_NO(3) ++#define AK_GPIO_4 AK_GPIO_GROUP1_NO(4) ++#define AK_GPIO_5 AK_GPIO_GROUP1_NO(5) ++#define AK_GPIO_6 AK_GPIO_GROUP1_NO(6) ++#define AK_GPIO_7 AK_GPIO_GROUP1_NO(7) ++#define AK_GPIO_8 AK_GPIO_GROUP1_NO(8) ++#define AK_GPIO_9 AK_GPIO_GROUP1_NO(9) ++#define AK_GPIO_10 AK_GPIO_GROUP1_NO(10) ++#define AK_GPIO_11 AK_GPIO_GROUP1_NO(11) ++#define AK_GPIO_12 AK_GPIO_GROUP1_NO(12) ++#define AK_GPIO_13 AK_GPIO_GROUP1_NO(13) ++#define AK_GPIO_14 AK_GPIO_GROUP1_NO(14) ++#define AK_GPIO_15 AK_GPIO_GROUP1_NO(15) ++#define AK_GPIO_16 AK_GPIO_GROUP1_NO(16) ++#define AK_GPIO_17 AK_GPIO_GROUP1_NO(17) ++#define AK_GPIO_18 AK_GPIO_GROUP1_NO(18) ++#define AK_GPIO_19 AK_GPIO_GROUP1_NO(19) ++#define AK_GPIO_20 AK_GPIO_GROUP1_NO(20) ++#define AK_GPIO_21 AK_GPIO_GROUP1_NO(21) ++#define AK_GPIO_22 AK_GPIO_GROUP1_NO(22) ++#define AK_GPIO_23 AK_GPIO_GROUP1_NO(23) ++#define AK_GPIO_24 AK_GPIO_GROUP1_NO(24) ++#define AK_GPIO_25 AK_GPIO_GROUP1_NO(25) ++#define AK_GPIO_26 AK_GPIO_GROUP1_NO(26) ++#define AK_GPIO_27 AK_GPIO_GROUP1_NO(27) ++#define AK_GPIO_28 AK_GPIO_GROUP1_NO(28) ++#define AK_GPIO_29 AK_GPIO_GROUP1_NO(29) ++#define AK_GPIO_30 AK_GPIO_GROUP1_NO(30) ++#define AK_GPIO_31 AK_GPIO_GROUP1_NO(31) ++ ++#define AK_GPIO_32 AK_GPIO_GROUP2_NO(0) ++#define AK_GPIO_33 AK_GPIO_GROUP2_NO(1) ++#define AK_GPIO_34 AK_GPIO_GROUP2_NO(2) ++#define AK_GPIO_35 AK_GPIO_GROUP2_NO(3) ++#define AK_GPIO_36 AK_GPIO_GROUP2_NO(4) ++#define AK_GPIO_37 AK_GPIO_GROUP2_NO(5) ++#define AK_GPIO_38 AK_GPIO_GROUP2_NO(6) ++#define AK_GPIO_39 AK_GPIO_GROUP2_NO(7) ++#define AK_GPIO_40 AK_GPIO_GROUP2_NO(8) ++#define AK_GPIO_41 AK_GPIO_GROUP2_NO(9) ++#define AK_GPIO_42 AK_GPIO_GROUP2_NO(10) ++#define AK_GPIO_43 AK_GPIO_GROUP2_NO(11) ++#define AK_GPIO_44 AK_GPIO_GROUP2_NO(12) ++#define AK_GPIO_45 AK_GPIO_GROUP2_NO(13) ++#define AK_GPIO_46 AK_GPIO_GROUP2_NO(14) ++#define AK_GPIO_47 AK_GPIO_GROUP2_NO(15) ++#define AK_GPIO_48 AK_GPIO_GROUP2_NO(16) ++#define AK_GPIO_49 AK_GPIO_GROUP2_NO(17) ++#define AK_GPIO_50 AK_GPIO_GROUP2_NO(18) ++#define AK_GPIO_51 AK_GPIO_GROUP2_NO(19) ++#define AK_GPIO_52 AK_GPIO_GROUP2_NO(20) ++#define AK_GPIO_53 AK_GPIO_GROUP2_NO(21) ++#define AK_GPIO_54 AK_GPIO_GROUP2_NO(22) ++#define AK_GPIO_55 AK_GPIO_GROUP2_NO(23) ++#define AK_GPIO_56 AK_GPIO_GROUP2_NO(24) ++#define AK_GPIO_57 AK_GPIO_GROUP2_NO(25) ++#define AK_GPIO_58 AK_GPIO_GROUP2_NO(26) ++#define AK_GPIO_59 AK_GPIO_GROUP2_NO(27) ++#define AK_GPIO_60 AK_GPIO_GROUP2_NO(28) ++#define AK_GPIO_61 AK_GPIO_GROUP2_NO(29) ++#define AK_GPIO_62 AK_GPIO_GROUP2_NO(30) ++#define AK_GPIO_63 AK_GPIO_GROUP2_NO(31) ++ ++ ++#define AK_GPIO_MIN AK_GPIO_0 ++#define AK_GPIO_MAX AK_GPIO_63 ++#define GPIO_UPLIMIT AK_GPIO_MAX ++ ++/****************** access gpio register addr **********************/ ++#define AK_GPIO_DIR1 (AK_VA_GPIO + 0x00) ++#define AK_GPIO_DIR2 (AK_VA_GPIO + 0x04) ++ ++#define AK_GPIO_OUT1 (AK_VA_GPIO + 0x08) ++#define AK_GPIO_OUT2 (AK_VA_GPIO + 0x0C) ++ ++#define AK_GPIO_INPUT1 (AK_VA_GPIO + 0x10) ++#define AK_GPIO_INPUT2 (AK_VA_GPIO + 0x14) ++ ++#define AK_GPIO_INT_MASK1 (AK_VA_GPIO + 0x18) ++#define AK_GPIO_INT_MASK2 (AK_VA_GPIO + 0x1C) ++ ++#define AK_GPIO_INT_MODE1 (AK_VA_GPIO + 0x20) ++#define AK_GPIO_INT_MODE2 (AK_VA_GPIO + 0x24) ++ ++#define AK_GPIO_INTP1 (AK_VA_GPIO + 0x28) ++#define AK_GPIO_INTP2 (AK_VA_GPIO + 0x2C) ++ ++#define AK_GPIO_EDGE_STATUS1 (AK_VA_GPIO + 0x30) ++#define AK_GPIO_EDGE_STATUS2 (AK_VA_GPIO + 0x34) ++ ++#define AK_PPU_PPD1 (AK_VA_SYSCTRL + 0x80) ++#define AK_PPU_PPD2 (AK_VA_SYSCTRL + 0x84) ++#define AK_PPU_PPD3 (AK_VA_SYSCTRL + 0x88) ++ ++#define AK_SHAREPIN_CON1 (AK_VA_SYSCTRL + 0x74) ++#define AK_SHAREPIN_CON2 (AK_VA_SYSCTRL + 0x78) ++#define AK_SHAREPIN_CON3 (AK_VA_SYSCTRL + 0x7C) ++ ++//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++#define AK_GPIO_DIR_BASE(pin) (((pin)>>5)*4 + AK_GPIO_DIR1) ++#define AK_GPIO_OUT_BASE(pin) (((pin)>>5)*4 + AK_GPIO_OUT1) ++ ++#define AK_GPIO_IN_BASE(pin) (((pin)>>5)*4 + AK_GPIO_INPUT1) ++#define AK_GPIO_INTEN_BASE(pin) (((pin)>>5)*4 + AK_GPIO_INT_MASK1) ++#define AK_GPIO_INTPOL_BASE(pin) (((pin)>>5)*4 + AK_GPIO_INTP1) ++#define AK_PPU_PPD_BASE(pin) (((pin)>>5)*4 + AK_PPU_PPD1) ++ ++/****************** end access gpio register addr **********************/ ++ ++#define AK_WGPIO_POLARITY (AK_VA_SYSCTRL + 0x3C) ++#define AK_WGPIO_CLEAR (AK_VA_SYSCTRL + 0x40) ++#define AK_WGPIO_ENABLE (AK_VA_SYSCTRL + 0x44) ++#define AK_WGPIO_STATUS (AK_VA_SYSCTRL + 0x48) ++ ++#define AK_PA_WGPIO_POLARITY (AK_PA_SYSCTRL + 0x3C) ++#define AK_PA_WGPIO_CLEAR (AK_PA_SYSCTRL + 0x40) ++#define AK_PA_WGPIO_ENABLE (AK_PA_SYSCTRL + 0x44) ++#define AK_PA_WGPIO_STATUS (AK_PA_SYSCTRL + 0x48) ++ ++#undef REG32 ++#define REG32(_reg) (*(volatile unsigned long *)(_reg)) ++#undef REG16 ++#define REG16(_reg) (*(volatile unsigned short *)(_reg)) ++ ++#define AK_GPIO_UART1_FLOW(x) REG32(AK_VA_SYSCTRL + 0x74) &= ~(0xa << 20) ++ ++/****************** enum defined **********************/ ++typedef enum { ++ ePIN_AS_GPIO = 0, // All pin as gpio ++ ePIN_AS_OPCLK, ++ ePIN_AS_JTAG, // share pin as JTAG ++ ePIN_AS_RTCK, // share pin as watch dog ++ ePIN_AS_I2S, // share pin as I2S ++ ePIN_AS_PWM1, // share pin as PWM1 ++ ePIN_AS_PWM2, // share pin as PWM2 ++ ePIN_AS_PWM3, // share pin as PWM3 ++ ePIN_AS_PWM4, // share pin as PWM4 ++ ePIN_AS_PWM5, // share pin as PWM5 ++ ePIN_AS_UART1, // share pin as UART1 ++ ePIN_AS_UART2, // share pin as UART2 ++ ePIN_AS_CAMERA, // share pin as CAMERA ++ ePIN_AS_MCI, // share pin as MDAT1, 4 lines ++ ePIN_AS_MCI_8LINE, // share pin as MDAT1, 8 lines ++ ePIN_AS_SDIO, // share pin as SDIO ++ ePIN_AS_SPI1, // share pin as SPI1 ++ ePIN_AS_SPI2, // share pin as SPI2 ++ ePIN_AS_MAC, // share pin as Ethernet MAC ++ ePIN_AS_I2C, // share pin as I2C ++ ePIN_AS_IRDA, // share png as IrDA ++ ePIN_AS_RAM, // share pin as RAM Controller ++ ++ ePIN_AS_DUMMY ++ } T_GPIO_SHAREPIN_CFG ; ++ ++typedef enum { ++ SHARE_CONFG1 = 0, ++ SHARE_CONFG2 ++} T_SHARE_CONFG; ++ ++typedef enum { ++ SHARE_CFG1 = 0, // share cfg1 ++ SHARE_CFG2, // share cfg2 ++ SHARE_CFG3, // share cfg2 ++ SHARE_CFG12, // share cfg1 and share cfg2 as used ++ SHARE_CFG13, // share cfg1 and share cfg3 as used ++ SHARE_CFG23, // share cfg2 and share cfg2 as used ++ SHARE_CFG123, // share cfg1, share config2 and cfg3 as used ++ EXIT_CFG ++}T_SHARE_CFG; ++ ++struct gpio_sharepin_cfg { ++ T_GPIO_SHAREPIN_CFG func_module; ++ T_SHARE_CFG share_config; ++ unsigned long reg1_bit_mask; ++ unsigned long reg1_bit_value; ++ unsigned long reg2_bit_mask; ++ unsigned long reg2_bit_value; ++ unsigned long reg3_bit_mask; ++ unsigned long reg3_bit_value; ++}; ++ ++typedef enum { ++ PULLUP = 0, ++ PULLDOWN, ++ PULLUPDOWN, ++ UNDEFINED ++ } T_PUPD_TYPE ; ++ ++typedef enum { ++ PUPD_CFG1 = 0, // share cfg1 ++ PUPD_CFG2, // share cfg2 ++ PUPD_CFG3, // share cfg2 ++}T_PUPD_CFG; ++ ++typedef enum { ++ AS_GPIO_CFG_BIT1 = 0, // share cfg1 ++ AS_GPIO_CFG_BIT2, // share cfg2 ++}T_AS_GPIO_CFG; ++ ++struct gpio_pupd_cfg { ++ int pin; ++ int index; ++ T_PUPD_CFG pupd_cfg; ++ T_PUPD_TYPE pupd_type; ++}; ++ ++struct sharepin_as_gpio { ++ unsigned char gpio_start; ++ unsigned char gpio_end; ++ int index; ++ T_AS_GPIO_CFG flag; ++}; ++ ++struct t_gpio_wakeup_cfg { ++ unsigned char gpio_start; ++ unsigned char gpio_end; ++ unsigned char start_bit; ++}; ++ ++struct gpio_info { ++ int pin; ++ char pulldown; ++ char pullup; ++ char value; ++ char dir; ++ char int_pol; ++}; ++ ++struct gpio_api_lut { ++ unsigned int pin_start; ++ unsigned int pin_end; ++ ++ int (*setpin_as_gpio) (unsigned int pin); ++ int (*gpio_pullup)(unsigned int pin, unsigned char enable); ++ int (*gpio_pulldown)(unsigned int pin, unsigned char enable); ++ ++ int (*gpio_dircfg)(unsigned int pin, unsigned int to); ++ ++ int (*gpio_intcfg)(unsigned int pin, unsigned int enable); ++ int (*gpio_set_intpol)(unsigned int pin, unsigned int level); ++ ++ int (*gpio_setpin)(unsigned int pin, unsigned int to); ++ int (*gpio_getpin)(unsigned int pin); ++ ++ int (*gpio_to_irq)(unsigned int pin); ++ int (*irq_to_gpio)(unsigned int irq); ++}; ++ ++void ak_group_config(T_GPIO_SHAREPIN_CFG mod_name); ++ ++/* set gpio's wake up polarity*/ ++void ak_gpio_wakeup_pol(unsigned int pin, unsigned char pol); ++/* enable/disable gpio wake up function*/ ++int ak_gpio_wakeup(unsigned int pin, unsigned char enable); ++ ++int ak_setpin_as_gpio (unsigned int pin); ++int ak_gpio_setpin(unsigned int pin, unsigned int to); ++int ak_gpio_getpin(unsigned int pin); ++int ak_gpio_pullup(unsigned int pin, unsigned char enable); ++int ak_gpio_pulldown(unsigned int pin, unsigned char enable); ++/* new version of ak_gpio_cfgpin */ ++int ak_gpio_dircfg(unsigned int pin, unsigned int to); ++/* new version of ak_gpio_inten*/ ++int ak_gpio_intcfg(unsigned int pin, unsigned int enable); ++/* new version of ak_gpio_intpol*/ ++int ak_gpio_set_intpol(unsigned int pin, unsigned int level); ++ ++/* to support backward compatibility*/ ++int ak_gpio_cfgpin(unsigned int pin, unsigned int to); ++int ak_gpio_inten(unsigned int pin, unsigned int enable); ++int ak_gpio_intpol(unsigned int pin, unsigned int level); ++int reg_set_mutli_bit(void __iomem *reg, unsigned int value, int bit, int index); ++ ++int ak_gpio_to_irq(unsigned int pin); ++int ak_irq_to_gpio(unsigned int irq); ++ ++extern int ak_gpio_request(unsigned long gpio, const char *label); ++extern void ak_gpio_free(unsigned long gpio); ++ ++ ++/*************** wrap gpio interface again ****************/ ++void ak_gpio_set(const struct gpio_info *info); ++ ++int g_ak39_setpin_as_gpio(unsigned int pin); ++void g_ak39_setgroup_attribute(T_GPIO_SHAREPIN_CFG mod_name); ++int g_ak39_gpio_pullup(unsigned int pin, unsigned char enable); ++int g_ak39_gpio_pulldown(unsigned int pin, unsigned char enable); ++int g_ak39_gpio_cfgpin(unsigned int pin, unsigned int to); ++int g_ak39_gpio_setpin(unsigned int pin, unsigned int to); ++int g_ak39_gpio_getpin(unsigned int pin); ++int g_ak39_gpio_inten(unsigned int pin, unsigned int enable); ++int g_ak39_gpio_intpol(unsigned int pin, unsigned int level); ++ ++int g_ak39_gpio_to_irq(unsigned int pin); ++int g_ak39_irq_to_gpio(unsigned int irq); ++ ++/*************** end wrap gpio interface again ****************/ ++ ++#endif /* __GPIO_H__ */ ++ +diff --git a/arch/arm/mach-ak39/include/mach/hardware.h b/arch/arm/mach-ak39/include/mach/hardware.h +new file mode 100644 +index 00000000..66488e52 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/hardware.h +@@ -0,0 +1,24 @@ ++/* arch/arm/mach-s3c2410/include/mach/hardware.h ++ * ++ * Copyright (c) 2003 Simtec Electronics ++ * Ben Dooks ++ * ++ * S3C2410 - hardware ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++*/ ++ ++#ifndef __ASM_ARCH_HARDWARE_H ++#define __ASM_ARCH_HARDWARE_H ++ ++#ifndef __ASSEMBLY__ ++#endif /* __ASSEMBLY__ */ ++ ++#include ++#include ++ ++/* machine specific hardware definitions should go after this */ ++ ++#endif /* __ASM_ARCH_HARDWARE_H */ +diff --git a/arch/arm/mach-ak39/include/mach/i2c.h b/arch/arm/mach-ak39/include/mach/i2c.h +new file mode 100755 +index 00000000..f89434f2 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/i2c.h +@@ -0,0 +1,57 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++*/ ++ ++#ifndef __AK39_IIC_H ++#define __AK39_IIC_H __FILE__ ++ ++#include ++ ++#define I2C_CLKPIN (27) ++#define I2C_DATPIN (28) ++#define I2C_INTPIN (24) ++ ++#define AK39_I2C_NACKEN (1 << 17) ++#define AK39_I2C_NOSTOP (1 << 16) ++#define AK39_I2C_ACKEN (1 << 15) ++#define AK39_I2C_START (1 << 14) ++#define AK39_I2C_TXRXSEL (1 << 13) ++#define AK39_I2C_TRX_BYTE (9) ++#define AK39_I2C_CLR_DELAY (0x3 << 7) ++#define AK39_I2C_SDA_DELAY (0x2 << 7) ++#define AK39_I2C_TXDIV_512 (1 << 6) ++#define AK39_I2C_INTEN (1 << 5) ++ ++#define INT_PEND_FLAG (1 << 4) ++#define AK39_TX_CLK_DIV (0xf) ++ ++#define AK39_I2C_CMD_EN (1 << 18) ++#define AK39_I2C_START_BIT (1 << 17) ++ ++#define AK39_I2C_READ 1 ++#define AK39_I2C_WRITE 0 ++ ++#define AK39_I2C_CTRL REG_VA_ADDR(AK_VA_I2C, 0x00) ++#define AK39_I2C_CMD1 REG_VA_ADDR(AK_VA_I2C, 0x10) ++#define AK39_I2C_CMD2 REG_VA_ADDR(AK_VA_I2C, 0x14) ++#define AK39_I2C_CMD3 REG_VA_ADDR(AK_VA_I2C, 0x18) ++#define AK39_I2C_CMD4 REG_VA_ADDR(AK_VA_I2C, 0x1C) ++#define AK39_I2C_DATA0 REG_VA_ADDR(AK_VA_I2C, 0x20) ++#define AK39_I2C_DATA1 REG_VA_ADDR(AK_VA_I2C, 0x24) ++#define AK39_I2C_DATA2 REG_VA_ADDR(AK_VA_I2C, 0x28) ++#define AK39_I2C_DATA3 REG_VA_ADDR(AK_VA_I2C, 0x2C) ++ ++ ++struct ak39_platform_i2c { ++ int bus_num; ++ unsigned int flags; ++ unsigned int slave_addr; ++ unsigned long frequency; ++ unsigned int sda_delay; ++ struct gpio_info *gpios; ++ int npins; ++}; ++ ++#endif /* __AK39_IIC_H */ +diff --git a/arch/arm/mach-ak39/include/mach/irqs.h b/arch/arm/mach-ak39/include/mach/irqs.h +new file mode 100755 +index 00000000..84822914 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/irqs.h +@@ -0,0 +1,132 @@ ++/* linux/arch/arm/mach-ak39/include/mach/irqs.h ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++*/ ++ ++#ifndef __ASM_ARCH_IRQS_H_ ++#define __ASM_ARCH_IRQS_H_ ++ ++#define AK39_IRQ(x) (x) ++ ++/* ++ * Main CPU Interrupts ++ */ ++#define IRQ_MEM AK39_IRQ(0) ++#define IRQ_CAMERA AK39_IRQ(1) ++#define IRQ_VIDEO_ENCODER AK39_IRQ(2) ++#define IRQ_SYSCTRL AK39_IRQ(3) ++#define IRQ_MCI AK39_IRQ(4) ++#define IRQ_SDIO AK39_IRQ(5) ++#define IRQ_ADC2 AK39_IRQ(6) ++#define IRQ_DAC AK39_IRQ(7) ++#define IRQ_SPI1 AK39_IRQ(8) ++#define IRQ_SPI2 AK39_IRQ(9) ++#define IRQ_UART0 AK39_IRQ(10) ++#define IRQ_UART1 AK39_IRQ(11) ++#define IRQ_L2MEM AK39_IRQ(12) ++#define IRQ_I2C AK39_IRQ(13) ++#define IRQ_IRDA AK39_IRQ(14) ++#define IRQ_GPIO AK39_IRQ(15) ++#define IRQ_MAC AK39_IRQ(16) ++#define IRQ_ENCRYTION AK39_IRQ(17) ++#define IRQ_USBOTG_MCU AK39_IRQ(18) ++#define IRQ_USBOTG_DMA AK39_IRQ(19) ++ ++/* ++ * System Control Module Sub-IRQs ++ */ ++#define IRQ_SYSCTRL_START (IRQ_USBOTG_DMA + 1) ++#define AK39_SYSCTRL_IRQ(x) (IRQ_SYSCTRL_START + (x)) ++ ++#define IRQ_SARADC AK39_SYSCTRL_IRQ(0) ++#define IRQ_TIMER5 AK39_SYSCTRL_IRQ(1) ++#define IRQ_TIMER4 AK39_SYSCTRL_IRQ(2) ++#define IRQ_TIMER3 AK39_SYSCTRL_IRQ(3) ++#define IRQ_TIMER2 AK39_SYSCTRL_IRQ(4) ++#define IRQ_TIMER1 AK39_SYSCTRL_IRQ(5) ++#define IRQ_WGPIO AK39_SYSCTRL_IRQ(6) ++#define IRQ_RTC_RDY AK39_SYSCTRL_IRQ(7) ++#define IRQ_RTC_ALARM AK39_SYSCTRL_IRQ(8) ++#define IRQ_RTC_TIMER AK39_SYSCTRL_IRQ(9) ++#define IRQ_RTC_WATCHDOG AK39_SYSCTRL_IRQ(10) ++ ++/* ++ * GPIO IRQs ++ */ ++#define IRQ_GPIO_START (IRQ_RTC_WATCHDOG + 1) ++#define AK39_GPIO_IRQ(x) (IRQ_GPIO_START + (x)) ++ ++#define IRQ_GPIO_0 AK39_GPIO_IRQ(0) ++#define IRQ_GPIO_1 AK39_GPIO_IRQ(1) ++#define IRQ_GPIO_2 AK39_GPIO_IRQ(2) ++#define IRQ_GPIO_3 AK39_GPIO_IRQ(3) ++#define IRQ_GPIO_4 AK39_GPIO_IRQ(4) ++#define IRQ_GPIO_5 AK39_GPIO_IRQ(5) ++#define IRQ_GPIO_6 AK39_GPIO_IRQ(6) ++#define IRQ_GPIO_7 AK39_GPIO_IRQ(7) ++#define IRQ_GPIO_8 AK39_GPIO_IRQ(8) ++#define IRQ_GPIO_9 AK39_GPIO_IRQ(9) ++#define IRQ_GPIO_10 AK39_GPIO_IRQ(10) ++#define IRQ_GPIO_11 AK39_GPIO_IRQ(11) ++#define IRQ_GPIO_12 AK39_GPIO_IRQ(12) ++#define IRQ_GPIO_13 AK39_GPIO_IRQ(13) ++#define IRQ_GPIO_14 AK39_GPIO_IRQ(14) ++#define IRQ_GPIO_15 AK39_GPIO_IRQ(15) ++#define IRQ_GPIO_16 AK39_GPIO_IRQ(16) ++#define IRQ_GPIO_17 AK39_GPIO_IRQ(17) ++#define IRQ_GPIO_18 AK39_GPIO_IRQ(18) ++#define IRQ_GPIO_19 AK39_GPIO_IRQ(19) ++#define IRQ_GPIO_20 AK39_GPIO_IRQ(20) ++#define IRQ_GPIO_21 AK39_GPIO_IRQ(21) ++#define IRQ_GPIO_22 AK39_GPIO_IRQ(22) ++#define IRQ_GPIO_23 AK39_GPIO_IRQ(23) ++#define IRQ_GPIO_24 AK39_GPIO_IRQ(24) ++#define IRQ_GPIO_25 AK39_GPIO_IRQ(25) ++#define IRQ_GPIO_26 AK39_GPIO_IRQ(26) ++#define IRQ_GPIO_27 AK39_GPIO_IRQ(27) ++#define IRQ_GPIO_28 AK39_GPIO_IRQ(28) ++#define IRQ_GPIO_29 AK39_GPIO_IRQ(29) ++#define IRQ_GPIO_30 AK39_GPIO_IRQ(30) ++#define IRQ_GPIO_31 AK39_GPIO_IRQ(31) ++ ++#define IRQ_GPIO_32 AK39_GPIO_IRQ(32) ++#define IRQ_GPIO_33 AK39_GPIO_IRQ(33) ++#define IRQ_GPIO_34 AK39_GPIO_IRQ(34) ++#define IRQ_GPIO_35 AK39_GPIO_IRQ(35) ++#define IRQ_GPIO_36 AK39_GPIO_IRQ(36) ++#define IRQ_GPIO_37 AK39_GPIO_IRQ(37) ++#define IRQ_GPIO_38 AK39_GPIO_IRQ(38) ++#define IRQ_GPIO_39 AK39_GPIO_IRQ(39) ++#define IRQ_GPIO_40 AK39_GPIO_IRQ(40) ++#define IRQ_GPIO_41 AK39_GPIO_IRQ(41) ++#define IRQ_GPIO_42 AK39_GPIO_IRQ(42) ++#define IRQ_GPIO_43 AK39_GPIO_IRQ(43) ++#define IRQ_GPIO_44 AK39_GPIO_IRQ(44) ++#define IRQ_GPIO_45 AK39_GPIO_IRQ(45) ++#define IRQ_GPIO_46 AK39_GPIO_IRQ(46) ++#define IRQ_GPIO_47 AK39_GPIO_IRQ(47) ++#define IRQ_GPIO_48 AK39_GPIO_IRQ(48) ++#define IRQ_GPIO_49 AK39_GPIO_IRQ(49) ++#define IRQ_GPIO_50 AK39_GPIO_IRQ(50) ++#define IRQ_GPIO_51 AK39_GPIO_IRQ(51) ++#define IRQ_GPIO_52 AK39_GPIO_IRQ(52) ++#define IRQ_GPIO_53 AK39_GPIO_IRQ(53) ++#define IRQ_GPIO_54 AK39_GPIO_IRQ(54) ++#define IRQ_GPIO_55 AK39_GPIO_IRQ(55) ++#define IRQ_GPIO_56 AK39_GPIO_IRQ(56) ++#define IRQ_GPIO_57 AK39_GPIO_IRQ(57) ++#define IRQ_GPIO_58 AK39_GPIO_IRQ(58) ++#define IRQ_GPIO_59 AK39_GPIO_IRQ(59) ++#define IRQ_GPIO_60 AK39_GPIO_IRQ(60) ++#define IRQ_GPIO_61 AK39_GPIO_IRQ(61) ++#define IRQ_GPIO_62 AK39_GPIO_IRQ(62) ++#define IRQ_GPIO_63 AK39_GPIO_IRQ(63) ++ ++ ++/* total irq number */ ++#define NR_IRQS (IRQ_GPIO_63 + 1) ++ ++#endif /* __ASM_ARCH_IRQS_H_ */ ++ +diff --git a/arch/arm/mach-ak39/include/mach/l2cache.h b/arch/arm/mach-ak39/include/mach/l2cache.h +new file mode 100644 +index 00000000..9b4084c2 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/l2cache.h +@@ -0,0 +1,17 @@ ++/* ++ * linux/arch/arm/mach-ak39/include/l2cache.h ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef __ASM_ARCH_L2CACHE_H ++#define __ASM_ARCH_L2CACHE_H ++ ++void l2cache_init(void); ++void l2cache_clean_finish(void); ++void l2cache_invalidate(void); ++ ++#endif /* __ASM_ARCH_L2CACHE_H */ +diff --git a/arch/arm/mach-ak39/include/mach/leds-gpio.h b/arch/arm/mach-ak39/include/mach/leds-gpio.h +new file mode 100755 +index 00000000..e2201287 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/leds-gpio.h +@@ -0,0 +1,29 @@ ++/* arch/arm/mach-ak98/include/mach/leds-gpio.h ++ * ++ * Copyright (c) Anyka ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++*/ ++ ++#ifndef __ASM_ARCH_LEDSGPIO_H ++#define __ASM_ARCH_LEDSGPIO_H "leds-gpio.h" ++ ++#define AK_LEDF_ACTLOW (1<<0) /* LED is on when GPIO low */ ++#define AK_LEDF_ACTHIGH (1<<1) /* LED is on when GPIO hight */ ++#define AK_LEDF_TRISTATE (1<<2) /* tristate to turn off */ ++ ++struct ak_led_data { ++ char *name; ++ char *def_trigger; ++ struct gpio_info gpio; ++}; ++ ++struct ak_led_pdata { ++ struct ak_led_data *leds; ++ int nr_led; ++}; ++ ++#endif /* __ASM_ARCH_LEDSGPIO_H */ +diff --git a/arch/arm/mach-ak39/include/mach/map.h b/arch/arm/mach-ak39/include/mach/map.h +new file mode 100755 +index 00000000..c1189cc7 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/map.h +@@ -0,0 +1,114 @@ ++/* arch/arm/arch-ak39/include/mach/map.h ++ * ++ * AK39 - Memory map definitions ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++*/ ++ ++#ifndef __ASM_ARCH_MAP_H ++#define __ASM_ARCH_MAP_H ++ ++#ifndef __ASSEMBLY__ ++#define AK39_ADDR(x) ((void __iomem *)0xF0000000 + (x)) ++#else ++#define AK39_ADDR(x) (0xF0000000 + (x)) ++#endif ++ ++#define AK_VA_OCROM AK39_ADDR(0x00000000) ++#define AK_PA_OCROM 0x00000000 ++#define AK_SZ_OCROM SZ_32K /* 32KB */ ++ ++#define AK_VA_SYSCTRL AK39_ADDR(0x00008000) ++#define AK_PA_SYSCTRL (0x08000000) ++#define AK_SZ_SYSCTRL SZ_32K /* 32KB */ ++ ++#define AK_VA_CAMERA AK39_ADDR(0x00010000) ++#define AK_PA_CAMERA (0x20000000) ++#define AK_SZ_CAMERA SZ_64K /* 64KB */ ++ ++#define AK_VA_VENCODE AK39_ADDR(0x00020000) ++#define AK_PA_VENCODE (0x20020000) ++#define AK_SZ_VENCODE SZ_64K /* 64KB */ ++ ++/* some sub system control register */ ++#define AK_VA_SUBCTRL AK39_ADDR(0x00030000) ++#define AK_PA_SUBCTRL (0x20100000) ++#define AK_SZ_SUBCTRL SZ_2M /* 2MB */ ++ ++#define AK_VA_MAC AK39_ADDR(0x00230000) ++#define AK_PA_MAC (0x20300000) ++#define AK_SZ_MAC SZ_8K /* 8KB */ ++ ++#define AK_VA_REGRAM AK39_ADDR(0x00232000) ++#define AK_PA_REGRAM (0x21000000) ++#define AK_SZ_REGRAM SZ_8K /* 8KB */ ++ ++#define AK_VA_L2MEM AK39_ADDR(0x00234000) ++#define AK_PA_L2MEM (0x4800C000) ++#define AK_SZ_L2MEM SZ_8K /* 8KB */ ++ ++/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ ++#define AK_VA_MCI (AK_VA_SUBCTRL + 0x0000) ++#define AK_PA_MCI (AK_PA_SUBCTRL + 0x0000) ++ ++#define AK_VA_SDIO (AK_VA_SUBCTRL + 0x8000) ++#define AK_PA_SDIO (AK_PA_SUBCTRL + 0x8000) ++ ++#define AK_VA_DAC (AK_VA_SUBCTRL + 0x10000) ++#define AK_PA_DAC (AK_PA_SUBCTRL + 0x10000) ++ ++#define AK_VA_ADC (AK_VA_SUBCTRL + 0x18000) ++#define AK_PA_ADC (AK_PA_SUBCTRL + 0x18000) ++ ++#define AK_VA_SPI1 (AK_VA_SUBCTRL + 0x20000) ++#define AK_PA_SPI1 (AK_PA_SUBCTRL + 0x20000) ++ ++#define AK_VA_SPI2 (AK_VA_SUBCTRL + 0x28000) ++#define AK_PA_SPI2 (AK_PA_SUBCTRL + 0x28000) ++ ++#define AK_VA_UART (AK_VA_SUBCTRL + 0x30000) ++#define AK_PA_UART (AK_PA_SUBCTRL + 0x30000) ++ ++#define AK_VA_L2CTRL (AK_VA_SUBCTRL + 0x40000) ++#define AK_PA_L2CTRL (AK_PA_SUBCTRL + 0x40000) ++ ++#define AK_VA_I2C (AK_VA_SUBCTRL + 0x50000) ++#define AK_PA_I2C (AK_VA_SUBCTRL + 0x50000) ++ ++#define AK_VA_IRDA (AK_VA_SUBCTRL + 0x60000) ++#define AK_PA_IRDA (AK_PA_SUBCTRL + 0x60000) ++ ++#define AK_VA_GPIO (AK_VA_SUBCTRL + 0x70000) ++#define AK_PA_GPIO (AK_PA_SUBCTRL + 0x70000) ++ ++/* encryption register */ ++#define AK_VA_ENCRY (AK_VA_SUBCTRL + 0x80000) ++#define AK_PA_ENCRY (AK_PA_SUBCTRL + 0x80000) ++ ++/* usb register */ ++#define AK_VA_USB (AK_VA_SUBCTRL + 0x100000) ++#define AK_PA_USB (AK_PA_SUBCTRL + 0x100000) ++ ++ ++#define write_ramb(v, p) (*(volatile unsigned char *)(p) = (v)) ++#define write_ramw(v, p) (*(volatile unsigned short *)(p) = (v)) ++#define write_raml(v, p) (*(volatile unsigned long *)(p) = (v)) ++ ++#define read_ramb(p) (*(volatile unsigned char *)(p)) ++#define read_ramw(p) (*(volatile unsigned short *)(p)) ++#define read_raml(p) (*(volatile unsigned long *)(p)) ++ ++#define write_buf(v, p) (*(volatile unsigned long *)(p) = (v)) ++#define read_buf(p) (*(volatile unsigned long *)(p)) ++ ++#define REG_VA_VAL(base_addr, offset) (*(volatile unsigned long *)((base_addr) + (offset))) ++#define REG_VA_ADDR(base_addr, offset) ((base_addr) + (offset)) ++ ++#define REG_PA_VAL(base_addr, offset) (*(volatile unsigned long *)((base_addr) + (offset))) ++#define REG_PA_ADDR(base_addr, offset) ((base_addr) + (offset)) ++ ++ ++#endif /* __ASM_ARCH_MAP_H */ ++ +diff --git a/arch/arm/mach-ak39/include/mach/pm.h b/arch/arm/mach-ak39/include/mach/pm.h +new file mode 100644 +index 00000000..90b21e8e +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/pm.h +@@ -0,0 +1,24 @@ ++#ifndef __PM_H ++#define __PM_H ++ ++#include ++ ++ ++/* ak39_pm_init ++ * ++ * called from board at initialisation time to setup the power ++ * management ++*/ ++ ++#ifdef CONFIG_PM ++extern int __init ak39_pm_init(void); ++#else ++static inline int ak39_pm_init(void) ++{ ++ return 0; ++} ++#endif ++ ++#endif /* __PM_H */ ++ ++ +diff --git a/arch/arm/mach-ak39/include/mach/pwm_timer.h b/arch/arm/mach-ak39/include/mach/pwm_timer.h +new file mode 100644 +index 00000000..f1d4f7e6 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/pwm_timer.h +@@ -0,0 +1,129 @@ ++#ifndef __PWM_TIMER_H__ ++#define __PWM_TIMER_H__ ++ ++ ++#define timer_cnt pt_u.timer.cnt ++#define timer_irq pt_u.timer.irq ++#define timer_cb pt_u.timer.callback ++#define timer_reload pt_u.timer.auto_reload ++#define timer_priv pt_u.timer.priv_data ++ ++#define pwm_hlimit pt_u.pwm.high_limit ++#define pwm_llimit pt_u.pwm.low_limit ++#define pwm_pin pt_u.pwm.pin ++ ++/*pwm/timer object*/ ++struct ak_pwm_timer ++{ ++ int id; ++ u8 mode; ++ u8 pre_div; ++ u8 __iomem *base; ++ bool status; ++ ++ union { ++ struct { ++ u32 cnt; ++ bool auto_reload; ++ void *priv_data; ++ int irq; ++ int (*callback)(void*); ++ }timer; /*timer private element*/ ++ struct { ++ u16 high_limit; ++ u16 low_limit; ++ int pin; ++ }pwm; /*pwm private element*/ ++ }pt_u; ++}; ++ ++ ++struct ak_platform_pwm_bl_data { ++ int pwm_id; ++ unsigned int max_brightness; ++ unsigned int dft_brightness; ++ unsigned int high_limit; ++ unsigned int low_limit; ++ unsigned int pwm_clk; ++ int (*init)(struct ak_pwm_timer *dev); ++ int (*notify)(int brightness); ++ void (*exit)(struct ak_pwm_timer *dev); ++}; ++ ++ ++ ++#define AK_PWM1_CTRL (AK_VA_SYSCTRL+0xB4) ++#define AK_PWM2_CTRL (AK_VA_SYSCTRL+0xBC) ++#define AK_PWM3_CTRL (AK_VA_SYSCTRL+0xC4) ++#define AK_PWM4_CTRL (AK_VA_SYSCTRL+0xCC) ++#define AK_PWM5_CTRL (AK_VA_SYSCTRL+0xD4) ++ ++ ++#define AK_PWM_TIMER_CTRL1 (0x00) ++#define AK_PWM_TIMER_CTRL2 (0x04) ++ ++#define AK_PWM_HIGH_LEVEL(x) ((x) << 16) ++#define AK_PWM_LOW_LEVEL(x) (x) ++ ++#define AK_TIMER_TIMEOUT_CLR (1<<30) ++#define AK_TIMER_FEED_TIMER (1<<29) ++#define AK_PWM_TIMER_EN (1<<28) ++#define AK_TIMER_TIMEOUT_STA (1<<27) ++#define AK_TIMER_READ_SEL (1<<26) ++ ++#define AK_TIMER_WORK_MODE(x) ((x)<<24) ++#define AK_PWM_TIMER_PRE_DIV(x) ((x) << 16) ++#define AK_PWM_TIMER_PRE_DIV_MASK ((0xff) << 16) ++ ++ ++#define REAL_CRYSTAL_FREQ (12*1000*1000) ++#define PWM_MAX_FREQ (6*1000*1000) ++#define PWM_MIN_FREQ (92*1000) ++ ++#define AK_PWM_TIMER_CNT (5) ++ ++/*the pwm/timer number.*/ ++enum ak_pwm_timer_nr { ++ AK_PWM_TIMER1, ++ AK_PWM_TIMER2, ++ AK_PWM_TIMER3, ++ AK_PWM_TIMER4, ++ AK_PWM_TIMER5, ++ AK_PWM_TIMER_NR, ++}; ++ ++ ++enum ak_pwm_timer_status { ++ PWM_TIMER_UNUSED, ++ PWM_TIMER_BUSY, ++ PWM_TIMER_RESERVED, ++}; ++ ++ ++enum ak_pwm_timer_mode ++{ ++ AK_PT_MODE_TIMER_AUTO_LOAD = 0, ++ AK_PT_MODE_TIMER_ONE_SHOT, ++ AK_PT_MODE_PWM, ++}; ++ ++ ++int ak_pwm_get_duty_cycle(struct ak_pwm_timer *pwm, unsigned short *high, unsigned short *low); ++int ak_pwm_config(struct ak_pwm_timer *pwm, unsigned short high, unsigned short low, unsigned int freq); ++ ++int ak_pwm_enable(struct ak_pwm_timer *pwm); ++void ak_pwm_disable(struct ak_pwm_timer *pwm); ++int ak_timer_enable(struct ak_pwm_timer *timer); ++int ak_timer_enable_sync(struct ak_pwm_timer *timer); ++void ak_timer_disable(struct ak_pwm_timer *timer); ++ ++struct ak_pwm_timer *ak_pwm_request(int pwm_id); ++struct ak_pwm_timer *ak_timer_request(int timer_id, bool auto_reload, int (*cb)(void* data)); ++ ++void ak_pwm_release(struct ak_pwm_timer *pwm); ++void ak_timer_release(struct ak_pwm_timer *timer); ++ ++int ak_timer_config(struct ak_pwm_timer *timer, u32 cnt, u8 pre_div); ++ ++#endif ++ +diff --git a/arch/arm/mach-ak39/include/mach/reboot.h b/arch/arm/mach-ak39/include/mach/reboot.h +new file mode 100644 +index 00000000..292f6507 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/reboot.h +@@ -0,0 +1,7 @@ ++#ifndef __AK39_REBOOT_H ++#define __AK39_REBOOT_H ++ ++void ak39_reboot_sys_by_soft(void); ++void ak39_jump_to_rom(unsigned long addr); ++ ++#endif +diff --git a/arch/arm/mach-ak39/include/mach/reset.h b/arch/arm/mach-ak39/include/mach/reset.h +new file mode 100644 +index 00000000..73718d18 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/reset.h +@@ -0,0 +1,58 @@ ++/* ++ * mach/reset.h ++ */ ++#ifndef _AK39_RESET_H_ ++#define _AK39_RESET_H_ __FILE__ ++ ++#include ++ ++extern void (*ak39_arch_reset) (void); ++ ++#define MODULE_RESET_CON1 (AK_VA_SYSCTRL + 0x20) ++ ++#define AK39_SRESET_MMCSD (1) ++#define AK39_SRESET_SDIO (2) ++#define AK39_SRESET_ADC (3) ++#define AK39_SRESET_DAC (4) ++#define AK39_SRESET_SPI1 (5) ++#define AK39_SRESET_SPI2 (6) ++#define AK39_SRESET_UART1 (7) ++#define AK39_SRESET_UART2 (8) ++#define AK39_SRESET_L2MEM (9) ++#define AK39_SRESET_I2C (10) ++#define AK39_SRESET_IRDA (11) ++#define AK39_SRESET_GPIO (12) ++#define AK39_SRESET_MAC (13) ++#define AK39_SRESET_ENCRY (14) ++#define AK39_SRESET_USBHS (15) ++#define AK39_SRESET_CAMERA (19) ++#define AK39_SRESET_VIDEO (20) ++#define AK39_SRESET_DRAM (24) ++ ++int ak39_soft_reset(u32 module); ++ ++/***** extern call for comm drivers compatible *****/ ++#define AK_SRESET_MMCSD AK39_SRESET_MMCSD ++#define AK_SRESET_SDIO AK39_SRESET_SDIO ++#define AK_SRESET_ADC AK39_SRESET_ADC ++#define AK_SRESET_DAC AK39_SRESET_DAC ++#define AK_SRESET_SPI1 AK39_SRESET_SPI1 ++#define AK_SRESET_SPI2 AK39_SRESET_SPI2 ++#define AK_SRESET_UART1 AK39_SRESET_UART1 ++#define AK_SRESET_UART2 AK39_SRESET_UART2 ++#define AK_SRESET_L2MEM AK39_SRESET_L2MEM ++#define AK_SRESET_I2C AK39_SRESET_I2C ++#define AK_SRESET_IRDA AK39_SRESET_IRDA ++#define AK_SRESET_GPIO AK39_SRESET_GPIO ++#define AK_SRESET_MAC AK39_SRESET_MAC ++#define AK_SRESET_ENCRY AK39_SRESET_ENCRY ++#define AK_SRESET_USBHS AK39_SRESET_USBHS ++#define AK_SRESET_CAMERA AK39_SRESET_CAMERA ++#define AK_SRESET_VIDEO AK39_SRESET_VIDEO ++#define AK_SRESET_DRAM AK39_SRESET_DRAM ++ ++ ++int ak_soft_reset(u32 module); ++/*** end extern call for comm drivers compatible ***/ ++ ++#endif +diff --git a/arch/arm/mach-ak39/include/mach/spi.h b/arch/arm/mach-ak39/include/mach/spi.h +new file mode 100644 +index 00000000..86072ff2 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/spi.h +@@ -0,0 +1,93 @@ ++/* ++ * include/asm-arm/arch-ak37/spi.h ++ */ ++ ++#ifndef __SPI_H__ ++#define __SPI_H__ ++ ++struct ak_spi_info { ++ unsigned long* pin_cs; ++ unsigned short num_cs; ++ unsigned long board_size; ++ unsigned short bus_num; ++ unsigned short mode_bits; ++ char clk_name[20]; ++ int xfer_mode; /*use for dma or cpu*/ ++}; ++ ++enum akspi_cs_num { ++ AKSPI_ONCHIP_CS = 0, /*on chip control cs index*/ ++ /*AKSPI_CS1,*/ ++ /*AKSPI_CS2,*/ ++ AKSPI_CS_NUM, ++}; ++ ++enum akspi_bus_num { ++ AKSPI_BUS_NUM1, ++ AKSPI_BUS_NUM2, ++ AKSPI_MAX_BUS_NUM, ++}; ++ ++ ++#define AKSPI_1DATAWIRE (0b00<<16) ++#define AKSPI_2DATAWIRE (0b01<<16) ++#define AKSPI_4DATAWIRE (0b10<<16) ++ ++#define AKSPI_XFER_MODE_DMA (1) ++#define AKSPI_XFER_MODE_CPU (2) ++ ++ ++#define AK_SPICON (0x00) ++#define AK_SPICON_WIRE (0x3<<16) ++#define AK_SPICON_CLKDIV (0x7F<<8) ++#define AK_SPICON_EN (1<<6) ++#define AK_SPICON_CS (1<<5) ++#define AK_SPICON_MS (1<<4) ++#define AK_SPICON_CPHA (1<<3) ++#define AK_SPICON_CPOL (1<<2) ++#define AK_SPICON_ARRM (1<<1) ++#define AK_SPICON_TGDM (1<<0) ++ ++#define AK_SPISTA (0x04) ++#define AK_SPISTA_TIMEOUT (1<<10) ++#define AK_SPISTA_MPROC (1<<9) ++#define AK_SPISTA_TRANSF (1<<8) ++#define AK_SPISTA_RXOVER (1<<7) ++#define AK_SPISTA_RXHFULL (1<<6) ++#define AK_SPISTA_RXFULL (1<<5) ++#define AK_SPISTA_RXEMP (1<<4) ++#define AK_SPISTA_TXUNDER (1<<3) ++#define AK_SPISTA_TXHEMP (1<<2) ++#define AK_SPISTA_TXFULL (1<<1) ++#define AK_SPISTA_TXEMP (1<<0) ++ ++#define AK_SPIINT (0x08) ++#define AK_SPIINT_TIMEOUT (1<<10) ++#define AK_SPIINT_MPROC (1<<9) ++#define AK_SPIINT_TRANSF (1<<8) ++#define AK_SPIINT_RXOVER (1<<7) ++#define AK_SPIINT_RXHFULL (1<<6) ++#define AK_SPIINT_RXFULL (1<<5) ++#define AK_SPIINT_RXEMP (1<<4) ++#define AK_SPIINT_TXUNDER (1<<3) ++#define AK_SPIINT_TXHEMP (1<<2) ++#define AK_SPIINT_TXFULL (1<<1) ++#define AK_SPIINT_TXEMP (1<<0) ++ ++#define AK_SPICNT (0x0C) ++ ++#define AK_SPIEXTX (0x10) ++#define AK_SPIEXTX_BUFEN (1<<0) ++#define AK_SPIEXTX_DMAEN (1<<16) ++ ++ ++#define AK_SPIEXRX (0x14) ++#define AK_SPIEXRX_BUFEN (1<<0) ++#define AK_SPIEXRX_DMAEN (1<<16) ++ ++#define AK_SPIOUT (0x18) ++ ++#define AK_SPIIN (0x1C) ++ ++#endif ++ +diff --git a/arch/arm/mach-ak39/include/mach/timex.h b/arch/arm/mach-ak39/include/mach/timex.h +new file mode 100644 +index 00000000..da916374 +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/timex.h +@@ -0,0 +1,24 @@ ++/* arch/arm/mach-ak39/include/mach/timex.h ++ * ++ * Copyright (c) 2003-2005 Simtec Electronics ++ * Ben Dooks ++ * ++ * AK39XX - time parameters ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++*/ ++ ++#ifndef __ASM_ARCH_TIMEX_H ++#define __ASM_ARCH_TIMEX_H ++ ++/* CLOCK_TICK_RATE needs to be evaluatable by the cpp, so making it ++ * a variable is useless. It seems as long as we make our timers an ++ * exact multiple of HZ, any value that makes a 1->1 correspondence ++ * for the time conversion functions to/from jiffies is acceptable. ++*/ ++ ++#define CLOCK_TICK_RATE 12000000 ++ ++#endif /* end __ASM_ARCH_TIMEX_H */ +diff --git a/arch/arm/mach-ak39/include/mach/uncompress.h b/arch/arm/mach-ak39/include/mach/uncompress.h +new file mode 100755 +index 00000000..aed3953a +--- /dev/null ++++ b/arch/arm/mach-ak39/include/mach/uncompress.h +@@ -0,0 +1,237 @@ ++/* ++ * linux/arch/arm/mach-ak39/include/mach/uncompress.h ++ * ++ */ ++#ifndef __UNCOMPRESS_H_ ++#define __UNCOMPRESS_H_ ++ ++#include ++#include ++ ++#if defined(CONFIG_CPU_AK3910) || defined(CONFIG_CPU_AK3916) || defined(CONFIG_CPU_AK3918) ++#define CONFIG_UART0_INIT ++#endif ++ ++#define BAUD_RATE 115200 ++#define ENDDING_OFFSET 60 ++ ++#undef REG32 ++#define REG32(_reg) (*(volatile unsigned long *)(_reg)) ++ ++#define CLK_ASIC_PLL_CTRL (AK_PA_SYSCTRL + 0x08) ++ ++/* L2 buffer address */ ++#define UART0_TXBUF_ADDR REG_PA_ADDR(0x48000000, 0x1000) //0x48001000 ++#define UART0_RXBUF_ADDR REG_PA_ADDR(0x48000000, 0x1080) ++#define UART1_TXBUF_ADDR REG_PA_ADDR(0x48000000, 0x1100) ++#define UART1_RXBUF_ADDR REG_PA_ADDR(0x48000000, 0x1180) ++ ++/* L2 buffer control register */ ++#define L2BUF_CONF2_REG REG_PA_VAL(0x20140000, 0x008C) //0x2014008c ++#define UART0_TXBUF_CLR_BIT 16 ++#define UART0_RXBUF_CLR_BIT 17 ++#define UART1_TXBUF_CLR_BIT 18 ++#define UART1_RXBUF_CLR_BIT 19 ++ ++/* pullup/pulldown configure registers */ ++#define PPU_PPD1_REG REG_PA_VAL(AK_PA_SYSCTRL, 0x80) //0x08000080 ++#define RTS1_PU_BIT 24 ++#define CTS1_PU_BIT 23 ++#define TXD1_PU_BIT 22 ++#define RXD1_PU_BIT 21 ++#define TXD0_PU_BIT 20 ++#define RXD0_PU_BIT 19 ++ ++/* Clock control register */ ++#define CLK_CTRL_REG1 REG_PA_VAL(AK_PA_SYSCTRL, 0x1C) //0x0800000C ++#define UART0_CLKEN_BIT 7 ++#define UART1_CLKEN_BIT 8 ++ ++/*********** Shared pin control reigsters ********/ ++#define SRDPIN_CTRL1_REG REG_PA_VAL(AK_PA_SYSCTRL, 0x74) //0x08000074 ++#define UART0_RXD 14 ++#define UART0_TXD 15 ++#define UART1_RXD 16 ++#define UART1_TXD 18 ++#define UART1_CTS 20 ++#define UART1_RTS 22 ++ ++/** ************ UART registers *****************************/ ++#define UART0_CONF1_REG REG_PA_VAL(0x20130000, 0x00) //0x20130000 ++#define UART0_CONF2_REG REG_PA_VAL(0x20130000, 0x04) ++#define UART0_DATA_CONF_REG REG_PA_VAL(0x20130000, 0x08) ++#define UART0_BUF_THRE_REG REG_PA_VAL(0x20130000, 0x0C) ++#define UART0_BUF_RX_REG REG_PA_VAL(0x20130000, 0x10) ++#define UART0_BUF_RX_BACKUP_REG REG_PA_VAL(0x20130000, 0x14) ++#define UART0_BUF_STOPBIT_REG REG_PA_VAL(0x20130000, 0x18) ++ ++#define UART1_CONF1_REG REG_PA_VAL(0x20138000, 0x00) //0x20138000 ++#define UART1_CONF2_REG REG_PA_VAL(0x20138000, 0x04) ++#define UART1_DATA_CONF_REG REG_PA_VAL(0x20138000, 0x08) ++#define UART1_BUF_THRE_REG REG_PA_VAL(0x20138000, 0x0C) ++#define UART1_BUF_RX_REG REG_PA_VAL(0x20138000, 0x10) ++#define UART1_BUF_RX_BACKUP_REG REG_PA_VAL(0x20138000, 0x14) ++#define UART1_BUF_STOPBIT_REG REG_PA_VAL(0x20138000, 0x18) ++ ++/* bit define of UARTx_CONF1_REG */ ++#define CTS_SEL_BIT 18 ++#define RTS_SEL_BIT 19 ++#define PORT_ENABLE_BIT 21 //0: disable, 1:enable ++#define TX_STATUS_CLR_BIT 28 ++#define RX_STATUS_CLR_BIT 29 ++ ++/* bit define of UARTx_CONF2_REG */ ++#define TX_COUNT_BIT 4 ++#define TX_COUNT_VALID_BIT 16 ++#define TX_END_BIT 19 ++#define TX_END_MASK (1 << TX_END_BIT) ++ ++#if defined CONFIG_UART0_INIT ++#define UART_TXBUF_CLR_BIT UART0_TXBUF_CLR_BIT ++#define SRDPIN_UART_RXTX_BIT ((1 << UART0_RXD)|(1 << UART0_RXD)) ++#define RXD_PU_BIT RXD0_PU_BIT ++#define TXD_PU_BIT TXD0_PU_BIT ++#define UART_CLKEN_BIT UART0_CLKEN_BIT ++#define UART_TXBUF_ADDR UART0_TXBUF_ADDR ++#define UART_CONF1_REG UART0_CONF1_REG ++#define UART_CONF2_REG UART0_CONF2_REG ++#define UART_DATA_CONF_REG UART0_DATA_CONF_REG ++#define UART_BUF_STOPBIT_REG UART0_BUF_STOPBIT_REG ++#elif defined CONFIG_UART1_INIT ++#define UART_TXBUF_CLR_BIT UART1_TXBUF_CLR_BIT ++#define SRDPIN_UART_RXTX_BIT ((0x2 << UART1_RXD)|(0x2 << UART1_RXD)) ++#define RXD_PU_BIT RXD1_PU_BIT ++#define TXD_PU_BIT TXD1_PU_BIT ++#define UART_CLKEN_BIT UART1_CLKEN_BIT ++#define UART_TXBUF_ADDR UART1_TXBUF_ADDR ++#define UART_CONF1_REG UART1_CONF1_REG ++#define UART_CONF2_REG UART1_CONF2_REG ++#define UART_DATA_CONF_REG UART1_DATA_CONF_REG ++#define UART_BUF_STOPBIT_REG UART1_BUF_STOPBIT_REG ++ ++#else ++#error One of UART0 ~ UART1 Must be defined ++#endif ++ ++static unsigned int __uidiv(unsigned int num, unsigned int den) ++{ ++ unsigned int i; ++ ++ if (den == 1) ++ return num; ++ ++ i = 1; ++ while (den * i < num) ++ i++; ++ ++ return i-1; ++} ++ ++static unsigned long __get_asic_pll_clk(void) ++{ ++ unsigned long pll_m, pll_n, pll_od; ++ unsigned long asic_pll_clk; ++ unsigned long regval; ++ ++ regval = REG32(CLK_ASIC_PLL_CTRL); ++ pll_od = (regval & (0x3 << 12)) >> 12; ++ pll_n = (regval & (0xf << 8)) >> 8; ++ pll_m = regval & 0xff; ++ ++ asic_pll_clk = (12 * pll_m)/(pll_n * (1 << pll_od)); // clk unit: MHz ++ ++ if ((pll_od >= 1) && ((pll_n >= 2) && (pll_n <= 6)) ++ && ((pll_m >= 84) && (pll_m <= 254))) ++ return asic_pll_clk; ++ return 0; ++} ++ ++static unsigned long __get_vclk(void) ++{ ++ unsigned long regval; ++ unsigned long div; ++ ++ regval = REG32(CLK_ASIC_PLL_CTRL); ++ div = (regval & (0x7 << 17)) >> 17; ++ if (div == 0) ++ return __get_asic_pll_clk() >> 1; ++ ++ return __get_asic_pll_clk() >> div; ++} ++ ++unsigned long __get_asic_clk(void) ++{ ++ unsigned long regval; ++ unsigned long div; ++ ++ regval = REG32(CLK_ASIC_PLL_CTRL); ++ div = regval & (1 << 24); ++ if (div == 0) ++ return __get_vclk(); ++ ++ return __get_vclk() >> 1; ++} ++ ++static void uart_init(void) ++{ ++ unsigned int asic_clk, clk_div; ++ ++ /* enable uart clock control */ ++ CLK_CTRL_REG1 &= ~(0x1 << UART_CLKEN_BIT); ++ ++ /* configuration shared pins to UART */ ++ SRDPIN_CTRL1_REG |= SRDPIN_UART_RXTX_BIT; ++ ++ /* configuration uart pin pullup disable */ ++ PPU_PPD1_REG |= (0x1 << RXD_PU_BIT) | (0x1 << TXD_PU_BIT); ++ ++ asic_clk = __get_asic_clk()*1000000; ++ clk_div = __uidiv(asic_clk, BAUD_RATE) - 1; ++ UART_CONF1_REG &= ~((0x1 << TX_STATUS_CLR_BIT) | (0x1 << RX_STATUS_CLR_BIT) | 0xFF); ++ UART_CONF1_REG |= (0x1 << TX_STATUS_CLR_BIT) | (0x1 << RX_STATUS_CLR_BIT) | clk_div; ++ ++#ifdef CONFIG_UART1_INIT ++ /* Disable flow control */ ++ UART_CONF1_REG |= (0x1 << CTS_SEL_BIT) | (0x1 << RTS_SEL_BIT); ++#endif ++ UART_BUF_STOPBIT_REG = (0x1F << 16) | (0x1 << 0); ++ ++ /* enable uart port */ ++ UART_CONF1_REG |= (0x1 << PORT_ENABLE_BIT); ++} ++ ++ ++/* print a char to uart */ ++static void putc(char c) ++{ ++ /* Clear uart tx buffer */ ++ L2BUF_CONF2_REG |= (0x1 << UART_TXBUF_CLR_BIT); ++ ++ /* write char to uart buffer */ ++ REG32(UART_TXBUF_ADDR) = (unsigned long)c; ++ REG32(UART_TXBUF_ADDR + ENDDING_OFFSET) = (unsigned long)'\0'; ++ ++ /* Clear uart tx count register */ ++ UART_CONF1_REG |= (0x1 << TX_STATUS_CLR_BIT); ++ ++ /* Send buffer, each time only send 1 byte */ ++ UART_CONF2_REG |= (1 << TX_COUNT_BIT) | (0x1 << TX_COUNT_VALID_BIT); ++ ++ /* Wait for finish */ ++ while((UART_CONF2_REG & TX_END_MASK) == 0) { ++ } ++} ++ ++static inline void flush(void) ++{ ++} ++ ++static inline void arch_decomp_setup(void) ++{ ++ uart_init(); ++} ++ ++/* nothing to do */ ++#define arch_decomp_wdog() ++ ++#endif /* __UNCOMPRESS_H_ */ +diff --git a/arch/arm/mach-ak39/irq.c b/arch/arm/mach-ak39/irq.c +new file mode 100755 +index 00000000..a008a164 +--- /dev/null ++++ b/arch/arm/mach-ak39/irq.c +@@ -0,0 +1,310 @@ ++/* ++ * arch/arm/mach-ak39/irq.c ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++ ++/* interrupt mask 0: mask 1: unmask */ ++#define AK_IRQ_MASK (AK_VA_SYSCTRL + 0x24) ++#define AK_FIQ_MASK (AK_VA_SYSCTRL + 0x28) ++#define AK_INT_STATUS (AK_VA_SYSCTRL + 0x4C) ++#define AK_SYSCTRL_INT_MASK (AK_VA_SYSCTRL + 0x2C) ++#define AK_SYSCTRL_INT_STATUS (AK_VA_SYSCTRL + 0x30) ++ ++#define AK_L2MEM_IRQ_ENABLE (AK_VA_L2CTRL + 0x9C) ++ ++ ++/* ++ * Disable interrupt number "irq" ++ */ ++static void ak39_mask_irq(struct irq_data *d) ++{ ++ unsigned long regval; ++ ++ regval = __raw_readl(AK_IRQ_MASK); ++ regval &= ~(1UL << d->irq); ++ __raw_writel(regval, AK_IRQ_MASK); ++} ++ ++/* ++ * Enable interrupt number "irq" ++ */ ++static void ak39_unmask_irq(struct irq_data *d) ++{ ++ unsigned long regval; ++ ++ regval = __raw_readl(AK_IRQ_MASK); ++ regval |= (1UL << d->irq); ++ __raw_writel(regval, AK_IRQ_MASK); ++} ++ ++static struct irq_chip ak39_irq_chip = { ++ .name = "module-irq", ++ .irq_mask_ack = ak39_mask_irq, ++ .irq_mask = ak39_mask_irq, ++ .irq_unmask = ak39_unmask_irq, ++}; ++ ++static void sysctrl_mask_irq(struct irq_data *d) ++{ ++ unsigned long regval; ++ ++ regval = __raw_readl(AK_SYSCTRL_INT_MASK); ++ regval &= ~(1 << (d->irq - IRQ_SYSCTRL_START)); ++ __raw_writel(regval, AK_SYSCTRL_INT_MASK); ++} ++ ++static void sysctrl_unmask_irq(struct irq_data *d) ++{ ++ unsigned long regval; ++ ++ regval = __raw_readl(AK_SYSCTRL_INT_MASK); ++ regval |= (1 << (d->irq - IRQ_SYSCTRL_START)); ++ __raw_writel(regval, AK_SYSCTRL_INT_MASK); ++} ++ ++/* enable rtc alarm to wake up systerm */ ++static int sysctrl_set_wake(struct irq_data *d, unsigned int on) ++{ ++#if 0 // wait for programming ++ ++ unsigned long clkdiv1; ++ ++ if (d->irq == IRQ_RTC_ALARM) { ++ ++ clkdiv1 = __raw_readl(AK98_CLKDIV1); ++ ++ if (on == 1) ++ clkdiv1 |= (1 << 16); ++ else ++ clkdiv1 &= ~(1 << 16); ++ ++ __raw_writel(clkdiv1, AK98_CLKDIV1); ++ ++ return 0; ++ } ++#endif ++ return 0; ++} ++ ++static struct irq_chip ak39_sysctrl_chip = { ++ .name = "sysctrl-irq", ++ .irq_mask_ack = sysctrl_mask_irq, ++ .irq_mask = sysctrl_mask_irq, ++ .irq_unmask = sysctrl_unmask_irq, ++ .irq_set_wake = sysctrl_set_wake, ++}; ++ ++static void ak39_sysctrl_handler(unsigned int irq, struct irq_desc *desc) ++{ ++ unsigned long regval_mask, regval_sta; ++ unsigned long intpnd; ++ unsigned int offset; ++ ++ regval_mask = __raw_readl(AK_SYSCTRL_INT_MASK); ++ regval_sta = __raw_readl(AK_SYSCTRL_INT_STATUS); ++ ++ intpnd = (regval_mask & 0x7FF) & (regval_sta & 0x7FF); ++ ++ for (offset = 0; intpnd && offset < 11; offset++) { ++ ++ if (intpnd & (1 << offset)) ++ intpnd &= ~(1 << offset); ++ else ++ continue; ++ ++ irq = AK39_SYSCTRL_IRQ(offset); //come back debug ++ generic_handle_irq(irq); ++ } ++} ++ ++static void ak39_gpioirq_mask(struct irq_data *d) ++{ ++ void __iomem *gpio_ctrl = AK_GPIO_INT_MASK1; ++ unsigned long regval; ++ unsigned int irq = d->irq; ++ ++ irq -= IRQ_GPIO_0; ++ gpio_ctrl += (irq / 32) * 4; ++ ++ regval = __raw_readl(gpio_ctrl); ++ regval &= ~(1 << (irq & 31)); ++ __raw_writel(regval, gpio_ctrl); ++} ++ ++static void ak39_gpioirq_unmask(struct irq_data *d) ++{ ++ void __iomem *gpio_ctrl = AK_GPIO_INT_MASK1; ++ unsigned long regval; ++ unsigned int irq = d->irq; ++ ++ irq -= IRQ_GPIO_0; ++ gpio_ctrl += (irq / 32) * 4; ++ ++ regval = __raw_readl(gpio_ctrl); ++ regval |= (1 << (irq & 31)); ++ __raw_writel(regval, gpio_ctrl); ++} ++ ++static int ak39_gpioirq_set_type(struct irq_data *d, unsigned int type) ++{ ++ void __iomem *reg_irqmod = AK_GPIO_INT_MODE1; ++ void __iomem *reg_irqpol = AK_GPIO_INTP1; ++ unsigned int irq = d->irq; ++ unsigned long regval_pol, regval_mod, offset; ++ int p, l; ++ ++ irq -= IRQ_GPIO_0; ++ ++ offset = irq & 31; ++ ++ reg_irqmod += (irq / 32) * 4; ++ reg_irqpol += (irq / 32) * 4; ++ ++ regval_mod = __raw_readl(reg_irqmod); ++ regval_pol = __raw_readl(reg_irqpol); ++ ++ switch (type) { ++ case IRQ_TYPE_EDGE_RISING: ++ p = 1; l = 0; break; ++ case IRQ_TYPE_EDGE_FALLING: ++ p = 1; l = 1; break; ++ case IRQ_TYPE_LEVEL_HIGH: ++ p = 0; l = 0; break; ++ case IRQ_TYPE_LEVEL_LOW: ++ p = 0; l = 1; break; ++ default: ++ pr_debug("%s: Incorrect GPIO interrupt type 0x%x\n", ++ __func__, type); ++ return -ENXIO; ++ } ++ ++ if (p) ++ regval_mod |= (1 << (offset)); ++ else ++ regval_mod &= ~(1 << (offset)); ++ if (l) ++ regval_pol |= (1 << (offset)); ++ else ++ regval_pol &= ~(1 << (offset)); ++ ++ __raw_writel(regval_mod, reg_irqmod); ++ __raw_writel(regval_pol, reg_irqpol); ++ ++ return 0; ++} ++ ++static int ak39_gpio_irq_set_wake(struct irq_data *d, unsigned int on) ++{ ++ unsigned long regval; ++ ++ regval = __raw_readl(AK_WGPIO_ENABLE); ++ ++ if (d->irq >= IRQ_GPIO_0 && d->irq <= IRQ_GPIO_7) ++ regval |= (1 << (d->irq - IRQ_GPIO_0)); ++ ++ else if (d->irq>= IRQ_GPIO_12 && d->irq <= IRQ_GPIO_14) ++ regval |= (1 << (d->irq - IRQ_GPIO_12 + 8)); ++ ++ else if (d->irq == IRQ_GPIO_22) ++ regval |= (1 << (d->irq - IRQ_GPIO_22 + 11)); ++ ++ else if (d->irq >= IRQ_GPIO_27 && d->irq <= IRQ_GPIO_30) ++ regval |= (1 << (d->irq - IRQ_GPIO_27 + 12)); ++ ++ else if (d->irq >= IRQ_GPIO_39 && d->irq <= IRQ_GPIO_44) ++ regval |= (1 << (d->irq- IRQ_GPIO_41 + 16)); ++ ++ else if (d->irq >= IRQ_GPIO_47 && d->irq <= IRQ_GPIO_55) ++ regval |= (1 << (d->irq - IRQ_GPIO_47 + 22)); ++ ++ else if (d->irq == IRQ_GPIO_57) ++ regval |= (1 << (d->irq - IRQ_GPIO_57 + 31)); ++ ++ else { ++ printk("Not WGPIO IRQ: %d\n", d->irq); ++ return -1; ++ } ++ ++ __raw_writel(regval, AK_WGPIO_ENABLE); ++ ++ return 0; ++} ++ ++static struct irq_chip ak39_gpioirq_chip = { ++ .name = "gpio-irq", ++ .irq_mask_ack = ak39_gpioirq_mask, ++ .irq_mask = ak39_gpioirq_mask, ++ .irq_unmask = ak39_gpioirq_unmask, ++ .irq_set_type = ak39_gpioirq_set_type, ++ .irq_set_wake = ak39_gpio_irq_set_wake, ++}; ++ ++static void ak39_gpio_irqhandler(unsigned int irq, struct irq_desc *desc) ++{ ++ unsigned long enabled_irq; ++ unsigned int i; ++ unsigned int off; ++ ++ for (i = 0; i < 4; i++) { ++ enabled_irq = __raw_readl(AK_GPIO_INT_MASK1 + i * 4); ++ ++ while (enabled_irq) { ++ off = __ffs(enabled_irq); ++ enabled_irq &= ~(1 << off); ++ if (test_bit(off, AK_GPIO_INTP1 + i * 4) != ++ test_bit(off, AK_GPIO_INPUT1 + i * 4)) { ++ irq = IRQ_GPIO_0 + i * 32 + off; ++ generic_handle_irq(irq); ++ } ++ } ++ } ++} ++ ++void __init ak39_init_irq(void) ++{ ++ int i; ++ ++ /* 1st, clear all interrupts */ ++ __raw_readl(AK_INT_STATUS); ++ __raw_readl(AK_SYSCTRL_INT_STATUS); ++ ++ /* 2nd, mask all interrutps */ ++ __raw_writel(0x0, AK_IRQ_MASK); ++ __raw_writel(0x0, AK_FIQ_MASK); ++ __raw_writel(0x0, AK_SYSCTRL_INT_MASK); ++ ++ /* mask all gpio interrupts */ ++ __raw_writel(0x0, AK_GPIO_INT_MASK1); ++ __raw_writel(0x0, AK_GPIO_INT_MASK2); ++ ++ /* mask all l2 interrupts */ ++ __raw_writel(0x0, AK_L2MEM_IRQ_ENABLE); ++ ++ for (i = IRQ_MEM; i <= IRQ_USBOTG_DMA; i++) { ++ irq_set_chip_and_handler(i, &ak39_irq_chip, handle_level_irq); ++ set_irq_flags(i, IRQF_VALID); ++ } ++ ++ irq_set_chained_handler(IRQ_SYSCTRL, ak39_sysctrl_handler); ++ ++ for (i = IRQ_SARADC; i <= IRQ_RTC_WATCHDOG; i++) { ++ irq_set_chip_and_handler(i, &ak39_sysctrl_chip, handle_level_irq); ++ set_irq_flags(i, IRQF_VALID); ++ } ++ irq_set_chained_handler(IRQ_GPIO, ak39_gpio_irqhandler); ++ ++ for (i = IRQ_GPIO_0; i < NR_IRQS; i++) { ++ irq_set_chip_and_handler(i, &ak39_gpioirq_chip, handle_level_irq); ++ set_irq_flags(i, IRQF_VALID); ++ } ++} ++ +diff --git a/arch/arm/mach-ak39/irq.h b/arch/arm/mach-ak39/irq.h +new file mode 100644 +index 00000000..07be10a5 +--- /dev/null ++++ b/arch/arm/mach-ak39/irq.h +@@ -0,0 +1 @@ ++void __init ak39_init_irq(void); +diff --git a/arch/arm/mach-ak39/l2cache.c b/arch/arm/mach-ak39/l2cache.c +new file mode 100644 +index 00000000..72322cc4 +--- /dev/null ++++ b/arch/arm/mach-ak39/l2cache.c +@@ -0,0 +1,27 @@ ++/* ++ * linux/arch/arm/mach-ak39/l2cache.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++ ++void l2cache_init(void) ++{ ++} ++EXPORT_SYMBOL(l2cache_init); ++ ++void l2cache_clean_finish(void) ++{ ++} ++EXPORT_SYMBOL(l2cache_clean_finish); ++ ++void l2cache_invalidate(void) ++{ ++} ++EXPORT_SYMBOL(l2cache_invalidate); ++ +diff --git a/arch/arm/mach-ak39/mach-aimer39_ak3916.c b/arch/arm/mach-ak39/mach-aimer39_ak3916.c +new file mode 100755 +index 00000000..1a5add13 +--- /dev/null ++++ b/arch/arm/mach-ak39/mach-aimer39_ak3916.c +@@ -0,0 +1,713 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include "cpu.h" ++#include "irq.h" ++#include ++#include ++#include ++ ++#define SPI_ONCHIP_CS (0) /*means not need gpio*/ ++static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { ++ [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ ++}; ++ ++struct ak_spi_info ak39_spi1_info = { ++ .pin_cs = ak39_spidev_cs, ++ .num_cs = ARRAY_SIZE(ak39_spidev_cs), ++ .bus_num = AKSPI_BUS_NUM1, ++ .clk_name = "spi1", ++ .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, ++ .xfer_mode = AKSPI_XFER_MODE_DMA, ++}; ++ ++static struct flash_platform_data ak39_spiflash_info= { ++ .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, ++ .type = NULL, ++}; ++ ++static struct spi_board_info ak39_spi_board_dev[] = { ++ { ++ .modalias = "ak-spiflash", ++ .bus_num = AKSPI_BUS_NUM1, ++ .chip_select = AKSPI_ONCHIP_CS, ++ .mode = SPI_MODE_0, ++ .max_speed_hz = 20*1000*1000, ++ .platform_data = &ak39_spiflash_info, ++ }, ++}; ++ ++static struct ak_motor_plat_data ak39_motor0_pdata = { ++ .gpio_phase[0] = { ++ .pin = AK_GPIO_37, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_phase[1] = { ++ .pin = AK_GPIO_38, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_phase[2] = { ++ .pin = AK_GPIO_39, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_phase[3] = { ++ .pin = AK_GPIO_40, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ ++ .gpio_hit[0] = { ++ .pin = AK_GPIO_62, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .gpio_hit[1] ={ ++ .pin = AK_GPIO_63, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, ++ .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, ++ ++ .angular_speed = 100, /* angle/s */ ++}; ++ ++ ++ ++static struct ak_motor_plat_data ak39_motor1_pdata = { ++ .gpio_phase[0] = { ++ .pin = AK_GPIO_56, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_phase[1] = { ++ .pin = AK_GPIO_58, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_phase[2] = { ++ .pin = AK_GPIO_59, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_phase[3] = { ++ .pin = AK_GPIO_60, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ ++ .gpio_hit[0] = { ++ .pin = AK_GPIO_61, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .gpio_hit[1] ={ ++ .pin = AK_GPIO_49, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, ++ .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, ++ ++ .angular_speed = 100, /* angle/s */ ++}; ++ ++ ++ ++/* SDIO platform data*/ ++struct ak_mci_platform_data sdio_plat_data = { ++ .irq_cd_type = IRQ_TYPE_LEVEL_LOW, ++ .detect_mode = AKMCI_PLUGIN_ALWAY, ++ .xfer_mode = AKMCI_XFER_L2DMA, ++ .mci_mode = MCI_MODE_SDIO, ++ .gpio_init = ak_gpio_set, ++ .max_speed_hz = 25*1000*1000, ++ .gpio_cd = { ++ .pin = AK_GPIO_18, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .gpio_wp = { ++ .pin = -1, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = AK_PULLUP_ENABLE, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ } ++}; ++ ++/* MMC/SD platform data*/ ++struct ak_mci_platform_data mmc_plat_data = { ++ .irq_cd_type = IRQ_TYPE_LEVEL_LOW, ++ .detect_mode = AKMCI_DETECT_MODE_AD, ++ .xfer_mode = AKMCI_XFER_L2DMA, ++ .mci_mode = MCI_MODE_MMC_SD, ++ .max_speed_hz = 25*1000*1000, ++ .gpio_init = ak_gpio_set, ++ .gpio_cd = { ++ .pin = -1,//AK_GPIO_47, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .gpio_wp = { ++ .pin = -1, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = AK_PULLUP_ENABLE, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ } ++}; ++ ++/* akwifi platform data */ ++struct akwifi_platform_data akwifi_pdata = { ++ .gpio_init = ak_gpio_set, ++#if defined(CONFIG_SDIO_WIFI) ++ .gpio_on = { ++ .pin= AK_GPIO_52, ++ .pulldown= -1, ++ .pullup = -1, ++ .value= AK_GPIO_OUT_HIGH, ++ .dir= AK_GPIO_DIR_OUTPUT, ++ .int_pol= -1, ++ }, ++ .gpio_off = { ++ .pin= AK_GPIO_52, ++ .pulldown= -1, ++ .pullup = -1, ++ .value= AK_GPIO_OUT_LOW, ++ .dir= AK_GPIO_DIR_OUTPUT, ++ .int_pol= -1, ++ }, ++ .power_on_delay = 10, ++ .power_off_delay = 10, ++#else ++ .gpio_on = { ++ .pin = AK_GPIO_50, ++ .pulldown = -1, ++ .pullup = AK_PULLUP_ENABLE, ++ .value = AK_GPIO_OUT_HIGH, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_off = { ++ .pin = AK_GPIO_50, ++ .pulldown = -1, ++ .pullup = AK_PULLUP_ENABLE, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .power_on_delay = 2000, ++ .power_off_delay = 0, ++#endif ++}; ++ ++struct platform_device anyka_wifi_device = { ++ .name = "anyka-wifi", ++ .id = -1, ++ .dev = { ++ .platform_data = &akwifi_pdata, ++ }, ++}; ++ ++static struct akotghc_usb_platform_data akotghc_plat_data = { ++ .gpio_init = ak_gpio_set, ++ .gpio_pwr_on = { ++ .pin = AK_GPIO_55, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_HIGH, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_pwr_off = { ++ .pin = AK_GPIO_55, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .switch_onboard = { ++ .pin = -1, ++ }, ++ .switch_extport = { ++ .pin = -1, ++ }, ++}; ++ ++static struct ak_mac_data ak39_mac_pdata = { ++ .gpio_init = ak_gpio_set, ++ .pwr_gpio = { ++ .pin = AK_GPIO_54, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_HIGH, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .phy_rst_gpio = { ++ .pin = AK_GPIO_53, ++ .pulldown = -1, ++ .pullup = AK_PULLUP_DISABLE, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++}; ++ ++ ++/** ++* @brief ak pcm device struct ++ hp and spk can identify by GPIO or AD. ++ wo can initialize it in this struct. ++* @author dengzhou ++* @date 2012-07-19 ++*/ ++struct ak39_codec_platform_data ak39_codec_pdata = ++{ ++ .hpdet_gpio = ++ { ++ .pin = AK_GPIO_7, ++ .dir = AK_GPIO_DIR_INPUT, ++ .pullup = AK_PULLUP_DISABLE, ++ .pulldown = -1, ++ .value = -1, ++ .int_pol = -1, ++ }, ++ .spk_down_gpio = ++ { ++ .pin = AK_GPIO_3, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .pullup = -1, ++ .pulldown = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .int_pol = -1, ++ }, ++ .hpmute_gpio = ++ { ++ .pin = INVALID_GPIO, //AK_GPIO_29, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .pullup = -1, ++ .pulldown = -1, ++ ++ .value = AK_GPIO_OUT_LOW, ++ .int_pol = -1, ++ }, ++ ++ .hp_on_value = AK_GPIO_OUT_LOW, ++ .hpdet_irq = IRQ_GPIO_7, ++ .bIsHPmuteUsed = 0, ++ .hp_mute_enable_value = AK_GPIO_OUT_HIGH, ++ .bIsMetalfixed = 0, ++ .boutput_only = 1, ++}; ++ ++struct resource ak39_codec_resources[] = { ++ [0] = { ++ .start = 0x08000000, ++ .end = 0x0800FFFF, ++ .flags = (int)IORESOURCE_MEM, ++ .name = "akpcm_AnalogCtrlRegs", ++ }, ++ [1] = { ++ .start = 0x20110000, ++ .end = 0x2011800F, ++ .flags = (int)IORESOURCE_MEM, ++ .name = "akpcm_ADC2ModeCfgRegs", ++ }, ++}; ++ ++struct platform_device ak39_codec_device = { ++ .name = "ak39-codec", ++ .id = -1, ++ .resource = ak39_codec_resources, ++ .num_resources = ARRAY_SIZE(ak39_codec_resources), ++ .dev = { ++ .platform_data = &ak39_codec_pdata, ++ }, ++}; ++ ++ ++/* camera platform data */ ++static struct i2c_board_info ak_camara_devices[] = { ++ { ++ I2C_BOARD_INFO("aksensor", 0x1), ++ }, ++}; ++ ++static struct aksensor_camera_info ak_soc_camera_info = { ++ .buswidth = SOCAM_DATAWIDTH_8, ++ .pin_avdd = INVALID_GPIO, ++ .pin_power = AK_GPIO_48, //initialize GPIO for the power of camera. ++ .pin_reset = AK_GPIO_51, //initialize GPIO for reset of camera. ++ .link = { ++ .bus_id = 39, ++ .power = NULL, ++ .board_info = &ak_camara_devices[0], ++ .i2c_adapter_id = 0, ++ .priv = &ak_soc_camera_info, ++ } ++}; ++ ++/* fake device for soc_camera subsystem */ ++static struct platform_device soc_camera_interface = { ++ .name = "soc-camera-pdrv", ++ .id = -1, ++ .dev = { ++ .platform_data = &ak_soc_camera_info.link, ++ } ++}; ++ ++/* Set LED parameter and initialis status */ ++static struct ak_led_data leds[] = { ++ { ++ .name = "wps_led", ++ .def_trigger = "none", ++ .gpio = { ++ .pin = AK_GPIO_57, ++ .pulldown = -1, ++ .pullup = AK_PULLUP_DISABLE, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ } ++ }, ++ { ++ .name = "vedio_led", ++ .def_trigger = "none", ++ .gpio = { ++ .pin = AK_GPIO_30, ++ .pulldown = -1, ++ .pullup = AK_PULLUP_DISABLE, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ } ++ }, ++}; ++ ++static struct ak_led_pdata led_pdata = { ++ .leds = leds, ++ .nr_led = ARRAY_SIZE(leds), ++}; ++ ++ ++/* unused GPIO number for the machine board is left*/ ++static unsigned int ak39_custom_gpiopin[] = { ++ AK_GPIO_4, ++ AK_GPIO_5, ++}; ++ ++static struct custom_gpio_data ak39_custom_gpios= { ++ .gpiopin = ak39_custom_gpiopin, ++ .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), ++}; ++ ++static struct platform_device ak39_custom_gpio = { ++ .name = "akgpio", ++ .id = -1, ++ .dev = { ++ .platform_data = &ak39_custom_gpios, ++ }, ++}; ++ ++ ++ ++/** ++ * GPIO buttons ++ */ ++static struct gpio_keys_button gpio_keys_button[] = { ++ { ++ .code = KEY_0, ++ .type = EV_KEY, ++ .gpio = AK_GPIO_0, ++ .active_low = 1, ++ .wakeup = 1, ++ .debounce_interval = 30, /* ms */ ++ .desc = "boot0", ++ .pullup = AK_PULLUP_ENABLE, ++ .pulldown = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = AK_GPIO_INT_LOWLEVEL, ++ }, ++}; ++ ++static struct akgpio_keys_platform_data gpio_keys_platform_data = { ++ .buttons = gpio_keys_button, ++ .nbuttons = ARRAY_SIZE(gpio_keys_button), ++ .rep = 0, ++}; ++ ++ ++/** ++* @brief ad-key platform device struct ++ we should initialize the correct voltage for each key. ++*/ ++struct multi_addetect multi_det[] = { ++ {.unpress_min = 3220, .unpress_max = 3258, .fixkeys = NULL, .plugdev = PLUGIN_NODEV}, // = null 3251+/-20 ++ {.unpress_min = 3259, .unpress_max = 3300, .fixkeys = NULL, .plugdev = PLUGIN_AC}, // = sddet 3261(+/-30) ++ {.unpress_min = 2230, .unpress_max = 2290, .fixkeys = NULL, .plugdev = PLUGIN_MMC}, // = sddet 2262(+/-30) ++ {.unpress_min = 1700, .unpress_max = 1780, .fixkeys = NULL, .plugdev = PLUGIN_MMC_AC}, // = sddet 1742(+/-30) ++}; ++ ++struct analog_gpio_key ak39_adkey_data = { ++ .desc = "adkey", ++ .interval = 300, /* ms */ ++ .debounce_interval = 20, /* ms */ ++ .addet = multi_det, ++ .naddet = ARRAY_SIZE(multi_det), ++ .nkey = 0, ++ .wakeup = 1, /* enable ad-key wakeup from standby mode */ ++}; ++ ++static struct platform_device ak39_adkey_device = { ++ .name = "ad-keys", ++ .id = -1, ++ .dev = { ++ .platform_data = &ak39_adkey_data, ++ } ++}; ++ ++ ++/*ak39 battery mach info*/ ++static struct ak_bat_mach_info ak39_bat_info = { ++ .gpio_init = ak_gpio_set, ++ .usb_gpio = { ++ .active = -1, ++ .irq = -ENOSYS, ++ .delay = 0, ++ .pindata ={ ++ .pin = -1, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = -1, ++ .dir = -1, ++ .int_pol = -1, ++ }, ++ }, ++ ++ .ac_gpio = { ++ .is_detect_mode = BAT_CHARGE_ADC_DETECT, ++ .active = -1, ++ .irq = -1, ++ .delay = 500, ++ .pindata ={ ++ .pin = -1, ++ .pulldown = -1, ++ .pulldown = -1, ++ .value = -1, ++ .dir = -1, ++ .int_pol = -1, ++ }, ++ }, ++ ++ .full_gpio = { ++ .active = -1, ++ .irq = -ENOSYS, ++ .delay = 0, ++ .pindata ={ ++ .pin = -1, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = -1, ++ .dir = -1, ++ .int_pol = -1, ++ }, ++ }, ++ ++ .bat_mach_info = { ++ .voltage_sample = 6, // the sample of read voltage ++ .power_on_voltage = 3650, // discharge power on voltage limit ++ .power_on_correct = 64, // mv ++ .charge_min_voltage = 3550, // charge minute voltage (mv) ++ .max_voltage = 4200, // max battery voltage ++ .min_voltage = 3500, // min battery voltage ++ .power_off = poweroff_disable, ++ .full_capacity = 100, // battery full ++ .poweroff_cap = 0, // user read value to power off ++ .low_cap = 5, // user read value to low power warring ++ .recover_cap = 30, ++ .cpower_on_voltage = 3700, // charge power on voltage limit ++ .full_delay = 30, // unit is minute ++ .full_voltage = 4100, ++ }, ++ ++ .bat_adc = { ++ .up_resistance = 10, ++ .dw_resistance = 10, ++ .voltage_correct = 32, // battery correct factor ++ .adc_avdd = 3300, // avdd voltage ++ }, ++ ++}; ++ ++ ++static struct platform_device *ak3910_platform_devices[] __initdata = { ++ &akfha_char_device, ++ &ak39_uart0_device, ++ &ak39_motor0_device, ++ &ak39_motor1_device, ++ &ak39_spi1_device, ++ &ak39_mmc_device, ++ &ak39_sdio_device, ++ &ak39_i2c_device, ++ &ak39_custom_gpio, ++ &ak39_usb_udc_device, ++ &ak39_usb_otg_hcd_device, ++ &anyka_wifi_device, ++ &soc_camera_interface, ++ &ak39_camera_interface, ++ &ak39_ion_device, ++ &ak39_pcm_device, ++ &ak39_codec_device, ++ &ak39_mmx_device, ++ &ak39_mac_device, ++ &ak39_led_pdev, ++ &ak39_gpio_keys_device, ++ &ak39_adkey_device, ++ &ak39_battery_power, ++ &ak39_rtc_device, ++ &ak39_gpio_uart_device, ++}; ++ ++void wdt_enable(void); ++void wdt_keepalive(unsigned int heartbeat); ++ ++static void ak39_restart(char str, const char *cmd) ++{ ++ //ak39_reboot_sys_by_soft(); ++#ifdef CONFIG_AK39_WATCHDOG ++ wdt_enable(); ++ wdt_keepalive(2); ++#endif ++} ++ ++static void __init ak3910_init_machine(void) ++{ ++ adc1_init(); ++ ++ spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); ++ ++ ak39_spi1_device.dev.platform_data = &ak39_spi1_info; ++ ++ ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; ++ ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; ++ ++ ak39_mmc_device.dev.platform_data = &mmc_plat_data; ++ ak39_sdio_device.dev.platform_data = &sdio_plat_data; ++ ++ ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; ++ ak39_mac_device.dev.platform_data = &ak39_mac_pdata; ++ ++ ak39_led_pdev.dev.platform_data = &led_pdata; ++ ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; ++ ak39_battery_power.dev.platform_data = &ak39_bat_info; ++ ++ platform_add_devices(ak3910_platform_devices, ++ ARRAY_SIZE(ak3910_platform_devices)); ++ ++ l2_init(); ++ ++ return; ++} ++ ++ ++MACHINE_START(AK39XX, "Aimer39_AK3916_MB_V1.0.0") ++/* Maintainer: */ ++ .atag_offset = 0x100, ++ .fixup = NULL, ++ .map_io = ak39_map_io, ++ .reserve = NULL, ++ .init_irq = ak39_init_irq, ++ .init_machine = ak3910_init_machine, ++ .init_early = NULL, ++ .timer = &ak39_timer, ++ .restart = ak39_restart, ++ ++MACHINE_END ++ +diff --git a/arch/arm/mach-ak39/mach-aimer39_ak3918.c b/arch/arm/mach-ak39/mach-aimer39_ak3918.c +new file mode 100755 +index 00000000..f57f1ef4 +--- /dev/null ++++ b/arch/arm/mach-ak39/mach-aimer39_ak3918.c +@@ -0,0 +1,543 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include "cpu.h" ++#include "irq.h" ++#include ++#include ++ ++#define SPI_ONCHIP_CS (0) /*means not need gpio*/ ++static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { ++ [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ ++}; ++ ++struct ak_spi_info ak39_spi1_info = { ++ .pin_cs = ak39_spidev_cs, ++ .num_cs = ARRAY_SIZE(ak39_spidev_cs), ++ .bus_num = AKSPI_BUS_NUM1, ++ .clk_name = "spi1", ++ .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, ++ .xfer_mode = AKSPI_XFER_MODE_DMA, ++}; ++ ++static struct flash_platform_data ak39_spiflash_info= { ++ .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, ++ .type = NULL, ++}; ++ ++static struct spi_board_info ak39_spi_board_dev[] = { ++ { ++ .modalias = "ak-spiflash", ++ .bus_num = AKSPI_BUS_NUM1, ++ .chip_select = AKSPI_ONCHIP_CS, ++ .mode = SPI_MODE_0, ++ .max_speed_hz = 20*1000*1000, ++ .platform_data = &ak39_spiflash_info, ++ }, ++}; ++ ++static struct ak_motor_plat_data ak39_motor0_pdata = { ++ .gpio_phase[0] = { ++ .pin = AK_GPIO_37, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_phase[1] = { ++ .pin = AK_GPIO_38, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_phase[2] = { ++ .pin = AK_GPIO_39, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_phase[3] = { ++ .pin = AK_GPIO_40, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ ++ .gpio_hit[0] = { ++ .pin = AK_GPIO_62, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .gpio_hit[1] ={ ++ .pin = AK_GPIO_63, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, ++ .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, ++ ++ .angular_speed = 100, /* angle/s */ ++}; ++ ++ ++ ++static struct ak_motor_plat_data ak39_motor1_pdata = { ++ .gpio_phase[0] = { ++ .pin = AK_GPIO_56, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_phase[1] = { ++ .pin = AK_GPIO_58, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_phase[2] = { ++ .pin = AK_GPIO_4, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_phase[3] = { ++ .pin = AK_GPIO_50, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ ++ .gpio_hit[0] = { ++ .pin = AK_GPIO_52, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .gpio_hit[1] ={ ++ .pin = AK_GPIO_30, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .irq_hit_type[0] = IRQ_TYPE_LEVEL_LOW, ++ .irq_hit_type[1] = IRQ_TYPE_LEVEL_LOW, ++ ++ .angular_speed = 100, /* angle/s */ ++}; ++ ++ ++/* MMC/SD platform data*/ ++struct ak_mci_platform_data mmc_plat_data = { ++ .irq_cd_type = IRQ_TYPE_LEVEL_LOW, ++ .detect_mode = AKMCI_DETECT_MODE_GPIO, ++ .xfer_mode = AKMCI_XFER_L2DMA, ++ .mci_mode = MCI_MODE_MMC_SD, ++ .max_speed_hz = 25*1000*1000, ++ .gpio_init = ak_gpio_set, ++ .gpio_cd = { ++ .pin = AK_GPIO_29, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .gpio_wp = { ++ .pin = -1, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = AK_PULLUP_ENABLE, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ } ++}; ++ ++/* akwifi platform data */ ++struct akwifi_platform_data akwifi_pdata = { ++ .gpio_init = ak_gpio_set, ++ .gpio_on = { ++ .pin = AK_GPIO_55, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_HIGH, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_off = { ++ .pin = AK_GPIO_55, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .power_on_delay = 2000, ++ .power_off_delay = 0, ++}; ++ ++struct platform_device anyka_wifi_device = { ++ .name = "anyka-wifi", ++ .id = -1, ++ .dev = { ++ .platform_data = &akwifi_pdata, ++ }, ++}; ++ ++static struct akotghc_usb_platform_data akotghc_plat_data = { ++ .gpio_init = ak_gpio_set, ++ .gpio_pwr_on = { ++ .pin = -1, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_HIGH, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_pwr_off = { ++ .pin = -1, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .switch_onboard = { ++ .pin = -1, ++ }, ++ .switch_extport = { ++ .pin = -1, ++ }, ++}; ++ ++static struct ak_mac_data ak39_mac_pdata = { ++ .gpio_init = ak_gpio_set, ++ .pwr_gpio = { ++ .pin = -1, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_HIGH, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .phy_rst_gpio = { ++ .pin = AK_GPIO_53, ++ .pulldown = -1, ++ .pullup = AK_PULLUP_DISABLE, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++}; ++ ++ ++/** ++* @brief ak pcm device struct ++ hp and spk can identify by GPIO or AD. ++ wo can initialize it in this struct. ++* @author dengzhou ++* @date 2012-07-19 ++*/ ++struct ak39_codec_platform_data ak39_codec_pdata = ++{ ++ .hpdet_gpio = ++ { ++ .pin = AK_GPIO_7, ++ .dir = AK_GPIO_DIR_INPUT, ++ .pullup = AK_PULLUP_DISABLE, ++ .pulldown = -1, ++ .value = -1, ++ .int_pol = -1, ++ }, ++ .spk_down_gpio = ++ { ++ .pin = AK_GPIO_3, //AK_GPIO_16, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .pullup = -1, ++ .pulldown = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .int_pol = -1, ++ }, ++ .hpmute_gpio = ++ { ++ .pin = INVALID_GPIO, //AK_GPIO_29, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .pullup = -1, ++ .pulldown = -1, ++ ++ .value = AK_GPIO_OUT_LOW, ++ .int_pol = -1, ++ }, ++ ++ .hp_on_value = AK_GPIO_OUT_LOW, ++ .hpdet_irq = IRQ_GPIO_7, ++ .bIsHPmuteUsed = 0, ++ .hp_mute_enable_value = AK_GPIO_OUT_HIGH, ++ .bIsMetalfixed = 0, ++ .boutput_only = 1, ++}; ++ ++struct resource ak39_codec_resources[] = { ++ [0] = { ++ .start = 0x08000000, ++ .end = 0x0800FFFF, ++ .flags = (int)IORESOURCE_MEM, ++ .name = "akpcm_AnalogCtrlRegs", ++ }, ++ [1] = { ++ .start = 0x20110000, ++ .end = 0x2011800F, ++ .flags = (int)IORESOURCE_MEM, ++ .name = "akpcm_ADC2ModeCfgRegs", ++ }, ++}; ++ ++ ++struct platform_device ak39_codec_device = { ++ .name = "ak39-codec", ++ .id = -1, ++ .resource = ak39_codec_resources, ++ .num_resources = ARRAY_SIZE(ak39_codec_resources), ++ .dev = { ++ .platform_data = &ak39_codec_pdata, ++ }, ++}; ++ ++ ++/* camera platform data */ ++static struct i2c_board_info ak_camara_devices[] = { ++ { ++ I2C_BOARD_INFO("aksensor", 0x1), ++ }, ++}; ++ ++static struct aksensor_camera_info ak_soc_camera_info = { ++ .buswidth = SOCAM_DATAWIDTH_8, ++ .pin_avdd = INVALID_GPIO, ++ .pin_power = AK_GPIO_48, //initialize GPIO for the power of camera. ++ .pin_reset = AK_GPIO_51, //initialize GPIO for reset of camera. ++ .link = { ++ .bus_id = 39, ++ .power = NULL, ++ .board_info = &ak_camara_devices[0], ++ .i2c_adapter_id = 0, ++ .priv = &ak_soc_camera_info, ++ } ++}; ++ ++/* fake device for soc_camera subsystem */ ++static struct platform_device soc_camera_interface = { ++ .name = "soc-camera-pdrv", ++ .id = -1, ++ .dev = { ++ .platform_data = &ak_soc_camera_info.link, ++ } ++}; ++ ++/* Set LED parameter and initialis status */ ++static struct ak_led_data leds[] = { ++ { ++ .name = "wps_led", ++ .def_trigger = "none", ++ .gpio = { ++ .pin = AK_GPIO_57, ++ .pulldown = -1, ++ .pullup = AK_PULLUP_DISABLE, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ } ++ }, ++}; ++ ++static struct ak_led_pdata led_pdata = { ++ .leds = leds, ++ .nr_led = ARRAY_SIZE(leds), ++}; ++ ++ ++/* unused GPIO number for the machine board is left*/ ++static unsigned int ak39_custom_gpiopin[] = { ++ AK_GPIO_5, ++ AK_GPIO_61, ++}; ++ ++static struct custom_gpio_data ak39_custom_gpios= { ++ .gpiopin = ak39_custom_gpiopin, ++ .ngpiopin = ARRAY_SIZE(ak39_custom_gpiopin), ++}; ++ ++static struct platform_device ak39_custom_gpio = { ++ .name = "akgpio", ++ .id = -1, ++ .dev = { ++ .platform_data = &ak39_custom_gpios, ++ }, ++}; ++ ++ ++/** ++ * GPIO buttons ++ */ ++static struct gpio_keys_button gpio_keys_button[] = { ++ { ++ .code = KEY_0, ++ .type = EV_KEY, ++ .gpio = AK_GPIO_0, ++ .active_low = 1, ++ .wakeup = 1, ++ .debounce_interval = 30, /* ms */ ++ .desc = "boot0", ++ .pullup = AK_PULLUP_ENABLE, ++ .pulldown = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = AK_GPIO_INT_LOWLEVEL, ++ }, ++}; ++ ++static struct akgpio_keys_platform_data gpio_keys_platform_data = { ++ .buttons = gpio_keys_button, ++ .nbuttons = ARRAY_SIZE(gpio_keys_button), ++ .rep = 0, ++}; ++ ++ ++static struct platform_device *ak3918_platform_devices[] __initdata = { ++ &akfha_char_device, ++ &ak39_uart0_device, ++ &ak39_motor0_device, ++ &ak39_motor1_device, ++ &ak39_spi1_device, ++ &ak39_mmc_device, ++ &ak39_i2c_device, ++ &ak39_custom_gpio, ++ &ak39_usb_udc_device, ++ &ak39_usb_otg_hcd_device, ++ &anyka_wifi_device, ++ &soc_camera_interface, ++ &ak39_camera_interface, ++ &ak39_ion_device, ++ &ak39_pcm_device, ++ &ak39_codec_device, ++ &ak39_mmx_device, ++ &ak39_mac_device, ++ &ak39_led_pdev, ++ &ak39_gpio_keys_device, ++ &ak39_rtc_device, ++}; ++ ++void wdt_enable(void); ++void wdt_keepalive(unsigned int heartbeat); ++ ++static void ak39_restart(char str, const char *cmd) ++{ ++ //ak39_reboot_sys_by_soft(); ++#ifdef CONFIG_AK39_WATCHDOG ++ wdt_enable(); ++ wdt_keepalive(2); ++#endif ++} ++ ++static void __init ak3918_init_machine(void) ++{ ++ adc1_init(); ++ ++ spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); ++ ++ ak39_spi1_device.dev.platform_data = &ak39_spi1_info; ++ ++ ak39_motor0_device.dev.platform_data = &ak39_motor0_pdata; ++ ak39_motor1_device.dev.platform_data = &ak39_motor1_pdata; ++ ++ ak39_mmc_device.dev.platform_data = &mmc_plat_data; ++ ++ ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; ++ ak39_mac_device.dev.platform_data = &ak39_mac_pdata; ++ ++ ak39_led_pdev.dev.platform_data = &led_pdata; ++ ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; ++ ++ platform_add_devices(ak3918_platform_devices, ++ ARRAY_SIZE(ak3918_platform_devices)); ++ ++ l2_init(); ++ ++ return; ++} ++ ++ ++MACHINE_START(AK39XX, "Aimer39_AK3918_MB_V1.0.0") ++/* Maintainer: */ ++ .atag_offset = 0x100, ++ .fixup = NULL, ++ .map_io = ak39_map_io, ++ .reserve = NULL, ++ .init_irq = ak39_init_irq, ++ .init_machine = ak3918_init_machine, ++ .init_early = NULL, ++ .timer = &ak39_timer, ++ .restart = ak39_restart, ++ ++MACHINE_END ++ +diff --git a/arch/arm/mach-ak39/mach-sdk3910.c b/arch/arm/mach-ak39/mach-sdk3910.c +new file mode 100755 +index 00000000..5bc67563 +--- /dev/null ++++ b/arch/arm/mach-ak39/mach-sdk3910.c +@@ -0,0 +1,442 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "cpu.h" ++#include "irq.h" ++ ++ ++#define SPI_ONCHIP_CS (0) /*means not need gpio*/ ++static unsigned long ak39_spidev_cs[AKSPI_CS_NUM] = { ++ [AKSPI_ONCHIP_CS] = SPI_ONCHIP_CS, /*gpio 25, spidev0: ak-spiflash*/ ++}; ++ ++struct ak_spi_info ak39_spi1_info = { ++ .pin_cs = ak39_spidev_cs, ++ .num_cs = ARRAY_SIZE(ak39_spidev_cs), ++ .bus_num = AKSPI_BUS_NUM1, ++ .clk_name = "spi1", ++ .mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH, ++ .xfer_mode = AKSPI_XFER_MODE_DMA, ++}; ++ ++static struct flash_platform_data ak39_spiflash_info= { ++ .bus_width = FLASH_BUS_WIDTH_4WIRE | FLASH_BUS_WIDTH_2WIRE | FLASH_BUS_WIDTH_1WIRE, ++ .type = NULL, ++}; ++ ++static struct spi_board_info ak39_spi_board_dev[] = { ++ { ++ .modalias = "ak-spiflash", ++ .bus_num = AKSPI_BUS_NUM1, ++ .chip_select = AKSPI_ONCHIP_CS, ++ .mode = SPI_MODE_0, ++ .max_speed_hz = 20*1000*1000, ++ .platform_data = &ak39_spiflash_info, ++ }, ++}; ++ ++ ++/* SDIO platform data*/ ++struct ak_mci_platform_data sdio_plat_data = { ++ .irq_cd_type = IRQ_TYPE_LEVEL_LOW, ++ .detect_mode = AKMCI_PLUGIN_ALWAY, ++ .xfer_mode = AKMCI_XFER_L2DMA, ++ .mci_mode = MCI_MODE_SDIO, ++ .gpio_init = ak_gpio_set, ++ .max_speed_hz = 25*1000*1000, ++ .gpio_cd = { ++ .pin = AK_GPIO_18, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .gpio_wp = { ++ .pin = -1, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = AK_PULLUP_ENABLE, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ } ++}; ++ ++/* MMC/SD platform data*/ ++struct ak_mci_platform_data mmc_plat_data = { ++ .irq_cd_type = IRQ_TYPE_LEVEL_LOW, ++ .detect_mode = AKMCI_DETECT_MODE_GPIO, ++ .xfer_mode = AKMCI_XFER_L2DMA, ++ .mci_mode = MCI_MODE_MMC_SD, ++ .gpio_init = ak_gpio_set, ++ .max_speed_hz = 25*1000*1000, ++ .gpio_cd = { ++ .pin = AK_GPIO_30, ++ .pulldown = -1, ++ .pullup = -1, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ }, ++ .gpio_wp = { ++ .pin = -1, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = AK_PULLUP_ENABLE, ++ .value = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = -1, ++ } ++}; ++ ++/* akwifi platform data */ ++struct platform_device anyka_wifi_device = { ++ .name = "anyka-wifi", ++ .id = -1, ++}; ++ ++static struct akotghc_usb_platform_data akotghc_plat_data = { ++ .gpio_init = ak_gpio_set, ++ .gpio_pwr_on = { ++ .pin = AK_GPIO_6, ++ .pulldown = -1, ++ .pullup = AK_PULLUP_DISABLE, ++ .value = AK_GPIO_OUT_HIGH, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .gpio_pwr_off = { ++ .pin = AK_GPIO_6, ++ .pulldown = -1, ++ .pullup = AK_PULLUP_DISABLE, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .switch_onboard = { ++ .pin = -1, ++ }, ++ .switch_extport = { ++ .pin = -1, ++ }, ++}; ++ ++/* MAC platform data */ ++static struct ak_mac_data ak39_mac_pdata = { ++ .gpio_init = ak_gpio_set, ++ .pwr_gpio = { ++ .pin = INVALID_GPIO, ++ .pulldown = AK_PULLDOWN_DISABLE, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_HIGH, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++ .phy_rst_gpio = { ++ .pin = INVALID_GPIO, ++ .pulldown = -1, ++ .pullup = AK_PULLUP_DISABLE, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ }, ++}; ++ ++ ++/** ++* @brief ak pcm device struct ++ hp and spk can identify by GPIO or AD. ++ wo can initialize it in this struct. ++* @author dengzhou ++* @date 2012-07-19 ++*/ ++struct ak39_codec_platform_data ak39_codec_pdata = ++{ ++ .hpdet_gpio = ++ { ++ .pin = AK_GPIO_48, ++ .dir = AK_GPIO_DIR_INPUT, ++ .pullup = -1/*AK_PULLUP_DISABLE*/, ++ .pulldown = AK_PULLDOWN_ENABLE, ++ .value = -1, ++ .int_pol = -1, ++ }, ++ .spk_down_gpio = ++ { ++ .pin = INVALID_GPIO, //AK_GPIO_16, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .pullup = AK_PULLUP_ENABLE, ++ .pulldown = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .int_pol = -1, ++ }, ++ .hpmute_gpio = ++ { ++ .pin = INVALID_GPIO, //AK_GPIO_29, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .pullup = -1, ++ .pulldown = -1, ++ ++ .value = AK_GPIO_OUT_LOW, ++ .int_pol = -1, ++ }, ++ ++ .hp_on_value = AK_GPIO_OUT_LOW, ++ .hpdet_irq = IRQ_GPIO_48, ++ .bIsHPmuteUsed = 0, ++ .hp_mute_enable_value = AK_GPIO_OUT_HIGH, ++ .bIsMetalfixed = 0, ++ .boutput_only = 1, ++}; ++ ++struct resource ak39_codec_resources[] = { ++ [0] = { ++ .start = 0x08000000, ++ .end = 0x0800FFFF, ++ .flags = (int)IORESOURCE_MEM, ++ .name = "akpcm_AnalogCtrlRegs", ++ }, ++ [1] = { ++ .start = 0x20110000, ++ .end = 0x2011800F, ++ .flags = (int)IORESOURCE_MEM, ++ .name = "akpcm_ADC2ModeCfgRegs", ++ }, ++}; ++ ++ ++struct platform_device ak39_codec_device = { ++ .name = "ak39-codec", ++ .id = -1, ++ .resource = ak39_codec_resources, ++ .num_resources = ARRAY_SIZE(ak39_codec_resources), ++ .dev = { ++ .platform_data = &ak39_codec_pdata, ++ }, ++}; ++ ++ ++/* camera platform data */ ++static struct i2c_board_info ak_camara_devices[] = { ++ { ++ I2C_BOARD_INFO("aksensor", 0x1), ++ }, ++}; ++ ++static struct aksensor_camera_info ak_soc_camera_info = { ++ .buswidth = SOCAM_DATAWIDTH_8, ++ .pin_avdd = INVALID_GPIO, ++ .pin_power = AK_GPIO_3, //initialize GPIO for the power of camera. ++ .pin_reset = AK_GPIO_0, //initialize GPIO for reset of camera. ++ .link = { ++ .bus_id = 39, ++ .power = NULL, ++ .board_info = &ak_camara_devices[0], ++ .i2c_adapter_id = 0, ++ .priv = &ak_soc_camera_info, ++ } ++}; ++ ++/* fake device for soc_camera subsystem */ ++static struct platform_device soc_camera_interface = { ++ .name = "soc-camera-pdrv", ++ .id = -1, ++ .dev = { ++ .platform_data = &ak_soc_camera_info.link, ++ } ++}; ++ ++/* Set LED parameter and initialis status */ ++static struct ak_led_data leds[] = { ++#if 0 ++ { ++ .name = "wifi_status", ++ .def_trigger = "none", ++ .gpio = { ++ .pin = AK_GPIO_0, ++ .pulldown = -1, ++// .pullup = AK_PULLUP_DISABLE, ++ .pullup = -1, ++ .value = AK_GPIO_OUT_LOW, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .int_pol = -1, ++ } ++ } ++#endif ++}; ++ ++static struct ak_led_pdata led_pdata = { ++ .leds = leds, ++ .nr_led = ARRAY_SIZE(leds), ++}; ++ ++/** ++ * GPIO buttons ++ */ ++static struct gpio_keys_button gpio_keys_button[] = { ++#if 0 ++ { ++ .code = KEY_0, ++ .type = EV_KEY, ++ .gpio = AK_GPIO_0, ++ .active_low = 1, ++ .wakeup = 1, ++ .debounce_interval = 30, /* ms */ ++ .desc = "boot0", ++ .pullup = AK_PULLUP_ENABLE, ++ .pulldown = -1, ++ .dir = AK_GPIO_DIR_INPUT, ++ .int_pol = AK_GPIO_INT_LOWLEVEL, ++ }, ++#endif ++}; ++ ++static struct akgpio_keys_platform_data gpio_keys_platform_data = { ++ .buttons = gpio_keys_button, ++ .nbuttons = ARRAY_SIZE(gpio_keys_button), ++ .rep = 0, ++}; ++ ++/** ++* @brief ad-key platform device struct ++ we should initialize the correct voltage for each key. ++*/ ++struct adgpio_key adkey[][6] = { ++ // = only ad key ++ {{ .code = KEY_LEFT, .min = 0, .max = 80}, //20+/-40 ++ { .code = KEY_RIGHT, .min = 140, .max = 200}, //174+/-30 ++ { .code = KEY_UP, .min = 400, .max = 480}, //444+/-40 ++ { .code = KEY_DOWN, .min = 620, .max = 720}, //673+/-50 ++ { .code = KEY_OK, .min = 940, .max = 1080}, //980+/-40 ++ { .code = KEY_MENU, .min = 1230, .max = 1350}}, //1308+/-40 ++#if 0 ++ // = key + sd ++ {{ .code = KEY_LEFT, .min = 0, .max = 80}, //20+/-40 ++ { .code = KEY_RIGHT, .min = 140, .max = 200}, //174+/-30 ++ { .code = KEY_UP, .min = 400, .max = 480}, //444+/-40 ++ { .code = KEY_DOWN, .min = 620, .max = 720}, //673+/-50 ++ { .code = KEY_OK, .min = 940, .max = 1080}, //980+/-40 ++ { .code = KEY_MENU, .min = 1230, .max = 1350}}, //1308+/-40 ++#endif ++}; ++ ++struct multi_addetect multi_det[] = { ++ {.unpress_min = 3220, .unpress_max = 3300, .fixkeys = adkey[0], .plugdev = PLUGIN_NODEV}, // = key 3296+/-80 ++// {.unpress_min = 1411, .unpress_max = 1591, .fixkeys = adkey[1], .plugdev = PLUGIN_MMC}, // = key+ sddet 1511+/-80) ++}; ++ ++struct analog_gpio_key ak39_adkey_data = { ++ .desc = "adkey", ++ .interval = 300, ++ .debounce_interval = 20, ++ .addet = multi_det, ++ .naddet = ARRAY_SIZE(multi_det), /*initialize the number of device*/ ++ .nkey = ARRAY_SIZE(adkey[0]), ++ .wakeup = 1, /* initialize key have function of wake up chip. */ ++}; ++ ++static struct platform_device ak39_adkey_device = { ++ .name = "ad-keys", ++ .id = -1, ++ .dev = { ++ .platform_data = &ak39_adkey_data, ++ } ++}; ++ ++static struct platform_device *ak3910_platform_devices[] __initdata = { ++ &akfha_char_device, ++ &ak39_uart0_device, ++ &ak39_uart1_device, ++ &ak39_spi1_device, ++ &ak39_mmc_device, ++ //&ak39_sdio_device, ++ &ak39_i2c_device, ++ &ak39_usb_udc_device, ++ &ak39_usb_otg_hcd_device, ++ &anyka_wifi_device, ++ &soc_camera_interface, ++ &ak39_camera_interface, ++ &ak39_ion_device, ++ &ak39_pcm_device, ++ &ak39_codec_device, ++ &ak39_mmx_device, ++ &ak39_mac_device, ++ &ak39_led_pdev, ++ &ak39_gpio_keys_device, ++ &ak39_adkey_device, ++}; ++ ++static void __init ak3910_init_machine(void) ++{ ++ adc1_init(); ++ ++ spi_register_board_info(ak39_spi_board_dev, ARRAY_SIZE(ak39_spi_board_dev)); ++ ++ ak39_spi1_device.dev.platform_data = &ak39_spi1_info; ++ ++ ak39_mmc_device.dev.platform_data = &mmc_plat_data; ++ //ak39_sdio_device.dev.platform_data = &sdio_plat_data; ++ ++ ak39_usb_otg_hcd_device.dev.platform_data = &akotghc_plat_data; ++ ak39_mac_device.dev.platform_data = &ak39_mac_pdata; ++ ++ ak39_led_pdev.dev.platform_data = &led_pdata; ++ ak39_gpio_keys_device.dev.platform_data = &gpio_keys_platform_data; ++ ++ platform_add_devices(ak3910_platform_devices, ++ ARRAY_SIZE(ak3910_platform_devices)); ++ ++ l2_init(); ++ ++ return; ++} ++ ++ ++MACHINE_START(AK39XX, "SDK3910 Board") ++/* Maintainer: */ ++ .atag_offset = 0x100, ++ .fixup = NULL, ++ .map_io = ak39_map_io, ++ .reserve = NULL, ++ .init_irq = ak39_init_irq, ++ .init_machine = ak3910_init_machine, ++ .init_early = NULL, ++ .timer = &ak39_timer, ++ .restart = NULL, ++ ++MACHINE_END ++ +diff --git a/arch/arm/mach-ak39/pm.c b/arch/arm/mach-ak39/pm.c +new file mode 100644 +index 00000000..329aa9c7 +--- /dev/null ++++ b/arch/arm/mach-ak39/pm.c +@@ -0,0 +1,154 @@ ++/* ++ * linux/arch/arm/mach-ak39/pm.c ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++//#include ++#include ++#include ++#include ++ ++void check_poweroff(void); ++ ++static suspend_state_t target_state; ++ ++#define ak39_pm_debug_init() do { } while(0) ++ ++static int ak39_pm_valid_state(suspend_state_t state) ++{ ++ switch (state) { ++ case PM_SUSPEND_ON: ++ case PM_SUSPEND_STANDBY: ++ case PM_SUSPEND_MEM: ++ return 1; ++ default: ++ return 0; ++ } ++} ++ ++/* ++ * Called after processes are frozen, but before we shutdown devices. ++ */ ++static int ak39_pm_begin(suspend_state_t state) ++{ ++ target_state = state; ++ return 0; ++} ++ ++void L2_LINK(standby) L2FUNC_NAME(standby)(unsigned long param1, ++ unsigned long param2,unsigned long param3, unsigned long param4) ++{ ++ unsigned long val; ++ ++ // invalidate and disable mmu ++ DISABLE_CACHE_MMU(); ++ ++ // check this bit and unitil both are empty ++ while(!((REG32(PHY_RAM_CFG_REG4) & (FIFO_R_EMPTY | FIFO_CMD_EMPTY)) == (FIFO_R_EMPTY | FIFO_CMD_EMPTY))) ++ ; ++ ++ // setup periodic of refresh interval and disable auto-refresh ++ REG32(PHY_RAM_CFG_REG4) &= ~(AUTO_REFRESH_EN); ++ ++ // send all bank precharge ++ DDR2_ENTER_SELFREFRESH(); ++ PM_DELAY(0x10);//at least more than 1 tck ++ ++ // set sdram mode before enter standby ++ val = REG32(0x2000e000); ++ REG32(0x2000e000) |= (0x3 << 0); ++ ++ // disable ram clock ++ REG32(PHY_CLOCK_CTRL_REG) |= RAM_CLOCK_DISABLE; ++ ++ // enter standby ++ REG32(PHY_CLOCK_DIV_REG) |= ENTER_STANDBY; ++ PM_DELAY(0x2000); //at least more than 3 tck only for selfresh ++ ++ // the system is standby ...... ++ ++ // enable ram clock ++ REG32(PHY_CLOCK_CTRL_REG) &= ~RAM_CLOCK_DISABLE; ++ ++ // restore from sdram mode after exit standby ++ REG32(0x2000e000) = val; ++ ++ /* instruction: ++ * PM_DELAY(0x1) = 128.8ns when mem clk = 200M ++ */ ++ PM_DELAY(0x10); // at least more than 1 tck prior exit self refresh ++ ++ // exit DDR2 self-refresch ++ DDR2_EXIT_SELFREFRESH(); ++ ++ // send auto refresh and open odt high ++ DDR2_ENTER_AUTOREFRESH(); ++ ++ // enable auto-refresh ++ REG32(PHY_RAM_CFG_REG4) |= AUTO_REFRESH_EN; ++ PM_DELAY(0x100); ++ ++ // enable ICache & DCache, mmu ++ ENABLE_CACHE_MMU(); ++} ++ ++static int ak39_pm_enter(suspend_state_t state) ++{ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ak39_pm_debug_init(); ++ flush_cache_all(); ++ ++ // change from low to normal mode before enter standby if current is low mode ++ //cpu_freq_suspend_check(); ++ ++ SPECIFIC_L2BUF_EXEC(standby, 0,0,0,0); ++ ++ // check power off ++ check_poweroff(); ++ ++ // restore low mode ++ //cpu_freq_resume_check(); ++ ++ local_irq_restore(flags); ++ return 0; ++} ++ ++/* ++ * Called right prior to thawing processes. ++ */ ++static void ak39_pm_end(void) ++{ ++ target_state = PM_SUSPEND_ON; ++} ++ ++static struct platform_suspend_ops ak39_pm_ops = { ++ .valid = ak39_pm_valid_state, ++ .begin = ak39_pm_begin, ++ .enter = ak39_pm_enter, ++ .end = ak39_pm_end, ++}; ++ ++/* ak39_pm_init ++ * ++ * Attach the power management functions. This should be called ++ * from the board specific initialisation if the board supports ++ * it. ++*/ ++int __init ak39_pm_init(void) ++{ ++ printk("AK39 Power Management, (c) 2010 ANYKA\n"); ++ suspend_set_ops(&ak39_pm_ops); ++ ++ return 0; ++} ++arch_initcall(ak39_pm_init); ++ +diff --git a/arch/arm/mach-ak39/pwm_timer.c b/arch/arm/mach-ak39/pwm_timer.c +new file mode 100644 +index 00000000..25fcc255 +--- /dev/null ++++ b/arch/arm/mach-ak39/pwm_timer.c +@@ -0,0 +1,427 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct ak_pwm_timer_info ++{ ++ int id; ++ int gpio; ++ int timerirq; ++ u8 __iomem *base; ++ T_GPIO_SHAREPIN_CFG sharepin; ++ int is_reserved; ++}; ++ ++ ++static struct ak_pwm_timer_info pt_init_info[] = { ++ {AK_PWM_TIMER1, AK_GPIO_4, IRQ_TIMER1, AK_PWM1_CTRL, ePIN_AS_PWM1, 1}, ++ {AK_PWM_TIMER2, AK_GPIO_5, IRQ_TIMER2, AK_PWM2_CTRL, ePIN_AS_PWM2, 0}, ++ {AK_PWM_TIMER3, AK_GPIO_6, IRQ_TIMER3, AK_PWM3_CTRL, ePIN_AS_PWM3, 0}, ++ {AK_PWM_TIMER4, AK_GPIO_7, IRQ_TIMER4, AK_PWM4_CTRL, ePIN_AS_PWM4, 0}, ++ {AK_PWM_TIMER5, AK_GPIO_8, IRQ_TIMER5, AK_PWM5_CTRL, ePIN_AS_PWM5, 0}, ++}; ++ ++ ++static DEFINE_MUTEX(timer_lock); ++static struct ak_pwm_timer pwm_timer[AK_PWM_TIMER_CNT]; ++static int is_initilize = 0; ++ ++#define for_each_pwm_timer(i, timer) \ ++ for((i)=0, timer=pwm_timer; i= PWM_MIN_FREQ) && (freq < PWM_MAX_FREQ)); ++} ++ ++ ++static inline int id_is_vaild(int id) ++{ ++ return ((id >= AK_PWM_TIMER1) && (id < AK_PWM_TIMER_NR)); ++} ++ ++static inline int timer_is_busy(int id) ++{ ++ return (pwm_timer[id].status == PWM_TIMER_BUSY); ++} ++ ++static inline int timer_is_reserved(int id) ++{ ++ return (pwm_timer[id].status == PWM_TIMER_RESERVED); ++} ++ ++/** ++* @brief set ducy cycle ++* write the value into corresponding regester ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[in] *pwm ++* @param[in] high ++* @param[in] low ++* @return int ++*/ ++static int ak_pwm_set_duty_cycle(struct ak_pwm_timer *pwm, u16 high, u16 low, u32 pre_div) ++{ ++ u32 regval; ++ REG32(pwm->base + AK_PWM_TIMER_CTRL1) = high << 16 | low; ++ ++ regval = REG32(pwm->base + AK_PWM_TIMER_CTRL2); ++ regval &= ~AK_PWM_TIMER_PRE_DIV_MASK; ++ regval |= AK_PWM_TIMER_PRE_DIV(pre_div); ++ REG32(pwm->base + AK_PWM_TIMER_CTRL2) = regval; ++ ++ return 0; ++} ++ ++ ++/** ++* @brief get current ducy cycle ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[in] *pwm ++* @param[out] *high ++* @param[out] *low ++* @return int ++*/ ++int ak_pwm_get_duty_cycle(struct ak_pwm_timer *pwm, unsigned short *high, unsigned short *low) ++{ ++ unsigned long regval; ++ ++ regval = __raw_readl(pwm->base + AK_PWM_TIMER_CTRL1); ++ ++ *high = regval >> 16; ++ *low = regval & 0xFFFF; ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_pwm_get_duty_cycle); ++ ++/** ++* @brief ak_pwm_config ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *pwm ++* @param[in] high ++* @param[in] low ++* @return int ++*/ ++int ak_pwm_config(struct ak_pwm_timer *pwm, unsigned short high, unsigned short low, unsigned int freq) ++{ ++ u32 pre_div; ++ BUG_ON(!pwm_freq_is_vaild(freq)); ++ ++ pre_div = (REAL_CRYSTAL_FREQ * (high + 1)+(low + 1))/freq - 1; ++ ++ pwm->pre_div = pre_div; ++ pwm->pwm_hlimit = high; ++ pwm->pwm_llimit = low; ++ ++ return ak_pwm_set_duty_cycle(pwm, high, low, pre_div); ++} ++EXPORT_SYMBOL(ak_pwm_config); ++ ++/** ++* @brief enable corresponding pwm ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[in] *pwm ++* @return int ++*/ ++int ak_pwm_enable(struct ak_pwm_timer *pwm) ++{ ++ T_GPIO_SHAREPIN_CFG sharepin; ++ ++ sharepin = pt_init_info[pwm->id].sharepin; ++ ak_group_config(sharepin); ++ return 0; ++} ++EXPORT_SYMBOL(ak_pwm_enable); ++ ++ ++/** ++* @brief disable corresponding pwm ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *pwm ++* @return void ++*/ ++void ak_pwm_disable(struct ak_pwm_timer *pwm) ++{ ++ ak_setpin_as_gpio(pwm->pwm_pin); ++} ++EXPORT_SYMBOL(ak_pwm_disable); ++ ++/** ++* @brief initilize the pwm/timer module. ++* @author lixinhai ++* @date 2013-06-9 ++* @return return 0 when success. ++*/ ++static int __init ak_pwm_timer_init(void) ++{ ++ int i; ++ struct ak_pwm_timer *timer; ++ ++ if(is_initilize == 1) ++ return 0; ++ ++ for_each_pwm_timer(i, timer) { ++ timer->id = i; ++ timer->base = pt_init_info[i].base; ++ if(pt_init_info[i].is_reserved == 0) ++ timer->status = PWM_TIMER_UNUSED; ++ else ++ timer->status = PWM_TIMER_RESERVED; ++ } ++ is_initilize = 1; ++ return 0; ++} ++late_initcall(ak_pwm_timer_init); ++ ++ ++/** ++* @brief request a timer. ++* @author lixinhai ++* @param[in] the pwm/timer id ++* @param[in] mode ++* @date 2013-06-9 ++* @return return the ak_pwm_timer handle. ++*/ ++static struct ak_pwm_timer *__pwm_timer_request(int id, u8 mode) ++{ ++ struct ak_pwm_timer *pt; ++ BUG_ON(!id_is_vaild(id)); ++ ++ if(is_initilize == 0) ++ ak_pwm_timer_init(); ++ ++ /*if timer is reserved or busy, request fail.*/ ++ if(timer_is_reserved(id) || timer_is_busy(id)) { ++ printk("pwm[%d] is working now. request fail.\n", id); ++ return NULL; ++ } ++ ++ pt = &pwm_timer[id]; ++ pt->mode = mode; ++ pt->status = PWM_TIMER_BUSY; ++ ++ return pt; ++} ++ ++/** ++* @brief release the timer. ++* @author lixinhai ++* @param[in] ak_pwm_timer pointer. ++* @date 2013-06-9 ++* @return void ++*/ ++static void __pwm_timer_release(struct ak_pwm_timer *pt) ++{ ++ pt->status = PWM_TIMER_UNUSED; ++} ++ ++ ++/** ++* @brief config the timer. ++* @author lixinhai ++* @param[in] ak_pwm_timer pointer ++* @param[in] timer count ++* @param[in] pre div ++* @date 2013-06-9 ++* @return success return 0, otherwise return negative value ++*/ ++int ak_timer_config(struct ak_pwm_timer *timer, u32 cnt, u8 pre_div) ++{ ++ u32 regval; ++ ++ timer->timer_cnt = cnt; ++ timer->pre_div = pre_div; ++ ++ ++ REG32(timer->base + AK_PWM_TIMER_CTRL1) = timer->timer_cnt; ++ ++ REG32(timer->base + AK_PWM_TIMER_CTRL2) = AK_TIMER_TIMEOUT_CLR; ++ ++ regval = AK_PWM_TIMER_PRE_DIV(timer->pre_div) | ++ AK_TIMER_WORK_MODE(timer->mode); ++ ++ REG32(timer->base + AK_PWM_TIMER_CTRL2) |= regval; ++ return 0; ++} ++EXPORT_SYMBOL(ak_timer_config); ++ ++ ++/** ++* @brief enable the timer. ++* @author lixinhai ++* @param[in] ak_pwm_timer pointer ++* @date 2013-06-9 ++* @return success return 0, otherwise return negative value ++*/ ++int ak_timer_enable(struct ak_pwm_timer *timer) ++{ ++ u32 regval; ++ ++ regval = AK_TIMER_FEED_TIMER | AK_PWM_TIMER_EN; ++ REG32(timer->base + AK_PWM_TIMER_CTRL2) |= regval; ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_timer_enable); ++ ++ ++/** ++* @brief enable the timer and wait for timeout. ++* @author lixinhai ++* @param[in] ak_pwm_timer pointer ++* @date 2013-06-9 ++* @return success return 0, otherwise return negative value ++*/ ++int ak_timer_enable_sync(struct ak_pwm_timer *timer) ++{ ++ ak_timer_enable(timer); ++ ++ while(!(REG32(timer->base + AK_PWM_TIMER_CTRL2)&(1<<27))) ++ ; ++ return 0; ++} ++EXPORT_SYMBOL(ak_timer_enable_sync); ++ ++ ++/** ++* @brief disable the timer. ++* @param[in] ak_pwm_timer pointer ++* @author lixinhai ++* @date 2013-06-9 ++* @return void ++*/ ++void ak_timer_disable(struct ak_pwm_timer *timer) ++{ ++ u32 regval; ++ ++ regval = REG32(timer->base + AK_PWM_TIMER_CTRL2); ++ regval &= ~AK_PWM_TIMER_EN; ++ REG32(timer->base + AK_PWM_TIMER_CTRL2) = regval; ++} ++EXPORT_SYMBOL(ak_timer_disable); ++ ++ ++/** ++* @brief request a pwm by pwm_id. ++* @param[in] pwm id ++* @author lixinhai ++* @date 2013-06-9 ++* @return ak_pwm_timer handle. ++*/ ++struct ak_pwm_timer *ak_pwm_request(int pwm_id) ++{ ++ struct ak_pwm_timer *pwm; ++ ++ mutex_lock(&timer_lock); ++ pwm = __pwm_timer_request(pwm_id, AK_PT_MODE_PWM); ++ mutex_unlock(&timer_lock); ++ return pwm; ++} ++EXPORT_SYMBOL(ak_pwm_request); ++ ++ ++/** ++* @brief the timer interrupt handler. ++* @author lixinhai ++* @date 2013-06-9 ++* @return irqreturn_t ++*/ ++static irqreturn_t timer_timeout_irq(int irq, void *dev_id) ++{ ++ struct ak_pwm_timer *timer = dev_id; ++ ++ REG32(timer->base + AK_PWM_TIMER_CTRL2) |= AK_TIMER_TIMEOUT_CLR; ++ timer->timer_cb(timer->timer_priv); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++/** ++* @brief request a timer by timer_id. ++* @param[in] timer id ++* @author lixinhai ++* @date 2013-06-9 ++* @return success return 0, otherwise return negative value ++*/ ++struct ak_pwm_timer *ak_timer_request(int timer_id, bool auto_reload, int (*cb)(void* data)) ++{ ++ int ret; ++ u8 mode; ++ ++ struct ak_pwm_timer *timer; ++ mode = auto_reload ?AK_PT_MODE_TIMER_AUTO_LOAD: ++ AK_PT_MODE_TIMER_ONE_SHOT; ++ ++ mutex_lock(&timer_lock); ++ timer = __pwm_timer_request(timer_id, mode); ++ ++ if(cb != NULL) { ++ timer->timer_cb = cb; ++ ++ timer->timer_irq = pt_init_info[timer_id].timerirq; ++ ++ /*if callback function not NULL, request timer irq.*/ ++ ret = request_irq(timer->timer_irq, timer_timeout_irq, IRQF_DISABLED, "timer", timer); ++ disable_irq(timer->timer_irq); ++ if(ret) { ++ printk("request timer irq fail.\n"); ++ __pwm_timer_release(timer); ++ return NULL; ++ } ++ } ++ ++ mutex_unlock(&timer_lock); ++ return timer; ++} ++EXPORT_SYMBOL(ak_timer_request); ++ ++ ++/** ++* @brief release the pwm. ++* @param[in] ak_pwm_timer pointer ++* @author lixinhai ++* @date 2013-06-9 ++* @return success return 0, otherwise return negative value ++*/ ++void ak_pwm_release(struct ak_pwm_timer *pwm) ++{ ++ ++ mutex_lock(&timer_lock); ++ __pwm_timer_release(pwm); ++ mutex_unlock(&timer_lock); ++} ++EXPORT_SYMBOL(ak_pwm_release); ++ ++ ++/** ++* @brief release the timer. ++* @param[in] ak_pwm_timer pointer ++* @author lixinhai ++* @date 2013-06-9 ++* @return success return 0, otherwise return negative value ++*/ ++void ak_timer_release(struct ak_pwm_timer *timer) ++{ ++ mutex_lock(&timer_lock); ++ free_irq(timer->timer_irq, timer); ++ __pwm_timer_release(timer); ++ mutex_unlock(&timer_lock); ++} ++EXPORT_SYMBOL(ak_timer_release); ++ ++ +diff --git a/arch/arm/mach-ak39/reboot.c b/arch/arm/mach-ak39/reboot.c +new file mode 100644 +index 00000000..4730b726 +--- /dev/null ++++ b/arch/arm/mach-ak39/reboot.c +@@ -0,0 +1,122 @@ ++/* ++ * reboot.c - implement a interface jump to ROM code ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++//#include ++#include ++#include ++#include ++ ++#define DIS_INTERRUPTS (0x00001FFF) ++#define RESET_DEVICE1 (0xE9A70000) ++#define RESET_DEVICE2 (0xBF7F0000) ++#define CLOSE_CLOCK1 (0x0000E9A7) ++#define CLOSE_CLOCK2 (0x0000BF7F) ++#define RESET_MULTI1 (0x003003C8) ++#define RESET_MULTI2 (0x24000000) ++#define RESET_SHARE1 (0x00000203) ++#define RESET_SHARE2 (0x00000001) ++/** ++ * @brief: ak39_jump_to_rom ++ * @author: zhongjunchao ++ * @date: 2011-10-13 ++ * ++ * @note: Invalidate I & D cache and TLBs, and then disable I & D cache and MMU ++ * At last, we jump to ROM ++ */ ++void ak39_jump_to_rom(unsigned long addr) ++{ ++ __asm__ __volatile__( ++ "1: mrc p15, 0, pc, c7, c14, 3\n\t" ++ "bne 1b\n\t" ++ "mov %0, #0x0\n\t" ++ "mcr p15, 0, %0, c8, c7, 0\n\t" ++ "mcr p15, 0, %0, c7, c5, 0\n\t" ++ "mrc p15, 0, %0, c1, c0, 0\n\t" ++ "bic %0, %0, #0x3000\n\t" ++ "bic %0, %0, #0x0005\n\t" ++ "mcr p15, 0, %0, c1, c0, 0\n\t" ++ "mov pc, %1\n\t" ++ : : "r"(0),"r"(addr)); ++} ++EXPORT_SYMBOL(ak39_jump_to_rom); ++ ++/** ++ * @brief: ak39_reboot_sys_by_soft ++ * @author: zhongjunchao ++ * @date: 2011-10-11 ++ * ++ * @note: I jump to 0x0 to reboot our system. ++ * TODO: Now on, the register config only for ak39xx. ++ */ ++void ak39_reboot_sys_by_soft(void) ++{ ++ /* disable some interrupt */ ++ REG32(AK_VA_SYSCTRL + 0x4c) &= ~DIS_INTERRUPTS; ++ printk("After disable some interrupt\n"); ++ ++ /* mask all normal interrupt */ ++ REG32(AK_VA_SYSCTRL + 0x34) = 0x0; ++ printk("After mask all normal interrupt\n"); ++ ++ /* mask all GPIO interrupt */ ++ REG32(AK_GPIO_INT_MASK1) = 0x0; ++ REG32(AK_GPIO_INT_MASK2) = 0x0; ++ printk("After disable all GPIO interrupt\n"); ++ ++ /* reset and close clock except UART1,RAM and L2 FIFO */ ++ REG32(AK_VA_SYSCTRL + 0x0C) |= RESET_DEVICE1; ++ udelay(500); ++ REG32(AK_VA_SYSCTRL + 0x0C) &= ~RESET_DEVICE1; ++ ++ REG32(AK_VA_SYSCTRL + 0x10) |= RESET_DEVICE2; ++ udelay(500); ++ REG32(AK_VA_SYSCTRL + 0x10) &= ~RESET_DEVICE2; ++ ++ REG32(AK_VA_SYSCTRL + 0x0C) |= CLOSE_CLOCK1; ++ REG32(AK_VA_SYSCTRL + 0x10) |= CLOSE_CLOCK2; ++ printk("After reset all device and close clock\n"); ++ ++ /* reset multiple-function control */ ++ REG32(AK_VA_SYSCTRL + 0x58) = RESET_MULTI1; ++ REG32(AK_VA_SYSCTRL + 0x14) = RESET_MULTI2; ++ printk("After reset multiple-function\n"); ++ ++ /* reset share pin control */ ++ REG32(AK_VA_SYSCTRL + 0x78) = RESET_SHARE1; ++ REG32(AK_VA_SYSCTRL + 0x74) = RESET_SHARE2; ++ printk("After reset all share pin\n"); ++ ++ /* reset GPIO interrupt polarity */ ++ REG32(AK_GPIO_INTP1) = 0x0; ++ REG32(AK_GPIO_INTP2) = 0x0; ++ printk("After reset all GPIO int pol\n"); ++ ++ /* reset GPIO direction */ ++ REG32(AK_GPIO_DIR1) = 0x0; ++ REG32(AK_GPIO_DIR2) = 0x0; ++ printk("After reset all GPIO dir\n"); ++ ++ /* reset GPIO pull up/down */ ++ REG32(AK_VA_SYSCTRL + 0x9C) = 0x0; ++ REG32(AK_VA_SYSCTRL + 0xA0) = 0x0; ++ REG32(AK_VA_SYSCTRL + 0xA4) = 0x0; ++ REG32(AK_VA_SYSCTRL + 0xA8) = 0x0; ++ printk("After reset all GPIO PDU\n"); ++ ++ /* disable l2 cache */ ++ l2cache_clean_finish(); ++ l2cache_invalidate(); ++ printk("After disable l2 cache\n"); ++ ++ /* jump to 0x0 anddress */ ++ ak39_jump_to_rom(0x0); ++} ++EXPORT_SYMBOL(ak39_reboot_sys_by_soft); +diff --git a/arch/arm/mach-ak39/reset.c b/arch/arm/mach-ak39/reset.c +new file mode 100755 +index 00000000..7836a0ab +--- /dev/null ++++ b/arch/arm/mach-ak39/reset.c +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (C) anyka 2012 ++ * Wangsheng Gao ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#undef REG32 ++#define REG32(_reg) (*(volatile unsigned long *)(_reg)) ++ ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief soft reset module ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] which module ++* @param[in] delay millisecond second ++* @return fail or not ++*/ ++int ak39_soft_reset(u32 module) ++{ ++ BUG_ON(module < AK39_SRESET_MMCSD || module > AK39_SRESET_DRAM); ++ ++ REG32(MODULE_RESET_CON1) |= (0x1 << module); ++ mdelay(5); ++ REG32(MODULE_RESET_CON1) &= ~(0x1 << module); ++ return 0; ++} ++EXPORT_SYMBOL(ak39_soft_reset); ++ ++/***** extern call for comm drivers compatible *****/ ++int ak_soft_reset(u32 module) ++{ ++ return ak39_soft_reset(module); ++} ++EXPORT_SYMBOL(ak_soft_reset); +diff --git a/arch/arm/mach-ak39/time.c b/arch/arm/mach-ak39/time.c +new file mode 100755 +index 00000000..b1c4a405 +--- /dev/null ++++ b/arch/arm/mach-ak39/time.c +@@ -0,0 +1,214 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++ ++#define AK39_TIMER1_CTRL1 (AK_VA_SYSCTRL + 0xB4) ++#define AK39_TIMER1_CTRL2 (AK_VA_SYSCTRL + 0xB8) ++#define AK39_TIMER2_CTRL1 (AK_VA_SYSCTRL + 0xBC) ++#define AK39_TIMER2_CTRL2 (AK_VA_SYSCTRL + 0xC0) ++#define AK39_TIMER3_CTRL1 (AK_VA_SYSCTRL + 0xC4) ++#define AK39_TIMER3_CTRL2 (AK_VA_SYSCTRL + 0xC8) ++#define AK39_TIMER4_CTRL1 (AK_VA_SYSCTRL + 0xCC) ++#define AK39_TIMER4_CTRL2 (AK_VA_SYSCTRL + 0xD0) ++#define AK39_TIMER5_CTRL1 (AK_VA_SYSCTRL + 0xD4) ++#define AK39_TIMER5_CTRL2 (AK_VA_SYSCTRL + 0xD8) ++ ++#define AK39_TIMER_CTRL1 AK39_TIMER1_CTRL1 ++#define AK39_TIMER_CTRL2 AK39_TIMER1_CTRL2 ++#define IRQ_TIMER IRQ_TIMER1 ++ ++#define TIMER_CNT (12000000/HZ) ++#define TIMER_USEC_SHIFT 16 ++#define TIMER_CNT_MASK (0x3F<<26) ++ ++//define timer register bits ++#define TIMER_CLEAR_BIT (1<<30) ++#define TIMER_FEED_BIT (1<<29) ++#define TIMER_ENABLE_BIT (1<<28) ++#define TIMER_STATUS_BIT (1<<27) ++#define TIMER_READ_SEL_BIT (1<<26) ++ ++//define pwm/pwm mode ++#define MODE_AUTO_RELOAD_TIMER 0x0 ++#define MODE_ONE_SHOT_TIMER 0x1 ++#define MODE_PWM 0x2 ++ ++ ++static u_int64_t ghrtick = 0; ++static unsigned long usec_per_tick; /* usec per tick, left shift 16 */ ++ ++/* copy from plat-s3c/time.c ++ * ++ * timer_mask_usec_ticks ++ * ++ * given a clock and divisor, make the value to pass into timer_ticks_to_usec ++ * to scale the ticks into usecs ++*/ ++static inline unsigned long ++timer_mask_usec_ticks(unsigned long scaler, unsigned long pclk) ++{ ++ unsigned long den = pclk / 1000; ++ ++ return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den; ++} ++ ++static inline unsigned long timer_ticks_to_usec(unsigned long ticks) ++{ ++ unsigned long ret; ++ ++ ret = ticks * usec_per_tick; ++ ret += 1 << (TIMER_USEC_SHIFT - 4); ++ ++ return ret >> TIMER_USEC_SHIFT; ++} ++ ++/* ++ * Returns microsecond since last clock interrupt. Note that interrupts ++ * will have been disabled by do_gettimeoffset() ++ * IRQs are disabled before entering here from do_gettimeofday() ++ * ++ * FIXME: this need be checked ++ */ ++static unsigned long ak39_gettimeoffset(void) ++{ ++ unsigned long tdone; ++ unsigned long tcnt; ++ ++ /* work out how many ticks have gone since last timer interrupt */ ++ ++ //select read current count mode ++ tdone = __raw_readl(AK39_TIMER_CTRL2); ++ __raw_writel(tdone | TIMER_READ_SEL_BIT, AK39_TIMER_CTRL2); ++ ++ tcnt = __raw_readl(AK39_TIMER_CTRL1); ++ ++ //recover read mode ++ tdone = __raw_readl(AK39_TIMER_CTRL2); ++ __raw_writel(tdone & (~TIMER_READ_SEL_BIT), AK39_TIMER_CTRL2); ++ ++ tdone = TIMER_CNT - tcnt; ++ ++ if (__raw_readl(AK39_TIMER_CTRL2) & TIMER_STATUS_BIT) { /* Timer1 has generated interrupt, and not clear */ ++ ++ /* Reread timer counter */ ++ //select read current count mode ++ tdone = __raw_readl(AK39_TIMER_CTRL2); ++ __raw_writel(tdone | TIMER_READ_SEL_BIT, AK39_TIMER_CTRL2); ++ ++ tcnt = __raw_readl(AK39_TIMER_CTRL1); ++ ++ //recover read mode ++ tdone = __raw_readl(AK39_TIMER_CTRL2); ++ __raw_writel(tdone & (~TIMER_READ_SEL_BIT), AK39_TIMER_CTRL2); ++ ++ tdone = TIMER_CNT - tcnt; ++ ++ if (tcnt != 0) ++ tdone += TIMER_CNT; ++ } ++ ++ return timer_ticks_to_usec(tdone); ++} ++ ++static inline void ak39_timer_setup(void) ++{ ++ unsigned long regval; ++ ++ /* clear timeout puls, reload */ ++ regval = __raw_readl(AK39_TIMER_CTRL2); ++ __raw_writel(regval | TIMER_CLEAR_BIT, AK39_TIMER_CTRL2); ++} ++ ++/* ++ * IRQ handler for the timer ++ */ ++static irqreturn_t ak39_timer_interrupt(int irq, void *dev_id) ++{ ++ if (__raw_readl(AK39_TIMER_CTRL2) & TIMER_STATUS_BIT) { ++ ++ ghrtick += TIMER_CNT; ++ ++ timer_tick(); ++ ++ ak39_timer_setup(); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++#if 0 ++u_int64_t ak39_gethrtick(void) ++{ ++ unsigned long timecnt = 0; ++ unsigned long tdone; ++ ++ //select read current count mode ++ tdone = __raw_readl(AK39_TIMER_CTRL2); ++ __raw_writel(tdone | TIMER_READ_SEL_BIT, AK39_TIMER_CTRL2); ++ ++ timecnt = __raw_readl(AK39_TIMER_CTRL1); ++ ++ //recover read mode ++ tdone = __raw_readl(AK39_TIMER_CTRL2); ++ __raw_writel(tdone & (~TIMER_READ_SEL_BIT), AK39_TIMER_CTRL2); ++ ++ timecnt &= (~TIMER_CNT_MASK); ++ ++ return (ghrtick + (u_int64_t)(TIMER_CNT-timecnt)); ++} ++#endif ++ ++static struct irqaction ak39_timer_irq = { ++ .name = "timer tick", ++ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, ++ .handler = ak39_timer_interrupt, ++}; ++ ++static void __init ak39_timer_init(void) ++{ ++ unsigned long timecnt = TIMER_CNT - 1; ++ ++ usec_per_tick = timer_mask_usec_ticks(1, 12000000); ++ ++ __raw_writel(timecnt, AK39_TIMER_CTRL1); ++ __raw_writel((TIMER_ENABLE_BIT | TIMER_FEED_BIT | (MODE_AUTO_RELOAD_TIMER << 24)), ++ AK39_TIMER_CTRL2); ++ ++ /* setup irq handler for IRQ_TIMER */ ++ setup_irq(IRQ_TIMER, &ak39_timer_irq); ++ ghrtick = 0; ++} ++ ++ ++struct sys_timer ak39_timer = { ++ .init = ak39_timer_init, ++ .offset = ak39_gettimeoffset, ++ //.resume = ak39_timer_setup ++}; ++ +diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile +index bca7e619..37da2cc8 100644 +--- a/arch/arm/mm/Makefile ++++ b/arch/arm/mm/Makefile +@@ -7,6 +7,7 @@ obj-y := dma-mapping.o extable.o fault.o init.o \ + + obj-$(CONFIG_MMU) += fault-armv.o flush.o idmap.o ioremap.o \ + mmap.o pgd.o mmu.o vmregion.o ++obj-$(CONFIG_DEBUG_RODATA) += rodata.o + + ifneq ($(CONFIG_MMU),y) + obj-y += nommu.o +diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c +index 2a8e3805..eaa6847e 100644 +--- a/arch/arm/mm/cache-l2x0.c ++++ b/arch/arm/mm/cache-l2x0.c +@@ -32,8 +32,18 @@ static void __iomem *l2x0_base; + static DEFINE_RAW_SPINLOCK(l2x0_lock); + static u32 l2x0_way_mask; /* Bitmask of active ways */ + static u32 l2x0_size; ++static u32 l2x0_cache_id; ++static unsigned int l2x0_sets; ++static unsigned int l2x0_ways; + static unsigned long sync_reg_offset = L2X0_CACHE_SYNC; + ++static inline bool is_pl310_rev(int rev) ++{ ++ return (l2x0_cache_id & ++ (L2X0_CACHE_ID_PART_MASK | L2X0_CACHE_ID_REV_MASK)) == ++ (L2X0_CACHE_ID_PART_L310 | rev); ++} ++ + struct l2x0_regs l2x0_saved_regs; + + struct l2x0_of_data { +@@ -130,6 +140,23 @@ static void l2x0_cache_sync(void) + raw_spin_unlock_irqrestore(&l2x0_lock, flags); + } + ++#ifdef CONFIG_PL310_ERRATA_727915 ++static void l2x0_for_each_set_way(void __iomem *reg) ++{ ++ int set; ++ int way; ++ unsigned long flags; ++ ++ for (way = 0; way < l2x0_ways; way++) { ++ raw_spin_lock_irqsave(&l2x0_lock, flags); ++ for (set = 0; set < l2x0_sets; set++) ++ writel_relaxed((way << 28) | (set << 5), reg); ++ cache_sync(); ++ raw_spin_unlock_irqrestore(&l2x0_lock, flags); ++ } ++} ++#endif ++ + static void __l2x0_flush_all(void) + { + debug_writel(0x03); +@@ -143,6 +170,13 @@ static void l2x0_flush_all(void) + { + unsigned long flags; + ++#ifdef CONFIG_PL310_ERRATA_727915 ++ if (is_pl310_rev(REV_PL310_R2P0)) { ++ l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_INV_LINE_IDX); ++ return; ++ } ++#endif ++ + /* clean all ways */ + raw_spin_lock_irqsave(&l2x0_lock, flags); + __l2x0_flush_all(); +@@ -153,11 +187,20 @@ static void l2x0_clean_all(void) + { + unsigned long flags; + ++#ifdef CONFIG_PL310_ERRATA_727915 ++ if (is_pl310_rev(REV_PL310_R2P0)) { ++ l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_LINE_IDX); ++ return; ++ } ++#endif ++ + /* clean all ways */ + raw_spin_lock_irqsave(&l2x0_lock, flags); ++ debug_writel(0x03); + writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY); + cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask); + cache_sync(); ++ debug_writel(0x00); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); + } + +@@ -309,26 +352,24 @@ static void l2x0_unlock(u32 cache_id) + void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) + { + u32 aux; +- u32 cache_id; + u32 way_size = 0; +- int ways; + const char *type; + + l2x0_base = base; + +- cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); ++ l2x0_cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); + aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); + + aux &= aux_mask; + aux |= aux_val; + + /* Determine the number of ways */ +- switch (cache_id & L2X0_CACHE_ID_PART_MASK) { ++ switch (l2x0_cache_id & L2X0_CACHE_ID_PART_MASK) { + case L2X0_CACHE_ID_PART_L310: + if (aux & (1 << 16)) +- ways = 16; ++ l2x0_ways = 16; + else +- ways = 8; ++ l2x0_ways = 8; + type = "L310"; + #ifdef CONFIG_PL310_ERRATA_753970 + /* Unmapped register. */ +@@ -337,24 +378,25 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) + outer_cache.set_debug = pl310_set_debug; + break; + case L2X0_CACHE_ID_PART_L210: +- ways = (aux >> 13) & 0xf; ++ l2x0_ways = (aux >> 13) & 0xf; + type = "L210"; + break; + default: + /* Assume unknown chips have 8 ways */ +- ways = 8; ++ l2x0_ways = 8; + type = "L2x0 series"; + break; + } + +- l2x0_way_mask = (1 << ways) - 1; ++ l2x0_way_mask = (1 << l2x0_ways) - 1; + + /* + * L2 cache Size = Way size * Number of ways + */ + way_size = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17; +- way_size = 1 << (way_size + 3); +- l2x0_size = ways * way_size * SZ_1K; ++ way_size = SZ_1K << (way_size + 3); ++ l2x0_size = l2x0_ways * way_size; ++ l2x0_sets = way_size / CACHE_LINE_SIZE; + + /* + * Check if l2x0 controller is already enabled. +@@ -363,7 +405,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) + */ + if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & 1)) { + /* Make sure that I&D is not locked down when starting */ +- l2x0_unlock(cache_id); ++ l2x0_unlock(l2x0_cache_id); + + /* l2x0 controller is disabled */ + writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL); +@@ -386,7 +428,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) + + printk(KERN_INFO "%s cache controller enabled\n", type); + printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n", +- ways, cache_id, aux, l2x0_size); ++ l2x0_ways, l2x0_cache_id, aux, l2x0_size); + } + + #ifdef CONFIG_OF +diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S +index 74c2e5a3..2edb6f67 100644 +--- a/arch/arm/mm/cache-v6.S ++++ b/arch/arm/mm/cache-v6.S +@@ -272,6 +272,11 @@ v6_dma_clean_range: + * - end - virtual end address of region + */ + ENTRY(v6_dma_flush_range) ++#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT ++ sub r2, r1, r0 ++ cmp r2, #CONFIG_CACHE_FLUSH_RANGE_LIMIT ++ bhi v6_dma_flush_dcache_all ++#endif + #ifdef CONFIG_DMA_CACHE_RWFO + ldrb r2, [r0] @ read for ownership + strb r2, [r0] @ write for ownership +@@ -294,6 +299,18 @@ ENTRY(v6_dma_flush_range) + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer + mov pc, lr + ++#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT ++v6_dma_flush_dcache_all: ++ mov r0, #0 ++#ifdef HARVARD_CACHE ++ mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate ++#else ++ mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate ++#endif ++ mcr p15, 0, r0, c7, c10, 4 @ drain write buffer ++ mov pc, lr ++#endif ++ + /* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address +diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c +index ee9bb363..806cc4f6 100644 +--- a/arch/arm/mm/context.c ++++ b/arch/arm/mm/context.c +@@ -18,30 +18,39 @@ + + static DEFINE_RAW_SPINLOCK(cpu_asid_lock); + unsigned int cpu_last_asid = ASID_FIRST_VERSION; +-#ifdef CONFIG_SMP +-DEFINE_PER_CPU(struct mm_struct *, current_mm); +-#endif + + #ifdef CONFIG_ARM_LPAE +-#define cpu_set_asid(asid) { \ +- unsigned long ttbl, ttbh; \ +- asm volatile( \ +- " mrrc p15, 0, %0, %1, c2 @ read TTBR0\n" \ +- " mov %1, %2, lsl #(48 - 32) @ set ASID\n" \ +- " mcrr p15, 0, %0, %1, c2 @ set TTBR0\n" \ +- : "=&r" (ttbl), "=&r" (ttbh) \ +- : "r" (asid & ~ASID_MASK)); \ ++void cpu_set_reserved_ttbr0(void) ++{ ++ unsigned long ttbl = __pa(swapper_pg_dir); ++ unsigned long ttbh = 0; ++ ++ /* ++ * Set TTBR0 to swapper_pg_dir which contains only global entries. The ++ * ASID is set to 0. ++ */ ++ asm volatile( ++ " mcrr p15, 0, %0, %1, c2 @ set TTBR0\n" ++ : ++ : "r" (ttbl), "r" (ttbh)); ++ isb(); + } + #else +-#define cpu_set_asid(asid) \ +- asm(" mcr p15, 0, %0, c13, c0, 1\n" : : "r" (asid)) ++void cpu_set_reserved_ttbr0(void) ++{ ++ u32 ttb; ++ /* Copy TTBR1 into TTBR0 */ ++ asm volatile( ++ " mrc p15, 0, %0, c2, c0, 1 @ read TTBR1\n" ++ " mcr p15, 0, %0, c2, c0, 0 @ set TTBR0\n" ++ : "=r" (ttb)); ++ isb(); ++} + #endif + + /* + * We fork()ed a process, and we need a new context for the child +- * to run in. We reserve version 0 for initial tasks so we will +- * always allocate an ASID. The ASID 0 is reserved for the TTBR +- * register changing sequence. ++ * to run in. + */ + void __init_new_context(struct task_struct *tsk, struct mm_struct *mm) + { +@@ -51,9 +60,7 @@ void __init_new_context(struct task_struct *tsk, struct mm_struct *mm) + + static void flush_context(void) + { +- /* set the reserved ASID before flushing the TLB */ +- cpu_set_asid(0); +- isb(); ++ cpu_set_reserved_ttbr0(); + local_flush_tlb_all(); + if (icache_is_vivt_asid_tagged()) { + __flush_icache_all(); +@@ -98,14 +105,7 @@ static void reset_context(void *info) + { + unsigned int asid; + unsigned int cpu = smp_processor_id(); +- struct mm_struct *mm = per_cpu(current_mm, cpu); +- +- /* +- * Check if a current_mm was set on this CPU as it might still +- * be in the early booting stages and using the reserved ASID. +- */ +- if (!mm) +- return; ++ struct mm_struct *mm = current->active_mm; + + smp_rmb(); + asid = cpu_last_asid + cpu + 1; +@@ -114,8 +114,7 @@ static void reset_context(void *info) + set_mm_context(mm, asid); + + /* set the new ASID */ +- cpu_set_asid(mm->context.id); +- isb(); ++ cpu_switch_mm(mm->pgd, mm); + } + + #else +diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c +index 5df54924..a2cf87d7 100644 +--- a/arch/arm/mm/mmu.c ++++ b/arch/arm/mm/mmu.c +@@ -563,11 +563,25 @@ static void __init *early_alloc(unsigned long sz) + return early_alloc_aligned(sz, sz); + } + +-static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, unsigned long prot) ++static pte_t * __init early_pte_alloc(pmd_t *pmd) ++{ ++ if (pmd_none(*pmd) || pmd_bad(*pmd)) ++ return early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE); ++ return pmd_page_vaddr(*pmd); ++} ++ ++static void __init early_pte_install(pmd_t *pmd, pte_t *pte, unsigned long prot) ++{ ++ __pmd_populate(pmd, __pa(pte), prot); ++ BUG_ON(pmd_bad(*pmd)); ++} ++ ++static pte_t * __init early_pte_alloc_and_install(pmd_t *pmd, ++ unsigned long addr, unsigned long prot) + { + if (pmd_none(*pmd)) { +- pte_t *pte = early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE); +- __pmd_populate(pmd, __pa(pte), prot); ++ pte_t *pte = early_pte_alloc(pmd); ++ early_pte_install(pmd, pte, prot); + } + BUG_ON(pmd_bad(*pmd)); + return pte_offset_kernel(pmd, addr); +@@ -577,16 +591,23 @@ static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr, + unsigned long end, unsigned long pfn, + const struct mem_type *type) + { +- pte_t *pte = early_pte_alloc(pmd, addr, type->prot_l1); ++ pte_t *start_pte = early_pte_alloc(pmd); ++ pte_t *pte = start_pte + pte_index(addr); ++ ++ /* If replacing a section mapping, the whole section must be replaced */ ++ BUG_ON(pmd_bad(*pmd) && ((addr | end) & ~PMD_MASK)); ++ + do { + set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0); + pfn++; + } while (pte++, addr += PAGE_SIZE, addr != end); ++ early_pte_install(pmd, start_pte, type->prot_l1); + } + + static void __init alloc_init_section(pud_t *pud, unsigned long addr, + unsigned long end, phys_addr_t phys, +- const struct mem_type *type) ++ const struct mem_type *type, ++ bool force_pages) + { + pmd_t *pmd = pmd_offset(pud, addr); + +@@ -596,7 +617,7 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr, + * L1 entries, whereas PGDs refer to a group of L1 entries making + * up one logical pointer to an L2 table. + */ +- if (((addr | end | phys) & ~SECTION_MASK) == 0) { ++ if (((addr | end | phys) & ~SECTION_MASK) == 0 && !force_pages) { + pmd_t *p = pmd; + + #ifndef CONFIG_ARM_LPAE +@@ -620,14 +641,15 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr, + } + + static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr, +- unsigned long end, unsigned long phys, const struct mem_type *type) ++ unsigned long end, unsigned long phys, const struct mem_type *type, ++ bool force_pages) + { + pud_t *pud = pud_offset(pgd, addr); + unsigned long next; + + do { + next = pud_addr_end(addr, end); +- alloc_init_section(pud, addr, next, phys, type); ++ alloc_init_section(pud, addr, next, phys, type, force_pages); + phys += next - addr; + } while (pud++, addr = next, addr != end); + } +@@ -701,7 +723,7 @@ static void __init create_36bit_mapping(struct map_desc *md, + * offsets, and we take full advantage of sections and + * supersections. + */ +-static void __init create_mapping(struct map_desc *md) ++static void __init create_mapping(struct map_desc *md, bool force_pages) + { + unsigned long addr, length, end; + phys_addr_t phys; +@@ -751,7 +773,7 @@ static void __init create_mapping(struct map_desc *md) + do { + unsigned long next = pgd_addr_end(addr, end); + +- alloc_init_pud(pgd, addr, next, phys, type); ++ alloc_init_pud(pgd, addr, next, phys, type, force_pages); + + phys += next - addr; + addr = next; +@@ -772,7 +794,7 @@ void __init iotable_init(struct map_desc *io_desc, int nr) + vm = early_alloc_aligned(sizeof(*vm) * nr, __alignof__(*vm)); + + for (md = io_desc; nr; md++, nr--) { +- create_mapping(md); ++ create_mapping(md, false); + vm->addr = (void *)(md->virtual & PAGE_MASK); + vm->size = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK)); + vm->phys_addr = __pfn_to_phys(md->pfn); +@@ -1124,12 +1146,12 @@ static void __init devicemaps_init(struct machine_desc *mdesc) + map.virtual = 0xffff0000; + map.length = PAGE_SIZE; + map.type = MT_HIGH_VECTORS; +- create_mapping(&map); ++ create_mapping(&map, false); + + if (!vectors_high()) { + map.virtual = 0; + map.type = MT_LOW_VECTORS; +- create_mapping(&map); ++ create_mapping(&map, false); + } + + /* +@@ -1152,20 +1174,23 @@ static void __init devicemaps_init(struct machine_desc *mdesc) + static void __init kmap_init(void) + { + #ifdef CONFIG_HIGHMEM +- pkmap_page_table = early_pte_alloc(pmd_off_k(PKMAP_BASE), ++ pkmap_page_table = early_pte_alloc_and_install(pmd_off_k(PKMAP_BASE), + PKMAP_BASE, _PAGE_KERNEL_TABLE); + #endif + } + ++ + static void __init map_lowmem(void) + { + struct memblock_region *reg; ++ phys_addr_t start; ++ phys_addr_t end; ++ struct map_desc map; + + /* Map all the lowmem memory banks. */ + for_each_memblock(memory, reg) { +- phys_addr_t start = reg->base; +- phys_addr_t end = start + reg->size; +- struct map_desc map; ++ start = reg->base; ++ end = start + reg->size; + + if (end > lowmem_limit) + end = lowmem_limit; +@@ -1177,8 +1202,20 @@ static void __init map_lowmem(void) + map.length = end - start; + map.type = MT_MEMORY; + +- create_mapping(&map); ++ create_mapping(&map, false); + } ++ ++#ifdef CONFIG_DEBUG_RODATA ++ start = __pa(_stext) & PMD_MASK; ++ end = ALIGN(__pa(__end_rodata), PMD_SIZE); ++ ++ map.pfn = __phys_to_pfn(start); ++ map.virtual = __phys_to_virt(start); ++ map.length = end - start; ++ map.type = MT_MEMORY; ++ ++ create_mapping(&map, true); ++#endif + } + + /* +diff --git a/arch/arm/mm/proc-v7-2level.S b/arch/arm/mm/proc-v7-2level.S +index 3a4b3e7b..42ac069c 100644 +--- a/arch/arm/mm/proc-v7-2level.S ++++ b/arch/arm/mm/proc-v7-2level.S +@@ -46,18 +46,13 @@ ENTRY(cpu_v7_switch_mm) + #ifdef CONFIG_ARM_ERRATA_430973 + mcr p15, 0, r2, c7, c5, 6 @ flush BTAC/BTB + #endif +-#ifdef CONFIG_ARM_ERRATA_754322 +- dsb +-#endif +- mcr p15, 0, r2, c13, c0, 1 @ set reserved context ID +- isb +-1: mcr p15, 0, r0, c2, c0, 0 @ set TTB 0 +- isb + #ifdef CONFIG_ARM_ERRATA_754322 + dsb + #endif + mcr p15, 0, r1, c13, c0, 1 @ set context ID + isb ++ mcr p15, 0, r0, c2, c0, 0 @ set TTB 0 ++ isb + #endif + mov pc, lr + ENDPROC(cpu_v7_switch_mm) +diff --git a/arch/arm/mm/rodata.c b/arch/arm/mm/rodata.c +new file mode 100644 +index 00000000..9a8eb841 +--- /dev/null ++++ b/arch/arm/mm/rodata.c +@@ -0,0 +1,159 @@ ++/* ++ * linux/arch/arm/mm/rodata.c ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * Author: Colin Cross ++ * ++ * Based on x86 implementation in arch/x86/mm/init_32.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "mm.h" ++ ++static int kernel_set_to_readonly __read_mostly; ++ ++#ifdef CONFIG_DEBUG_RODATA_TEST ++static const int rodata_test_data = 0xC3; ++ ++static noinline void rodata_test(void) ++{ ++ int result; ++ ++ pr_info("%s: attempting to write to read-only section:\n", __func__); ++ ++ if (*(volatile int *)&rodata_test_data != 0xC3) { ++ pr_err("read only data changed before test\n"); ++ return; ++ } ++ ++ /* ++ * Attempt to to write to rodata_test_data, trapping the expected ++ * data abort. If the trap executed, result will be 1. If it didn't, ++ * result will be 0xFF. ++ */ ++ asm volatile( ++ "0: str %[zero], [%[rodata_test_data]]\n" ++ " mov %[result], #0xFF\n" ++ " b 2f\n" ++ "1: mov %[result], #1\n" ++ "2:\n" ++ ++ /* Exception fixup - if store at label 0 faults, jumps to 1 */ ++ ".pushsection __ex_table, \"a\"\n" ++ " .long 0b, 1b\n" ++ ".popsection\n" ++ ++ : [result] "=r" (result) ++ : [rodata_test_data] "r" (&rodata_test_data), [zero] "r" (0) ++ : "memory" ++ ); ++ ++ if (result == 1) ++ pr_info("write to read-only section trapped, success\n"); ++ else ++ pr_err("write to read-only section NOT trapped, test failed\n"); ++ ++ if (*(volatile int *)&rodata_test_data != 0xC3) ++ pr_err("read only data changed during write\n"); ++} ++#else ++static inline void rodata_test(void) { } ++#endif ++ ++static int set_page_attributes(unsigned long virt, int numpages, ++ pte_t (*f)(pte_t)) ++{ ++ pmd_t *pmd; ++ pte_t *pte; ++ unsigned long start = virt; ++ unsigned long end = virt + (numpages << PAGE_SHIFT); ++ unsigned long pmd_end; ++ ++ while (virt < end) { ++ pmd = pmd_off_k(virt); ++ pmd_end = min(ALIGN(virt + 1, PMD_SIZE), end); ++ ++ if ((pmd_val(*pmd) & PMD_TYPE_MASK) != PMD_TYPE_TABLE) { ++ pr_err("%s: pmd %p=%08lx for %08lx not page table\n", ++ __func__, pmd, pmd_val(*pmd), virt); ++ virt = pmd_end; ++ continue; ++ } ++ ++ while (virt < pmd_end) { ++ pte = pte_offset_kernel(pmd, virt); ++ set_pte_ext(pte, f(*pte), 0); ++ virt += PAGE_SIZE; ++ } ++ } ++ ++ flush_tlb_kernel_range(start, end); ++ ++ return 0; ++} ++ ++int set_memory_ro(unsigned long virt, int numpages) ++{ ++ return set_page_attributes(virt, numpages, pte_wrprotect); ++} ++EXPORT_SYMBOL(set_memory_ro); ++ ++int set_memory_rw(unsigned long virt, int numpages) ++{ ++ return set_page_attributes(virt, numpages, pte_mkwrite); ++} ++EXPORT_SYMBOL(set_memory_rw); ++ ++void set_kernel_text_rw(void) ++{ ++ unsigned long start = PAGE_ALIGN((unsigned long)_text); ++ unsigned long size = PAGE_ALIGN((unsigned long)__end_rodata) - start; ++ ++ if (!kernel_set_to_readonly) ++ return; ++ ++ pr_debug("Set kernel text: %lx - %lx to read-write\n", ++ start, start + size); ++ ++ set_memory_rw(start, size >> PAGE_SHIFT); ++} ++ ++void set_kernel_text_ro(void) ++{ ++ unsigned long start = PAGE_ALIGN((unsigned long)_text); ++ unsigned long size = PAGE_ALIGN((unsigned long)__end_rodata) - start; ++ ++ if (!kernel_set_to_readonly) ++ return; ++ ++ pr_info_once("Write protecting the kernel text section %lx - %lx\n", ++ start, start + size); ++ ++ pr_debug("Set kernel text: %lx - %lx to read only\n", ++ start, start + size); ++ ++ set_memory_ro(start, size >> PAGE_SHIFT); ++} ++ ++void mark_rodata_ro(void) ++{ ++ kernel_set_to_readonly = 1; ++ ++ set_kernel_text_ro(); ++ ++ rodata_test(); ++} +diff --git a/arch/arm/plat-anyka/Kconfig b/arch/arm/plat-anyka/Kconfig +new file mode 100644 +index 00000000..d5539923 +--- /dev/null ++++ b/arch/arm/plat-anyka/Kconfig +@@ -0,0 +1,15 @@ ++# arch/arm/plat-anyka/Kconfig ++# ++# Copyright 2012 Anaka Microelectronics ++# ++# Licensed under GPLv2 ++ ++config PLAT_ANYKA ++ bool ++ depends on ARCH_AK39 ++ default y ++ help ++ Base platform code for ak39xx chips common device ++ ++# low-level serial option nodes ++ +diff --git a/arch/arm/plat-anyka/Makefile b/arch/arm/plat-anyka/Makefile +new file mode 100644 +index 00000000..857eada7 +--- /dev/null ++++ b/arch/arm/plat-anyka/Makefile +@@ -0,0 +1,19 @@ ++# arch/arm/plat-anyka/Makefile ++# ++# Copyright 2012 Anyka Microelectronics ++# ++# Licensed under GPLv2 ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Core support for all Anyka SoCs ++ ++obj-y += l2.o ++obj-y += rtc.o ++obj-y += drv_module_lock.o ++obj-y += l2_exebuf.o ++obj-y += notify.o ++obj-y += reg.o +diff --git a/arch/arm/plat-anyka/drv_module_lock.c b/arch/arm/plat-anyka/drv_module_lock.c +new file mode 100755 +index 00000000..39528094 +--- /dev/null ++++ b/arch/arm/plat-anyka/drv_module_lock.c +@@ -0,0 +1,279 @@ ++ ++/** ++ * arch/arm/plat-anyka/drv_module_lock.c ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++//#define DRV_LOCK_DEBUG ++#ifdef DRV_LOCK_DEBUG ++#define PK(fmt...) printk(const char * fmt,...) ++#endif ++ ++#if defined CONFIG_ARCH_AK39 ++/* the param drv_shpin_lock[i].name indicate different lock */ ++struct ak_drv_module_lock drv_mod_lock[] = { ++ {DRV_MODULE_SDIO,AK_MODULE_LOCK_1, TYPE_LOCK_SEMAPHORE, 0, ePIN_AS_SDIO}, ++ {DRV_MODULE_SPI,AK_MODULE_LOCK_1, TYPE_LOCK_SEMAPHORE, 0, ePIN_AS_SPI1}, ++ ++}; ++#endif ++ ++static void *module_lock_array[AK_MODULE_COUNT] = { 0 }; ++ ++#define GET_DRV_LOCK_INFO(type, table, len, lock_name, lock) \ ++{ \ ++ int i; \ ++ type *drv_lock = table; \ ++ for (i = 0; i < len; i++) { \ ++ if (drv_lock[i].lock_name == lock_name) { \ ++ lock = &drv_lock[i]; \ ++ break; \ ++ } \ ++ } \ ++} ++ ++static void ak_acquire_lock(void **lock_array, ++ E_LOCK_NAME lock_name, E_LOCK_TYPE lock_type, unsigned long *flags) ++{ ++ unsigned long flag; ++ ++ switch(lock_type) { ++ case TYPE_LOCK_MUTEX: ++ mutex_lock((struct mutex *)lock_array[lock_name]); ++ break; ++ case TYPE_LOCK_SEMAPHORE: ++ down((struct semaphore *)lock_array[lock_name]); ++ break; ++ case TYPE_LOCK_SPINLOCK: ++ spin_lock((spinlock_t *)lock_array[lock_name]); ++ break; ++ case TYPE_LOCK_SPINLOCK_IRQ: ++ spin_lock_irqsave((spinlock_t *)lock_array[lock_name], flag); ++ *flags = flag; ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static void ak_release_lock(void **lock_array, ++ E_LOCK_NAME lock_name, E_LOCK_TYPE lock_type, unsigned long flags) ++{ ++ switch(lock_type) { ++ case TYPE_LOCK_MUTEX: ++ mutex_unlock((struct mutex *)lock_array[lock_name]); ++ break; ++ case TYPE_LOCK_SEMAPHORE: ++ up((struct semaphore *)lock_array[lock_name]); ++ break; ++ case TYPE_LOCK_SPINLOCK: ++ spin_unlock((spinlock_t *)lock_array[lock_name]); ++ break; ++ case TYPE_LOCK_SPINLOCK_IRQ: ++ spin_unlock_irqrestore((spinlock_t *)lock_array[lock_name], flags); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static int ak_module_lock(E_DRV_MODULE drv_mod_name, ++ struct ak_drv_module_lock *mod_lock, int len, void **lock_array) ++{ ++ struct ak_drv_module_lock *lock = NULL; ++ GET_DRV_LOCK_INFO(struct ak_drv_module_lock, mod_lock, len, drv_mod_name, lock); ++ if (lock == NULL) ++ return -1; ++ ++ ak_acquire_lock(lock_array, lock->lock_name, lock->lock_type, &lock->flags); ++ ak_group_config(lock->sharepin_cfg); ++ return 0; ++} ++ ++static void ak_module_unlock(E_DRV_MODULE drv_mod_name, ++ struct ak_drv_module_lock *mod_lock, int len, void **lock_array) ++{ ++ struct ak_drv_module_lock *lock = NULL; ++ GET_DRV_LOCK_INFO(struct ak_drv_module_lock, mod_lock, len, drv_mod_name, lock); ++ if (lock == NULL) ++ return; ++ ++ ak_release_lock(lock_array, lock->lock_name, lock->lock_type, lock->flags); ++} ++ ++int ak_drv_module_lock(E_DRV_MODULE drv_mod_name) ++{ ++ return ak_module_lock(drv_mod_name, drv_mod_lock, ++ ARRAY_SIZE(drv_mod_lock), module_lock_array); ++} ++EXPORT_SYMBOL(ak_drv_module_lock); ++ ++void ak_drv_module_unlock(E_DRV_MODULE drv_mod_name) ++{ ++ ak_module_unlock(drv_mod_name, drv_mod_lock, ++ ARRAY_SIZE(drv_mod_lock), module_lock_array); ++} ++EXPORT_SYMBOL(ak_drv_module_unlock); ++ ++ ++static void ak_init_lock_array(void **lock_array, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) ++ lock_array[i] = NULL; ++} ++ ++static void ak_init_lock(void **lock_array, E_LOCK_NAME lock_name, E_LOCK_TYPE lock_type) ++{ ++ if (lock_array[lock_name] == NULL) { ++ switch (lock_type) { ++ case TYPE_LOCK_MUTEX: ++ lock_array[lock_name] = kmalloc(sizeof(struct mutex), GFP_KERNEL); ++ mutex_init((struct mutex *)lock_array[lock_name]); ++ break; ++ case TYPE_LOCK_SEMAPHORE: ++ lock_array[lock_name] = kmalloc(sizeof(struct semaphore), GFP_KERNEL); ++ sema_init((struct semaphore *)lock_array[lock_name], 1); ++ break; ++ case TYPE_LOCK_SPINLOCK: ++ case TYPE_LOCK_SPINLOCK_IRQ: ++ lock_array[lock_name] = kmalloc(sizeof(spinlock_t), GFP_KERNEL); ++ spin_lock_init((spinlock_t *)lock_array[lock_name]); ++ break; ++ default: ++ BUG(); ++ } ++ } ++} ++ ++static int ak_init_module_locks(void) ++{ ++ int i, len; ++ ++ if ((len = ARRAY_SIZE(drv_mod_lock)) == 0) ++ return -1; ++ ++ ak_init_lock_array(module_lock_array, AK_MODULE_COUNT); ++ ++ for (i = 0; i < len; i++) { ++ ak_init_lock(module_lock_array, drv_mod_lock[i].lock_name, drv_mod_lock[i].lock_type); ++ drv_mod_lock[i].flags = 0; ++ } ++ ++ return 0; ++} ++ ++ ++/* ************* CONFIG GPIO' SHARE FUNC FOR MACHINE ************ */ ++struct ak_drv_sharepin_lock *shpin_lock = NULL; ++int shpin_lock_count = 0; ++void **shpin_lock_array = NULL; ++ ++/** ++ * ak_set_sharepin_lock_table - get machine sharepin lock table valid callback ++ */ ++void ak_set_sharepin_lock_table( ++ struct ak_drv_sharepin_lock *shpin_lock_table, ++ int count, void **lock_array) ++{ ++ shpin_lock = shpin_lock_table; ++ shpin_lock_count = count; ++ shpin_lock_array = lock_array; ++} ++EXPORT_SYMBOL(ak_set_sharepin_lock_table); ++ ++static int ak_sharepin_lock(E_DRV_MODULE drv_mod_name, ++ struct ak_drv_sharepin_lock *shpin_lock, int len, void **lock_array) ++{ ++ struct ak_drv_sharepin_lock *lock = NULL; ++ GET_DRV_LOCK_INFO(struct ak_drv_sharepin_lock, shpin_lock, len, drv_mod_name, lock); ++ if (lock == NULL) ++ return -1; ++ ++ ak_acquire_lock(lock_array, lock->lock_name, lock->lock_type, &lock->flags); ++ if (lock->config_share_pin != NULL) ++ lock->config_share_pin(); ++ return 0; ++} ++ ++static void ak_sharepin_unlock(E_DRV_MODULE drv_mod_name, ++ struct ak_drv_sharepin_lock *shpin_lock, int len, void **lock_array) ++{ ++ struct ak_drv_sharepin_lock *lock = NULL; ++ GET_DRV_LOCK_INFO(struct ak_drv_sharepin_lock, shpin_lock, len, drv_mod_name, lock); ++ if (lock == NULL) ++ return; ++ ++ ak_release_lock(lock_array, lock->lock_name, lock->lock_type, lock->flags); ++} ++ ++int ak_drv_sharepin_lock(E_DRV_MODULE drv_mod_name) ++{ ++ return ak_sharepin_lock(drv_mod_name, shpin_lock, ++ shpin_lock_count, shpin_lock_array); ++} ++EXPORT_SYMBOL(ak_drv_sharepin_lock); ++ ++void ak_drv_sharepin_unlock(E_DRV_MODULE drv_mod_name) ++{ ++ ak_sharepin_unlock(drv_mod_name, shpin_lock, ++ shpin_lock_count, shpin_lock_array); ++} ++EXPORT_SYMBOL(ak_drv_sharepin_unlock); ++ ++ ++/* Brief: Init sharepin locks for drivers module ++ * param: void ++ * ret: ++ * 0: if acquire lock successed; ++ * <0: indicate the Board or products being not sharepin for mutual drivers ++ */ ++static int ak_init_sharepin_locks(void) ++{ ++ int i; ++ ++ if (shpin_lock_count == 0) ++ return -1; ++ ++ ak_init_lock_array(shpin_lock_array, AK_MODULE_COUNT); ++ ++ for (i = 0; i < shpin_lock_count; i++) { ++ ak_init_lock(shpin_lock_array, shpin_lock[i].lock_name, shpin_lock[i].lock_type); ++ shpin_lock[i].flags = 0; ++ } ++ ++ return 0; ++} ++ ++ ++void ak_drv_module_protect(E_DRV_MODULE drv_mod_name) ++{ ++ ak_drv_module_lock(drv_mod_name); ++ ak_drv_sharepin_lock(drv_mod_name); ++} ++EXPORT_SYMBOL(ak_drv_module_protect); ++ ++void ak_drv_module_unprotect(E_DRV_MODULE drv_mod_name) ++{ ++ ak_drv_sharepin_unlock(drv_mod_name); ++ ak_drv_module_unlock(drv_mod_name); ++} ++EXPORT_SYMBOL(ak_drv_module_unprotect); ++ ++static int __init ak_init_module_lock(void) ++{ ++ printk("Anyka platform share gpio locks initialize.\n"); ++ ++ ak_init_module_locks(); ++ ak_init_sharepin_locks(); ++ return 0; ++} ++subsys_initcall(ak_init_module_lock); ++ +diff --git a/arch/arm/plat-anyka/include/mach/reg.h b/arch/arm/plat-anyka/include/mach/reg.h +new file mode 100644 +index 00000000..751117b6 +--- /dev/null ++++ b/arch/arm/plat-anyka/include/mach/reg.h +@@ -0,0 +1,16 @@ ++/* ++ * reg.h ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef _REG_H_ ++#define _REG_H_ ++ ++void sys_ctrl_reg_set(unsigned long reg_phy_addr, unsigned long reg_mask, unsigned long reg_val); ++ ++#endif /* _REG_H_ */ ++ +diff --git a/arch/arm/plat-anyka/include/mach/regs-l2.h b/arch/arm/plat-anyka/include/mach/regs-l2.h +new file mode 100644 +index 00000000..cc4dd35f +--- /dev/null ++++ b/arch/arm/plat-anyka/include/mach/regs-l2.h +@@ -0,0 +1,54 @@ ++ ++/* ++ * arch/arm/plat-anyka/include/mach/regs-l2.h ++ */ ++#ifndef __REGS_L2_H_ ++#define __REGS_L2_H_ ++ ++#include ++ ++#define vL2DMA_ADDRBUF0 REG_VA_ADDR(AK_VA_L2CTRL, 0x00) ++#define vL2DMA_CONBUF0 REG_VA_ADDR(AK_VA_L2CTRL, 0x40) ++ ++#define L2_DMA_ADDR (AK_VA_L2CTRL + 0x00) ++#define L2_DMA_CON (AK_VA_L2CTRL + 0x40) ++#define L2_DMAREQ (AK_VA_L2CTRL + 0x80) ++#define L2_FRACDMAADDR (AK_VA_L2CTRL + 0x84) ++#define L2_CONBUF0_7 (AK_VA_L2CTRL + 0x88) ++#define L2_CONBUF8_15 (AK_VA_L2CTRL + 0x8C) ++#define L2_BUFASSIGN1 (AK_VA_L2CTRL + 0x90) ++#define L2_BUFASSIGN2 (AK_VA_L2CTRL + 0x94) ++#define L2_LDMACON (AK_VA_L2CTRL + 0x98) ++#define L2_BUFINTEN (AK_VA_L2CTRL + 0x9C) ++#define L2_BUFSTAT1 (AK_VA_L2CTRL + 0xA0) ++#define L2_BUFSTAT2 (AK_VA_L2CTRL + 0xA8) ++ ++/*************************** L2 MEMORY CONTROL *********************/ ++#define rL2_DMAREQ REG_VA_VAL(AK_VA_L2CTRL, 0x80) ++#define rL2_FRACDMAADDR REG_VA_VAL(AK_VA_L2CTRL, 0x84) ++#define rL2_CONBUF0_7 REG_VA_VAL(AK_VA_L2CTRL, 0x88) ++#define rL2_CONBUF8_15 REG_VA_VAL(AK_VA_L2CTRL, 0x8C) ++#define rL2_BUFASSIGN1 REG_VA_VAL(AK_VA_L2CTRL, 0x90) ++#define rL2_BUFASSIGN2 REG_VA_VAL(AK_VA_L2CTRL, 0x94) ++#define rL2_LDMACON REG_VA_VAL(AK_VA_L2CTRL, 0x98) ++#define rL2_BUFINTEN REG_VA_VAL(AK_VA_L2CTRL, 0x9C) ++#define rL2_BUFSTAT1 REG_VA_VAL(AK_VA_L2CTRL, 0xA0) ++#define rL2_BUFSTAT2 REG_VA_VAL(AK_VA_L2CTRL, 0xA8) ++ ++/*************************** L2 MEMORY BUFFER **********************/ ++#define rL2_ADDRBUF0 REG_VA_VAL(AK_VA_L2MEM, 0x0000) ++#define rL2_ADDRBUF1 REG_VA_VAL(AK_VA_L2MEM, 0x0200) ++#define rL2_ADDRBUF2 REG_VA_VAL(AK_VA_L2MEM, 0x0400) ++#define rL2_ADDRBUF3 REG_VA_VAL(AK_VA_L2MEM, 0x0600) ++#define rL2_ADDRBUF4 REG_VA_VAL(AK_VA_L2MEM, 0x0800) ++#define rL2_ADDRBUF5 REG_VA_VAL(AK_VA_L2MEM, 0x0A00) ++#define rL2_ADDRBUF6 REG_VA_VAL(AK_VA_L2MEM, 0x0C00) ++#define rL2_ADDRBUF7 REG_VA_VAL(AK_VA_L2MEM, 0x0E00) ++ ++#define rL2_ADDRTX1_BUF8 REG_VA_VAL(AK_VA_L2MEM, 0x1000) ++#define rL2_ADDRRX1_BUF9 REG_VA_VAL(AK_VA_L2MEM, 0x1080) ++#define rL2_ADDRTX2_BUF10 REG_VA_VAL(AK_VA_L2MEM, 0x1100) ++#define rL2_ADDRRX2_BUF11 REG_VA_VAL(AK_VA_L2MEM, 0x1180) ++ ++#endif /* __REGS_L2_H_ */ ++ +diff --git a/arch/arm/plat-anyka/include/plat/gpio_keys.h b/arch/arm/plat-anyka/include/plat/gpio_keys.h +new file mode 100644 +index 00000000..4f01565c +--- /dev/null ++++ b/arch/arm/plat-anyka/include/plat/gpio_keys.h +@@ -0,0 +1,27 @@ ++#ifndef __GPIO_KEYS_H_ ++#define __GPIO_KEYS_H_ ++ ++struct ak_gpio_keys_button { ++ /* Configuration parameters */ ++ int code; /* input event code (KEY_*, SW_*) */ ++ int gpio; ++ int active_low; ++ char *desc; ++ int type; /* input event type (EV_KEY, EV_SW) */ ++ int wakeup; /* configure the button as a wake-up source */ ++ int debounce_interval; /* debounce ticks interval in msecs */ ++ ++ char pulldown; //pulldown function flag ++ char pullup; //pullup function flag ++ char dir; //direction input/output ++ char int_pol; //interrupt polarity ++}; ++ ++struct ak_gpio_keys_platform_data { ++ struct ak_gpio_keys_button *buttons; ++ int nbuttons; ++ unsigned int rep:1; /* enable input subsystem auto repeat */ ++}; ++ ++ ++#endif /* __GPIO_KEYS_H_ */ +diff --git a/arch/arm/plat-anyka/include/plat/l2.h b/arch/arm/plat-anyka/include/plat/l2.h +new file mode 100755 +index 00000000..c559aa7e +--- /dev/null ++++ b/arch/arm/plat-anyka/include/plat/l2.h +@@ -0,0 +1,374 @@ ++/* ++ * linux/arch/arm/plat-anyka/include/plat/l2.h ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef __ASM_ARCH_L2_H ++#define __ASM_ARCH_L2_H ++ ++#include ++#include ++ ++#define L2_DEBUG 1 ++//#undef L2_DEBUG ++ ++#undef REG32 ++#define REG32(_reg) (*(volatile unsigned long *)(_reg)) ++ ++#undef REG16 ++#define REG16(_reg) (*(volatile unsigned short *)(_reg)) ++ ++/* ++ * ANYKA L2 Control Register List and Bit map definition ++ * TODO: Add all register bit maps and move all to map.h in the future. ++*/ ++#define L2_DMA_REQ_BUF_START 24 ++#define L2_DMA_REQ_BUF_REQ_MASK (0xFFFF << 16) ++#define L2_DMA_REQ_UART_BUF_REQ_START 16 ++#define L2_DMA_REQ_FRAC_DMA_LEN_START 10 ++#define L2_DMA_REQ_FRAC_DMA_LEN_MASK (0x3F << L2_DMA_REQ_FRAC_DMA_LEN_START) ++#define L2_DMA_REQ_FRAC_DMA_REQ (1 << 9) ++#define L2_DMA_REQ_FRAC_DMA_DIR_WR (1 << 8) ++#define L2_DMA_REQ_FRAC_DMA_L2_ADDR_START 1 ++#define L2_DMA_REQ_FRAC_DMA_L2_ADDR_MASK (0x7F << L2_DMA_REQ_FRAC_DMA_L2_ADDR_START) ++#define L2_DMA_REQ_EN (1 << 0) ++ ++#define L2_FRAC_DMA_AHB_FLAG_EN (1 << 29) ++#define L2_FRAC_DMA_LDMA_FLAG_EN (1 << 28) ++ ++#define L2_FRAC_DMA_LOW_ADDR_MASK (0xFFFFFFF << 0) ++ ++#define L2_COMMON_BUF_CFG_BUF7_CLR (1 << 31) ++#define L2_COMMON_BUF_CFG_BUF6_CLR (1 << 30) ++#define L2_COMMON_BUF_CFG_BUF5_CLR (1 << 29) ++#define L2_COMMON_BUF_CFG_BUF4_CLR (1 << 28) ++#define L2_COMMON_BUF_CFG_BUF3_CLR (1 << 27) ++#define L2_COMMON_BUF_CFG_BUF2_CLR (1 << 26) ++#define L2_COMMON_BUF_CFG_BUF1_CLR (1 << 25) ++#define L2_COMMON_BUF_CFG_BUF0_CLR (1 << 24) ++#define L2_COMMON_BUF_CFG_BUF_CLR_START 24 ++#define L2_COMMON_BUF_CFG_BUF0_7_CLR_MASK (0xFF << L2_COMMON_BUF_CFG_BUF_START) ++#define L2_COMMON_BUF_CFG_BUF_VLD_START 16 ++#define L2_COMMON_BUF_CFG_BUF0_7_VLD_MASK (0xFF << L2_COMMON_BUF_CFG_BUF_VLD_START ++#define L2_COMMON_BUF_CFG_BUF_DMA_VLD_START 0 ++#define L2_COMMON_BUF_CFG_BUF_DIR_START 8 ++ ++#define L2_UART_BUF_CFG_BUF_START 16 ++ ++#define L2_UART_BUF_CFG_UART_EN_MASK (0xF << 28) ++#define L2_UART_BUF_CFG_UART_CLR_MASK (0xFF << 16) ++#define L2_UART_BUF_CFG_CPU_BUF_SEL_EN (1 << 3) ++#define L2_UART_BUF_CFG_CPU_BUF_NUM_START 4 ++#define L2_UART_BUF_CFG_CPU_BUF_NUM_MASK (0xF << L2_UART_BUF_CFG_CPU_BUF_NUM_START) ++#define L2_UART_BUF_CFG_CPU_BUF_SEL_START 0 ++#define L2_UART_BUF_CFG_CPU_BUF_SEL_MASK (0x7 << L2_UART_BUF_CFG_CPU_BUF_SEL_START) ++ ++#define L2_DMA_INTR_ENABLE_BUF_START 9 ++#define L2_DMA_INTR_ENABLE_UART_BUF_START 1 ++#define L2_DMA_INTR_ENABLE_FRAC_INTR_EN (1 << 0) ++ ++/* ++ * ANYKA L2 buffer size(buffer 0 to buffer 7), all 512Bytes. ++ * NOTE: L2 buffer 8 to 15 dedicate to UART 1 to 4 & USB 2.0 Controller, ++ * and the corresponding L2 buffer size could be 64, 128 and 256 Bytes. ++ * See ANYKA Programmer's Guide for details (ANYKA preferably). ++ */ ++#define L2_BUFFER_SIZE 512 ++ ++/* ++ * ANYKA L2 DMA waiting times in loop ++ * TODO: Must be change to waiting time based on CPU frequency when frequency APIs are done. ++ */ ++#define L2_MAX_DMA_WAIT_TIME 50 * 1000000UL ++ ++/* ++ * ANYKA DMA size in bytes per transfer (Always 64Bytes) ++ * L2 DMA transfer follows this definition. ++ */ ++#define DMA_ONE_SHOT_LEN 64 ++ ++/* ++ * ANYKA L2 Buffer Status Multiply Ratio(64) ++ * Buffer Status Register 1 & 2: The number of data = Bufn_sta * L2_BUF_STATUS_MULTIPLY_RATIO ++ */ ++#define L2_BUF_STATUS_MULTIPLY_RATIO 64 ++ ++/* ++ * L2 Buffer ID Assignment: ++ * 0 - 7: L2 common buffer, could be used by different peripherals ++ * 8 - 15: Dedicate L2 buffer for UART ++ * 16 - 18: Dedicate L2 buffer for USB ++ */ ++#define L2_COMMON_BUFFER_NUM 8 ++#define L2_UART_BUFFER_NUM 4 ++ ++#define L2_UART_BUFFER_INDEX L2_COMMON_BUFFER_NUM ++#define L2_USB_HOST_BUFFER_INDEX (L2_UART_BUFFER_INDEX + L2_UART_BUFFER_NUM) ++ ++ ++#define L2_COMMON_BUFFER_LEN 512 ++#define L2_UART_BUFFER_LEN 128 ++ ++#define L2_COMMON_BUFFER_OFFSET 0 ++#define L2_UART_BUFFER_OFFSET (L2_COMMON_BUFFER_LEN * L2_COMMON_BUFFER_NUM) ++ ++/* ++ * L2 controller working clock define ++ */ ++#define L2_CLOCK_EN (0x1 << 9) ++#define L2_CLOCK_REG (AK_VA_SYSCTRL + 0x1C) ++ ++/* ++ * AK39XX L2 device list which may use L2 memory. ++ * The devices are defined according to L2 Buffer Assignement 1 & 2 register bit sequence. ++ */ ++typedef enum { ++ ADDR_USB_EP2 = 0, /* USB 2.0 HS Controller: Endpoint 2 */ ++ ADDR_USB_EP3, /* USB 2.0 HS Controller: Endpoint 3 */ ++ ADDR_USB_EP4, /* USB 2.0 HS Controller: Endpoint 4 */ ++ ADDR_RESERVED, /* Reserved */ ++ ADDR_MMC_SD, /* MMC/SD interface */ ++ ADDR_SDIO, /* SDIO interface */ ++ ADDR_SPI1_RX = 7, /* Rx buffer of SPI1 Controller */ ++ ADDR_SPI1_TX, /* Tx buffer of SPI1 Controller */ ++ ADDR_DAC, /* DAC control module */ ++ ADDR_SPI2_RX, /* Rx buffer of SPI2 Controller */ ++ ADDR_SPI2_TX, /* Tx buffer of SPI2 Controller */ ++ ADDR_GPS, /* GPS interface */ ++ ADDR_PCM_TX, /* Tx buffer of PCM Controller */ ++ ADDR_ADC, /* ADC2 */ ++ ADDR_USB_EP5, /* USB 2.0 HS Controller: Endpoint 5 */ ++} l2_device_t; ++ ++ ++#define BUF_NULL 0xFF /* Invalid L2 buffer ID */ ++#define L2_UART_BUF_START_ID L2_COMMON_BUFFER_NUM /* UART used buffer ID from 8 */ ++ ++/* ++ * Maximum L2 DMA status value (The value in CPU-Controlled Buffer and Buffer8 ~ Buffer15 Configuration Register) ++ * The maximum DMA transfer bytes = MAX_L2_DMA_STATUS_VALUE * 64 ++ */ ++#define MAX_L2_DMA_STATUS_VALUE 0x8 ++#define MAX_L2_BUFFER_USED_TIMES 0xFFFF ++ ++/* ++ * Data transfer direction between L2 memory and external RAM ++ */ ++typedef enum { ++ BUF2MEM = 0, /* Data transfer from L2 buffer to external RAM */ ++ MEM2BUF, /* Data transfer from external RAM to L2 buffer */ ++} l2_dma_transfer_direction_t; ++ ++/* ++ * Callback function when L2 DMA/fraction DMA interrupt handler is done ++ */ ++typedef void (*l2_callback_func_t)(unsigned long data); ++ ++/* ++ * L2 buffer status ++ */ ++typedef enum { ++ L2_STAT_USED = 0, /* Current L2 buffer is used by some device */ ++ L2_STAT_IDLE, /* Current L2 buffer is NOT used by some device, thus could be allocated */ ++} l2_buffer_status_t; ++ ++/* ++ * L2 buffer information ++ */ ++typedef struct { ++ u8 id; /* L2 buffer ID (0~17) */ ++ l2_buffer_status_t usable; /* L2 buffer status(used or idle) */ ++ u16 used_time; /* Counter on L2 buffer used times */ ++} l2_buffer_info_t; ++ ++/* ++ * Information on device which use L2 memory ++ */ ++typedef struct { ++ l2_device_t device; /* Device ID */ ++ u8 id; /* TODO: Remove id in the future since array index already represent buffer id */ ++} l2_device_info_t; ++ ++/* ++ * L2 DMA usage information (including DMA/fraction DMA/external RAM/Callback function) ++ */ ++typedef struct { ++ bool dma_start; ++ bool intr_enable; ++ l2_dma_transfer_direction_t direction; ++ void *dma_addr; ++ u32 dma_op_times; ++ bool need_frac; ++ bool dma_frac_start; ++ void *dma_frac_addr; ++ u32 dma_frac_offset; ++ u32 dma_frac_data_len; ++ l2_callback_func_t callback_func; ++ unsigned long data; ++} l2_dma_info_t; ++ ++/** ++ * l2_init - Initialize linux kernel L2 memory support ++ */ ++void __init l2_init(void); ++ ++/** ++ * l2_alloc - Allocate a common L2 buffer for given device ++ * @device: Device ID which need common L2 buffer ++ * Return L2 buffer ID (0 ~ 7) ++ * ++ * Only common L2 buffers(ID 0 ~ 7) could be allocated by l2_alloc. ++ * Other L2 buffers (UART/USB used) is handled by corresponding devices directly. ++ */ ++u8 l2_alloc(l2_device_t device); ++ ++/** ++ * l2_alloc_nowait - Allocate a common L2 buffer for given device ++ * @device: Device ID which need common L2 buffer ++ * Return L2 buffer ID (0 ~ 7) ++ * ++ * Only common L2 buffers(ID 0 ~ 7) could be allocated by l2_alloc_nowait. ++ * Other L2 buffers (UART/USB used) is handled by corresponding devices directly. ++ * return BUF_NULL immediately if no buf was alloc . ++ */ ++u8 l2_alloc_nowait(l2_device_t device); ++ ++/** ++ * l2_free - Free L2 common buffer for given device ++ * @device: Device ID which need common L2 buffer ++ * Return L2 buffer ID (0 ~ 7) ++ * ++ * Only common L2 buffers(ID 0 ~ 7) could be allocated by l2_alloc. ++ * Other L2 buffers (UART/USB used) is handled by corresponding devices directly. ++ * NOTE: Return the previous L2 buffer ID if a L2 buffer has been allocated to the device. ++ * This means one device could get only one L2 buffer maximum. ++ */ ++void l2_free(l2_device_t device); ++ ++/** ++ * l2_set_dma_callback - Set callback function when L2 DMA/fraction DMA interrupt handler is done ++ * @id: L2 buffer ID ++ * @func: Callback function ++ * @data: Arguments used by callback function ++ * Return true(Always) ++ * ++ * NOTE: Caller MUST guarantee that L2 buffer ID is valid. And since the callback function is called ++ * in interrupt handler, it MUST NOT call any functions which may sleep. ++ */ ++bool l2_set_dma_callback(u8 id, l2_callback_func_t func, unsigned long data); ++ ++/** ++ * l2_combuf_dma - Start data tranferring between memory and l2 common buffer in DMA mode ++ * @ram_addr: External RAM address(Physical) ++ * @id: L2 buffer ID involved in DMA transfer ++ * @bytes: Data transfer size ++ * @direction: Data transfer direction between L2 memory and external RAM ++ * @intr_enable: Open interrupt for this L2 buffer or not ++ */ ++void l2_combuf_dma(unsigned long ram_addr, u8 id, unsigned int bytes, l2_dma_transfer_direction_t direction, bool intr_enable); ++ ++/** ++ * l2_combuf_wait_dma_finish - Wait for L2 DMA finish ++ * @id: L2 buffer ID involved in DMA transfer ++ * Return true: DMA transfer finished successfully. ++ * false: DMA transfer failed. ++ * NOTE: DMA transfer is started by l2_combuf_dma. ++ */ ++bool l2_combuf_wait_dma_finish(u8 id); ++ ++/** ++ * l2_combuf_cpu - Transfer data between memory and l2 common buffer in CPU mode ++ * @ram_addr: External RAM address(Physical) ++ * @id: L2 buffer ID ++ * @bytes: Data transfer size ++ * @direction: Data transfer direction between L2 memory and external RAM ++ * ++ * NOTE: According to XuChang, if one transfer data from Peripheral --> L2 Buffer --> RAM, ++ * special care need to be taken when data size is NOT multiple of 64Bytes. ++ * Pheripheral driver must check hardware signals to confirm data has been transfer from ++ * peripheral to L2 buffer since L2 do NOT provide some mechanism to confirm data has ++ * been in L2 Buffer. Driver can and only can call l2_combuf_cpu() to copy data from L2 ++ * Buffer --> RAM after checking hardware signals. ++ * As to 64Bytes * n size data, L2 could check Buffer Status Status Counter to confirm that ++ * Data has been transfer from peripheral to L2 buffer, so no hardware signals checking needed. ++ */ ++void l2_combuf_cpu(unsigned long ram_addr, u8 id, unsigned int bytes, l2_dma_transfer_direction_t direction); ++ ++/** ++ * l2_get_status - Get L2 buffer status ++ * @id: L2 buffer ID ++ */ ++u8 l2_get_status(u8 id); ++ ++/** ++ * l2_clr_status - Clear L2 buffer status ++ * @id: L2 buffer ID ++ */ ++void l2_clr_status(u8 id); ++ ++/** ++ * l2_set_status - Clear L2 buffer status ++ * @id: L2 buffer ID ++ * @status: Status to be set (0 ~ 8) ++ */ ++void l2_set_status(u8 id, u8 status); ++ ++static inline void l2_enable_clock(int enable) ++{ ++ if (enable) ++ REG32(L2_CLOCK_REG) &= ~L2_CLOCK_EN; ++ else ++ REG32(L2_CLOCK_REG) |= L2_CLOCK_EN; ++ return; ++} ++ ++#ifdef L2_DEBUG ++#define L2_PRINT_FUNCLINES() do { printk("%s(): line: %d\n", __func__, __LINE__); } while (0) ++ ++static inline void l2_dump_registers(void) ++{ ++ printk("ANYKA L2 Register Dumping Begin:\n"); ++ ++ printk(" rL2_DMAREQ(C080) = 0x%08X, rL2_FRACDMAADDR(C084) = 0x%0X\n", ++ (unsigned int)rL2_DMAREQ, (unsigned int)rL2_FRACDMAADDR); ++ printk(" rL2_CONBUF0_7(C088) = 0x%08X, rL2_CONBUF8_15(C08C) = 0x%0X\n", ++ (unsigned int)rL2_CONBUF0_7, (unsigned int)rL2_CONBUF8_15); ++ printk(" rL2_BUFASSIGN1(C090) = 0x%08X, rL2_BUFINTEN(C09C) = 0x%0X\n", ++ (unsigned int)rL2_BUFASSIGN1, (unsigned int)rL2_BUFINTEN); ++ printk(" rL2_BUFSTAT1(C0A0) = 0x%08X, rL2_BUFSTAT2(C0A8) = 0x%0X\n", ++ (unsigned int)rL2_BUFSTAT1, (unsigned int)rL2_BUFSTAT2); ++ ++ printk("ANYKA L2 Register Dumping End.\n"); ++} ++ ++static inline void l2_print_array(const char *name, unsigned char *array, int len) ++{ ++ int i; ++ ++ printk("%s[%d] = {\n ", name, len); ++ for (i = 0; i < len; i++) { ++ printk(" 0x%02X,", array[i]); ++ if (i % 16 == 15) ++ printk("\n "); ++ } ++ printk("};\n"); ++ ++} ++ ++#else ++#define L2_PRINT_FUNCLINES() do { } while (0) ++ ++static inline void l2_dump_registers(void) ++{ ++} ++static inline void l2_print_array(const char *name, unsigned int *array, int len) ++{ ++} ++#endif ++ ++#endif /* __ASM_ARCH_L2_H */ ++ +diff --git a/arch/arm/plat-anyka/include/plat/l2_exebuf.h b/arch/arm/plat-anyka/include/plat/l2_exebuf.h +new file mode 100755 +index 00000000..7fd4e248 +--- /dev/null ++++ b/arch/arm/plat-anyka/include/plat/l2_exebuf.h +@@ -0,0 +1,157 @@ ++#ifndef __L2_EXEBUF ++#define __L2_EXEBUF ++ ++ ++#include ++#include ++#include ++ ++ ++#define CPU_CHIP_ID (AK_VA_SYSCTRL + 0x00) ++#define CLOCK_DIV_REG (AK_VA_SYSCTRL + 0x04) ++#define CLOCK_CTRL_REG (AK_VA_SYSCTRL + 0x0C) ++#define PHY_CLOCK_CTRL_REG (AK_PA_SYSCTRL + 0x0C) ++#define PHY_CLOCK_DIV_REG (AK_PA_SYSCTRL + 0x04) ++#define PHY_RAM_CFG_REG4 (AK_PA_REGRAM + 0x0C) ++ ++#define RAM_CFG_REG1 (AK_VA_REGRAM + 0x00) ++#define RAM_CFG_REG2 (AK_VA_REGRAM + 0x04) ++#define RAM_CFG_REG3 (AK_VA_REGRAM + 0x08) ++#define RAM_CFG_REG4 (AK_VA_REGRAM + 0x0C) ++#define RAM_CPU_CMD (AK_VA_REGRAM + 0x10) ++#define PHY_RAM_CFG_REG1 (AK_PA_REGRAM + 0x00) ++#define PHY_RAM_CFG_REG2 (AK_PA_REGRAM + 0x04) ++#define PHY_RAM_CFG_REG3 (AK_PA_REGRAM + 0x08) ++#define PHY_RAM_CFG_REG4 (AK_PA_REGRAM + 0x0C) ++#define PHY_RAM_CPU_CMD (AK_PA_REGRAM + 0x10) ++ ++#define FIFO_R_EMPTY (1 << 16) ++#define FIFO_CMD_EMPTY (1 << 14) ++#define AUTO_REFRESH_EN (1 << 0) ++#define RAM_CLOCK_DISABLE (1 << 10) ++#define ENTER_STANDBY (1 << 13) ++#define REFRESH_PERIOD_INTERVAL (0x39f << 1) ++ ++#if 0 ++#define LED_INIT_DEBUG do {\ ++ unsigned long value;\ ++ REG32(AK_VA_SYSCTRL + 0x00a8) |= (1 << 16);\ ++ REG32(AK_VA_SYSCTRL + 0x0094) &= ~(1 << 16);\ ++ } while(0) ++#define LED_PHY_ON do {\ ++ REG32(AK_PA_SYSCTRL + 0x0098) &= ~(1 << 16);\ ++ } while(0) ++ ++#define LED_PHY_OFF do {\ ++ REG32(AK_PA_SYSCTRL + 0x0098) |= (1 << 16);\ ++ } while(0) ++ ++#define LED_VIRT_ON do {\ ++ REG32(AK_VA_SYSCTRL + 0x0098) &= ~(1 << 16);\ ++ } while(0) ++ ++#define LED_VIRT_OFF do {\ ++ REG32(AK_VA_SYSCTRL + 0x0098) |= (1 << 16);\ ++ } while(0) ++#endif ++ ++#define DISABLE_CACHE_MMU() do { \ ++ __asm__ __volatile__( \ ++ "tci_loop: mrc p15, 0, r15, c7, c14, 3\n\t" /* test,clean,invalidate D cache */\ ++ "bne tci_loop\n\t" \ ++ "mcr p15, 0, %0, c8, c7, 0\n\t" /* invalidate I & D TLBs */ \ ++ "mcr p15, 0, %0, c7, c5, 0\n\t" /* invalidate I caches */ \ ++ "mrc p15, 0, %0, c1, c0, 0\n\t" \ ++ "bic %0, %0, #0x1000\n\t" /* disable Icache */ \ ++ "bic %0, %0, #0x0005\n\t" /* disable Dcache,mmu*/ \ ++ "ldr %1, =l2_phys_run\n\t" /* load 0x480000xx address */ \ ++ "b suspend_turn_off_mmu\n\t" \ ++ " .align 5\n\t" /* 32 byte aligned */ \ ++ "suspend_turn_off_mmu:\n\t"\ ++ "mcr p15, 0, %0, c1, c0, 0\n\t"\ ++ "mov pc, %1\n\t" /* jumpto 0x480000xx then run */ \ ++ "l2_phys_run:\n\t" /* mark the real running addr--> L2 buff */\ ++ : : "r"(0),"r"(1)); \ ++ } while(0) ++ ++#define ENABLE_CACHE_MMU() do { \ ++ __asm__ __volatile__( \ ++ "mcr p15, 0, %0, c8, c7, 0\n\t" /* invalidate I & D TLBs */ \ ++ "mcr p15, 0, %0, c7, c7, 0\n\t" /* invalidate I & D caches */\ ++ "mcr p15, 0, %0, c7, c10, 4\n\t" /* Drain write buffer */ \ ++ "mrc p15, 0, %0, c1, c0, 0\n\t" \ ++ "orr %0, %0, #0x1000\n\t" \ ++ "orr %0, %0, #0x0005\n\t" \ ++ "b resume_turn_on_mmu\n\t" \ ++ " .align 5\n\t" \ ++ "resume_turn_on_mmu:\n\t" \ ++ "mcr p15, 0, %0, c1, c0, 0\n\t" \ ++ ::"r"(2)); \ ++ } while(0) ++ ++#define PM_DELAY(time) do { \ ++ __asm__ __volatile__( \ ++ "1:\n\t" \ ++ "subs %0, %0, #1\n\t" \ ++ "bne 1b\n\t" \ ++ ::"r"(time)); \ ++ } while(0) ++ ++ ++#define DDR2_ENTER_POWERDOWN() do {\ ++ /* send precharge all banks */\ ++ __raw_writel(0x0aa00400, PHY_RAM_CPU_CMD);\ ++ /* close odt and asserting low on cke ,close odt and send enter powerdown command */\ ++ __raw_writel(0x04f00000, PHY_RAM_CPU_CMD);\ ++ }while(0) ++ ++#define DDR2_EXIT_POWERDOWN() do {\ ++ /* exit precharge power-down mode after delay at least 3 tck */\ ++ /* by asserting high on cke and odt remain low */\ ++ __raw_writel(0x02f00000, PHY_RAM_CPU_CMD);\ ++ }while(0) ++ ++ ++#define DDR2_ENTER_SELFREFRESH() do {\ ++ /* send precharge all banks */\ ++ __raw_writel(0x0aa00400, PHY_RAM_CPU_CMD);\ ++ /* close odt and delay taofd=2.5 tck */\ ++ __raw_writel(0x02f00000, PHY_RAM_CPU_CMD);\ ++ PM_DELAY(0x1);\ ++ /* asserting low on cke ,close odt and entry self-refresh mode */\ ++ __raw_writel(0x04c00000, PHY_RAM_CPU_CMD);\ ++ }while(0) ++ ++#define DDR2_EXIT_SELFREFRESH() do {\ ++ /* exit self-refresh by asserting high on cke and odt remain low */\ ++ __raw_writel(0x02f00000, PHY_RAM_CPU_CMD);\ ++ /* delay txsrd=200tck as send nop cmd */ \ ++ __raw_writel(0x02f00000, PHY_RAM_CPU_CMD);\ ++ PM_DELAY(0x8);\ ++ }while(0) ++ ++#define DDR2_ENTER_AUTOREFRESH() do {\ ++ /* send auto refresh and open odt high */\ ++ __raw_writel(0x0ac00000, PHY_RAM_CPU_CMD);\ ++ __raw_writel(0x0ac00000, PHY_RAM_CPU_CMD);\ ++ __raw_writel(0x0ac00000, PHY_RAM_CPU_CMD);\ ++ }while(0) ++ ++ ++#define L2_LINK(flag) __section(.l2mem_##flag) ++#define L2FUNC_NAME(name) l2_enter_##name ++ ++#define SPECIFIC_L2BUF_EXEC(flag, param1,param2,param3,param4) do {\ ++ extern char _end_##flag[], _start_##flag[];\ ++ int len;\ ++ len = _end_##flag - _start_##flag;\ ++ l2_exec_buf(_start_##flag,len, param1,param2,param3,param4);\ ++}while(0) ++ ++int l2_exec_buf(const char *vaddr, int len, unsigned long param1, ++ unsigned long param2,unsigned long param3, unsigned long param4); ++ ++ ++#endif /* L2_EXEBUF */ ++ ++ +diff --git a/arch/arm/plat-anyka/include/plat/rtc.h b/arch/arm/plat-anyka/include/plat/rtc.h +new file mode 100644 +index 00000000..9ad3f45d +--- /dev/null ++++ b/arch/arm/plat-anyka/include/plat/rtc.h +@@ -0,0 +1,133 @@ ++/* ++ * linux/arch/arm/plat-anyka/include/plat/rtc.h ++ * ++ * AK RTC related routines ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef __ASM_ARCH_RTC_H ++#define __ASM_ARCH_RTC_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#define EPOCH_START_YEAR (1900) ++#define RTC_START_YEAR (1980) ++#define RTC_YEAR_COUNT (127) ++ ++#define AK_RTC_CONF (AK_VA_SYSCTRL + 0x50) ++#define AK_RTC_DATA (AK_VA_SYSCTRL + 0x54) ++#define OTHER_WAKEUP_CTRL (AK_VA_SYSCTRL + 0x34) ++#define OTHER_WAKEUP_STAT (AK_VA_SYSCTRL + 0x38) ++ ++#define RTC_RDY_INT_CTRL (AK_VA_SYSCTRL + 0x2C) ++#define RTC_RDY_INT_STAT (AK_VA_SYSCTRL + 0x30) ++#define RTC_RDY_CTRL_BIT (1 << 7) ++#define RTC_RDY_STAT_BIT (1 << 7) ++ ++#define RTC_WAKEUP_EN (1 << 12) ++#define RTC_CONF_RTC_WR_EN (1 << 25) ++#define RTC_CONF_RTC_EN (1 << 24) ++#define RTC_CONF_RTC_READ ((1 << 21) | (2 << 18) | (1 << 17)) ++#define RTC_CONF_RTC_WRITE ((1 << 21) | (2 << 18) | (0 << 17)) ++ ++#define AK_RTC_REAL_TIME1 (0x0) ++#define AK_RTC_REAL_TIME2 (0x1) ++#define AK_RTC_REAL_TIME3 (0x2) ++#define AK_RTC_ALARM_TIME1 (0x3) ++#define AK_RTC_ALARM_TIME2 (0x4) ++#define AK_RTC_ALARM_TIME3 (0x5) ++#define AK_WDT_RTC_TIMER_CONF (0x6) ++#define AK_RTC_SETTING (0x7) ++#define AK_RTC_REG_MAX AK_RTC_SETTING ++ ++#define RTC_ON 1 ++#define RTC_OFF 0 ++#define RTC_SETTING_REAL_TIME_RE (1 << 4) ++#define RTC_SETTING_REAL_TIME_WR (1 << 3) ++#define RTC_WAIT_TIME_OUT 2000 ++/* ++ * When the RTC module begins to receive/send data, bit [24] of Interrupt Enable/Status ++ * Register of System Control Module (Add: 0x0800, 004C) is set to 0; and then this ++ * bit is set to 1 automatically to indicate that the data has been well received/sent ++ */ ++static void inline ak_rtc_wait_ready(void) ++{ ++ unsigned long timeout = 0; ++ ++ while (!(__raw_readl(RTC_RDY_INT_STAT) & RTC_RDY_STAT_BIT)) { ++ ++timeout; ++ if (timeout >= RTC_WAIT_TIME_OUT) { ++ //printk("--ak_rtc_wait_ready\n"); ++ break; ++ } ++ } ++} ++ ++static void inline rtc_ready_irq_enable(void) ++{ ++ unsigned long regval; ++ ++ /* ++ * Mask RTC Ready Interrupt ++ */ ++ regval = __raw_readl(RTC_RDY_INT_CTRL); ++ __raw_writel(regval | (RTC_RDY_CTRL_BIT), RTC_RDY_INT_CTRL); ++ ++ /* ++ * Wait for RTC Ready Interrupt to be cleared ++ */ ++ ak_rtc_wait_ready(); ++ ++ /* ++ * Enable RTC Register Read/Write ++ */ ++ regval = __raw_readl(AK_RTC_CONF); ++ regval |= RTC_CONF_RTC_WR_EN; ++ __raw_writel(regval, AK_RTC_CONF); ++} ++ ++static void inline rtc_ready_irq_disable(void) ++{ ++ unsigned long regval; ++ /* ++ * Disable RTC Register Read/Write ++ */ ++ regval = __raw_readl(AK_RTC_CONF); ++ regval &= ~RTC_CONF_RTC_WR_EN; ++ __raw_writel(regval, AK_RTC_CONF); ++ ++ /* ++ * Unmask RTC Ready Interrupt ++ */ ++ regval = __raw_readl(RTC_RDY_INT_CTRL); ++ __raw_writel(regval & ~RTC_RDY_CTRL_BIT, RTC_RDY_INT_CTRL); ++} ++ ++void ak_rtc_power(int op); ++ ++unsigned int ak_rtc_read(unsigned int addr); ++unsigned int ak_rtc_write(unsigned int addr, unsigned int value); ++unsigned int ak_rtc_set_wpin(bool level); ++void ak_reboot_sys_by_wtd(void); ++void ak_reboot_sys_by_wakeup(void); ++int test_rtc_inter_reg(unsigned int addr); ++ ++ ++#endif /* __ASM_ARCH_RTC_H */ +diff --git a/arch/arm/plat-anyka/l2.c b/arch/arm/plat-anyka/l2.c +new file mode 100755 +index 00000000..73b5f170 +--- /dev/null ++++ b/arch/arm/plat-anyka/l2.c +@@ -0,0 +1,1180 @@ ++/* ++ * linux/arch/arm/plat-anyka/l2.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++//#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++static l2_buffer_info_t l2_buffer_info[L2_COMMON_BUFFER_NUM]; ++static l2_dma_info_t l2_dma_info[L2_COMMON_BUFFER_NUM + L2_UART_BUFFER_NUM]; ++static bool l2_frac_started = false; /* L2 fraction DMA start flag */ ++ ++static l2_device_info_t l2_device_info[] = { ++ { ADDR_USB_EP2, BUF_NULL }, ++ { ADDR_USB_EP3, BUF_NULL }, ++ { ADDR_USB_EP4, BUF_NULL }, ++ { ADDR_RESERVED, BUF_NULL }, ++ { ADDR_MMC_SD, BUF_NULL }, ++ { ADDR_SDIO, BUF_NULL }, ++ { ADDR_RESERVED, BUF_NULL }, ++ { ADDR_SPI1_RX, BUF_NULL }, ++ { ADDR_SPI1_TX, BUF_NULL }, ++ { ADDR_DAC, BUF_NULL }, ++ { ADDR_SPI2_RX, BUF_NULL }, ++ { ADDR_SPI2_TX, BUF_NULL }, ++ { ADDR_GPS, BUF_NULL }, ++ { ADDR_PCM_TX, BUF_NULL }, ++ { ADDR_ADC, BUF_NULL }, ++ { ADDR_USB_EP5, BUF_NULL }, ++ { ADDR_RESERVED, BUF_NULL }, ++}; ++ ++static int l2_wait = 0; ++static wait_queue_head_t l2_wq; ++ ++extern void l2cache_invalidate(void); ++ ++static void l2_combuf_ctrl(u8 id, bool enable); ++static void l2_select_combuf(l2_device_t device, u8 id); ++static void l2_assert_combuf_id(u8 id); ++static void l2_assert_buf_id(u8 id); ++static void l2_clear_dma(u8 id); ++static void l2_frac_dma(unsigned long ram_addr, u8 id, u8 frac_offset, ++ unsigned int bytes, l2_dma_transfer_direction_t direction, bool intr_enable); ++static u32 l2_get_addr(u8 id); ++static bool l2_get_dma_param(unsigned int bytes, unsigned int *low, unsigned int *high); ++static void l2_dma(unsigned long ram_addr, u8 id, unsigned int bytes, ++ l2_dma_transfer_direction_t direction, bool intr_enable); ++static bool l2_wait_dma_finish(u8 id); ++static void l2_cpu(unsigned long ram_addr, u8 id, ++ unsigned long buf_offset, unsigned int bytes, l2_dma_transfer_direction_t direction); ++static irqreturn_t l2_interrupt_handler(int irq, void *dev_id); ++ ++/** ++ * l2_assert_buf_id - Assert a L2 buffer ID is valid ++ * @id: L2 buffer ID ++ * ++ * NOTE: Assert only L2 common buffer and UART buffer, USB buffer is not checked. ++ * Since this function is called internally by other L2 API, invalid id will cause ++ * linux kernel to oops for bug tracking. ++ */ ++static void l2_assert_buf_id(u8 id) ++{ ++ if (id >= L2_COMMON_BUFFER_NUM + L2_UART_BUFFER_NUM) ++ BUG(); ++} ++ ++/** ++ * l2_assert_combuf_id - Assert a L2 common buffer ID is valid ++ * @id: L2 buffer ID ++ * ++ * NOTE: Assert only L2 common buffer, UART & USB buffer is not checked. ++ * Since this function is called internally by other L2 API, invalid id will cause ++ * linux kernel to oops for bug tracking. ++ */ ++static void l2_assert_combuf_id(u8 id) ++{ ++ if (id >= L2_COMMON_BUFFER_NUM) ++ BUG(); ++} ++ ++/** ++ * l2_combuf_ctrl - L2 buffer enable/disable ++ * @id: L2 buffer ID ++ * @enable: true to enable L2 buffer, false to disable L2 buffer ++ */ ++static void l2_combuf_ctrl(u8 id, bool enable) ++{ ++ unsigned long regval; ++ unsigned long flags; ++ ++ l2_assert_buf_id(id); ++ ++ local_irq_save(flags); ++ ++ regval = rL2_CONBUF0_7; ++ if (enable) { ++ /* Enable L2 buffer & L2 Buffer DMA */ ++ regval |= (1 << (id + L2_COMMON_BUF_CFG_BUF_DMA_VLD_START)) | ++ (1 << (id + L2_COMMON_BUF_CFG_BUF_VLD_START)); ++ } else { ++ /* Disable L2 buffer & L2 Buffer DMA */ ++ regval &= ~((1 << (id + L2_COMMON_BUF_CFG_BUF_DMA_VLD_START)) | ++ (1 << (id + L2_COMMON_BUF_CFG_BUF_VLD_START))); ++ } ++ rL2_CONBUF0_7 = regval; ++ ++ local_irq_restore(flags); ++ ++} ++ ++/** ++ * l2_select_combuf - Select a L2 buffer for given device ++ * @device: Device which need to assign a L2 buffer ++ * @id: L2 buffer ID ++ */ ++static void l2_select_combuf(l2_device_t device, u8 id) ++{ ++ unsigned long regval; ++ unsigned long bits_offset; ++ ++ l2_assert_combuf_id(id); ++ ++ if ((u8)device < 10) { ++ /* ++ * USB Bulkout ~ DAC (Device 0 ~ 9) is controlled by Buffer Assignment Register 1 ++ */ ++ regval = rL2_BUFASSIGN1; ++ bits_offset = (u8)device * 3; ++ regval &= ~(0x7 << bits_offset); ++ regval |= ((id & 0x7) << bits_offset); ++ rL2_BUFASSIGN1 = regval; ++ } else { ++ /* ++ * SPI2 Rx ~ ADC (Device 10 ~ 14) is controlled by Buffer Assignment Register 2 ++ */ ++ regval = rL2_BUFASSIGN2; ++ bits_offset = ((u8)device - 10) * 3; ++ regval &= ~(0x7 << bits_offset); ++ regval |= ((id & 0x7) << bits_offset); ++ rL2_BUFASSIGN2 = regval; ++ } ++ ++} ++ ++ ++/** ++ * l2_deselect_combuf - deselect a L2 buffer for given device ++ * @device: Device which need to assign a L2 buffer ++ * @id: L2 buffer ID ++ * ++ * note: buffer 0 is reserved for softuse, no hardware is assigned ++ * so when free a l2 buffer, select to this device use buffer 0 as deselect ++ */ ++static void l2_deselect_combuf(l2_device_t device, u8 id) ++{ ++ unsigned long regval; ++ unsigned long bits_offset; ++ ++ l2_assert_combuf_id(id); ++ ++ if ((u8)device < 10) { ++ /* ++ * USB Bulkout ~ DAC (Device 0 ~ 9) is controlled by Buffer Assignment Register 1 ++ */ ++ regval = rL2_BUFASSIGN1; ++ bits_offset = (u8)device * 3; ++ regval &= ~(0x7 << bits_offset); ++ rL2_BUFASSIGN1 = regval; ++ } else { ++ /* ++ * SPI2 Rx ~ ADC (Device 10 ~ 14) is controlled by Buffer Assignment Register 2 ++ */ ++ regval = rL2_BUFASSIGN2; ++ bits_offset = ((u8)device - 10) * 3; ++ regval &= ~(0x7 << bits_offset); ++ rL2_BUFASSIGN2 = regval; ++ } ++ ++} ++ ++/** ++ * l2_clear_dma - Clear L2 buffer DMA status ++ * @id: L2 buffer ID which need to clear DMA status ++ */ ++static void l2_clear_dma(u8 id) ++{ ++ bool dmapending; ++ u8 status; ++ ++ dmapending = rL2_DMAREQ & (1 << (id + L2_DMA_REQ_BUF_START)); ++ status = l2_get_status(id); ++ ++ if (status == 0) { ++ return ; /* NO DMA request, so do nothing */ ++ } ++ ++ if(l2_dma_info[id].direction == BUF2MEM) { ++ printk("l2r:[%d]..", id); ++ while (dmapending) { ++ l2_set_status(id, 8); ++ dmapending = rL2_DMAREQ & (1 << (id + L2_DMA_REQ_BUF_START)); ++ } ++ printk("done\n"); ++ } else { ++ /* ++ * Wait until DMA request of this L2 buffer is finished. ++ */ ++ printk("l2t:[%d]..", id); ++ while (dmapending) { ++ l2_clr_status(id); ++ dmapending = rL2_DMAREQ & (1 << (id + L2_DMA_REQ_BUF_START)); ++ } ++ printk("done\n"); ++ } ++} ++ ++/** ++ * l2_frac_dma - Start data tranferring between memory and l2 common buffer in fraction DMA mode ++ * @ram_addr: External RAM address(Physical) ++ * @id: L2 buffer ID involved in DMA transfer ++ * @frac_offset: The region offset between buffer start address and transfer start address ++ * @bytes: Data transfer size ++ * @direction: Data transfer direction between L2 memory and external RAM ++ * @intr_enable: Open interrupt for this L2 buffer or not ++ * ++ * NOTE: Data transfer size should be 1~64Bytes, frac_offset should be 0~7 (*64Bytes) ++ */ ++static void l2_frac_dma(unsigned long ram_addr, u8 id, u8 frac_offset, ++ unsigned int bytes, l2_dma_transfer_direction_t direction, bool intr_enable) ++{ ++ u32 bufaddr; ++ u32 highaddr; ++ unsigned long regval; ++ unsigned long flags; ++ ++#if 0 ++ printk("%s(): ram_addr=0x%08X, l2 buffer id=%d, frac_offset=%d, bytes=%d, direction=%s, intr_enable=%d.\n", ++ __func__, (unsigned int)ram_addr, id, frac_offset, bytes, (direction == BUF2MEM)?"BUF2MEM":"MEM2BUF", intr_enable); ++#endif ++ ++ if (bytes == 0) { ++ printk("l2: no need to start fraction dma transfer: bytes=0.\n"); ++ return ; ++ } ++ ++ local_irq_save(flags); ++ ++ /* ++ * Set fraction external RAM address. ++ */ ++ highaddr = (ram_addr << 2) & 0xC0000000; ++ ++ regval = rL2_FRACDMAADDR; ++ regval &= ~(L2_FRAC_DMA_LOW_ADDR_MASK | (3<<30)); //modified by anyka chenyingyu ++ regval |= (ram_addr & L2_FRAC_DMA_LOW_ADDR_MASK) | highaddr; ++ rL2_FRACDMAADDR = regval; ++ ++ /* Set fraction DMA address */ ++ bufaddr = (id < L2_COMMON_BUFFER_NUM) ? ((id & 0x7) << 3) | (frac_offset & 0x7) : ++ (0x40 + ((id - L2_COMMON_BUFFER_NUM) << 1)) | (frac_offset & 0x1); ++ ++ /* Clear other fraction DMA request and info */ ++ regval = rL2_DMAREQ; ++ regval &= ~(L2_DMA_REQ_FRAC_DMA_LEN_MASK | L2_DMA_REQ_FRAC_DMA_L2_ADDR_MASK | ++ L2_DMA_REQ_FRAC_DMA_REQ | L2_DMA_REQ_BUF_REQ_MASK); ++ ++ switch (direction) { ++ case MEM2BUF: ++ if (bytes & 0x1) ++ bytes = bytes + 1; /* Round to even number when read data from external ram */ ++ regval |= L2_DMA_REQ_FRAC_DMA_REQ | L2_DMA_REQ_FRAC_DMA_DIR_WR | ++ (bufaddr << L2_DMA_REQ_FRAC_DMA_L2_ADDR_START) | ++ ((bytes - 1) << L2_DMA_REQ_FRAC_DMA_LEN_START); ++ rL2_DMAREQ = regval; ++ break; ++ case BUF2MEM: ++ regval &= ~(L2_DMA_REQ_FRAC_DMA_DIR_WR); ++ regval |= L2_DMA_REQ_FRAC_DMA_REQ | ++ (bufaddr << L2_DMA_REQ_FRAC_DMA_L2_ADDR_START) | ++ ((bytes - 1) << L2_DMA_REQ_FRAC_DMA_LEN_START); ++ rL2_DMAREQ = regval; ++ break; ++ default: ++ BUG(); ++ } ++ ++ if (intr_enable) { ++ regval = rL2_BUFINTEN; ++ regval |= L2_DMA_INTR_ENABLE_FRAC_INTR_EN; ++ rL2_BUFINTEN = regval; ++ } ++ ++ local_irq_restore(flags); ++} ++ ++/** ++ * l2_get_addr - Get L2 memory start address for given L2 buffer ++ * @id: L2 buffer ID ++ * Return L2 memory start address(Logical/Virtual) (NOT physical address) ++ */ ++static u32 l2_get_addr(u8 id) ++{ ++ u32 bufaddr = 0; ++ ++ if (id < L2_UART_BUFFER_INDEX) { /* L2 common buffer */ ++ bufaddr = (u32)AK_VA_L2MEM + L2_COMMON_BUFFER_OFFSET + ++ id * L2_COMMON_BUFFER_LEN; ++ } else if (id < L2_USB_HOST_BUFFER_INDEX) { /* UART L2 buffer */ ++ bufaddr = (u32)AK_VA_L2MEM + L2_UART_BUFFER_OFFSET + ++ (id - L2_COMMON_BUFFER_NUM) * L2_UART_BUFFER_LEN; ++ } else { ++ printk("l2: invalid buffer id %d.\n", (int)id); ++ } ++ ++ return bufaddr; ++} ++ ++/** ++ * l2_get_dma_param - Calculate l2 buffer big loop/small loop counter value ++ * @bytes: L2 buffer ID ++ * @low: CNT_cfg (bit[7:0] of DMA Operation Times Configuration Register) ++ * @high: CNT_cfg_H (bit[23:16] of DMA Operation Times Configuration Register) ++ * Return true when correct counter value (high/low) is found, else return false. ++ * ++ * NOTE: Use a simplified calculation method for L2 buffer 0~7 and 8~15 for bytes > 8KB ++ */ ++static bool l2_get_dma_param(unsigned int bytes, unsigned int *low, unsigned int *high) ++{ ++ unsigned int factor; ++ unsigned int dma_times = bytes / DMA_ONE_SHOT_LEN; ++ ++ if (bytes <= 8 * 1024) { ++ *low = dma_times; ++ *high = 0; ++ ++ return true; ++ } else if (dma_times & 0x7) { ++ printk("l2: Invalid L2 DMA buffer size(%u).\n", bytes); ++ return false; ++ } ++ ++ factor = 16 * 8; ++ ++ while (factor > 0) { ++ if ((dma_times % factor) == 0) { ++ *low = factor; ++ *high = dma_times / factor - 1; ++ ++ return (*high < 0xFF) ? true : false; ++ } ++ ++ factor -= 8; ++ } ++ ++ return false; ++} ++ ++ ++/** ++ * l2_dma - Start data tranferring between memory and l2 buffer in DMA mode ++ * @ram_addr: External RAM address(Physical) ++ * @id: L2 buffer ID involved in DMA transfer ++ * @bytes: Data transfer size ++ * @direction: Data transfer direction between L2 memory and external RAM ++ * @intr_enable: Open interrupt for this L2 buffer or not ++ */ ++static void l2_dma(unsigned long ram_addr, u8 id, unsigned int bytes, ++ l2_dma_transfer_direction_t direction, bool intr_enable) ++{ ++ unsigned long regid; ++ unsigned long regval; ++ unsigned long flags; ++ unsigned int cnt_low; ++ unsigned int cnt_high; ++ ++#if 0 ++ printk("%s(): ram_addr=0x%0X, id=%d, bytes=%d, direction=%d, intr_enable=%d.\n", ++ __func__, (unsigned int)ram_addr, id, bytes, direction, intr_enable); ++#endif ++ if (bytes == 0) { ++ printk("l2: no need to start dma transfer: bytes=0.\n"); ++ return ; ++ } ++ ++ if (!l2_get_dma_param(bytes, &cnt_low, &cnt_high)) { ++ printk("l2: L2 DMA buffer size error: bytes=%d.\n", bytes); ++ return ; ++ } ++ ++ if (l2_dma_info[id].dma_start || l2_dma_info[id].dma_frac_start) { ++ printk("l2: unable to start dma, dma NOT finished, buf id=%d.\n", (int)id); ++ return ; ++ } ++ ++ l2_dma_info[id].dma_op_times = bytes / DMA_ONE_SHOT_LEN; ++ l2_dma_info[id].dma_frac_data_len = bytes % DMA_ONE_SHOT_LEN; ++ l2_dma_info[id].dma_addr = (void *)ram_addr; ++ l2_dma_info[id].direction = direction; ++ l2_dma_info[id].intr_enable = intr_enable; ++ l2_dma_info[id].need_frac = false; ++ ++ if (l2_dma_info[id].dma_frac_data_len > 0) { ++ l2_dma_info[id].need_frac = true; ++ l2_dma_info[id].dma_frac_addr = (void *)(u8 *)l2_dma_info[id].dma_addr + ++ l2_dma_info[id].dma_op_times* DMA_ONE_SHOT_LEN; ++ l2_dma_info[id].dma_frac_offset = l2_dma_info[id].dma_op_times; ++ } ++ ++ if (l2_dma_info[id].dma_op_times== 0) { ++ /* ++ * If DMA transfer size < 64, we start fraction DMA immediately. ++ */ ++ ++ l2_dma_info[id].dma_start = false; ++ l2_dma_info[id].dma_frac_start = true; ++ ++ l2_frac_dma((unsigned long)l2_dma_info[id].dma_frac_addr, id, ++ l2_dma_info[id].dma_frac_offset, l2_dma_info[id].dma_frac_data_len, ++ l2_dma_info[id].direction, intr_enable); ++ return ; ++ } ++ l2_dma_info[id].dma_start = true; ++ ++ local_irq_save(flags); ++ ++ l2cache_invalidate(); ++ asm("MMU_Clean_Invalidate_Dcache:\n" "mrc p15,0,r15,c7,c14,3\n" "bne MMU_Clean_Invalidate_Dcache"); ++ ++ /* ++ * Set address of external RAM ++ */ ++ regval = (unsigned long)l2_dma_info[id].dma_addr; ++ regid = (unsigned long)vL2DMA_ADDRBUF0 + id * 4; ++ __raw_writel(regval, regid); ++ ++ /* ++ * Set DMA operation times ++ */ ++ regid = (unsigned long)vL2DMA_CONBUF0 + id * 4; ++ regval = (cnt_high << 16) | (cnt_low & 0xFF); ++ __raw_writel(regval, regid); ++ ++ /* ++ * Set DMA direction for L2 common buffer ++ */ ++ if (id < L2_COMMON_BUFFER_NUM) { ++ regval = rL2_CONBUF0_7; ++ if (l2_dma_info[id].direction == MEM2BUF) { ++ regval |= (1 << (id + L2_COMMON_BUF_CFG_BUF_DIR_START));; ++ } else { ++ regval &= ~(1 << (id + L2_COMMON_BUF_CFG_BUF_DIR_START)); ++ } ++ rL2_CONBUF0_7 = regval; ++ } ++ ++ ++ /* ++ * Start buffer DMA request ++ */ ++ regval = rL2_DMAREQ; ++ regval &= ~(L2_DMA_REQ_FRAC_DMA_REQ | L2_DMA_REQ_BUF_REQ_MASK); ++ if (id < L2_COMMON_BUFFER_NUM) { ++ regval |= (1 << (id + L2_DMA_REQ_BUF_START)); ++ } else { ++ regval |= (1 << ((id - L2_UART_BUF_START_ID + L2_UART_BUF_CFG_BUF_START))); ++ } ++ rL2_DMAREQ = regval; ++ ++ ++ /* ++ * Enable DMA interrupt now ++ */ ++ if (intr_enable) { ++ regval = rL2_BUFINTEN; ++ if (id < L2_COMMON_BUFFER_NUM) { ++ regval |= 1 << (id + L2_DMA_INTR_ENABLE_BUF_START); ++ } else { ++ regval |= 1 << (id - L2_COMMON_BUFFER_NUM + L2_DMA_INTR_ENABLE_UART_BUF_START); ++ } ++ rL2_BUFINTEN = regval; ++ } ++ ++ local_irq_restore(flags); ++} ++ ++/** ++ * l2_wait_dma_finish - Wait for L2 DMA to finish ++ * @id: L2 buffer ID involved in DMA transfer ++ * Return true: DMA transfer finished successfully. ++ * false: DMA transfer failed. ++ * NOTE: DMA transfer is started by l2_dma. ++ */ ++static bool l2_wait_dma_finish(u8 id) ++{ ++ unsigned int timeout; ++ unsigned long dmareq; ++ unsigned long dma_bit; ++ const unsigned int max_wait_time = L2_MAX_DMA_WAIT_TIME; ++ ++ timeout = 0; ++ if (l2_dma_info[id].dma_start) { ++ dma_bit = (id < L2_COMMON_BUFFER_NUM) ? (1 << (id + L2_DMA_REQ_BUF_START)) : ++ (1 << (id - L2_COMMON_BUFFER_NUM + L2_DMA_REQ_UART_BUF_REQ_START)); ++ do { ++ dmareq = rL2_DMAREQ; ++ } while((dmareq & dma_bit) && timeout++ < max_wait_time); ++ ++ l2_dma_info[id].dma_start = false; ++ ++ if (timeout >= max_wait_time) { ++ printk("l2: wait dma timeout, buf id=%d, status=%d.\n", id, l2_get_status(id)); ++ l2_clear_dma(id); ++ __raw_writel(0x0, vL2DMA_CONBUF0 + id * 4); ++ return false; ++ } ++ ++ /* ++ * If fraction DMA is NOT need, then everything is done. ++ */ ++ if (!l2_dma_info[id].need_frac) { ++ return true; ++ } ++ ++ ++ /* ++ * Start fraction DMA here for remain bytes transfer (<64Bytes). ++ */ ++ l2_dma_info[id].dma_frac_start = true; ++ l2_frac_dma((unsigned long)l2_dma_info[id].dma_frac_addr, id, ++ l2_dma_info[id].dma_frac_offset, l2_dma_info[id].dma_frac_data_len, ++ l2_dma_info[id].direction, false); ++ ++ } ++ ++ /* ++ * Fraction DMA handling starts here. ++ */ ++ if (l2_dma_info[id].dma_frac_start) { ++ timeout = 0; ++ do { ++ dmareq = rL2_DMAREQ; ++ } while((dmareq & L2_DMA_REQ_FRAC_DMA_REQ) && (timeout++ < max_wait_time)); ++ ++ l2_dma_info[id].dma_frac_start = false; ++ ++ if (timeout >= max_wait_time) { ++ printk("l2:wait frac dma timeout, buf id=%d, status=%d.\n", id, l2_get_status(id)); ++ return false; ++ } ++ ++ if ((l2_dma_info[id].direction == MEM2BUF) && ++ (l2_dma_info[id].dma_frac_data_len < 60)) { ++ ++ unsigned int bufaddr; ++ ++ bufaddr = l2_get_addr(id); ++ write_buf(0, bufaddr + (l2_dma_info[id].dma_frac_offset & 0x1FF) + 60); ++ } ++ } ++ ++ return true; ++} ++ ++/** ++ * l2_interrupt_handler - L2 memory interrupt handler ++ * @irq: IRQ number for L2 memory (Must be IRQ_L2MEM) ++ * @dev_id: Device specific information used by interrupt handler ++ * ++ * NOTE: Only shared IRQ need to check @irq & @dev_id. ++ * No need to check them here since L2 memory IRQ is NOT shared IRQ. ++ */ ++static irqreturn_t l2_interrupt_handler(int irq, void *dev_id) ++{ ++ unsigned long regval; ++ int i = 0; ++ ++ regval = rL2_DMAREQ; ++ ++ for (i = 0; i < L2_COMMON_BUFFER_NUM; i++) { ++ unsigned long dmapending = regval & (1 << ( i + L2_DMA_REQ_BUF_START)); ++ ++ if (l2_dma_info[i].dma_start && !dmapending) { ++ if (!l2_frac_started && l2_dma_info[i].need_frac) { ++ l2_dma_info[i].dma_frac_start = true; ++ l2_dma_info[i].dma_start = false; ++ ++ l2_frac_dma((unsigned long)l2_dma_info[i].dma_frac_addr, i, ++ l2_dma_info[i].dma_frac_offset, l2_dma_info[i].dma_frac_data_len, ++ l2_dma_info[i].direction, true); ++ ++ l2_frac_started = true; ++ } else { ++ /* DMA has finished */ ++ unsigned long regval; ++ ++ regval = rL2_BUFINTEN; ++ regval &= ~(1 << (i + L2_DMA_INTR_ENABLE_BUF_START)); ++ rL2_BUFINTEN = regval; ++ ++ l2_dma_info[i].dma_start = false; ++ ++ if (l2_dma_info[i].callback_func != NULL) ++ l2_dma_info[i].callback_func(l2_dma_info[i].data); ++ ++ } ++ } ++ ++ if (l2_dma_info[i].dma_frac_start) { ++ unsigned long frac_dmapending = regval & L2_DMA_REQ_FRAC_DMA_REQ; ++ if (l2_frac_started && !frac_dmapending) { ++ l2_frac_started = false; ++ ++ switch (l2_dma_info[i].direction) { ++ case MEM2BUF: ++ if (l2_dma_info[i].dma_frac_data_len <= 60) ++ __raw_writel(0x0, AK_VA_L2MEM + i * 512 + 0x1FC); ++ break; ++ case BUF2MEM: ++ if (l2_dma_info[i].dma_frac_data_len <= 512 - 4) ++ l2_clear_dma(i); ++ break; ++ default: ++ BUG(); ++ } ++ l2_dma_info[i].dma_frac_start = false; ++ ++ if (l2_dma_info[i].callback_func != NULL) ++ l2_dma_info[i].callback_func(l2_dma_info[i].data); ++ ++ } ++ } ++ ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * l2_cpu - Transfer data between memory and l2 buffer in CPU mode ++ * @ram_addr: External RAM address(Physical) ++ * @id: L2 buffer ID ++ * @buf_offset: The buffer offset ++ * @bytes: Data transfer size ++ * @direction: Data transfer direction between L2 memory and external RAM ++ */ ++static void l2_cpu(unsigned long ram_addr, u8 id, ++ unsigned long buf_offset, unsigned int bytes, l2_dma_transfer_direction_t direction) ++{ ++ int i; ++ int j; ++ unsigned long trans_no; ++ unsigned long frac_no; ++ unsigned long buf_count; ++ unsigned long buf_remain; ++ unsigned long temp_ram; ++ unsigned long temp_buf; ++ unsigned long bufaddr; ++ ++ /* ++ * L2 buffer caller MUST guarantee L2 buffer offset is 4-byte aligned ++ */ ++ if (unlikely(buf_offset % 4)) ++ BUG(); ++ ++ bufaddr = l2_get_addr(id); ++ ++ if (bufaddr == 0) { ++ return ; ++ } ++ ++ bufaddr += buf_offset; ++ trans_no = bytes / 4; ++ frac_no = bytes % 4; ++ ++ buf_count = (buf_offset + bytes) / L2_BUF_STATUS_MULTIPLY_RATIO; ++ buf_remain = (buf_offset + bytes) % L2_BUF_STATUS_MULTIPLY_RATIO; ++ ++ switch (direction) { ++ case MEM2BUF: ++ if (ram_addr % 4) { ++ for (i = 0; i < trans_no; i++) { ++ temp_ram = 0; ++ for (j = 0; j < 4; j++) ++ temp_ram |= ((read_ramb(ram_addr + i*4 + j))<<(j*8)); ++ write_buf(temp_ram, (bufaddr + i * 4)); ++ } ++ if (frac_no) { ++ temp_ram = 0; ++ for (j = 0; j < frac_no; j++) ++ temp_ram |= ((read_ramb(ram_addr + trans_no*4 + j))<<(j*8)); ++ write_buf(temp_ram, (bufaddr + trans_no * 4)); ++ } ++ } else { ++ for (i = 0; i < trans_no; i++) ++ write_buf(read_raml(ram_addr + i*4), (bufaddr + i*4)); ++ if (frac_no) ++ write_buf(read_raml(ram_addr + trans_no*4), (bufaddr + trans_no*4)); ++ } ++ ++ /* ++ * If we do NOT write data to L2 in multiple of 64Bytes, we must write something to the 4Bytes in 64Bytes- ++ * boundary so that CPU knows writing ends.. ++ */ ++ if ((buf_remain > 0) && (buf_remain <= L2_BUF_STATUS_MULTIPLY_RATIO - 4)) ++ write_buf(0, (bufaddr - buf_offset + buf_count*L2_BUF_STATUS_MULTIPLY_RATIO + L2_BUF_STATUS_MULTIPLY_RATIO - 4)); ++ break; ++ case BUF2MEM: ++ if (ram_addr % 4) { ++ for (i = 0; i < trans_no; i++) { ++ temp_buf = read_buf(bufaddr + i * 4); ++ for (j = 0; j < 4; j++) ++ write_ramb((u8)((temp_buf>>j*8) & 0xFF), (ram_addr + i*4 + j)); ++ } ++ if (frac_no) { ++ temp_buf = read_buf(bufaddr+trans_no*4); ++ for (j = 0; j < frac_no; j++) ++ write_ramb((u8)((temp_buf>>j*8) & 0xFF), (ram_addr + trans_no*4 + j)); ++ } ++ } else { ++ for (i = 0; i < trans_no; i++) ++ write_raml(read_buf(bufaddr+i*4), (ram_addr+i*4)); ++ if (frac_no) { ++ temp_buf = read_buf(bufaddr+trans_no*4); ++ temp_ram = read_raml(ram_addr+trans_no*4); ++ temp_buf &= ((1<<(frac_no*8+1))-1); ++ temp_ram &= ~((1<<(frac_no*8+1))-1); ++ temp_ram |= temp_buf; ++ write_raml(temp_ram, (ram_addr+trans_no*4)); ++ } ++ } ++ ++ /* ++ * If we do NOT read data from L2 in multiple of 64Bytes, we must read the 4Bytes in 64Bytes- ++ * boundary so that CPU knows reading ends.. ++ */ ++ if ((buf_remain > 0) && (buf_remain <= L2_BUF_STATUS_MULTIPLY_RATIO - 4)) ++ temp_buf = read_buf(bufaddr-buf_offset+buf_count*L2_BUF_STATUS_MULTIPLY_RATIO+L2_BUF_STATUS_MULTIPLY_RATIO - 4); ++ break; ++ default: ++ BUG(); ++ } ++ ++} ++ ++ ++/** ++ * l2_init - Initialize linux kernel L2 memory support ++ */ ++void __init l2_init(void) ++{ ++ int i; ++ int retval; ++ ++ /* ++ * Enable L2 controller working clock ++ */ ++ l2_enable_clock(true); ++ ++ /* ++ * Initialize all L2 common buffer status to IDLE(could be allocated) ++ */ ++ for (i = 0; i < L2_COMMON_BUFFER_NUM; i++) { ++ l2_buffer_info[i].id = (u8)i; ++ l2_buffer_info[i].usable = L2_STAT_IDLE; ++ l2_buffer_info[i].used_time = 0; ++ } ++ ++ /* L2 Memory Register initializations */ ++ rL2_DMAREQ = L2_DMA_REQ_EN; ++ rL2_FRACDMAADDR = L2_FRAC_DMA_AHB_FLAG_EN | L2_FRAC_DMA_LDMA_FLAG_EN; ++ rL2_CONBUF0_7 = 0x0; ++ rL2_CONBUF8_15 = L2_UART_BUF_CFG_UART_EN_MASK | L2_UART_BUF_CFG_UART_CLR_MASK; ++ rL2_BUFINTEN = 0x0; ++ rL2_BUFASSIGN1 = 0x0; ++ rL2_BUFASSIGN2 = 0x0; ++ ++ /* Initialize L2 DMA information status */ ++ memset(l2_dma_info, 0, ARRAY_SIZE(l2_dma_info)); ++ ++ /* Initialize global L2 fraction DMA start flag */ ++ l2_frac_started = false; ++ ++ init_waitqueue_head(&l2_wq); ++ ++ /* L2 Memory Interrupt handler registered */ ++ if ((retval = request_irq(IRQ_L2MEM, &l2_interrupt_handler, IRQF_DISABLED, "l2", NULL)) < 0) ++ printk(KERN_ERR "l2: failed to request_irq, irq number: %d, retval=%d.\n", IRQ_L2MEM, retval); ++ ++ printk("On-chip L2 memory initialized\n"); ++} ++ ++/** ++ * __l2_alloc - Allocate a common L2 buffer for given device ++ * @device: Device ID which need common L2 buffer ++ * Return L2 buffer ID (0 ~ 7) ++ * ++ * Only common L2 buffers(ID 0 ~ 7) could be allocated by __l2_alloc. ++ * Other L2 buffers (UART/USB used) is handled by corresponding devices directly. ++ */ ++static u8 __l2_alloc(l2_device_t device, bool need_wait) ++{ ++ int i; ++ u16 used_times = MAX_L2_BUFFER_USED_TIMES; ++ u8 id = BUF_NULL; ++ u8 first_id = BUF_NULL; ++ unsigned long flags; ++ bool l2_allocated = false; ++ ++ if (unlikely(device == ADDR_RESERVED)) { ++ printk("l2: unable to allocate l2 buffer for reserved device.\n"); ++ ++ return BUF_NULL; ++ } ++ ++ if (unlikely(l2_device_info[(u8)device].id != BUF_NULL)) { ++ printk("l2: device %d already have a l2 buffer %d\n", ++ (int)(u8)device, (int)(u8)l2_device_info[(u8)device].id); ++ ++ return l2_device_info[(u8)device].id; ++ } ++ ++ do { ++ local_irq_save(flags); ++ ++ l2_allocated = false; ++ ++ for (i = 1; i < L2_COMMON_BUFFER_NUM; i++) { ++ if (l2_buffer_info[i].usable == L2_STAT_IDLE) { ++ if (first_id == BUF_NULL) { ++ first_id = l2_buffer_info[i].id; ++ used_times = l2_buffer_info[i].used_time; ++ id = first_id; ++ } ++ if (l2_buffer_info[i].used_time < used_times) { ++ used_times = l2_buffer_info[i].used_time; ++ id = l2_buffer_info[i].id; ++ } ++ } ++ } ++ ++ if (unlikely(first_id == BUF_NULL)) { ++ if(!need_wait) { ++ local_irq_restore(flags); ++ return BUF_NULL; ++ } ++ local_irq_restore(flags); ++ l2_wait = 0; ++ wait_event(l2_wq, l2_wait); ++ } else { ++ l2_allocated = true; ++ } ++ } while (!l2_allocated); ++ ++ /* ++ * Got a L2 buffer successfully... ++ */ ++ l2_buffer_info[id].usable = L2_STAT_USED; ++ l2_buffer_info[id].used_time++; ++ if (l2_buffer_info[id].used_time == 0) { ++ /* ++ * In case when the new allocated L2 buffer has been used MAX_L2_BUFFER_USED_TIMES, ++ * we just clear all L2 buffer used times as a simpfied method of balancing 8 L2 buffer usage. ++ */ ++ for (i = 0; i < L2_COMMON_BUFFER_NUM; i++) ++ l2_buffer_info[i].used_time = 0; ++ } ++ ++ /* Enable L2 buffer */ ++ l2_combuf_ctrl(id, true); ++ ++ /* Change device info */ ++ l2_device_info[device].id = id; ++ ++ /* Select L2 common buffer for device */ ++ l2_select_combuf(device, id); ++ ++ local_irq_restore(flags); ++ ++ /* Clear L2 buffer status */ ++ l2_clr_status(id); ++ ++ return id; ++} ++ ++u8 l2_alloc(l2_device_t device) ++{ ++ return __l2_alloc(device, true); ++} ++EXPORT_SYMBOL(l2_alloc); ++ ++u8 l2_alloc_nowait(l2_device_t device) ++{ ++ return __l2_alloc(device, false); ++} ++EXPORT_SYMBOL(l2_alloc_nowait); ++ ++/** ++ * l2_free - Free L2 common buffer for given device ++ * @device: Device ID which need common L2 buffer ++ * Return L2 buffer ID (0 ~ 7) ++ * ++ * Only common L2 buffers(ID 0 ~ 7) could be allocated by l2_alloc. ++ * Other L2 buffers (UART/USB used) is handled by corresponding devices directly. ++ * NOTE: Return the previous L2 buffer ID if a L2 buffer has been allocated to the device. ++ * This means one device could get only one L2 buffer maximum. ++ */ ++void l2_free(l2_device_t device) ++{ ++ u8 id; ++ unsigned long regval; ++ unsigned long flags; ++ ++ id = l2_device_info[(u8)device].id; ++ if (unlikely(id == BUF_NULL)) { ++ printk("l2: trying to free invalid buffer id %d\n", (int)id); ++ return ; ++ } ++ ++ l2_clear_dma(id); ++ ++ local_irq_save(flags); ++ ++ /* ++ * Disable DMA interrupt of this L2 buffer. ++ */ ++ regval = rL2_BUFINTEN; ++ regval &= ~(1 << (id + L2_DMA_INTR_ENABLE_BUF_START)); ++ rL2_BUFINTEN = regval; ++ ++ /* Set DMA count to 0 */ ++ __raw_writel(0x0, vL2DMA_CONBUF0 + id * 4); ++ ++ /* Disable this L2 buffer */ ++ l2_combuf_ctrl(id, false); ++ l2_deselect_combuf(device, id); ++ ++ /* Clear DMA & DMA fraction flags */ ++ if (l2_dma_info[id].dma_start || l2_dma_info[id].dma_frac_start) { ++ l2_dma_info[id].dma_start = false; ++ l2_dma_info[id].dma_frac_start = false; ++ } ++ ++ l2_dma_info[id].callback_func = NULL; ++ l2_dma_info[id].data = 0; ++ ++ l2_device_info[(u8)device].id = BUF_NULL; ++ l2_buffer_info[id].usable = L2_STAT_IDLE; ++ ++ l2_wait = 1; ++ wake_up(&l2_wq); ++ ++ local_irq_restore(flags); ++ ++} ++EXPORT_SYMBOL(l2_free); ++ ++/** ++ * l2_set_dma_callback - Set callback function when L2 DMA/fraction DMA interrupt handler is done ++ * @id: L2 buffer ID ++ * @func: Callback function ++ * Return true(Always) ++ * ++ * NOTE: Caller MUST guarantee that L2 buffer ID is valid. And since the callback function is called ++ * in interrupt handler, it MUST NOT call any functions which may sleep. ++ */ ++bool l2_set_dma_callback(u8 id, l2_callback_func_t func, unsigned long data) ++{ ++ if (unlikely(id >= L2_COMMON_BUFFER_NUM)) { ++ printk(KERN_ERR "l2: Set dma callback, invalid buf id[%d].\n", id); ++ return false; ++ } ++ ++ if (unlikely(l2_dma_info[id].dma_start || l2_dma_info[id].dma_frac_start)) { ++ printk(KERN_ERR "l2: Set dma callback, dma not finished.\n"); ++ return false; ++ } ++ ++ l2_dma_info[id].callback_func = func; ++ l2_dma_info[id].data = data; ++ ++ return true; ++} ++EXPORT_SYMBOL(l2_set_dma_callback); ++ ++/** ++ * l2_combuf_dma - Start data tranferring between memory and l2 common buffer in DMA mode ++ * @ram_addr: External RAM address(Physical) ++ * @id: L2 buffer ID involved in DMA transfer ++ * @bytes: Data transfer size ++ * @direction: Data transfer direction between L2 memory and external RAM ++ * @intr_enable: Open interrupt for this L2 buffer or not ++ */ ++void l2_combuf_dma(unsigned long ram_addr, u8 id, unsigned int bytes, l2_dma_transfer_direction_t direction, bool intr_enable) ++{ ++ if (unlikely(id >= L2_COMMON_BUFFER_NUM)) { ++ printk("l2: begin common buffer dma, error buf id=[%d].\n", id); ++ return ; ++ } ++ ++ l2_dma(ram_addr, id, bytes, direction, intr_enable); ++} ++EXPORT_SYMBOL(l2_combuf_dma); ++ ++/** ++ * l2_combuf_wait_dma_finish - Wait for L2 DMA to finish ++ * @id: L2 buffer ID involved in DMA transfer ++ * Return true: DMA transfer finished successfully. ++ * false: DMA transfer failed. ++ * NOTE: DMA transfer is started by l2_combuf_dma. ++ */ ++bool l2_combuf_wait_dma_finish(u8 id) ++{ ++ if (unlikely(id >= L2_COMMON_BUFFER_NUM)) { ++ printk("l2: begin common buffer dma, error buf id=[%d].\n", id); ++ return false; ++ } ++ return l2_wait_dma_finish(id); ++} ++EXPORT_SYMBOL(l2_combuf_wait_dma_finish); ++ ++/** ++ * l2_combuf_cpu - Transfer data between memory and l2 common buffer in CPU mode ++ * @ram_addr: External RAM address(Physical) ++ * @id: L2 buffer ID ++ * @bytes: Data transfer size ++ * @direction: Data transfer direction between L2 memory and external RAM ++ * ++ * NOTE: According to XuChang, if one transfer data from Peripheral --> L2 Buffer --> RAM, ++ * special care need to be taken when data size is NOT multiple of 64Bytes. ++ * Pheripheral driver must check hardware signals to confirm data has been transfer from ++ * peripheral to L2 buffer since L2 do NOT provide some mechanism to confirm data has ++ * been in L2 Buffer. Driver can and only can call l2_combuf_cpu() to copy data from L2 ++ * Buffer --> RAM after checking hardware signals. ++ * As to 64Bytes * n size data, L2 could check Buffer Status Status Counter to confirm that ++ * Data has been transfer from peripheral to L2 buffer, so no hardware signals checking needed. ++ */ ++void l2_combuf_cpu(unsigned long ram_addr, u8 id, ++ unsigned int bytes, l2_dma_transfer_direction_t direction) ++{ ++ int i; ++ int loop; ++ int remain; ++ ++ loop = bytes / L2_BUF_STATUS_MULTIPLY_RATIO; ++ remain = bytes % L2_BUF_STATUS_MULTIPLY_RATIO; ++ ++ switch (direction) { ++ case MEM2BUF: ++ for (i = 0; i < loop; i++) { ++ ++ while (l2_get_status(id) == (L2_BUFFER_SIZE / L2_BUF_STATUS_MULTIPLY_RATIO)) ++ ; /* Waiting for L2 buffer to NOT full(means writable) */ ++ ++ l2_cpu(ram_addr + i * L2_BUF_STATUS_MULTIPLY_RATIO, id, ++ (i % 8) * L2_BUF_STATUS_MULTIPLY_RATIO, L2_BUF_STATUS_MULTIPLY_RATIO, direction); ++ } ++ if (remain > 0) { ++ while (l2_get_status(id) > 0) ++ ; /* Waiting for L2 buffer to empty */ ++ ++ l2_cpu(ram_addr + loop * L2_BUF_STATUS_MULTIPLY_RATIO, id, ++ (loop % 8) * L2_BUF_STATUS_MULTIPLY_RATIO, remain, direction); ++ } ++ break; ++ case BUF2MEM: ++ for (i = 0; i < loop; i++) { ++ while (l2_get_status(id) == 0) ++ ; /* Waiting for L2 buffer to be not empty (means readable) */ ++ ++ l2_cpu(ram_addr + i * L2_BUF_STATUS_MULTIPLY_RATIO, id, ++ (i % 8) * L2_BUF_STATUS_MULTIPLY_RATIO, L2_BUF_STATUS_MULTIPLY_RATIO, direction); ++ ++ } ++ if (remain > 0) { ++ l2_cpu(ram_addr + loop * L2_BUF_STATUS_MULTIPLY_RATIO, id, ++ (loop % 8) * L2_BUF_STATUS_MULTIPLY_RATIO, remain, direction); ++ } ++ break; ++ default: ++ BUG(); ++ } ++} ++EXPORT_SYMBOL(l2_combuf_cpu); ++ ++/** ++ * l2_get_status - Get L2 buffer status ++ * @id: L2 buffer ID ++ */ ++u8 l2_get_status(u8 id) ++{ ++ l2_assert_buf_id(id); ++ ++ return (id < L2_COMMON_BUFFER_NUM) ? (rL2_BUFSTAT1 >> (id * 4)) & 0xF : ++ (rL2_BUFSTAT2 >> ((id - L2_UART_BUF_START_ID) << 1)) & 0x3; ++} ++EXPORT_SYMBOL(l2_get_status); ++ ++/** ++ * l2_clr_status - Clear L2 buffer status ++ * @id: L2 buffer ID ++ */ ++void l2_clr_status(u8 id) ++{ ++ unsigned long flags; ++ ++ l2_assert_buf_id(id); ++ ++ local_irq_save(flags); ++ ++ if (id < L2_COMMON_BUFFER_NUM) { ++ rL2_CONBUF0_7 |= 1 << (id + L2_COMMON_BUF_CFG_BUF_CLR_START); ++ } else { ++ rL2_CONBUF8_15 |= (1 << (id - L2_UART_BUF_START_ID + L2_UART_BUF_CFG_BUF_START)); ++ } ++ ++ local_irq_restore(flags); ++ ++} ++EXPORT_SYMBOL(l2_clr_status); ++ ++/** ++ * l2_set_status - Clear L2 buffer status ++ * @id: L2 buffer ID ++ * @status: Status to be set (0 ~ 8) ++ */ ++void l2_set_status(u8 id, u8 status) ++{ ++ unsigned long regval; ++ unsigned long flags; ++ ++ l2_assert_buf_id(id); ++ ++ if ((id >= L2_COMMON_BUFFER_NUM) || status > MAX_L2_DMA_STATUS_VALUE) ++ BUG(); ++ ++ local_irq_save(flags); ++ ++ /* ++ * Enable CPU-controlled buffer function and set L2 buffer `id' status ++ * status = current number of data in the CPU controlled buffer. ++ */ ++ regval = rL2_CONBUF8_15; ++ regval &= ~(L2_UART_BUF_CFG_CPU_BUF_NUM_MASK | L2_UART_BUF_CFG_CPU_BUF_SEL_EN | ++ L2_UART_BUF_CFG_CPU_BUF_SEL_MASK); ++ regval |= (id << L2_UART_BUF_CFG_CPU_BUF_SEL_START) | L2_UART_BUF_CFG_CPU_BUF_SEL_EN | ++ (status << L2_UART_BUF_CFG_CPU_BUF_NUM_START); ++ rL2_CONBUF8_15 = regval; ++ ++ /* ++ * Disable CPU-controlled buffer function ++ */ ++ regval = rL2_CONBUF8_15; ++ regval &= ~(L2_UART_BUF_CFG_CPU_BUF_NUM_MASK | L2_UART_BUF_CFG_CPU_BUF_SEL_EN | ++ L2_UART_BUF_CFG_CPU_BUF_SEL_MASK); ++ rL2_CONBUF8_15 = regval; ++ ++ local_irq_restore(flags); ++ ++} ++EXPORT_SYMBOL(l2_set_status); +diff --git a/arch/arm/plat-anyka/l2_exebuf.c b/arch/arm/plat-anyka/l2_exebuf.c +new file mode 100755 +index 00000000..50cb314e +--- /dev/null ++++ b/arch/arm/plat-anyka/l2_exebuf.c +@@ -0,0 +1,67 @@ ++/* ++ * arch/arm/plat-anyka/l2_exebuf.c ++ */ ++#include ++#include ++#include ++ ++// #define PM_DEBUG ++#define L2_BUFFER0_SIZE 512 ++ ++void (*jumpto_L2)(unsigned long param1,unsigned long param2, ++ unsigned long param3,unsigned long param4); ++ ++static void pm_print_info(const char *start, int len) ++{ ++#ifdef PM_DEBUG ++ int i; ++ unsigned char local_l2mem[L2_BUFFER0_SIZE] = {0} ; ++ ++ printk("start = %p, len = %d\n", start, len); ++ for (i = 0; i < len; i += 4) { ++ *(unsigned long *)(local_l2mem + i) = ++ *(volatile unsigned long *)(AK_VA_L2MEM + i); ++ } ++ for (i = 0; i < len; i++) { ++ printk(" 0x%02x", local_l2mem[i]); ++ if (i % 16 == 15) printk("\n"); ++ } ++#endif ++} ++ ++ ++/* ++ * copy from ddr2 to l2 memory to run, and exit standby ++ */ ++int l2_exec_buf(const char *vaddr, int len, unsigned long param1, ++unsigned long param2,unsigned long param3,unsigned long param4) ++{ ++ unsigned long i, flags ; ++ ++ //disable ARM interrupt ++ local_irq_save(flags); ++ ++ memset((void *)AK_VA_L2MEM, 0, L2_BUFFER0_SIZE); ++ ++ //copy from ddr2 to l2 memory ++ for (i = 0; i < len; i += 4) { ++ *(volatile unsigned long *)(AK_VA_L2MEM + i) = ++ *(unsigned long *)(vaddr + i); ++ } ++ ++ pm_print_info(vaddr, len); ++ ++ REG32(AK_VA_L2CTRL + 0x84) &= ~(1 << 29); ++ ++ //jumpto_L2 run ++ jumpto_L2 = (void *)(AK_VA_L2MEM); ++ jumpto_L2(param1,param2,param3,param4); ++ ++ REG32(AK_VA_L2CTRL + 0x84) |= (1 << 29); ++ ++ //enable ARM interrupt ++ local_irq_restore(flags); ++ return 0; ++} ++ ++ +diff --git a/arch/arm/plat-anyka/notify.c b/arch/arm/plat-anyka/notify.c +new file mode 100755 +index 00000000..32e8127c +--- /dev/null ++++ b/arch/arm/plat-anyka/notify.c +@@ -0,0 +1,97 @@ ++/* ++ * power_notify.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++#include ++#include ++#include ++#include ++ ++static BLOCKING_NOTIFIER_HEAD(power_notifier_list); ++static BLOCKING_NOTIFIER_HEAD(addetect_notifier_list); ++ ++/* ++ * @brief register a power client notifier ++ * @author Li Xiaoping ++ * @date 2011-08-02 ++ * @param [in] nb notifier block to callback on events ++ * @return 0 ++ */ ++int power_register_client(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_register(&power_notifier_list, nb); ++} ++EXPORT_SYMBOL(power_register_client); ++ ++/* ++ * @brief unregister a power client notifier ++ * @author Li Xiaoping ++ * @date 2011-08-02 ++ * @param [in] nb notifier block to callback on events ++ * @return 0 ++ */ ++int power_unregister_client(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_unregister(&power_notifier_list, nb); ++} ++EXPORT_SYMBOL(power_unregister_client); ++ ++/* ++ * @brief notify clients of power_events ++ * @author Li Xiaoping ++ * @date 2011-08-02 ++ * @param [in] val - notifier events dispatch to clients ++ * @param [in] v - event data associated with events ++ * @return 0 ++ */ ++int power_notifier_call_chain(unsigned long val, void *v) ++{ ++ return blocking_notifier_call_chain(&power_notifier_list, val, v); ++} ++EXPORT_SYMBOL_GPL(power_notifier_call_chain); ++ ++ ++/* ++ * @brief register a ad detect notifier ++ * @author caolianming ++ * @date 2012-09-28 ++ * @param [in] nb notifier block to callback on events ++ * @return 0 ++ */ ++int addetect_register_client(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_register(&addetect_notifier_list, nb); ++} ++EXPORT_SYMBOL(addetect_register_client); ++ ++/* ++ * @brief unregister a ad detect client notifier ++ * @author caolianming ++ * @date 2012-09-28 ++ * @param [in] nb notifier block to callback on events ++ * @return 0 ++ */ ++int addetect_unregister_client(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_unregister(&addetect_notifier_list, nb); ++} ++EXPORT_SYMBOL(addetect_unregister_client); ++ ++/* ++ * @brief notify clients of ad deteced events ++ * @author caolianming ++ * @date 2012-09-28 ++ * @param [in] val - notifier events dispatch to clients ++ * @param [in] v - event data associated with events ++ * @return 0 ++ */ ++int addetect_notifier_call_chain(unsigned long val, void *v) ++{ ++ return blocking_notifier_call_chain(&addetect_notifier_list, val, v); ++} ++EXPORT_SYMBOL_GPL(addetect_notifier_call_chain); ++ +diff --git a/arch/arm/plat-anyka/reg.c b/arch/arm/plat-anyka/reg.c +new file mode 100644 +index 00000000..b00ee54a +--- /dev/null ++++ b/arch/arm/plat-anyka/reg.c +@@ -0,0 +1,50 @@ ++/* ++ * reg.c - Register Access Routines ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++static DEFINE_SPINLOCK(sys_ctrl_reg_lock); ++ ++/* ++ * @brief Anyka system control register setting routine ++ * @author Li Xiaoping ++ * @date 2011-07-04 ++ * @param reg_phy_addr [in] Register Physical Address ++ * @param reg_mask [in] Bit mask of the bits which need to be set ++ * @param reg_val [in] The value of the bits which need to be set ++ * @return void ++ * @note This routine is created in order to solve the problem of access the sam system control ++ * register from different kernel path (ISR, system call, etc...). ++ * @note This routine depends on that corresponding registers are mapped, currently this is ++ * done in akxx_map_io(), so be careful about akxx_map_io() changes. ++ * the akxx_ is instead of ak37_. ++ * @sample sys_ctrl_reg_set(0x0800000C, (1 << 29), (1 << 29)) will set bit 29 of ++ * Clock Control and Soft Reset Control Register to 1 ++ */ ++void sys_ctrl_reg_set(unsigned long reg_phy_addr, unsigned long reg_mask, unsigned long reg_val) ++{ ++ unsigned long flags; ++ unsigned long val; ++ unsigned long reg_virt_addr; ++ ++ BUG_ON((reg_phy_addr < AK_PA_SYSCTRL) || (reg_phy_addr > (AK_PA_SYSCTRL + AK_SZ_SYSCTRL))); ++ ++ spin_lock_irqsave(&sys_ctrl_reg_lock, flags); ++ ++ reg_virt_addr = (unsigned long)AK_VA_SYSCTRL + reg_phy_addr - AK_PA_SYSCTRL; ++ val = __raw_readl(reg_virt_addr); ++ val = (val & ~reg_mask) | (reg_val & reg_mask); ++ __raw_writel(val, reg_virt_addr); ++ ++ spin_unlock_irqrestore(&sys_ctrl_reg_lock, flags); ++} ++EXPORT_SYMBOL(sys_ctrl_reg_set); +diff --git a/arch/arm/plat-anyka/rtc.c b/arch/arm/plat-anyka/rtc.c +new file mode 100644 +index 00000000..85043e09 +--- /dev/null ++++ b/arch/arm/plat-anyka/rtc.c +@@ -0,0 +1,367 @@ ++/* ++ * linux/arch/arm/plat-anyka/rtc.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static int rtc_cnt = 0; ++ ++#undef REG32 ++#define REG32(_reg_) (*(volatile unsigned long *)(_reg_)) ++ ++//reboot system by watchdog ++void ak_reboot_sys_by_wtd(void) ++{ ++ unsigned long val; ++ unsigned long flags; ++ //static spinlock_t loc_lock; ++ ++ //spin_lock_init(&loc_lock); ++ //spin_lock(&loc_lock); ++ local_irq_save(flags); ++ ak_rtc_power(RTC_ON); ++ ++ //select wdt ++ val = ak_rtc_read(AK_RTC_SETTING); ++ val |= (1 << 10); ++ ak_rtc_write(AK_RTC_SETTING, val); ++ ++ //clear timer ++ val = ak_rtc_read(AK_RTC_SETTING); ++ val |= (1<<6); ++ ak_rtc_write(AK_RTC_SETTING, val); ++ ++ ++ //enable watchdog timer ++ val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); ++ val |= (1<<13); ++ ak_rtc_write(AK_WDT_RTC_TIMER_CONF, val); ++ ++ //set timer ++ val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); ++ val &= (1<<13); ++ val |= (5 & 0x1FFF); ++ ak_rtc_write(AK_WDT_RTC_TIMER_CONF, val); ++ ++ ++ //open watchdog and watchdog output ++ val = ak_rtc_read(AK_RTC_SETTING); ++ val |= ((1<<5) | (1<<2)); ++ val &= ~(1<<11); ++ ak_rtc_write(AK_RTC_SETTING, val); ++ ++ local_irq_restore(flags); ++// spin_unlock(&loc_lock); ++} ++EXPORT_SYMBOL(ak_reboot_sys_by_wtd); ++ ++void ak_reboot_sys_by_wakeup(void) ++{ ++ //static spinlock_t loc_lock; ++ struct rtc_time ptm; ++ struct rtc_time *tm = &ptm; ++ //unsigned char mdays[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; ++ unsigned long flags; ++ ++ unsigned long rtcset; ++ unsigned long rtc_time1; ++ unsigned long rtc_time2; ++ unsigned long rtc_time3; ++ ++ unsigned long rtc_alarm1; ++ unsigned long rtc_alarm2; ++ unsigned long rtc_alarm3; ++ unsigned int val_1, val_2; ++ unsigned long time; ++ ++ //spin_lock_init(&loc_lock); ++ //spin_lock(&loc_lock); ++ local_irq_save(flags); ++ ak_rtc_power(RTC_ON); ++ ++ rtcset = ak_rtc_read(AK_RTC_SETTING); ++ rtcset |= RTC_SETTING_REAL_TIME_RE; ++ ak_rtc_write(AK_RTC_SETTING, rtcset); ++ ++ rtc_time1 = ak_rtc_read(AK_RTC_REAL_TIME1); ++ rtc_time2 = ak_rtc_read(AK_RTC_REAL_TIME2); ++ rtc_time3 = ak_rtc_read(AK_RTC_REAL_TIME3); ++ ++ tm->tm_year = ((rtc_time3 >> 4) & 0x7F) - EPOCH_START_YEAR + RTC_START_YEAR; ++ tm->tm_mon = (rtc_time3 & 0xF) - 1; ++ tm->tm_mday = (rtc_time2 >> 5) & 0x1F; ++ tm->tm_hour = rtc_time2 & 0x1F; ++ tm->tm_min = (rtc_time1 >> 6) & 0x3F; ++ tm->tm_sec = rtc_time1 & 0x3F; ++ tm->tm_wday = (rtc_time2 >> 10) & 0x7; ++ tm->tm_isdst = -1; ++ ++ rtc_alarm1 = ak_rtc_read(AK_RTC_ALARM_TIME1); ++ rtc_alarm2 = ak_rtc_read(AK_RTC_ALARM_TIME2); ++ rtc_alarm3 = ak_rtc_read(AK_RTC_ALARM_TIME3); ++ ++ ++ rtc_alarm1 &= ~(0xFFF); ++ rtc_alarm2 &= ~(0x3FF); ++ rtc_alarm3 &= ~(0x7FF); ++ ++ #define DELAY_TIME 5 ++ ++ rtc_tm_to_time(tm, &time); ++ ++ time += DELAY_TIME; ++ ++ rtc_time_to_tm(time, tm); ++ /*tm->tm_sec += DELAY_TIME; ++ ++ if (tm->tm_sec >= 60) ++ { ++ tm->tm_sec -= 60; ++ tm->tm_min++; ++ if (tm->tm_min >= 60) ++ { ++ tm->tm_min = 0; ++ tm->tm_hour++; ++ if (tm->tm_hour >= 24) ++ { ++ tm->tm_hour = 0; ++ tm->tm_mday++; ++ if ((tm->tm_year%400==0) || ((tm->tm_year%100!=0) && (tm->tm_year%4==0))) ++ mdays[2] = 29; ++ if (tm->tm_mday > mdays[tm->tm_mon]) ++ { ++ tm->tm_mday = 1; ++ tm->tm_mon++; ++ if (tm->tm_mon > 12) ++ { ++ tm->tm_mon = 1; ++ tm->tm_year++; ++ } ++ } ++ } ++ } ++ ++ }*/ ++ rtc_alarm1 |= ((tm->tm_min << 6) + tm->tm_sec); ++ rtc_alarm2 |= ((tm->tm_mday << 5) + tm->tm_hour); ++ rtc_alarm3 |= (((tm->tm_year + EPOCH_START_YEAR - RTC_START_YEAR) << 4) + (tm->tm_mon + 1)); ++ ++ ak_rtc_write(AK_RTC_ALARM_TIME1, rtc_alarm1); ++ ak_rtc_write(AK_RTC_ALARM_TIME2, rtc_alarm2); ++ ak_rtc_write(AK_RTC_ALARM_TIME3, rtc_alarm3); ++ ++ val_1 = REG32(OTHER_WAKEUP_CTRL); ++ ++ ++ val_2 = ak_rtc_read(AK_RTC_SETTING); ++ ++ if (1) ++ { ++ /* enable wakeup signal */ ++ val_1 |= RTC_WAKEUP_EN;// | RTC_WAKEUP_SIGNAL_LOWACTIVE); ++ REG32(OTHER_WAKEUP_CTRL) = val_1; ++ ++ val_2 |= (1<<2); ++ ak_rtc_write(AK_RTC_SETTING, val_2); ++ } ++ ++ rtc_alarm1 = ak_rtc_read(AK_RTC_ALARM_TIME1); ++ rtc_alarm2 = ak_rtc_read(AK_RTC_ALARM_TIME2); ++ rtc_alarm3 = ak_rtc_read(AK_RTC_ALARM_TIME3); ++ ++ rtc_alarm1 |= (1<<13); ++ rtc_alarm2 |= (1<<13); ++ rtc_alarm3 |= (1<<13); ++ ++ ak_rtc_write(AK_RTC_ALARM_TIME1, rtc_alarm1); ++ ak_rtc_write(AK_RTC_ALARM_TIME2, rtc_alarm2); ++ ak_rtc_write(AK_RTC_ALARM_TIME3, rtc_alarm3); ++ ++ local_irq_restore(flags); ++// spin_unlock(&loc_lock); ++ ak_rtc_set_wpin(0); ++} ++ ++EXPORT_SYMBOL(ak_reboot_sys_by_wakeup); ++ ++void ak_rtc_power(int op) ++{ ++ unsigned long rtcconf; ++ ++ switch(op) ++ { ++ case RTC_ON: ++ if (++rtc_cnt == 1) ++ { ++ rtcconf = __raw_readl(AK_RTC_CONF); ++ rtcconf |= RTC_CONF_RTC_EN; ++ __raw_writel(rtcconf, AK_RTC_CONF); ++ } ++ break; ++ /* ++ When RTC is powered off, this bit(AK_RTC_CONF [24] ) should be set to 0 ++ */ ++ case RTC_OFF: ++ if (!(--rtc_cnt)) ++ { ++ rtcconf = __raw_readl(AK_RTC_CONF); ++ rtcconf &= ~RTC_CONF_RTC_EN; ++ __raw_writel(rtcconf, AK_RTC_CONF); ++ } ++ break; ++ default: ++ printk("Error RTC power operation.\n"); ++ break; ++ } ++} ++ ++EXPORT_SYMBOL(ak_rtc_power); ++ ++/* ++* check if internal rtc works or not ++* return -1 no rtc device;0 have rtc device ++*/ ++int test_rtc_inter_reg(unsigned int addr) ++{ ++ unsigned long regval = 0; ++ unsigned long flags; ++ int timeout = 0; ++ int ret = 0; ++ ++ local_irq_save(flags); ++ ++ // unmask rtc_ready irq ++ regval = __raw_readl(RTC_RDY_INT_CTRL); ++ __raw_writel(regval | (RTC_RDY_CTRL_BIT), RTC_RDY_INT_CTRL); ++ ++ // wait for more 1ms to access rtc register ++ while (!(__raw_readl(RTC_RDY_INT_STAT) & RTC_RDY_STAT_BIT)) ++ { ++ if (timeout++ > 1000) ++ { ++ ret = -1; ++ break; ++ } ++ udelay(1); ++ } ++ ++ // mask rtc_ready irq ++ regval = __raw_readl(RTC_RDY_INT_CTRL); ++ __raw_writel(regval & ~(RTC_RDY_CTRL_BIT), RTC_RDY_INT_CTRL); ++ ++ local_irq_restore(flags); ++ return ret; ++} ++EXPORT_SYMBOL(test_rtc_inter_reg); ++ ++unsigned int ak_rtc_read(unsigned int addr) ++{ ++ unsigned long regval = 0; ++ unsigned long flags; ++ ++ if (addr > AK_RTC_REG_MAX) { ++ printk("%s(): Invalid RTC Register, address=%d\n", ++ __func__, addr); ++ return -1; ++ } ++ ++ local_irq_save(flags); ++ ++ rtc_ready_irq_enable(); ++ ++ regval = __raw_readl(AK_RTC_CONF); ++ regval &= ~(0x3FFFFF) ; ++ regval |= (RTC_CONF_RTC_READ | (addr << 14)); ++ __raw_writel(regval, AK_RTC_CONF); ++ ++ udelay(100); ++ ++ ak_rtc_wait_ready(); ++ rtc_ready_irq_disable(); ++ local_irq_restore(flags); ++ ++ // according to ATC drivers ,this want to wait 1/32K s here ++ udelay(312); ++ ++ regval = __raw_readl(AK_RTC_DATA); ++ regval &= 0x3FFF; ++ ++ return regval; ++} ++EXPORT_SYMBOL(ak_rtc_read); ++ ++unsigned int ak_rtc_write(unsigned int addr, unsigned int value) ++{ ++ unsigned long regval = 0; ++ unsigned long flags; ++ ++ if (addr > AK_RTC_REG_MAX) { ++ printk("%s(): Invalid RTC Register, address=%d\n", ++ __func__, addr); ++ return -1; ++ } ++ ++ local_irq_save(flags); ++ ++ rtc_ready_irq_enable(); ++ ++ regval = __raw_readl(AK_RTC_CONF); ++ regval &= ~0x3FFFFF; ++ regval |= (RTC_CONF_RTC_WRITE | (addr << 14) | value); ++ __raw_writel(regval, AK_RTC_CONF); ++ ++ udelay(100); ++ ++ ak_rtc_wait_ready(); ++ ++ rtc_ready_irq_disable(); ++ ++ local_irq_restore(flags); ++ ++ // according to ATC drivers ,this want to wait 1/32K s here ++ udelay(312); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_rtc_write); ++ ++unsigned int ak_rtc_set_wpin(bool level) ++{ ++ unsigned long regval; ++ unsigned int bit; ++ unsigned int timeout = 0; ++ ++ ak_rtc_power(RTC_ON); ++ ++ if (test_rtc_inter_reg(0) < 0) { ++ printk("Board has not RTC support. exit %s\n", __func__); ++ ak_rtc_power(RTC_OFF); ++ return -ENODEV; ++ } ++ ++ bit = level ? 8 : 7; ++ printk("---ak_rtc_set_wpin:%d---\r\n", bit); ++ regval = ak_rtc_read(AK_RTC_SETTING); ++ regval |= (1 << bit); ++ ak_rtc_write(AK_RTC_SETTING, regval); ++ ++ while (ak_rtc_read(AK_RTC_SETTING) & (1 << bit)) { ++ ++timeout; ++ if (timeout >= RTC_WAIT_TIME_OUT) { ++ //printk("--ak_rtc_set_wpin--\n"); ++ break; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(ak_rtc_set_wpin); +diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types +index f9c9f33f..9ee01ada 100644 +--- a/arch/arm/tools/mach-types ++++ b/arch/arm/tools/mach-types +@@ -1169,3 +1169,4 @@ elite_ulk MACH_ELITE_ULK ELITE_ULK 3888 + pov2 MACH_POV2 POV2 3889 + ipod_touch_2g MACH_IPOD_TOUCH_2G IPOD_TOUCH_2G 3890 + da850_pqab MACH_DA850_PQAB DA850_PQAB 3891 ++anyka_ak39xx MACH_AK39XX AK39XX 3892 +diff --git a/arch/x86/include/asm/idle.h b/arch/x86/include/asm/idle.h +index c5d17853..02bab097 100644 +--- a/arch/x86/include/asm/idle.h ++++ b/arch/x86/include/asm/idle.h +@@ -1,13 +1,6 @@ + #ifndef _ASM_X86_IDLE_H + #define _ASM_X86_IDLE_H + +-#define IDLE_START 1 +-#define IDLE_END 2 +- +-struct notifier_block; +-void idle_notifier_register(struct notifier_block *n); +-void idle_notifier_unregister(struct notifier_block *n); +- + #ifdef CONFIG_X86_64 + void enter_idle(void); + void exit_idle(void); +diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c +index 1d92a5ab..fdd151ce 100644 +--- a/arch/x86/kernel/process.c ++++ b/arch/x86/kernel/process.c +@@ -29,19 +29,6 @@ + + #ifdef CONFIG_X86_64 + static DEFINE_PER_CPU(unsigned char, is_idle); +-static ATOMIC_NOTIFIER_HEAD(idle_notifier); +- +-void idle_notifier_register(struct notifier_block *n) +-{ +- atomic_notifier_chain_register(&idle_notifier, n); +-} +-EXPORT_SYMBOL_GPL(idle_notifier_register); +- +-void idle_notifier_unregister(struct notifier_block *n) +-{ +- atomic_notifier_chain_unregister(&idle_notifier, n); +-} +-EXPORT_SYMBOL_GPL(idle_notifier_unregister); + #endif + + struct kmem_cache *task_xstate_cachep; +@@ -378,14 +365,14 @@ static inline void play_dead(void) + void enter_idle(void) + { + percpu_write(is_idle, 1); +- atomic_notifier_call_chain(&idle_notifier, IDLE_START, NULL); ++ idle_notifier_call_chain(IDLE_START); + } + + static void __exit_idle(void) + { + if (x86_test_and_clear_bit_percpu(0, is_idle) == 0) + return; +- atomic_notifier_call_chain(&idle_notifier, IDLE_END, NULL); ++ idle_notifier_call_chain(IDLE_END); + } + + /* Called from interrupts to signify idle end */ +diff --git a/block/genhd.c b/block/genhd.c +index 60108d9f..2ade7561 100644 +--- a/block/genhd.c ++++ b/block/genhd.c +@@ -1110,6 +1110,22 @@ static void disk_release(struct device *dev) + blk_put_queue(disk->queue); + kfree(disk); + } ++ ++static int disk_uevent(struct device *dev, struct kobj_uevent_env *env) ++{ ++ struct gendisk *disk = dev_to_disk(dev); ++ struct disk_part_iter piter; ++ struct hd_struct *part; ++ int cnt = 0; ++ ++ disk_part_iter_init(&piter, disk, 0); ++ while((part = disk_part_iter_next(&piter))) ++ cnt++; ++ disk_part_iter_exit(&piter); ++ add_uevent_var(env, "NPARTS=%u", cnt); ++ return 0; ++} ++ + struct class block_class = { + .name = "block", + }; +@@ -1128,6 +1144,7 @@ static struct device_type disk_type = { + .groups = disk_attr_groups, + .release = disk_release, + .devnode = block_devnode, ++ .uevent = disk_uevent, + }; + + #ifdef CONFIG_PROC_FS +diff --git a/block/partition-generic.c b/block/partition-generic.c +index 7b8b8d17..264028c3 100644 +--- a/block/partition-generic.c ++++ b/block/partition-generic.c +@@ -216,10 +216,21 @@ static void part_release(struct device *dev) + kfree(p); + } + ++static int part_uevent(struct device *dev, struct kobj_uevent_env *env) ++{ ++ struct hd_struct *part = dev_to_part(dev); ++ ++ add_uevent_var(env, "PARTN=%u", part->partno); ++ if (part->info && part->info->volname[0]) ++ add_uevent_var(env, "PARTNAME=%s", part->info->volname); ++ return 0; ++} ++ + struct device_type part_type = { + .name = "partition", + .groups = part_attr_groups, + .release = part_release, ++ .uevent = part_uevent, + }; + + static void delete_partition_rcu_cb(struct rcu_head *head) +diff --git a/drivers/Kconfig b/drivers/Kconfig +index d236aef7..a765f400 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -96,6 +96,8 @@ source "drivers/memstick/Kconfig" + + source "drivers/leds/Kconfig" + ++source "drivers/switch/Kconfig" ++ + source "drivers/accessibility/Kconfig" + + source "drivers/infiniband/Kconfig" +diff --git a/drivers/Makefile b/drivers/Makefile +index 95952c82..b5d2823d 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -100,6 +100,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle/ + obj-y += mmc/ + obj-$(CONFIG_MEMSTICK) += memstick/ + obj-y += leds/ ++obj-$(CONFIG_SWITCH) += switch/ + obj-$(CONFIG_INFINIBAND) += infiniband/ + obj-$(CONFIG_SGI_SN) += sn/ + obj-y += firmware/ +diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig +index 9aa618ac..1131dd73 100644 +--- a/drivers/base/Kconfig ++++ b/drivers/base/Kconfig +@@ -192,4 +192,30 @@ config DMA_SHARED_BUFFER + APIs extension; the file's descriptor can then be passed on to other + driver. + ++config SYNC ++ bool "Synchronization framework" ++ default n ++ select ANON_INODES ++ help ++ This option enables the framework for synchronization between multiple ++ drivers. Sync implementations can take advantage of hardware ++ synchronization built into devices like GPUs. ++ ++config SW_SYNC ++ bool "Software synchronization objects" ++ default n ++ depends on SYNC ++ help ++ A sync object driver that uses a 32bit counter to coordinate ++ syncrhronization. Useful when there is no hardware primitive backing ++ the synchronization. ++ ++config SW_SYNC_USER ++ bool "Userspace API for SW_SYNC" ++ default n ++ depends on SW_SYNC ++ help ++ Provides a user space API to the sw sync object. ++ *WARNING* improper use of this can result in deadlocking kernel ++ drivers from userspace. + endmenu +diff --git a/drivers/base/Makefile b/drivers/base/Makefile +index b6d1b9c4..0e4d3dad 100644 +--- a/drivers/base/Makefile ++++ b/drivers/base/Makefile +@@ -21,5 +21,8 @@ obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o + obj-$(CONFIG_REGMAP) += regmap/ + obj-$(CONFIG_SOC_BUS) += soc.o + ++obj-$(CONFIG_SYNC) += sync.o ++obj-$(CONFIG_SW_SYNC) += sw_sync.o ++ + ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG + +diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c +index 07cbbc6f..7cfb405b 100644 +--- a/drivers/base/dma-buf.c ++++ b/drivers/base/dma-buf.c +@@ -44,8 +44,26 @@ static int dma_buf_release(struct inode *inode, struct file *file) + return 0; + } + ++static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma) ++{ ++ struct dma_buf *dmabuf; ++ ++ if (!is_dma_buf_file(file)) ++ return -EINVAL; ++ ++ dmabuf = file->private_data; ++ ++ /* check for overflowing the buffer's size */ ++ if (vma->vm_pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) > ++ dmabuf->size >> PAGE_SHIFT) ++ return -EINVAL; ++ ++ return dmabuf->ops->mmap(dmabuf, vma); ++} ++ + static const struct file_operations dma_buf_fops = { + .release = dma_buf_release, ++ .mmap = dma_buf_mmap_internal, + }; + + /* +@@ -82,7 +100,8 @@ struct dma_buf *dma_buf_export(void *priv, const struct dma_buf_ops *ops, + || !ops->unmap_dma_buf + || !ops->release + || !ops->kmap_atomic +- || !ops->kmap)) { ++ || !ops->kmap ++ || !ops->mmap)) { + return ERR_PTR(-EINVAL); + } + +@@ -406,3 +425,46 @@ void dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long page_num, + dmabuf->ops->kunmap(dmabuf, page_num, vaddr); + } + EXPORT_SYMBOL_GPL(dma_buf_kunmap); ++ ++ ++/** ++ * dma_buf_mmap - Setup up a userspace mmap with the given vma ++ * @dma_buf: [in] buffer that should back the vma ++ * @vma: [in] vma for the mmap ++ * @pgoff: [in] offset in pages where this mmap should start within the ++ * dma-buf buffer. ++ * ++ * This function adjusts the passed in vma so that it points at the file of the ++ * dma_buf operation. It alsog adjusts the starting pgoff and does bounds ++ * checking on the size of the vma. Then it calls the exporters mmap function to ++ * set up the mapping. ++ * ++ * Can return negative error values, returns 0 on success. ++ */ ++int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, ++ unsigned long pgoff) ++{ ++ if (WARN_ON(!dmabuf || !vma)) ++ return -EINVAL; ++ ++ /* check for offset overflow */ ++ if (pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) < pgoff) ++ return -EOVERFLOW; ++ ++ /* check for overflowing the buffer's size */ ++ if (pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) > ++ dmabuf->size >> PAGE_SHIFT) ++ return -EINVAL; ++ ++ /* readjust the vma */ ++ if (vma->vm_file) ++ fput(vma->vm_file); ++ ++ vma->vm_file = dmabuf->file; ++ get_file(vma->vm_file); ++ ++ vma->vm_pgoff = pgoff; ++ ++ return dmabuf->ops->mmap(dmabuf, vma); ++} ++EXPORT_SYMBOL_GPL(dma_buf_mmap); +diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c +index 73ce9fbe..83aa694a 100644 +--- a/drivers/base/power/domain.c ++++ b/drivers/base/power/domain.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -38,11 +39,13 @@ + ktime_t __start = ktime_get(); \ + type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev); \ + s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start)); \ +- struct generic_pm_domain_data *__gpd_data = dev_gpd_data(dev); \ +- if (__elapsed > __gpd_data->td.field) { \ +- __gpd_data->td.field = __elapsed; \ ++ struct gpd_timing_data *__td = &dev_gpd_data(dev)->td; \ ++ if (!__retval && __elapsed > __td->field) { \ ++ __td->field = __elapsed; \ + dev_warn(dev, name " latency exceeded, new value %lld ns\n", \ + __elapsed); \ ++ genpd->max_off_time_changed = true; \ ++ __td->constraint_changed = true; \ + } \ + __retval; \ + }) +@@ -211,6 +214,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) + elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); + if (elapsed_ns > genpd->power_on_latency_ns) { + genpd->power_on_latency_ns = elapsed_ns; ++ genpd->max_off_time_changed = true; + if (genpd->name) + pr_warning("%s: Power-on latency exceeded, " + "new value %lld ns\n", genpd->name, +@@ -247,6 +251,53 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd) + + #ifdef CONFIG_PM_RUNTIME + ++static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, ++ unsigned long val, void *ptr) ++{ ++ struct generic_pm_domain_data *gpd_data; ++ struct device *dev; ++ ++ gpd_data = container_of(nb, struct generic_pm_domain_data, nb); ++ ++ mutex_lock(&gpd_data->lock); ++ dev = gpd_data->base.dev; ++ if (!dev) { ++ mutex_unlock(&gpd_data->lock); ++ return NOTIFY_DONE; ++ } ++ mutex_unlock(&gpd_data->lock); ++ ++ for (;;) { ++ struct generic_pm_domain *genpd; ++ struct pm_domain_data *pdd; ++ ++ spin_lock_irq(&dev->power.lock); ++ ++ pdd = dev->power.subsys_data ? ++ dev->power.subsys_data->domain_data : NULL; ++ if (pdd) { ++ to_gpd_data(pdd)->td.constraint_changed = true; ++ genpd = dev_to_genpd(dev); ++ } else { ++ genpd = ERR_PTR(-ENODATA); ++ } ++ ++ spin_unlock_irq(&dev->power.lock); ++ ++ if (!IS_ERR(genpd)) { ++ mutex_lock(&genpd->lock); ++ genpd->max_off_time_changed = true; ++ mutex_unlock(&genpd->lock); ++ } ++ ++ dev = dev->parent; ++ if (!dev || dev->power.ignore_children) ++ break; ++ } ++ ++ return NOTIFY_DONE; ++} ++ + /** + * __pm_genpd_save_device - Save the pre-suspend state of a device. + * @pdd: Domain data of the device to save the state of. +@@ -435,6 +486,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) + elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); + if (elapsed_ns > genpd->power_off_latency_ns) { + genpd->power_off_latency_ns = elapsed_ns; ++ genpd->max_off_time_changed = true; + if (genpd->name) + pr_warning("%s: Power-off latency exceeded, " + "new value %lld ns\n", genpd->name, +@@ -443,17 +495,6 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) + } + + genpd->status = GPD_STATE_POWER_OFF; +- genpd->power_off_time = ktime_get(); +- +- /* Update PM QoS information for devices in the domain. */ +- list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) { +- struct gpd_timing_data *td = &to_gpd_data(pdd)->td; +- +- pm_runtime_update_max_time_suspended(pdd->dev, +- td->start_latency_ns + +- td->restore_state_latency_ns + +- genpd->power_on_latency_ns); +- } + + list_for_each_entry(link, &genpd->slave_links, slave_node) { + genpd_sd_counter_dec(link->master); +@@ -514,9 +555,6 @@ static int pm_genpd_runtime_suspend(struct device *dev) + if (ret) + return ret; + +- pm_runtime_update_max_time_suspended(dev, +- dev_gpd_data(dev)->td.start_latency_ns); +- + /* + * If power.irq_safe is set, this routine will be run with interrupts + * off, so it can't use mutexes. +@@ -613,6 +651,12 @@ void pm_genpd_poweroff_unused(void) + + #else + ++static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb, ++ unsigned long val, void *ptr) ++{ ++ return NOTIFY_DONE; ++} ++ + static inline void genpd_power_off_work_fn(struct work_struct *work) {} + + #define pm_genpd_runtime_suspend NULL +@@ -1209,12 +1253,15 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, + if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) + return -EINVAL; + +- genpd_acquire_lock(genpd); ++ gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); ++ if (!gpd_data) ++ return -ENOMEM; + +- if (genpd->status == GPD_STATE_POWER_OFF) { +- ret = -EINVAL; +- goto out; +- } ++ mutex_init(&gpd_data->lock); ++ gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; ++ dev_pm_qos_add_notifier(dev, &gpd_data->nb); ++ ++ genpd_acquire_lock(genpd); + + if (genpd->prepared_count > 0) { + ret = -EAGAIN; +@@ -1227,26 +1274,35 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, + goto out; + } + +- gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); +- if (!gpd_data) { +- ret = -ENOMEM; +- goto out; +- } +- + genpd->device_count++; ++ genpd->max_off_time_changed = true; + +- dev->pm_domain = &genpd->domain; + dev_pm_get_subsys_data(dev); ++ ++ mutex_lock(&gpd_data->lock); ++ spin_lock_irq(&dev->power.lock); ++ dev->pm_domain = &genpd->domain; + dev->power.subsys_data->domain_data = &gpd_data->base; + gpd_data->base.dev = dev; +- gpd_data->need_restore = false; + list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); ++ gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; + if (td) + gpd_data->td = *td; + ++ gpd_data->td.constraint_changed = true; ++ gpd_data->td.effective_constraint_ns = -1; ++ spin_unlock_irq(&dev->power.lock); ++ mutex_unlock(&gpd_data->lock); ++ ++ genpd_release_lock(genpd); ++ ++ return 0; ++ + out: + genpd_release_lock(genpd); + ++ dev_pm_qos_remove_notifier(dev, &gpd_data->nb); ++ kfree(gpd_data); + return ret; + } + +@@ -1290,12 +1346,15 @@ int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev, + int pm_genpd_remove_device(struct generic_pm_domain *genpd, + struct device *dev) + { ++ struct generic_pm_domain_data *gpd_data; + struct pm_domain_data *pdd; +- int ret = -EINVAL; ++ int ret = 0; + + dev_dbg(dev, "%s()\n", __func__); + +- if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) ++ if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev) ++ || IS_ERR_OR_NULL(dev->pm_domain) ++ || pd_to_genpd(dev->pm_domain) != genpd) + return -EINVAL; + + genpd_acquire_lock(genpd); +@@ -1305,21 +1364,27 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, + goto out; + } + +- list_for_each_entry(pdd, &genpd->dev_list, list_node) { +- if (pdd->dev != dev) +- continue; ++ genpd->device_count--; ++ genpd->max_off_time_changed = true; + +- list_del_init(&pdd->list_node); +- pdd->dev = NULL; +- dev_pm_put_subsys_data(dev); +- dev->pm_domain = NULL; +- kfree(to_gpd_data(pdd)); ++ spin_lock_irq(&dev->power.lock); ++ dev->pm_domain = NULL; ++ pdd = dev->power.subsys_data->domain_data; ++ list_del_init(&pdd->list_node); ++ dev->power.subsys_data->domain_data = NULL; ++ spin_unlock_irq(&dev->power.lock); + +- genpd->device_count--; ++ gpd_data = to_gpd_data(pdd); ++ mutex_lock(&gpd_data->lock); ++ pdd->dev = NULL; ++ mutex_unlock(&gpd_data->lock); + +- ret = 0; +- break; +- } ++ genpd_release_lock(genpd); ++ ++ dev_pm_qos_remove_notifier(dev, &gpd_data->nb); ++ kfree(gpd_data); ++ dev_pm_put_subsys_data(dev); ++ return 0; + + out: + genpd_release_lock(genpd); +@@ -1347,6 +1412,26 @@ void pm_genpd_dev_always_on(struct device *dev, bool val) + } + EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on); + ++/** ++ * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag. ++ * @dev: Device to set/unset the flag for. ++ * @val: The new value of the device's "need restore" flag. ++ */ ++void pm_genpd_dev_need_restore(struct device *dev, bool val) ++{ ++ struct pm_subsys_data *psd; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->power.lock, flags); ++ ++ psd = dev_to_psd(dev); ++ if (psd && psd->domain_data) ++ to_gpd_data(psd->domain_data)->need_restore = val; ++ ++ spin_unlock_irqrestore(&dev->power.lock, flags); ++} ++EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore); ++ + /** + * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. + * @genpd: Master PM domain to add the subdomain to. +@@ -1378,7 +1463,7 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, + goto out; + } + +- list_for_each_entry(link, &genpd->slave_links, slave_node) { ++ list_for_each_entry(link, &genpd->master_links, master_node) { + if (link->slave == subdomain && link->master == genpd) { + ret = -EINVAL; + goto out; +@@ -1690,6 +1775,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd, + genpd->resume_count = 0; + genpd->device_count = 0; + genpd->max_off_time_ns = -1; ++ genpd->max_off_time_changed = true; + genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; + genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; + genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; +diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c +index 66a265bf..28dee305 100644 +--- a/drivers/base/power/domain_governor.c ++++ b/drivers/base/power/domain_governor.c +@@ -14,6 +14,31 @@ + + #ifdef CONFIG_PM_RUNTIME + ++static int dev_update_qos_constraint(struct device *dev, void *data) ++{ ++ s64 *constraint_ns_p = data; ++ s32 constraint_ns = -1; ++ ++ if (dev->power.subsys_data && dev->power.subsys_data->domain_data) ++ constraint_ns = dev_gpd_data(dev)->td.effective_constraint_ns; ++ ++ if (constraint_ns < 0) { ++ constraint_ns = dev_pm_qos_read_value(dev); ++ constraint_ns *= NSEC_PER_USEC; ++ } ++ if (constraint_ns == 0) ++ return 0; ++ ++ /* ++ * constraint_ns cannot be negative here, because the device has been ++ * suspended. ++ */ ++ if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0) ++ *constraint_ns_p = constraint_ns; ++ ++ return 0; ++} ++ + /** + * default_stop_ok - Default PM domain governor routine for stopping devices. + * @dev: Device to check. +@@ -21,14 +46,52 @@ + bool default_stop_ok(struct device *dev) + { + struct gpd_timing_data *td = &dev_gpd_data(dev)->td; ++ unsigned long flags; ++ s64 constraint_ns; + + dev_dbg(dev, "%s()\n", __func__); + +- if (dev->power.max_time_suspended_ns < 0 || td->break_even_ns == 0) +- return true; ++ spin_lock_irqsave(&dev->power.lock, flags); ++ ++ if (!td->constraint_changed) { ++ bool ret = td->cached_stop_ok; + +- return td->stop_latency_ns + td->start_latency_ns < td->break_even_ns +- && td->break_even_ns < dev->power.max_time_suspended_ns; ++ spin_unlock_irqrestore(&dev->power.lock, flags); ++ return ret; ++ } ++ td->constraint_changed = false; ++ td->cached_stop_ok = false; ++ td->effective_constraint_ns = -1; ++ constraint_ns = __dev_pm_qos_read_value(dev); ++ ++ spin_unlock_irqrestore(&dev->power.lock, flags); ++ ++ if (constraint_ns < 0) ++ return false; ++ ++ constraint_ns *= NSEC_PER_USEC; ++ /* ++ * We can walk the children without any additional locking, because ++ * they all have been suspended at this point and their ++ * effective_constraint_ns fields won't be modified in parallel with us. ++ */ ++ if (!dev->power.ignore_children) ++ device_for_each_child(dev, &constraint_ns, ++ dev_update_qos_constraint); ++ ++ if (constraint_ns > 0) { ++ constraint_ns -= td->start_latency_ns; ++ if (constraint_ns == 0) ++ return false; ++ } ++ td->effective_constraint_ns = constraint_ns; ++ td->cached_stop_ok = constraint_ns > td->stop_latency_ns || ++ constraint_ns == 0; ++ /* ++ * The children have been suspended already, so we don't need to take ++ * their stop latencies into account here. ++ */ ++ return td->cached_stop_ok; + } + + /** +@@ -42,9 +105,27 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) + struct generic_pm_domain *genpd = pd_to_genpd(pd); + struct gpd_link *link; + struct pm_domain_data *pdd; +- s64 min_dev_off_time_ns; ++ s64 min_off_time_ns; + s64 off_on_time_ns; +- ktime_t time_now = ktime_get(); ++ ++ if (genpd->max_off_time_changed) { ++ struct gpd_link *link; ++ ++ /* ++ * We have to invalidate the cached results for the masters, so ++ * use the observation that default_power_down_ok() is not ++ * going to be called for any master until this instance ++ * returns. ++ */ ++ list_for_each_entry(link, &genpd->slave_links, slave_node) ++ link->master->max_off_time_changed = true; ++ ++ genpd->max_off_time_changed = false; ++ genpd->cached_power_down_ok = false; ++ genpd->max_off_time_ns = -1; ++ } else { ++ return genpd->cached_power_down_ok; ++ } + + off_on_time_ns = genpd->power_off_latency_ns + + genpd->power_on_latency_ns; +@@ -61,6 +142,7 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) + to_gpd_data(pdd)->td.save_state_latency_ns; + } + ++ min_off_time_ns = -1; + /* + * Check if subdomains can be off for enough time. + * +@@ -73,8 +155,6 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) + if (sd_max_off_ns < 0) + continue; + +- sd_max_off_ns -= ktime_to_ns(ktime_sub(time_now, +- sd->power_off_time)); + /* + * Check if the subdomain is allowed to be off long enough for + * the current domain to turn off and on (that's how much time +@@ -82,60 +162,64 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) + */ + if (sd_max_off_ns <= off_on_time_ns) + return false; ++ ++ if (min_off_time_ns > sd_max_off_ns || min_off_time_ns < 0) ++ min_off_time_ns = sd_max_off_ns; + } + + /* + * Check if the devices in the domain can be off enough time. + */ +- min_dev_off_time_ns = -1; + list_for_each_entry(pdd, &genpd->dev_list, list_node) { + struct gpd_timing_data *td; +- struct device *dev = pdd->dev; +- s64 dev_off_time_ns; ++ s64 constraint_ns; + +- if (!dev->driver || dev->power.max_time_suspended_ns < 0) ++ if (!pdd->dev->driver) + continue; + ++ /* ++ * Check if the device is allowed to be off long enough for the ++ * domain to turn off and on (that's how much time it will ++ * have to wait worst case). ++ */ + td = &to_gpd_data(pdd)->td; +- dev_off_time_ns = dev->power.max_time_suspended_ns - +- (td->start_latency_ns + td->restore_state_latency_ns + +- ktime_to_ns(ktime_sub(time_now, +- dev->power.suspend_time))); +- if (dev_off_time_ns <= off_on_time_ns) +- return false; +- +- if (min_dev_off_time_ns > dev_off_time_ns +- || min_dev_off_time_ns < 0) +- min_dev_off_time_ns = dev_off_time_ns; +- } ++ constraint_ns = td->effective_constraint_ns; ++ /* default_stop_ok() need not be called before us. */ ++ if (constraint_ns < 0) { ++ constraint_ns = dev_pm_qos_read_value(pdd->dev); ++ constraint_ns *= NSEC_PER_USEC; ++ } ++ if (constraint_ns == 0) ++ continue; + +- if (min_dev_off_time_ns < 0) { + /* +- * There are no latency constraints, so the domain can spend +- * arbitrary time in the "off" state. ++ * constraint_ns cannot be negative here, because the device has ++ * been suspended. + */ +- genpd->max_off_time_ns = -1; +- return true; ++ constraint_ns -= td->restore_state_latency_ns; ++ if (constraint_ns <= off_on_time_ns) ++ return false; ++ ++ if (min_off_time_ns > constraint_ns || min_off_time_ns < 0) ++ min_off_time_ns = constraint_ns; + } + ++ genpd->cached_power_down_ok = true; ++ + /* +- * The difference between the computed minimum delta and the time needed +- * to turn the domain on is the maximum theoretical time this domain can +- * spend in the "off" state. ++ * If the computed minimum device off time is negative, there are no ++ * latency constraints, so the domain can spend arbitrary time in the ++ * "off" state. + */ +- min_dev_off_time_ns -= genpd->power_on_latency_ns; ++ if (min_off_time_ns < 0) ++ return true; + + /* +- * If the difference between the computed minimum delta and the time +- * needed to turn the domain off and back on on is smaller than the +- * domain's power break even time, removing power from the domain is not +- * worth it. ++ * The difference between the computed minimum subdomain or device off ++ * time and the time needed to turn the domain on is the maximum ++ * theoretical time this domain can spend in the "off" state. + */ +- if (genpd->break_even_ns > +- min_dev_off_time_ns - genpd->power_off_latency_ns) +- return false; +- +- genpd->max_off_time_ns = min_dev_off_time_ns; ++ genpd->max_off_time_ns = min_off_time_ns - genpd->power_on_latency_ns; + return true; + } + +diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c +index ebc272f8..77e3b97b 100644 +--- a/drivers/base/power/main.c ++++ b/drivers/base/power/main.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + + #include "../base.h" + #include "power.h" +@@ -54,6 +55,12 @@ struct suspend_stats suspend_stats; + static DEFINE_MUTEX(dpm_list_mtx); + static pm_message_t pm_transition; + ++struct dpm_watchdog { ++ struct device *dev; ++ struct task_struct *tsk; ++ struct timer_list timer; ++}; ++ + static int async_error; + + /** +@@ -389,6 +396,56 @@ static int dpm_run_callback(pm_callback_t cb, struct device *dev, + return error; + } + ++/** ++ * dpm_wd_handler - Driver suspend / resume watchdog handler. ++ * ++ * Called when a driver has timed out suspending or resuming. ++ * There's not much we can do here to recover so BUG() out for ++ * a crash-dump ++ */ ++static void dpm_wd_handler(unsigned long data) ++{ ++ struct dpm_watchdog *wd = (void *)data; ++ struct device *dev = wd->dev; ++ struct task_struct *tsk = wd->tsk; ++ ++ dev_emerg(dev, "**** DPM device timeout ****\n"); ++ show_stack(tsk, NULL); ++ ++ BUG(); ++} ++ ++/** ++ * dpm_wd_set - Enable pm watchdog for given device. ++ * @wd: Watchdog. Must be allocated on the stack. ++ * @dev: Device to handle. ++ */ ++static void dpm_wd_set(struct dpm_watchdog *wd, struct device *dev) ++{ ++ struct timer_list *timer = &wd->timer; ++ ++ wd->dev = dev; ++ wd->tsk = get_current(); ++ ++ init_timer_on_stack(timer); ++ timer->expires = jiffies + HZ * 12; ++ timer->function = dpm_wd_handler; ++ timer->data = (unsigned long)wd; ++ add_timer(timer); ++} ++ ++/** ++ * dpm_wd_clear - Disable pm watchdog. ++ * @wd: Watchdog to disable. ++ */ ++static void dpm_wd_clear(struct dpm_watchdog *wd) ++{ ++ struct timer_list *timer = &wd->timer; ++ ++ del_timer_sync(timer); ++ destroy_timer_on_stack(timer); ++} ++ + /*------------------------- Resume routines -------------------------*/ + + /** +@@ -565,6 +622,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) + char *info = NULL; + int error = 0; + bool put = false; ++ struct dpm_watchdog wd; + + TRACE_DEVICE(dev); + TRACE_RESUME(0); +@@ -577,6 +635,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) + * a resumed device, even if the device hasn't been completed yet. + */ + dev->power.is_prepared = false; ++ dpm_wd_set(&wd, dev); + + if (!dev->power.is_suspended) + goto Unlock; +@@ -631,6 +690,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) + + Unlock: + device_unlock(dev); ++ dpm_wd_clear(&wd); + complete_all(&dev->power.completion); + + TRACE_RESUME(error); +@@ -889,6 +949,11 @@ static int dpm_suspend_noirq(pm_message_t state) + if (!list_empty(&dev->power.entry)) + list_move(&dev->power.entry, &dpm_noirq_list); + put_device(dev); ++ ++ if (pm_wakeup_pending()) { ++ error = -EBUSY; ++ break; ++ } + } + mutex_unlock(&dpm_list_mtx); + if (error) +@@ -962,6 +1027,11 @@ static int dpm_suspend_late(pm_message_t state) + if (!list_empty(&dev->power.entry)) + list_move(&dev->power.entry, &dpm_late_early_list); + put_device(dev); ++ ++ if (pm_wakeup_pending()) { ++ error = -EBUSY; ++ break; ++ } + } + mutex_unlock(&dpm_list_mtx); + if (error) +@@ -1025,6 +1095,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) + pm_callback_t callback = NULL; + char *info = NULL; + int error = 0; ++ struct dpm_watchdog wd; + + dpm_wait_for_children(dev, async); + +@@ -1041,6 +1112,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) + goto Complete; + } + ++ dpm_wd_set(&wd, dev); ++ + device_lock(dev); + + if (dev->pm_domain) { +@@ -1096,6 +1169,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) + + device_unlock(dev); + ++ dpm_wd_clear(&wd); ++ + Complete: + complete_all(&dev->power.completion); + +diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c +index c365c93a..b95ebf2d 100644 +--- a/drivers/base/power/qos.c ++++ b/drivers/base/power/qos.c +@@ -352,21 +352,26 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request); + * + * Will register the notifier into a notification chain that gets called + * upon changes to the target value for the device. ++ * ++ * If the device's constraints object doesn't exist when this routine is called, ++ * it will be created (or error code will be returned if that fails). + */ + int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) + { +- int retval = 0; ++ int ret = 0; + + mutex_lock(&dev_pm_qos_mtx); + +- /* Silently return if the constraints object is not present. */ +- if (dev->power.constraints) +- retval = blocking_notifier_chain_register( +- dev->power.constraints->notifiers, +- notifier); ++ if (!dev->power.constraints) ++ ret = dev->power.power_state.event != PM_EVENT_INVALID ? ++ dev_pm_qos_constraints_allocate(dev) : -ENODEV; ++ ++ if (!ret) ++ ret = blocking_notifier_chain_register( ++ dev->power.constraints->notifiers, notifier); + + mutex_unlock(&dev_pm_qos_mtx); +- return retval; ++ return ret; + } + EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier); + +diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c +index bb82b181..b6e9d9b7 100644 +--- a/drivers/base/power/runtime.c ++++ b/drivers/base/power/runtime.c +@@ -282,47 +282,6 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) + return retval != -EACCES ? retval : -EIO; + } + +-struct rpm_qos_data { +- ktime_t time_now; +- s64 constraint_ns; +-}; +- +-/** +- * rpm_update_qos_constraint - Update a given PM QoS constraint data. +- * @dev: Device whose timing data to use. +- * @data: PM QoS constraint data to update. +- * +- * Use the suspend timing data of @dev to update PM QoS constraint data pointed +- * to by @data. +- */ +-static int rpm_update_qos_constraint(struct device *dev, void *data) +-{ +- struct rpm_qos_data *qos = data; +- unsigned long flags; +- s64 delta_ns; +- int ret = 0; +- +- spin_lock_irqsave(&dev->power.lock, flags); +- +- if (dev->power.max_time_suspended_ns < 0) +- goto out; +- +- delta_ns = dev->power.max_time_suspended_ns - +- ktime_to_ns(ktime_sub(qos->time_now, dev->power.suspend_time)); +- if (delta_ns <= 0) { +- ret = -EBUSY; +- goto out; +- } +- +- if (qos->constraint_ns > delta_ns || qos->constraint_ns == 0) +- qos->constraint_ns = delta_ns; +- +- out: +- spin_unlock_irqrestore(&dev->power.lock, flags); +- +- return ret; +-} +- + /** + * rpm_suspend - Carry out runtime suspend of given device. + * @dev: Device to suspend. +@@ -349,7 +308,6 @@ static int rpm_suspend(struct device *dev, int rpmflags) + { + int (*callback)(struct device *); + struct device *parent = NULL; +- struct rpm_qos_data qos; + int retval; + + trace_rpm_suspend(dev, rpmflags); +@@ -444,38 +402,14 @@ static int rpm_suspend(struct device *dev, int rpmflags) + goto out; + } + +- qos.constraint_ns = __dev_pm_qos_read_value(dev); +- if (qos.constraint_ns < 0) { +- /* Negative constraint means "never suspend". */ ++ if (__dev_pm_qos_read_value(dev) < 0) { ++ /* Negative PM QoS constraint means "never suspend". */ + retval = -EPERM; + goto out; + } +- qos.constraint_ns *= NSEC_PER_USEC; +- qos.time_now = ktime_get(); + + __update_runtime_status(dev, RPM_SUSPENDING); + +- if (!dev->power.ignore_children) { +- if (dev->power.irq_safe) +- spin_unlock(&dev->power.lock); +- else +- spin_unlock_irq(&dev->power.lock); +- +- retval = device_for_each_child(dev, &qos, +- rpm_update_qos_constraint); +- +- if (dev->power.irq_safe) +- spin_lock(&dev->power.lock); +- else +- spin_lock_irq(&dev->power.lock); +- +- if (retval) +- goto fail; +- } +- +- dev->power.suspend_time = qos.time_now; +- dev->power.max_time_suspended_ns = qos.constraint_ns ? : -1; +- + if (dev->pm_domain) + callback = dev->pm_domain->ops.runtime_suspend; + else if (dev->type && dev->type->pm) +@@ -529,8 +463,6 @@ static int rpm_suspend(struct device *dev, int rpmflags) + + fail: + __update_runtime_status(dev, RPM_ACTIVE); +- dev->power.suspend_time = ktime_set(0, 0); +- dev->power.max_time_suspended_ns = -1; + dev->power.deferred_resume = false; + wake_up_all(&dev->power.wait_queue); + +@@ -705,9 +637,6 @@ static int rpm_resume(struct device *dev, int rpmflags) + if (dev->power.no_callbacks) + goto no_callback; /* Assume success. */ + +- dev->power.suspend_time = ktime_set(0, 0); +- dev->power.max_time_suspended_ns = -1; +- + __update_runtime_status(dev, RPM_RESUMING); + + if (dev->pm_domain) +@@ -1370,9 +1299,6 @@ void pm_runtime_init(struct device *dev) + setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, + (unsigned long)dev); + +- dev->power.suspend_time = ktime_set(0, 0); +- dev->power.max_time_suspended_ns = -1; +- + init_waitqueue_head(&dev->power.wait_queue); + } + +@@ -1390,28 +1316,3 @@ void pm_runtime_remove(struct device *dev) + if (dev->power.irq_safe && dev->parent) + pm_runtime_put_sync(dev->parent); + } +- +-/** +- * pm_runtime_update_max_time_suspended - Update device's suspend time data. +- * @dev: Device to handle. +- * @delta_ns: Value to subtract from the device's max_time_suspended_ns field. +- * +- * Update the device's power.max_time_suspended_ns field by subtracting +- * @delta_ns from it. The resulting value of power.max_time_suspended_ns is +- * never negative. +- */ +-void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns) +-{ +- unsigned long flags; +- +- spin_lock_irqsave(&dev->power.lock, flags); +- +- if (delta_ns > 0 && dev->power.max_time_suspended_ns > 0) { +- if (dev->power.max_time_suspended_ns > delta_ns) +- dev->power.max_time_suspended_ns -= delta_ns; +- else +- dev->power.max_time_suspended_ns = 0; +- } +- +- spin_unlock_irqrestore(&dev->power.lock, flags); +-} +diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c +index 95c12f6c..48be2ad4 100644 +--- a/drivers/base/power/sysfs.c ++++ b/drivers/base/power/sysfs.c +@@ -314,22 +314,41 @@ static ssize_t wakeup_active_count_show(struct device *dev, + + static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL); + +-static ssize_t wakeup_hit_count_show(struct device *dev, +- struct device_attribute *attr, char *buf) ++static ssize_t wakeup_abort_count_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ unsigned long count = 0; ++ bool enabled = false; ++ ++ spin_lock_irq(&dev->power.lock); ++ if (dev->power.wakeup) { ++ count = dev->power.wakeup->wakeup_count; ++ enabled = true; ++ } ++ spin_unlock_irq(&dev->power.lock); ++ return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); ++} ++ ++static DEVICE_ATTR(wakeup_abort_count, 0444, wakeup_abort_count_show, NULL); ++ ++static ssize_t wakeup_expire_count_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) + { + unsigned long count = 0; + bool enabled = false; + + spin_lock_irq(&dev->power.lock); + if (dev->power.wakeup) { +- count = dev->power.wakeup->hit_count; ++ count = dev->power.wakeup->expire_count; + enabled = true; + } + spin_unlock_irq(&dev->power.lock); + return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); + } + +-static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL); ++static DEVICE_ATTR(wakeup_expire_count, 0444, wakeup_expire_count_show, NULL); + + static ssize_t wakeup_active_show(struct device *dev, + struct device_attribute *attr, char *buf) +@@ -398,6 +417,27 @@ static ssize_t wakeup_last_time_show(struct device *dev, + } + + static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL); ++ ++#ifdef CONFIG_PM_AUTOSLEEP ++static ssize_t wakeup_prevent_sleep_time_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ s64 msec = 0; ++ bool enabled = false; ++ ++ spin_lock_irq(&dev->power.lock); ++ if (dev->power.wakeup) { ++ msec = ktime_to_ms(dev->power.wakeup->prevent_sleep_time); ++ enabled = true; ++ } ++ spin_unlock_irq(&dev->power.lock); ++ return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n"); ++} ++ ++static DEVICE_ATTR(wakeup_prevent_sleep_time_ms, 0444, ++ wakeup_prevent_sleep_time_show, NULL); ++#endif /* CONFIG_PM_AUTOSLEEP */ + #endif /* CONFIG_PM_SLEEP */ + + #ifdef CONFIG_PM_ADVANCED_DEBUG +@@ -486,11 +526,15 @@ static struct attribute *wakeup_attrs[] = { + &dev_attr_wakeup.attr, + &dev_attr_wakeup_count.attr, + &dev_attr_wakeup_active_count.attr, +- &dev_attr_wakeup_hit_count.attr, ++ &dev_attr_wakeup_abort_count.attr, ++ &dev_attr_wakeup_expire_count.attr, + &dev_attr_wakeup_active.attr, + &dev_attr_wakeup_total_time_ms.attr, + &dev_attr_wakeup_max_time_ms.attr, + &dev_attr_wakeup_last_time_ms.attr, ++#ifdef CONFIG_PM_AUTOSLEEP ++ &dev_attr_wakeup_prevent_sleep_time_ms.attr, ++#endif + #endif + NULL, + }; +diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c +index 2a3e581b..e6ee5e80 100644 +--- a/drivers/base/power/wakeup.c ++++ b/drivers/base/power/wakeup.c +@@ -14,16 +14,15 @@ + #include + #include + #include ++#include + + #include "power.h" + +-#define TIMEOUT 100 +- + /* + * If set, the suspend/hibernate code will abort transitions to a sleep state + * if wakeup events are registered during or immediately before the transition. + */ +-bool events_check_enabled; ++bool events_check_enabled __read_mostly; + + /* + * Combined counters of registered wakeup events and wakeup events in progress. +@@ -52,6 +51,8 @@ static void pm_wakeup_timer_fn(unsigned long data); + + static LIST_HEAD(wakeup_sources); + ++static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue); ++ + /** + * wakeup_source_prepare - Prepare a new wakeup source for initialization. + * @ws: Wakeup source to prepare. +@@ -126,16 +127,19 @@ EXPORT_SYMBOL_GPL(wakeup_source_destroy); + */ + void wakeup_source_add(struct wakeup_source *ws) + { ++ unsigned long flags; ++ + if (WARN_ON(!ws)) + return; + + spin_lock_init(&ws->lock); + setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws); + ws->active = false; ++ ws->last_time = ktime_get(); + +- spin_lock_irq(&events_lock); ++ spin_lock_irqsave(&events_lock, flags); + list_add_rcu(&ws->entry, &wakeup_sources); +- spin_unlock_irq(&events_lock); ++ spin_unlock_irqrestore(&events_lock, flags); + } + EXPORT_SYMBOL_GPL(wakeup_source_add); + +@@ -145,12 +149,14 @@ EXPORT_SYMBOL_GPL(wakeup_source_add); + */ + void wakeup_source_remove(struct wakeup_source *ws) + { ++ unsigned long flags; ++ + if (WARN_ON(!ws)) + return; + +- spin_lock_irq(&events_lock); ++ spin_lock_irqsave(&events_lock, flags); + list_del_rcu(&ws->entry); +- spin_unlock_irq(&events_lock); ++ spin_unlock_irqrestore(&events_lock, flags); + synchronize_rcu(); + } + EXPORT_SYMBOL_GPL(wakeup_source_remove); +@@ -374,12 +380,33 @@ EXPORT_SYMBOL_GPL(device_set_wakeup_enable); + */ + static void wakeup_source_activate(struct wakeup_source *ws) + { ++ unsigned int cec; ++ + ws->active = true; + ws->active_count++; + ws->last_time = ktime_get(); ++ if (ws->autosleep_enabled) ++ ws->start_prevent_time = ws->last_time; + + /* Increment the counter of events in progress. */ +- atomic_inc(&combined_event_count); ++ cec = atomic_inc_return(&combined_event_count); ++ ++ trace_wakeup_source_activate(ws->name, cec); ++} ++ ++/** ++ * wakeup_source_report_event - Report wakeup event using the given source. ++ * @ws: Wakeup source to report the event for. ++ */ ++static void wakeup_source_report_event(struct wakeup_source *ws) ++{ ++ ws->event_count++; ++ /* This is racy, but the counter is approximate anyway. */ ++ if (events_check_enabled) ++ ws->wakeup_count++; ++ ++ if (!ws->active) ++ wakeup_source_activate(ws); + } + + /** +@@ -397,10 +424,7 @@ void __pm_stay_awake(struct wakeup_source *ws) + + spin_lock_irqsave(&ws->lock, flags); + +- ws->event_count++; +- if (!ws->active) +- wakeup_source_activate(ws); +- ++ wakeup_source_report_event(ws); + del_timer(&ws->timer); + ws->timer_expires = 0; + +@@ -432,6 +456,17 @@ void pm_stay_awake(struct device *dev) + } + EXPORT_SYMBOL_GPL(pm_stay_awake); + ++#ifdef CONFIG_PM_AUTOSLEEP ++static void update_prevent_sleep_time(struct wakeup_source *ws, ktime_t now) ++{ ++ ktime_t delta = ktime_sub(now, ws->start_prevent_time); ++ ws->prevent_sleep_time = ktime_add(ws->prevent_sleep_time, delta); ++} ++#else ++static inline void update_prevent_sleep_time(struct wakeup_source *ws, ++ ktime_t now) {} ++#endif ++ + /** + * wakup_source_deactivate - Mark given wakeup source as inactive. + * @ws: Wakeup source to handle. +@@ -442,6 +477,7 @@ EXPORT_SYMBOL_GPL(pm_stay_awake); + */ + static void wakeup_source_deactivate(struct wakeup_source *ws) + { ++ unsigned int cnt, inpr, cec; + ktime_t duration; + ktime_t now; + +@@ -468,14 +504,23 @@ static void wakeup_source_deactivate(struct wakeup_source *ws) + if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time)) + ws->max_time = duration; + ++ ws->last_time = now; + del_timer(&ws->timer); + ws->timer_expires = 0; + ++ if (ws->autosleep_enabled) ++ update_prevent_sleep_time(ws, now); ++ + /* + * Increment the counter of registered wakeup events and decrement the + * couter of wakeup events in progress simultaneously. + */ +- atomic_add(MAX_IN_PROGRESS, &combined_event_count); ++ cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count); ++ trace_wakeup_source_deactivate(ws->name, cec); ++ ++ split_counters(&cnt, &inpr); ++ if (!inpr && waitqueue_active(&wakeup_count_wait_queue)) ++ wake_up(&wakeup_count_wait_queue); + } + + /** +@@ -536,8 +581,10 @@ static void pm_wakeup_timer_fn(unsigned long data) + spin_lock_irqsave(&ws->lock, flags); + + if (ws->active && ws->timer_expires +- && time_after_eq(jiffies, ws->timer_expires)) ++ && time_after_eq(jiffies, ws->timer_expires)) { + wakeup_source_deactivate(ws); ++ ws->expire_count++; ++ } + + spin_unlock_irqrestore(&ws->lock, flags); + } +@@ -564,9 +611,7 @@ void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) + + spin_lock_irqsave(&ws->lock, flags); + +- ws->event_count++; +- if (!ws->active) +- wakeup_source_activate(ws); ++ wakeup_source_report_event(ws); + + if (!msec) { + wakeup_source_deactivate(ws); +@@ -608,21 +653,28 @@ void pm_wakeup_event(struct device *dev, unsigned int msec) + } + EXPORT_SYMBOL_GPL(pm_wakeup_event); + +-/** +- * pm_wakeup_update_hit_counts - Update hit counts of all active wakeup sources. +- */ +-static void pm_wakeup_update_hit_counts(void) ++static void print_active_wakeup_sources(void) + { +- unsigned long flags; + struct wakeup_source *ws; ++ int active = 0; ++ struct wakeup_source *last_activity_ws = NULL; + + rcu_read_lock(); + list_for_each_entry_rcu(ws, &wakeup_sources, entry) { +- spin_lock_irqsave(&ws->lock, flags); +- if (ws->active) +- ws->hit_count++; +- spin_unlock_irqrestore(&ws->lock, flags); ++ if (ws->active) { ++ pr_info("active wakeup source: %s\n", ws->name); ++ active = 1; ++ } else if (!active && ++ (!last_activity_ws || ++ ktime_to_ns(ws->last_time) > ++ ktime_to_ns(last_activity_ws->last_time))) { ++ last_activity_ws = ws; ++ } + } ++ ++ if (!active && last_activity_ws) ++ pr_info("last active wakeup source: %s\n", ++ last_activity_ws->name); + rcu_read_unlock(); + } + +@@ -648,32 +700,42 @@ bool pm_wakeup_pending(void) + events_check_enabled = !ret; + } + spin_unlock_irqrestore(&events_lock, flags); ++ + if (ret) +- pm_wakeup_update_hit_counts(); ++ print_active_wakeup_sources(); ++ + return ret; + } + + /** + * pm_get_wakeup_count - Read the number of registered wakeup events. + * @count: Address to store the value at. ++ * @block: Whether or not to block. + * +- * Store the number of registered wakeup events at the address in @count. Block +- * if the current number of wakeup events being processed is nonzero. ++ * Store the number of registered wakeup events at the address in @count. If ++ * @block is set, block until the current number of wakeup events being ++ * processed is zero. + * +- * Return 'false' if the wait for the number of wakeup events being processed to +- * drop down to zero has been interrupted by a signal (and the current number +- * of wakeup events being processed is still nonzero). Otherwise return 'true'. ++ * Return 'false' if the current number of wakeup events being processed is ++ * nonzero. Otherwise return 'true'. + */ +-bool pm_get_wakeup_count(unsigned int *count) ++bool pm_get_wakeup_count(unsigned int *count, bool block) + { + unsigned int cnt, inpr; + +- for (;;) { +- split_counters(&cnt, &inpr); +- if (inpr == 0 || signal_pending(current)) +- break; +- pm_wakeup_update_hit_counts(); +- schedule_timeout_interruptible(msecs_to_jiffies(TIMEOUT)); ++ if (block) { ++ DEFINE_WAIT(wait); ++ ++ for (;;) { ++ prepare_to_wait(&wakeup_count_wait_queue, &wait, ++ TASK_INTERRUPTIBLE); ++ split_counters(&cnt, &inpr); ++ if (inpr == 0 || signal_pending(current)) ++ break; ++ ++ schedule(); ++ } ++ finish_wait(&wakeup_count_wait_queue, &wait); + } + + split_counters(&cnt, &inpr); +@@ -694,20 +756,47 @@ bool pm_get_wakeup_count(unsigned int *count) + bool pm_save_wakeup_count(unsigned int count) + { + unsigned int cnt, inpr; ++ unsigned long flags; + + events_check_enabled = false; +- spin_lock_irq(&events_lock); ++ spin_lock_irqsave(&events_lock, flags); + split_counters(&cnt, &inpr); + if (cnt == count && inpr == 0) { + saved_count = count; + events_check_enabled = true; + } +- spin_unlock_irq(&events_lock); +- if (!events_check_enabled) +- pm_wakeup_update_hit_counts(); ++ spin_unlock_irqrestore(&events_lock, flags); + return events_check_enabled; + } + ++#ifdef CONFIG_PM_AUTOSLEEP ++/** ++ * pm_wakep_autosleep_enabled - Modify autosleep_enabled for all wakeup sources. ++ * @enabled: Whether to set or to clear the autosleep_enabled flags. ++ */ ++void pm_wakep_autosleep_enabled(bool set) ++{ ++ struct wakeup_source *ws; ++ ktime_t now = ktime_get(); ++ ++ rcu_read_lock(); ++ list_for_each_entry_rcu(ws, &wakeup_sources, entry) { ++ spin_lock_irq(&ws->lock); ++ if (ws->autosleep_enabled != set) { ++ ws->autosleep_enabled = set; ++ if (ws->active) { ++ if (set) ++ ws->start_prevent_time = now; ++ else ++ update_prevent_sleep_time(ws, now); ++ } ++ } ++ spin_unlock_irq(&ws->lock); ++ } ++ rcu_read_unlock(); ++} ++#endif /* CONFIG_PM_AUTOSLEEP */ ++ + static struct dentry *wakeup_sources_stats_dentry; + + /** +@@ -723,27 +812,37 @@ static int print_wakeup_source_stats(struct seq_file *m, + ktime_t max_time; + unsigned long active_count; + ktime_t active_time; ++ ktime_t prevent_sleep_time; + int ret; + + spin_lock_irqsave(&ws->lock, flags); + + total_time = ws->total_time; + max_time = ws->max_time; ++ prevent_sleep_time = ws->prevent_sleep_time; + active_count = ws->active_count; + if (ws->active) { +- active_time = ktime_sub(ktime_get(), ws->last_time); ++ ktime_t now = ktime_get(); ++ ++ active_time = ktime_sub(now, ws->last_time); + total_time = ktime_add(total_time, active_time); + if (active_time.tv64 > max_time.tv64) + max_time = active_time; ++ ++ if (ws->autosleep_enabled) ++ prevent_sleep_time = ktime_add(prevent_sleep_time, ++ ktime_sub(now, ws->start_prevent_time)); + } else { + active_time = ktime_set(0, 0); + } + +- ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t" +- "%lld\t\t%lld\t\t%lld\t\t%lld\n", +- ws->name, active_count, ws->event_count, ws->hit_count, ++ ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t" ++ "%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n", ++ ws->name, active_count, ws->event_count, ++ ws->wakeup_count, ws->expire_count, + ktime_to_ms(active_time), ktime_to_ms(total_time), +- ktime_to_ms(max_time), ktime_to_ms(ws->last_time)); ++ ktime_to_ms(max_time), ktime_to_ms(ws->last_time), ++ ktime_to_ms(prevent_sleep_time)); + + spin_unlock_irqrestore(&ws->lock, flags); + +@@ -758,8 +857,9 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused) + { + struct wakeup_source *ws; + +- seq_puts(m, "name\t\tactive_count\tevent_count\thit_count\t" +- "active_since\ttotal_time\tmax_time\tlast_change\n"); ++ seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t" ++ "expire_count\tactive_since\ttotal_time\tmax_time\t" ++ "last_change\tprevent_suspend_time\n"); + + rcu_read_lock(); + list_for_each_entry_rcu(ws, &wakeup_sources, entry) +diff --git a/drivers/base/sw_sync.c b/drivers/base/sw_sync.c +new file mode 100644 +index 00000000..b4d8529e +--- /dev/null ++++ b/drivers/base/sw_sync.c +@@ -0,0 +1,262 @@ ++/* ++ * drivers/base/sw_sync.c ++ * ++ * Copyright (C) 2012 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int sw_sync_cmp(u32 a, u32 b) ++{ ++ if (a == b) ++ return 0; ++ ++ return ((s32)a - (s32)b) < 0 ? -1 : 1; ++} ++ ++struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) ++{ ++ struct sw_sync_pt *pt; ++ ++ pt = (struct sw_sync_pt *) ++ sync_pt_create(&obj->obj, sizeof(struct sw_sync_pt)); ++ ++ pt->value = value; ++ ++ return (struct sync_pt *)pt; ++} ++EXPORT_SYMBOL(sw_sync_pt_create); ++ ++static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt) ++{ ++ struct sw_sync_pt *pt = (struct sw_sync_pt *) sync_pt; ++ struct sw_sync_timeline *obj = ++ (struct sw_sync_timeline *)sync_pt->parent; ++ ++ return (struct sync_pt *) sw_sync_pt_create(obj, pt->value); ++} ++ ++static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt) ++{ ++ struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; ++ struct sw_sync_timeline *obj = ++ (struct sw_sync_timeline *)sync_pt->parent; ++ ++ return sw_sync_cmp(obj->value, pt->value) >= 0; ++} ++ ++static int sw_sync_pt_compare(struct sync_pt *a, struct sync_pt *b) ++{ ++ struct sw_sync_pt *pt_a = (struct sw_sync_pt *)a; ++ struct sw_sync_pt *pt_b = (struct sw_sync_pt *)b; ++ ++ return sw_sync_cmp(pt_a->value, pt_b->value); ++} ++ ++static int sw_sync_fill_driver_data(struct sync_pt *sync_pt, ++ void *data, int size) ++{ ++ struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; ++ ++ if (size < sizeof(pt->value)) ++ return -ENOMEM; ++ ++ memcpy(data, &pt->value, sizeof(pt->value)); ++ ++ return sizeof(pt->value); ++} ++ ++static void sw_sync_timeline_value_str(struct sync_timeline *sync_timeline, ++ char *str, int size) ++{ ++ struct sw_sync_timeline *timeline = ++ (struct sw_sync_timeline *)sync_timeline; ++ snprintf(str, size, "%d", timeline->value); ++} ++ ++static void sw_sync_pt_value_str(struct sync_pt *sync_pt, ++ char *str, int size) ++{ ++ struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; ++ snprintf(str, size, "%d", pt->value); ++} ++ ++struct sync_timeline_ops sw_sync_timeline_ops = { ++ .driver_name = "sw_sync", ++ .dup = sw_sync_pt_dup, ++ .has_signaled = sw_sync_pt_has_signaled, ++ .compare = sw_sync_pt_compare, ++ .fill_driver_data = sw_sync_fill_driver_data, ++ .timeline_value_str = sw_sync_timeline_value_str, ++ .pt_value_str = sw_sync_pt_value_str, ++}; ++ ++ ++struct sw_sync_timeline *sw_sync_timeline_create(const char *name) ++{ ++ struct sw_sync_timeline *obj = (struct sw_sync_timeline *) ++ sync_timeline_create(&sw_sync_timeline_ops, ++ sizeof(struct sw_sync_timeline), ++ name); ++ ++ return obj; ++} ++EXPORT_SYMBOL(sw_sync_timeline_create); ++ ++void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) ++{ ++ obj->value += inc; ++ ++ sync_timeline_signal(&obj->obj); ++} ++EXPORT_SYMBOL(sw_sync_timeline_inc); ++ ++#ifdef CONFIG_SW_SYNC_USER ++/* *WARNING* ++ * ++ * improper use of this can result in deadlocking kernel drivers from userspace. ++ */ ++ ++/* opening sw_sync create a new sync obj */ ++int sw_sync_open(struct inode *inode, struct file *file) ++{ ++ struct sw_sync_timeline *obj; ++ char task_comm[TASK_COMM_LEN]; ++ ++ get_task_comm(task_comm, current); ++ ++ obj = sw_sync_timeline_create(task_comm); ++ if (obj == NULL) ++ return -ENOMEM; ++ ++ file->private_data = obj; ++ ++ return 0; ++} ++ ++int sw_sync_release(struct inode *inode, struct file *file) ++{ ++ struct sw_sync_timeline *obj = file->private_data; ++ sync_timeline_destroy(&obj->obj); ++ return 0; ++} ++ ++long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, unsigned long arg) ++{ ++ int fd = get_unused_fd(); ++ int err; ++ struct sync_pt *pt; ++ struct sync_fence *fence; ++ struct sw_sync_create_fence_data data; ++ ++ if (fd < 0) ++ return fd; ++ ++ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { ++ err = -EFAULT; ++ goto err; ++ } ++ ++ pt = sw_sync_pt_create(obj, data.value); ++ if (pt == NULL) { ++ err = -ENOMEM; ++ goto err; ++ } ++ ++ data.name[sizeof(data.name) - 1] = '\0'; ++ fence = sync_fence_create(data.name, pt); ++ if (fence == NULL) { ++ sync_pt_free(pt); ++ err = -ENOMEM; ++ goto err; ++ } ++ ++ data.fence = fd; ++ if (copy_to_user((void __user *)arg, &data, sizeof(data))) { ++ sync_fence_put(fence); ++ err = -EFAULT; ++ goto err; ++ } ++ ++ sync_fence_install(fence, fd); ++ ++ return 0; ++ ++err: ++ put_unused_fd(fd); ++ return err; ++} ++ ++long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg) ++{ ++ u32 value; ++ ++ if (copy_from_user(&value, (void __user *)arg, sizeof(value))) ++ return -EFAULT; ++ ++ sw_sync_timeline_inc(obj, value); ++ ++ return 0; ++} ++ ++long sw_sync_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct sw_sync_timeline *obj = file->private_data; ++ ++ switch (cmd) { ++ case SW_SYNC_IOC_CREATE_FENCE: ++ return sw_sync_ioctl_create_fence(obj, arg); ++ ++ case SW_SYNC_IOC_INC: ++ return sw_sync_ioctl_inc(obj, arg); ++ ++ default: ++ return -ENOTTY; ++ } ++} ++ ++static const struct file_operations sw_sync_fops = { ++ .owner = THIS_MODULE, ++ .open = sw_sync_open, ++ .release = sw_sync_release, ++ .unlocked_ioctl = sw_sync_ioctl, ++}; ++ ++static struct miscdevice sw_sync_dev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "sw_sync", ++ .fops = &sw_sync_fops, ++}; ++ ++int __init sw_sync_device_init(void) ++{ ++ return misc_register(&sw_sync_dev); ++} ++ ++void __exit sw_sync_device_remove(void) ++{ ++ misc_deregister(&sw_sync_dev); ++} ++ ++module_init(sw_sync_device_init); ++module_exit(sw_sync_device_remove); ++ ++#endif /* CONFIG_SW_SYNC_USER */ +diff --git a/drivers/base/sync.c b/drivers/base/sync.c +new file mode 100644 +index 00000000..2e359968 +--- /dev/null ++++ b/drivers/base/sync.c +@@ -0,0 +1,1015 @@ ++/* ++ * drivers/base/sync.c ++ * ++ * Copyright (C) 2012 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define CREATE_TRACE_POINTS ++#include ++ ++static void sync_fence_signal_pt(struct sync_pt *pt); ++static int _sync_pt_has_signaled(struct sync_pt *pt); ++static void sync_fence_free(struct kref *kref); ++static void sync_dump(void); ++ ++static LIST_HEAD(sync_timeline_list_head); ++static DEFINE_SPINLOCK(sync_timeline_list_lock); ++ ++static LIST_HEAD(sync_fence_list_head); ++static DEFINE_SPINLOCK(sync_fence_list_lock); ++ ++struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, ++ int size, const char *name) ++{ ++ struct sync_timeline *obj; ++ unsigned long flags; ++ ++ if (size < sizeof(struct sync_timeline)) ++ return NULL; ++ ++ obj = kzalloc(size, GFP_KERNEL); ++ if (obj == NULL) ++ return NULL; ++ ++ kref_init(&obj->kref); ++ obj->ops = ops; ++ strlcpy(obj->name, name, sizeof(obj->name)); ++ ++ INIT_LIST_HEAD(&obj->child_list_head); ++ spin_lock_init(&obj->child_list_lock); ++ ++ INIT_LIST_HEAD(&obj->active_list_head); ++ spin_lock_init(&obj->active_list_lock); ++ ++ spin_lock_irqsave(&sync_timeline_list_lock, flags); ++ list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head); ++ spin_unlock_irqrestore(&sync_timeline_list_lock, flags); ++ ++ return obj; ++} ++EXPORT_SYMBOL(sync_timeline_create); ++ ++static void sync_timeline_free(struct kref *kref) ++{ ++ struct sync_timeline *obj = ++ container_of(kref, struct sync_timeline, kref); ++ unsigned long flags; ++ ++ if (obj->ops->release_obj) ++ obj->ops->release_obj(obj); ++ ++ spin_lock_irqsave(&sync_timeline_list_lock, flags); ++ list_del(&obj->sync_timeline_list); ++ spin_unlock_irqrestore(&sync_timeline_list_lock, flags); ++ ++ kfree(obj); ++} ++ ++void sync_timeline_destroy(struct sync_timeline *obj) ++{ ++ obj->destroyed = true; ++ ++ /* ++ * If this is not the last reference, signal any children ++ * that their parent is going away. ++ */ ++ ++ if (!kref_put(&obj->kref, sync_timeline_free)) ++ sync_timeline_signal(obj); ++} ++EXPORT_SYMBOL(sync_timeline_destroy); ++ ++static void sync_timeline_add_pt(struct sync_timeline *obj, struct sync_pt *pt) ++{ ++ unsigned long flags; ++ ++ pt->parent = obj; ++ ++ spin_lock_irqsave(&obj->child_list_lock, flags); ++ list_add_tail(&pt->child_list, &obj->child_list_head); ++ spin_unlock_irqrestore(&obj->child_list_lock, flags); ++} ++ ++static void sync_timeline_remove_pt(struct sync_pt *pt) ++{ ++ struct sync_timeline *obj = pt->parent; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&obj->active_list_lock, flags); ++ if (!list_empty(&pt->active_list)) ++ list_del_init(&pt->active_list); ++ spin_unlock_irqrestore(&obj->active_list_lock, flags); ++ ++ spin_lock_irqsave(&obj->child_list_lock, flags); ++ if (!list_empty(&pt->child_list)) { ++ list_del_init(&pt->child_list); ++ } ++ spin_unlock_irqrestore(&obj->child_list_lock, flags); ++} ++ ++void sync_timeline_signal(struct sync_timeline *obj) ++{ ++ unsigned long flags; ++ LIST_HEAD(signaled_pts); ++ struct list_head *pos, *n; ++ ++ trace_sync_timeline(obj); ++ ++ spin_lock_irqsave(&obj->active_list_lock, flags); ++ ++ list_for_each_safe(pos, n, &obj->active_list_head) { ++ struct sync_pt *pt = ++ container_of(pos, struct sync_pt, active_list); ++ ++ if (_sync_pt_has_signaled(pt)) { ++ list_del_init(pos); ++ list_add(&pt->signaled_list, &signaled_pts); ++ kref_get(&pt->fence->kref); ++ } ++ } ++ ++ spin_unlock_irqrestore(&obj->active_list_lock, flags); ++ ++ list_for_each_safe(pos, n, &signaled_pts) { ++ struct sync_pt *pt = ++ container_of(pos, struct sync_pt, signaled_list); ++ ++ list_del_init(pos); ++ sync_fence_signal_pt(pt); ++ kref_put(&pt->fence->kref, sync_fence_free); ++ } ++} ++EXPORT_SYMBOL(sync_timeline_signal); ++ ++struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size) ++{ ++ struct sync_pt *pt; ++ ++ if (size < sizeof(struct sync_pt)) ++ return NULL; ++ ++ pt = kzalloc(size, GFP_KERNEL); ++ if (pt == NULL) ++ return NULL; ++ ++ INIT_LIST_HEAD(&pt->active_list); ++ kref_get(&parent->kref); ++ sync_timeline_add_pt(parent, pt); ++ ++ return pt; ++} ++EXPORT_SYMBOL(sync_pt_create); ++ ++void sync_pt_free(struct sync_pt *pt) ++{ ++ if (pt->parent->ops->free_pt) ++ pt->parent->ops->free_pt(pt); ++ ++ sync_timeline_remove_pt(pt); ++ ++ kref_put(&pt->parent->kref, sync_timeline_free); ++ ++ kfree(pt); ++} ++EXPORT_SYMBOL(sync_pt_free); ++ ++/* call with pt->parent->active_list_lock held */ ++static int _sync_pt_has_signaled(struct sync_pt *pt) ++{ ++ int old_status = pt->status; ++ ++ if (!pt->status) ++ pt->status = pt->parent->ops->has_signaled(pt); ++ ++ if (!pt->status && pt->parent->destroyed) ++ pt->status = -ENOENT; ++ ++ if (pt->status != old_status) ++ pt->timestamp = ktime_get(); ++ ++ return pt->status; ++} ++ ++static struct sync_pt *sync_pt_dup(struct sync_pt *pt) ++{ ++ return pt->parent->ops->dup(pt); ++} ++ ++/* Adds a sync pt to the active queue. Called when added to a fence */ ++static void sync_pt_activate(struct sync_pt *pt) ++{ ++ struct sync_timeline *obj = pt->parent; ++ unsigned long flags; ++ int err; ++ ++ spin_lock_irqsave(&obj->active_list_lock, flags); ++ ++ err = _sync_pt_has_signaled(pt); ++ if (err != 0) ++ goto out; ++ ++ list_add_tail(&pt->active_list, &obj->active_list_head); ++ ++out: ++ spin_unlock_irqrestore(&obj->active_list_lock, flags); ++} ++ ++static int sync_fence_release(struct inode *inode, struct file *file); ++static unsigned int sync_fence_poll(struct file *file, poll_table *wait); ++static long sync_fence_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg); ++ ++ ++static const struct file_operations sync_fence_fops = { ++ .release = sync_fence_release, ++ .poll = sync_fence_poll, ++ .unlocked_ioctl = sync_fence_ioctl, ++}; ++ ++static struct sync_fence *sync_fence_alloc(const char *name) ++{ ++ struct sync_fence *fence; ++ unsigned long flags; ++ ++ fence = kzalloc(sizeof(struct sync_fence), GFP_KERNEL); ++ if (fence == NULL) ++ return NULL; ++ ++ fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops, ++ fence, 0); ++ if (fence->file == NULL) ++ goto err; ++ ++ kref_init(&fence->kref); ++ strlcpy(fence->name, name, sizeof(fence->name)); ++ ++ INIT_LIST_HEAD(&fence->pt_list_head); ++ INIT_LIST_HEAD(&fence->waiter_list_head); ++ spin_lock_init(&fence->waiter_list_lock); ++ ++ init_waitqueue_head(&fence->wq); ++ ++ spin_lock_irqsave(&sync_fence_list_lock, flags); ++ list_add_tail(&fence->sync_fence_list, &sync_fence_list_head); ++ spin_unlock_irqrestore(&sync_fence_list_lock, flags); ++ ++ return fence; ++ ++err: ++ kfree(fence); ++ return NULL; ++} ++ ++/* TODO: implement a create which takes more that one sync_pt */ ++struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt) ++{ ++ struct sync_fence *fence; ++ ++ if (pt->fence) ++ return NULL; ++ ++ fence = sync_fence_alloc(name); ++ if (fence == NULL) ++ return NULL; ++ ++ pt->fence = fence; ++ list_add(&pt->pt_list, &fence->pt_list_head); ++ sync_pt_activate(pt); ++ ++ /* ++ * signal the fence in case pt was activated before ++ * sync_pt_activate(pt) was called ++ */ ++ sync_fence_signal_pt(pt); ++ ++ return fence; ++} ++EXPORT_SYMBOL(sync_fence_create); ++ ++static int sync_fence_copy_pts(struct sync_fence *dst, struct sync_fence *src) ++{ ++ struct list_head *pos; ++ ++ list_for_each(pos, &src->pt_list_head) { ++ struct sync_pt *orig_pt = ++ container_of(pos, struct sync_pt, pt_list); ++ struct sync_pt *new_pt = sync_pt_dup(orig_pt); ++ ++ if (new_pt == NULL) ++ return -ENOMEM; ++ ++ new_pt->fence = dst; ++ list_add(&new_pt->pt_list, &dst->pt_list_head); ++ } ++ ++ return 0; ++} ++ ++static int sync_fence_merge_pts(struct sync_fence *dst, struct sync_fence *src) ++{ ++ struct list_head *src_pos, *dst_pos, *n; ++ ++ list_for_each(src_pos, &src->pt_list_head) { ++ struct sync_pt *src_pt = ++ container_of(src_pos, struct sync_pt, pt_list); ++ bool collapsed = false; ++ ++ list_for_each_safe(dst_pos, n, &dst->pt_list_head) { ++ struct sync_pt *dst_pt = ++ container_of(dst_pos, struct sync_pt, pt_list); ++ /* collapse two sync_pts on the same timeline ++ * to a single sync_pt that will signal at ++ * the later of the two ++ */ ++ if (dst_pt->parent == src_pt->parent) { ++ if (dst_pt->parent->ops->compare(dst_pt, src_pt) == -1) { ++ struct sync_pt *new_pt = ++ sync_pt_dup(src_pt); ++ if (new_pt == NULL) ++ return -ENOMEM; ++ ++ new_pt->fence = dst; ++ list_replace(&dst_pt->pt_list, ++ &new_pt->pt_list); ++ sync_pt_free(dst_pt); ++ } ++ collapsed = true; ++ break; ++ } ++ } ++ ++ if (!collapsed) { ++ struct sync_pt *new_pt = sync_pt_dup(src_pt); ++ ++ if (new_pt == NULL) ++ return -ENOMEM; ++ ++ new_pt->fence = dst; ++ list_add(&new_pt->pt_list, &dst->pt_list_head); ++ } ++ } ++ ++ return 0; ++} ++ ++static void sync_fence_detach_pts(struct sync_fence *fence) ++{ ++ struct list_head *pos, *n; ++ ++ list_for_each_safe(pos, n, &fence->pt_list_head) { ++ struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list); ++ sync_timeline_remove_pt(pt); ++ } ++} ++ ++static void sync_fence_free_pts(struct sync_fence *fence) ++{ ++ struct list_head *pos, *n; ++ ++ list_for_each_safe(pos, n, &fence->pt_list_head) { ++ struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list); ++ sync_pt_free(pt); ++ } ++} ++ ++struct sync_fence *sync_fence_fdget(int fd) ++{ ++ struct file *file = fget(fd); ++ ++ if (file == NULL) ++ return NULL; ++ ++ if (file->f_op != &sync_fence_fops) ++ goto err; ++ ++ return file->private_data; ++ ++err: ++ fput(file); ++ return NULL; ++} ++EXPORT_SYMBOL(sync_fence_fdget); ++ ++void sync_fence_put(struct sync_fence *fence) ++{ ++ fput(fence->file); ++} ++EXPORT_SYMBOL(sync_fence_put); ++ ++void sync_fence_install(struct sync_fence *fence, int fd) ++{ ++ fd_install(fd, fence->file); ++} ++EXPORT_SYMBOL(sync_fence_install); ++ ++static int sync_fence_get_status(struct sync_fence *fence) ++{ ++ struct list_head *pos; ++ int status = 1; ++ ++ list_for_each(pos, &fence->pt_list_head) { ++ struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list); ++ int pt_status = pt->status; ++ ++ if (pt_status < 0) { ++ status = pt_status; ++ break; ++ } else if (status == 1) { ++ status = pt_status; ++ } ++ } ++ ++ return status; ++} ++ ++struct sync_fence *sync_fence_merge(const char *name, ++ struct sync_fence *a, struct sync_fence *b) ++{ ++ struct sync_fence *fence; ++ struct list_head *pos; ++ int err; ++ ++ fence = sync_fence_alloc(name); ++ if (fence == NULL) ++ return NULL; ++ ++ err = sync_fence_copy_pts(fence, a); ++ if (err < 0) ++ goto err; ++ ++ err = sync_fence_merge_pts(fence, b); ++ if (err < 0) ++ goto err; ++ ++ list_for_each(pos, &fence->pt_list_head) { ++ struct sync_pt *pt = ++ container_of(pos, struct sync_pt, pt_list); ++ sync_pt_activate(pt); ++ } ++ ++ /* ++ * signal the fence in case one of it's pts were activated before ++ * they were activated ++ */ ++ sync_fence_signal_pt(list_first_entry(&fence->pt_list_head, ++ struct sync_pt, ++ pt_list)); ++ ++ return fence; ++err: ++ sync_fence_free_pts(fence); ++ kfree(fence); ++ return NULL; ++} ++EXPORT_SYMBOL(sync_fence_merge); ++ ++static void sync_fence_signal_pt(struct sync_pt *pt) ++{ ++ LIST_HEAD(signaled_waiters); ++ struct sync_fence *fence = pt->fence; ++ struct list_head *pos; ++ struct list_head *n; ++ unsigned long flags; ++ int status; ++ ++ status = sync_fence_get_status(fence); ++ ++ spin_lock_irqsave(&fence->waiter_list_lock, flags); ++ /* ++ * this should protect against two threads racing on the signaled ++ * false -> true transition ++ */ ++ if (status && !fence->status) { ++ list_for_each_safe(pos, n, &fence->waiter_list_head) ++ list_move(pos, &signaled_waiters); ++ ++ fence->status = status; ++ } else { ++ status = 0; ++ } ++ spin_unlock_irqrestore(&fence->waiter_list_lock, flags); ++ ++ if (status) { ++ list_for_each_safe(pos, n, &signaled_waiters) { ++ struct sync_fence_waiter *waiter = ++ container_of(pos, struct sync_fence_waiter, ++ waiter_list); ++ ++ list_del(pos); ++ waiter->callback(fence, waiter); ++ } ++ wake_up(&fence->wq); ++ } ++} ++ ++int sync_fence_wait_async(struct sync_fence *fence, ++ struct sync_fence_waiter *waiter) ++{ ++ unsigned long flags; ++ int err = 0; ++ ++ spin_lock_irqsave(&fence->waiter_list_lock, flags); ++ ++ if (fence->status) { ++ err = fence->status; ++ goto out; ++ } ++ ++ list_add_tail(&waiter->waiter_list, &fence->waiter_list_head); ++out: ++ spin_unlock_irqrestore(&fence->waiter_list_lock, flags); ++ ++ return err; ++} ++EXPORT_SYMBOL(sync_fence_wait_async); ++ ++int sync_fence_cancel_async(struct sync_fence *fence, ++ struct sync_fence_waiter *waiter) ++{ ++ struct list_head *pos; ++ struct list_head *n; ++ unsigned long flags; ++ int ret = -ENOENT; ++ ++ spin_lock_irqsave(&fence->waiter_list_lock, flags); ++ /* ++ * Make sure waiter is still in waiter_list because it is possible for ++ * the waiter to be removed from the list while the callback is still ++ * pending. ++ */ ++ list_for_each_safe(pos, n, &fence->waiter_list_head) { ++ struct sync_fence_waiter *list_waiter = ++ container_of(pos, struct sync_fence_waiter, ++ waiter_list); ++ if (list_waiter == waiter) { ++ list_del(pos); ++ ret = 0; ++ break; ++ } ++ } ++ spin_unlock_irqrestore(&fence->waiter_list_lock, flags); ++ return ret; ++} ++EXPORT_SYMBOL(sync_fence_cancel_async); ++ ++static bool sync_fence_check(struct sync_fence *fence) ++{ ++ /* ++ * Make sure that reads to fence->status are ordered with the ++ * wait queue event triggering ++ */ ++ smp_rmb(); ++ return fence->status != 0; ++} ++ ++int sync_fence_wait(struct sync_fence *fence, long timeout) ++{ ++ int err = 0; ++ struct sync_pt *pt; ++ ++ trace_sync_wait(fence, 1); ++ list_for_each_entry(pt, &fence->pt_list_head, pt_list) ++ trace_sync_pt(pt); ++ ++ if (timeout > 0) { ++ timeout = msecs_to_jiffies(timeout); ++ err = wait_event_interruptible_timeout(fence->wq, ++ sync_fence_check(fence), ++ timeout); ++ } else if (timeout < 0) { ++ err = wait_event_interruptible(fence->wq, ++ sync_fence_check(fence)); ++ } ++ trace_sync_wait(fence, 0); ++ ++ if (err < 0) ++ return err; ++ ++ if (fence->status < 0) { ++ pr_info("fence error %d on [%p]\n", fence->status, fence); ++ sync_dump(); ++ return fence->status; ++ } ++ ++ if (fence->status == 0) { ++ if (timeout > 0) { ++ pr_info("fence timeout on [%p] after %dms\n", fence, ++ jiffies_to_msecs(timeout)); ++ sync_dump(); ++ } ++ return -ETIME; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(sync_fence_wait); ++ ++static void sync_fence_free(struct kref *kref) ++{ ++ struct sync_fence *fence = container_of(kref, struct sync_fence, kref); ++ ++ sync_fence_free_pts(fence); ++ ++ kfree(fence); ++} ++ ++static int sync_fence_release(struct inode *inode, struct file *file) ++{ ++ struct sync_fence *fence = file->private_data; ++ unsigned long flags; ++ ++ /* ++ * We need to remove all ways to access this fence before droping ++ * our ref. ++ * ++ * start with its membership in the global fence list ++ */ ++ spin_lock_irqsave(&sync_fence_list_lock, flags); ++ list_del(&fence->sync_fence_list); ++ spin_unlock_irqrestore(&sync_fence_list_lock, flags); ++ ++ /* ++ * remove its pts from their parents so that sync_timeline_signal() ++ * can't reference the fence. ++ */ ++ sync_fence_detach_pts(fence); ++ ++ kref_put(&fence->kref, sync_fence_free); ++ ++ return 0; ++} ++ ++static unsigned int sync_fence_poll(struct file *file, poll_table *wait) ++{ ++ struct sync_fence *fence = file->private_data; ++ ++ poll_wait(file, &fence->wq, wait); ++ ++ /* ++ * Make sure that reads to fence->status are ordered with the ++ * wait queue event triggering ++ */ ++ smp_rmb(); ++ ++ if (fence->status == 1) ++ return POLLIN; ++ else if (fence->status < 0) ++ return POLLERR; ++ else ++ return 0; ++} ++ ++static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg) ++{ ++ __s32 value; ++ ++ if (copy_from_user(&value, (void __user *)arg, sizeof(value))) ++ return -EFAULT; ++ ++ return sync_fence_wait(fence, value); ++} ++ ++static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg) ++{ ++ int fd = get_unused_fd(); ++ int err; ++ struct sync_fence *fence2, *fence3; ++ struct sync_merge_data data; ++ ++ if (fd < 0) ++ return fd; ++ ++ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { ++ err = -EFAULT; ++ goto err_put_fd; ++ } ++ ++ fence2 = sync_fence_fdget(data.fd2); ++ if (fence2 == NULL) { ++ err = -ENOENT; ++ goto err_put_fd; ++ } ++ ++ data.name[sizeof(data.name) - 1] = '\0'; ++ fence3 = sync_fence_merge(data.name, fence, fence2); ++ if (fence3 == NULL) { ++ err = -ENOMEM; ++ goto err_put_fence2; ++ } ++ ++ data.fence = fd; ++ if (copy_to_user((void __user *)arg, &data, sizeof(data))) { ++ err = -EFAULT; ++ goto err_put_fence3; ++ } ++ ++ sync_fence_install(fence3, fd); ++ sync_fence_put(fence2); ++ return 0; ++ ++err_put_fence3: ++ sync_fence_put(fence3); ++ ++err_put_fence2: ++ sync_fence_put(fence2); ++ ++err_put_fd: ++ put_unused_fd(fd); ++ return err; ++} ++ ++static int sync_fill_pt_info(struct sync_pt *pt, void *data, int size) ++{ ++ struct sync_pt_info *info = data; ++ int ret; ++ ++ if (size < sizeof(struct sync_pt_info)) ++ return -ENOMEM; ++ ++ info->len = sizeof(struct sync_pt_info); ++ ++ if (pt->parent->ops->fill_driver_data) { ++ ret = pt->parent->ops->fill_driver_data(pt, info->driver_data, ++ size - sizeof(*info)); ++ if (ret < 0) ++ return ret; ++ ++ info->len += ret; ++ } ++ ++ strlcpy(info->obj_name, pt->parent->name, sizeof(info->obj_name)); ++ strlcpy(info->driver_name, pt->parent->ops->driver_name, ++ sizeof(info->driver_name)); ++ info->status = pt->status; ++ info->timestamp_ns = ktime_to_ns(pt->timestamp); ++ ++ return info->len; ++} ++ ++static long sync_fence_ioctl_fence_info(struct sync_fence *fence, ++ unsigned long arg) ++{ ++ struct sync_fence_info_data *data; ++ struct list_head *pos; ++ __u32 size; ++ __u32 len = 0; ++ int ret; ++ ++ if (copy_from_user(&size, (void __user *)arg, sizeof(size))) ++ return -EFAULT; ++ ++ if (size < sizeof(struct sync_fence_info_data)) ++ return -EINVAL; ++ ++ if (size > 4096) ++ size = 4096; ++ ++ data = kzalloc(size, GFP_KERNEL); ++ if (data == NULL) ++ return -ENOMEM; ++ ++ strlcpy(data->name, fence->name, sizeof(data->name)); ++ data->status = fence->status; ++ len = sizeof(struct sync_fence_info_data); ++ ++ list_for_each(pos, &fence->pt_list_head) { ++ struct sync_pt *pt = ++ container_of(pos, struct sync_pt, pt_list); ++ ++ ret = sync_fill_pt_info(pt, (u8 *)data + len, size - len); ++ ++ if (ret < 0) ++ goto out; ++ ++ len += ret; ++ } ++ ++ data->len = len; ++ ++ if (copy_to_user((void __user *)arg, data, len)) ++ ret = -EFAULT; ++ else ++ ret = 0; ++ ++out: ++ kfree(data); ++ ++ return ret; ++} ++ ++static long sync_fence_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct sync_fence *fence = file->private_data; ++ switch (cmd) { ++ case SYNC_IOC_WAIT: ++ return sync_fence_ioctl_wait(fence, arg); ++ ++ case SYNC_IOC_MERGE: ++ return sync_fence_ioctl_merge(fence, arg); ++ ++ case SYNC_IOC_FENCE_INFO: ++ return sync_fence_ioctl_fence_info(fence, arg); ++ ++ default: ++ return -ENOTTY; ++ } ++} ++ ++#ifdef CONFIG_DEBUG_FS ++static const char *sync_status_str(int status) ++{ ++ if (status > 0) ++ return "signaled"; ++ else if (status == 0) ++ return "active"; ++ else ++ return "error"; ++} ++ ++static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence) ++{ ++ int status = pt->status; ++ seq_printf(s, " %s%spt %s", ++ fence ? pt->parent->name : "", ++ fence ? "_" : "", ++ sync_status_str(status)); ++ if (pt->status) { ++ struct timeval tv = ktime_to_timeval(pt->timestamp); ++ seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec); ++ } ++ ++ if (pt->parent->ops->timeline_value_str && ++ pt->parent->ops->pt_value_str) { ++ char value[64]; ++ pt->parent->ops->pt_value_str(pt, value, sizeof(value)); ++ seq_printf(s, ": %s", value); ++ if (fence) { ++ pt->parent->ops->timeline_value_str(pt->parent, value, ++ sizeof(value)); ++ seq_printf(s, " / %s", value); ++ } ++ } else if (pt->parent->ops->print_pt) { ++ seq_printf(s, ": "); ++ pt->parent->ops->print_pt(s, pt); ++ } ++ ++ seq_printf(s, "\n"); ++} ++ ++static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj) ++{ ++ struct list_head *pos; ++ unsigned long flags; ++ ++ seq_printf(s, "%s %s", obj->name, obj->ops->driver_name); ++ ++ if (obj->ops->timeline_value_str) { ++ char value[64]; ++ obj->ops->timeline_value_str(obj, value, sizeof(value)); ++ seq_printf(s, ": %s", value); ++ } else if (obj->ops->print_obj) { ++ seq_printf(s, ": "); ++ obj->ops->print_obj(s, obj); ++ } ++ ++ seq_printf(s, "\n"); ++ ++ spin_lock_irqsave(&obj->child_list_lock, flags); ++ list_for_each(pos, &obj->child_list_head) { ++ struct sync_pt *pt = ++ container_of(pos, struct sync_pt, child_list); ++ sync_print_pt(s, pt, false); ++ } ++ spin_unlock_irqrestore(&obj->child_list_lock, flags); ++} ++ ++static void sync_print_fence(struct seq_file *s, struct sync_fence *fence) ++{ ++ struct list_head *pos; ++ unsigned long flags; ++ ++ seq_printf(s, "[%p] %s: %s\n", fence, fence->name, ++ sync_status_str(fence->status)); ++ ++ list_for_each(pos, &fence->pt_list_head) { ++ struct sync_pt *pt = ++ container_of(pos, struct sync_pt, pt_list); ++ sync_print_pt(s, pt, true); ++ } ++ ++ spin_lock_irqsave(&fence->waiter_list_lock, flags); ++ list_for_each(pos, &fence->waiter_list_head) { ++ struct sync_fence_waiter *waiter = ++ container_of(pos, struct sync_fence_waiter, ++ waiter_list); ++ ++ seq_printf(s, "waiter %pF\n", waiter->callback); ++ } ++ spin_unlock_irqrestore(&fence->waiter_list_lock, flags); ++} ++ ++static int sync_debugfs_show(struct seq_file *s, void *unused) ++{ ++ unsigned long flags; ++ struct list_head *pos; ++ ++ seq_printf(s, "objs:\n--------------\n"); ++ ++ spin_lock_irqsave(&sync_timeline_list_lock, flags); ++ list_for_each(pos, &sync_timeline_list_head) { ++ struct sync_timeline *obj = ++ container_of(pos, struct sync_timeline, ++ sync_timeline_list); ++ ++ sync_print_obj(s, obj); ++ seq_printf(s, "\n"); ++ } ++ spin_unlock_irqrestore(&sync_timeline_list_lock, flags); ++ ++ seq_printf(s, "fences:\n--------------\n"); ++ ++ spin_lock_irqsave(&sync_fence_list_lock, flags); ++ list_for_each(pos, &sync_fence_list_head) { ++ struct sync_fence *fence = ++ container_of(pos, struct sync_fence, sync_fence_list); ++ ++ sync_print_fence(s, fence); ++ seq_printf(s, "\n"); ++ } ++ spin_unlock_irqrestore(&sync_fence_list_lock, flags); ++ return 0; ++} ++ ++static int sync_debugfs_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, sync_debugfs_show, inode->i_private); ++} ++ ++static const struct file_operations sync_debugfs_fops = { ++ .open = sync_debugfs_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static __init int sync_debugfs_init(void) ++{ ++ debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops); ++ return 0; ++} ++late_initcall(sync_debugfs_init); ++ ++#define DUMP_CHUNK 256 ++static char sync_dump_buf[64 * 1024]; ++void sync_dump(void) ++{ ++ struct seq_file s = { ++ .buf = sync_dump_buf, ++ .size = sizeof(sync_dump_buf) - 1, ++ }; ++ int i; ++ ++ sync_debugfs_show(&s, NULL); ++ ++ for (i = 0; i < s.count; i += DUMP_CHUNK) { ++ if ((s.count - i) > DUMP_CHUNK) { ++ char c = s.buf[i + DUMP_CHUNK]; ++ s.buf[i + DUMP_CHUNK] = 0; ++ pr_cont("%s", s.buf + i); ++ s.buf[i + DUMP_CHUNK] = c; ++ } else { ++ s.buf[s.count] = 0; ++ pr_cont("%s", s.buf + i); ++ } ++ } ++} ++#else ++static void sync_dump(void) ++{ ++} ++#endif +diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig +index ee946865..f48cd688 100644 +--- a/drivers/char/Kconfig ++++ b/drivers/char/Kconfig +@@ -6,6 +6,19 @@ menu "Character devices" + + source "drivers/tty/Kconfig" + ++config DEVMEM ++ bool "Memory device driver" ++ default y ++ help ++ The memory driver provides two character devices, mem and kmem, which ++ provide access to the system's memory. The mem device is a view of ++ physical memory, and each byte in the device corresponds to the ++ matching physical address. The kmem device is the same as mem, but ++ the addresses correspond to the kernel's virtual address space rather ++ than physical memory. These devices are standard parts of a Linux ++ system and most users should say Y here. You might say N if very ++ security conscience or memory is tight. ++ + config DEVKMEM + bool "/dev/kmem virtual device support" + default y +@@ -583,6 +596,10 @@ config DEVPORT + depends on ISA || PCI + default y + ++config DCC_TTY ++ tristate "DCC tty driver" ++ depends on ARM ++ + source "drivers/s390/char/Kconfig" + + config RAMOOPS +diff --git a/drivers/char/Makefile b/drivers/char/Makefile +index 0dc5d7ce..8f188917 100644 +--- a/drivers/char/Makefile ++++ b/drivers/char/Makefile +@@ -57,6 +57,7 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi/ + obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o + obj-$(CONFIG_TCG_TPM) += tpm/ + ++obj-$(CONFIG_DCC_TTY) += dcc_tty.o + obj-$(CONFIG_PS3_FLASH) += ps3flash.o + obj-$(CONFIG_RAMOOPS) += ramoops.o + +diff --git a/drivers/char/dcc_tty.c b/drivers/char/dcc_tty.c +new file mode 100644 +index 00000000..a787accd +--- /dev/null ++++ b/drivers/char/dcc_tty.c +@@ -0,0 +1,326 @@ ++/* drivers/char/dcc_tty.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("DCC TTY Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("1.0"); ++ ++static spinlock_t g_dcc_tty_lock = SPIN_LOCK_UNLOCKED; ++static struct hrtimer g_dcc_timer; ++static char g_dcc_buffer[16]; ++static int g_dcc_buffer_head; ++static int g_dcc_buffer_count; ++static unsigned g_dcc_write_delay_usecs = 1; ++static struct tty_driver *g_dcc_tty_driver; ++static struct tty_struct *g_dcc_tty; ++static int g_dcc_tty_open_count; ++ ++static void dcc_poll_locked(void) ++{ ++ char ch; ++ int rch; ++ int written; ++ ++ while (g_dcc_buffer_count) { ++ ch = g_dcc_buffer[g_dcc_buffer_head]; ++ asm( ++ "mrc 14, 0, r15, c0, c1, 0\n" ++ "mcrcc 14, 0, %1, c0, c5, 0\n" ++ "movcc %0, #1\n" ++ "movcs %0, #0\n" ++ : "=r" (written) ++ : "r" (ch) ++ ); ++ if (written) { ++ if (ch == '\n') ++ g_dcc_buffer[g_dcc_buffer_head] = '\r'; ++ else { ++ g_dcc_buffer_head = (g_dcc_buffer_head + 1) % ARRAY_SIZE(g_dcc_buffer); ++ g_dcc_buffer_count--; ++ if (g_dcc_tty) ++ tty_wakeup(g_dcc_tty); ++ } ++ g_dcc_write_delay_usecs = 1; ++ } else { ++ if (g_dcc_write_delay_usecs > 0x100) ++ break; ++ g_dcc_write_delay_usecs <<= 1; ++ udelay(g_dcc_write_delay_usecs); ++ } ++ } ++ ++ if (g_dcc_tty && !test_bit(TTY_THROTTLED, &g_dcc_tty->flags)) { ++ asm( ++ "mrc 14, 0, %0, c0, c1, 0\n" ++ "tst %0, #(1 << 30)\n" ++ "moveq %0, #-1\n" ++ "mrcne 14, 0, %0, c0, c5, 0\n" ++ : "=r" (rch) ++ ); ++ if (rch >= 0) { ++ ch = rch; ++ tty_insert_flip_string(g_dcc_tty, &ch, 1); ++ tty_flip_buffer_push(g_dcc_tty); ++ } ++ } ++ ++ ++ if (g_dcc_buffer_count) ++ hrtimer_start(&g_dcc_timer, ktime_set(0, g_dcc_write_delay_usecs * NSEC_PER_USEC), HRTIMER_MODE_REL); ++ else ++ hrtimer_start(&g_dcc_timer, ktime_set(0, 20 * NSEC_PER_MSEC), HRTIMER_MODE_REL); ++} ++ ++static int dcc_tty_open(struct tty_struct * tty, struct file * filp) ++{ ++ int ret; ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); ++ if (g_dcc_tty == NULL || g_dcc_tty == tty) { ++ g_dcc_tty = tty; ++ g_dcc_tty_open_count++; ++ ret = 0; ++ } else ++ ret = -EBUSY; ++ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); ++ ++ printk("dcc_tty_open, tty %p, f_flags %x, returned %d\n", tty, filp->f_flags, ret); ++ ++ return ret; ++} ++ ++static void dcc_tty_close(struct tty_struct * tty, struct file * filp) ++{ ++ printk("dcc_tty_close, tty %p, f_flags %x\n", tty, filp->f_flags); ++ if (g_dcc_tty == tty) { ++ if (--g_dcc_tty_open_count == 0) ++ g_dcc_tty = NULL; ++ } ++} ++ ++static int dcc_write(const unsigned char *buf_start, int count) ++{ ++ const unsigned char *buf = buf_start; ++ unsigned long irq_flags; ++ int copy_len; ++ int space_left; ++ int tail; ++ ++ if (count < 1) ++ return 0; ++ ++ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); ++ do { ++ tail = (g_dcc_buffer_head + g_dcc_buffer_count) % ARRAY_SIZE(g_dcc_buffer); ++ copy_len = ARRAY_SIZE(g_dcc_buffer) - tail; ++ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; ++ if (copy_len > space_left) ++ copy_len = space_left; ++ if (copy_len > count) ++ copy_len = count; ++ memcpy(&g_dcc_buffer[tail], buf, copy_len); ++ g_dcc_buffer_count += copy_len; ++ buf += copy_len; ++ count -= copy_len; ++ if (copy_len < count && copy_len < space_left) { ++ space_left -= copy_len; ++ copy_len = count; ++ if (copy_len > space_left) { ++ copy_len = space_left; ++ } ++ memcpy(g_dcc_buffer, buf, copy_len); ++ buf += copy_len; ++ count -= copy_len; ++ g_dcc_buffer_count += copy_len; ++ } ++ dcc_poll_locked(); ++ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; ++ } while(count && space_left); ++ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); ++ return buf - buf_start; ++} ++ ++static int dcc_tty_write(struct tty_struct * tty, const unsigned char *buf, int count) ++{ ++ int ret; ++ /* printk("dcc_tty_write %p, %d\n", buf, count); */ ++ ret = dcc_write(buf, count); ++ if (ret != count) ++ printk("dcc_tty_write %p, %d, returned %d\n", buf, count, ret); ++ return ret; ++} ++ ++static int dcc_tty_write_room(struct tty_struct *tty) ++{ ++ int space_left; ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); ++ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; ++ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); ++ return space_left; ++} ++ ++static int dcc_tty_chars_in_buffer(struct tty_struct *tty) ++{ ++ int ret; ++ asm( ++ "mrc 14, 0, %0, c0, c1, 0\n" ++ "mov %0, %0, LSR #30\n" ++ "and %0, %0, #1\n" ++ : "=r" (ret) ++ ); ++ return ret; ++} ++ ++static void dcc_tty_unthrottle(struct tty_struct * tty) ++{ ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); ++ dcc_poll_locked(); ++ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); ++} ++ ++static enum hrtimer_restart dcc_tty_timer_func(struct hrtimer *timer) ++{ ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); ++ dcc_poll_locked(); ++ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); ++ return HRTIMER_NORESTART; ++} ++ ++void dcc_console_write(struct console *co, const char *b, unsigned count) ++{ ++#if 1 ++ dcc_write(b, count); ++#else ++ /* blocking printk */ ++ while (count > 0) { ++ int written; ++ written = dcc_write(b, count); ++ if (written) { ++ b += written; ++ count -= written; ++ } ++ } ++#endif ++} ++ ++static struct tty_driver *dcc_console_device(struct console *c, int *index) ++{ ++ *index = 0; ++ return g_dcc_tty_driver; ++} ++ ++static int __init dcc_console_setup(struct console *co, char *options) ++{ ++ if (co->index != 0) ++ return -ENODEV; ++ return 0; ++} ++ ++ ++static struct console dcc_console = ++{ ++ .name = "ttyDCC", ++ .write = dcc_console_write, ++ .device = dcc_console_device, ++ .setup = dcc_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++}; ++ ++static struct tty_operations dcc_tty_ops = { ++ .open = dcc_tty_open, ++ .close = dcc_tty_close, ++ .write = dcc_tty_write, ++ .write_room = dcc_tty_write_room, ++ .chars_in_buffer = dcc_tty_chars_in_buffer, ++ .unthrottle = dcc_tty_unthrottle, ++}; ++ ++static int __init dcc_tty_init(void) ++{ ++ int ret; ++ ++ hrtimer_init(&g_dcc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ g_dcc_timer.function = dcc_tty_timer_func; ++ ++ g_dcc_tty_driver = alloc_tty_driver(1); ++ if (!g_dcc_tty_driver) { ++ printk(KERN_ERR "dcc_tty_probe: alloc_tty_driver failed\n"); ++ ret = -ENOMEM; ++ goto err_alloc_tty_driver_failed; ++ } ++ g_dcc_tty_driver->owner = THIS_MODULE; ++ g_dcc_tty_driver->driver_name = "dcc"; ++ g_dcc_tty_driver->name = "ttyDCC"; ++ g_dcc_tty_driver->major = 0; // auto assign ++ g_dcc_tty_driver->minor_start = 0; ++ g_dcc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; ++ g_dcc_tty_driver->subtype = SERIAL_TYPE_NORMAL; ++ g_dcc_tty_driver->init_termios = tty_std_termios; ++ g_dcc_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; ++ tty_set_operations(g_dcc_tty_driver, &dcc_tty_ops); ++ ret = tty_register_driver(g_dcc_tty_driver); ++ if (ret) { ++ printk(KERN_ERR "dcc_tty_probe: tty_register_driver failed, %d\n", ret); ++ goto err_tty_register_driver_failed; ++ } ++ tty_register_device(g_dcc_tty_driver, 0, NULL); ++ ++ register_console(&dcc_console); ++ hrtimer_start(&g_dcc_timer, ktime_set(0, 0), HRTIMER_MODE_REL); ++ ++ return 0; ++ ++err_tty_register_driver_failed: ++ put_tty_driver(g_dcc_tty_driver); ++ g_dcc_tty_driver = NULL; ++err_alloc_tty_driver_failed: ++ return ret; ++} ++ ++static void __exit dcc_tty_exit(void) ++{ ++ int ret; ++ ++ tty_unregister_device(g_dcc_tty_driver, 0); ++ ret = tty_unregister_driver(g_dcc_tty_driver); ++ if (ret < 0) { ++ printk(KERN_ERR "dcc_tty_remove: tty_unregister_driver failed, %d\n", ret); ++ } else { ++ put_tty_driver(g_dcc_tty_driver); ++ } ++ g_dcc_tty_driver = NULL; ++} ++ ++module_init(dcc_tty_init); ++module_exit(dcc_tty_exit); ++ ++ +diff --git a/drivers/char/mem.c b/drivers/char/mem.c +index d6e9d081..67e19b63 100644 +--- a/drivers/char/mem.c ++++ b/drivers/char/mem.c +@@ -57,6 +57,7 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) + } + #endif + ++#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) + #ifdef CONFIG_STRICT_DEVMEM + static inline int range_is_allowed(unsigned long pfn, unsigned long size) + { +@@ -82,7 +83,9 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size) + return 1; + } + #endif ++#endif + ++#ifdef CONFIG_DEVMEM + void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr) + { + } +@@ -209,6 +212,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf, + *ppos += written; + return written; + } ++#endif /* CONFIG_DEVMEM */ ++ ++#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) + + int __weak phys_mem_access_prot_allowed(struct file *file, + unsigned long pfn, unsigned long size, pgprot_t *vma_prot) +@@ -330,6 +336,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma) + } + return 0; + } ++#endif /* CONFIG_DEVMEM */ + + #ifdef CONFIG_DEVKMEM + static int mmap_kmem(struct file *file, struct vm_area_struct *vma) +@@ -694,6 +701,8 @@ static loff_t null_lseek(struct file *file, loff_t offset, int orig) + return file->f_pos = 0; + } + ++#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT) ++ + /* + * The memory devices use the full 32/64 bits of the offset, and so we cannot + * check against negative addresses: they are ok. The return value is weird, +@@ -727,10 +736,14 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig) + return ret; + } + ++#endif ++ ++#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT) + static int open_port(struct inode * inode, struct file * filp) + { + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; + } ++#endif + + #define zero_lseek null_lseek + #define full_lseek null_lseek +@@ -740,6 +753,7 @@ static int open_port(struct inode * inode, struct file * filp) + #define open_kmem open_mem + #define open_oldmem open_mem + ++#ifdef CONFIG_DEVMEM + static const struct file_operations mem_fops = { + .llseek = memory_lseek, + .read = read_mem, +@@ -748,6 +762,7 @@ static const struct file_operations mem_fops = { + .open = open_mem, + .get_unmapped_area = get_unmapped_area_mem, + }; ++#endif + + #ifdef CONFIG_DEVKMEM + static const struct file_operations kmem_fops = { +@@ -851,7 +866,9 @@ static const struct memdev { + const struct file_operations *fops; + struct backing_dev_info *dev_info; + } devlist[] = { ++#ifdef CONFIG_DEVMEM + [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi }, ++#endif + #ifdef CONFIG_DEVKMEM + [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi }, + #endif +diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig +index e24a2a1b..57f96ebb 100644 +--- a/drivers/cpufreq/Kconfig ++++ b/drivers/cpufreq/Kconfig +@@ -99,6 +99,16 @@ config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE + Be aware that not all cpufreq drivers support the conservative + governor. If unsure have a look at the help section of the + driver. Fallback governor will be the performance governor. ++ ++config CPU_FREQ_DEFAULT_GOV_INTERACTIVE ++ bool "interactive" ++ select CPU_FREQ_GOV_INTERACTIVE ++ help ++ Use the CPUFreq governor 'interactive' as default. This allows ++ you to get a full dynamic cpu frequency capable system by simply ++ loading your cpufreq low-level hardware driver, using the ++ 'interactive' governor for latency-sensitive workloads. ++ + endchoice + + config CPU_FREQ_GOV_PERFORMANCE +@@ -156,6 +166,23 @@ config CPU_FREQ_GOV_ONDEMAND + + If in doubt, say N. + ++config CPU_FREQ_GOV_INTERACTIVE ++ tristate "'interactive' cpufreq policy governor" ++ help ++ 'interactive' - This driver adds a dynamic cpufreq policy governor ++ designed for latency-sensitive workloads. ++ ++ This governor attempts to reduce the latency of clock ++ increases so that the system is more responsive to ++ interactive workloads. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cpufreq_interactive. ++ ++ For details, take a look at linux/Documentation/cpu-freq. ++ ++ If in doubt, say N. ++ + config CPU_FREQ_GOV_CONSERVATIVE + tristate "'conservative' cpufreq governor" + depends on CPU_FREQ +diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile +index 9531fc2e..35835b7c 100644 +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -9,6 +9,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o + obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o + obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o + obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o ++obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o + + # CPUfreq cross-arch helpers + obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o +diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c +new file mode 100644 +index 00000000..7d1952c5 +--- /dev/null ++++ b/drivers/cpufreq/cpufreq_interactive.c +@@ -0,0 +1,1066 @@ ++/* ++ * drivers/cpufreq/cpufreq_interactive.c ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Author: Mike Chan (mike@android.com) ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CREATE_TRACE_POINTS ++#include ++ ++static int active_count; ++ ++struct cpufreq_interactive_cpuinfo { ++ struct timer_list cpu_timer; ++ struct timer_list cpu_slack_timer; ++ spinlock_t load_lock; /* protects the next 4 fields */ ++ u64 time_in_idle; ++ u64 time_in_idle_timestamp; ++ u64 cputime_speedadj; ++ u64 cputime_speedadj_timestamp; ++ struct cpufreq_policy *policy; ++ struct cpufreq_frequency_table *freq_table; ++ unsigned int target_freq; ++ unsigned int floor_freq; ++ u64 floor_validate_time; ++ u64 hispeed_validate_time; ++ struct rw_semaphore enable_sem; ++ int governor_enabled; ++}; ++ ++static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); ++ ++/* realtime thread handles frequency scaling */ ++static struct task_struct *speedchange_task; ++static cpumask_t speedchange_cpumask; ++static spinlock_t speedchange_cpumask_lock; ++static struct mutex gov_lock; ++ ++/* Hi speed to bump to from lo speed when load burst (default max) */ ++static unsigned int hispeed_freq; ++ ++/* Go to hi speed when CPU load at or above this value. */ ++#define DEFAULT_GO_HISPEED_LOAD 99 ++static unsigned long go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; ++ ++/* Target load. Lower values result in higher CPU speeds. */ ++#define DEFAULT_TARGET_LOAD 90 ++static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD}; ++static spinlock_t target_loads_lock; ++static unsigned int *target_loads = default_target_loads; ++static int ntarget_loads = ARRAY_SIZE(default_target_loads); ++ ++/* ++ * The minimum amount of time to spend at a frequency before we can ramp down. ++ */ ++#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC) ++static unsigned long min_sample_time = DEFAULT_MIN_SAMPLE_TIME; ++ ++/* ++ * The sample rate of the timer used to increase frequency ++ */ ++#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC) ++static unsigned long timer_rate = DEFAULT_TIMER_RATE; ++ ++/* ++ * Wait this long before raising speed above hispeed, by default a single ++ * timer interval. ++ */ ++#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE ++static unsigned long above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY; ++ ++/* Non-zero means indefinite speed boost active */ ++static int boost_val; ++/* Duration of a boot pulse in usecs */ ++static int boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME; ++/* End time of boost pulse in ktime converted to usecs */ ++static u64 boostpulse_endtime; ++ ++/* ++ * Max additional time to wait in idle, beyond timer_rate, at speeds above ++ * minimum before wakeup to reduce speed, or -1 if unnecessary. ++ */ ++#define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE) ++static int timer_slack_val = DEFAULT_TIMER_SLACK; ++ ++static int cpufreq_governor_interactive(struct cpufreq_policy *policy, ++ unsigned int event); ++ ++#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE ++static ++#endif ++struct cpufreq_governor cpufreq_gov_interactive = { ++ .name = "interactive", ++ .governor = cpufreq_governor_interactive, ++ .max_transition_latency = 10000000, ++ .owner = THIS_MODULE, ++}; ++ ++static void cpufreq_interactive_timer_resched( ++ struct cpufreq_interactive_cpuinfo *pcpu) ++{ ++ unsigned long expires = jiffies + usecs_to_jiffies(timer_rate); ++ unsigned long flags; ++ ++ mod_timer_pinned(&pcpu->cpu_timer, expires); ++ if (timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) { ++ expires += usecs_to_jiffies(timer_slack_val); ++ mod_timer_pinned(&pcpu->cpu_slack_timer, expires); ++ } ++ ++ spin_lock_irqsave(&pcpu->load_lock, flags); ++ pcpu->time_in_idle = ++ get_cpu_idle_time_us(smp_processor_id(), ++ &pcpu->time_in_idle_timestamp); ++ pcpu->cputime_speedadj = 0; ++ pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; ++ spin_unlock_irqrestore(&pcpu->load_lock, flags); ++} ++ ++static unsigned int freq_to_targetload(unsigned int freq) ++{ ++ int i; ++ unsigned int ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&target_loads_lock, flags); ++ ++ for (i = 0; i < ntarget_loads - 1 && freq >= target_loads[i+1]; i += 2) ++ ; ++ ++ ret = target_loads[i]; ++ spin_unlock_irqrestore(&target_loads_lock, flags); ++ return ret; ++} ++ ++/* ++ * If increasing frequencies never map to a lower target load then ++ * choose_freq() will find the minimum frequency that does not exceed its ++ * target load given the current load. ++ */ ++ ++static unsigned int choose_freq( ++ struct cpufreq_interactive_cpuinfo *pcpu, unsigned int loadadjfreq) ++{ ++ unsigned int freq = pcpu->policy->cur; ++ unsigned int prevfreq, freqmin, freqmax; ++ unsigned int tl; ++ int index; ++ ++ freqmin = 0; ++ freqmax = UINT_MAX; ++ ++ do { ++ prevfreq = freq; ++ tl = freq_to_targetload(freq); ++ ++ /* ++ * Find the lowest frequency where the computed load is less ++ * than or equal to the target load. ++ */ ++ ++ cpufreq_frequency_table_target( ++ pcpu->policy, pcpu->freq_table, loadadjfreq / tl, ++ CPUFREQ_RELATION_L, &index); ++ freq = pcpu->freq_table[index].frequency; ++ ++ if (freq > prevfreq) { ++ /* The previous frequency is too low. */ ++ freqmin = prevfreq; ++ ++ if (freq >= freqmax) { ++ /* ++ * Find the highest frequency that is less ++ * than freqmax. ++ */ ++ cpufreq_frequency_table_target( ++ pcpu->policy, pcpu->freq_table, ++ freqmax - 1, CPUFREQ_RELATION_H, ++ &index); ++ freq = pcpu->freq_table[index].frequency; ++ ++ if (freq == freqmin) { ++ /* ++ * The first frequency below freqmax ++ * has already been found to be too ++ * low. freqmax is the lowest speed ++ * we found that is fast enough. ++ */ ++ freq = freqmax; ++ break; ++ } ++ } ++ } else if (freq < prevfreq) { ++ /* The previous frequency is high enough. */ ++ freqmax = prevfreq; ++ ++ if (freq <= freqmin) { ++ /* ++ * Find the lowest frequency that is higher ++ * than freqmin. ++ */ ++ cpufreq_frequency_table_target( ++ pcpu->policy, pcpu->freq_table, ++ freqmin + 1, CPUFREQ_RELATION_L, ++ &index); ++ freq = pcpu->freq_table[index].frequency; ++ ++ /* ++ * If freqmax is the first frequency above ++ * freqmin then we have already found that ++ * this speed is fast enough. ++ */ ++ if (freq == freqmax) ++ break; ++ } ++ } ++ ++ /* If same frequency chosen as previous then done. */ ++ } while (freq != prevfreq); ++ ++ return freq; ++} ++ ++static u64 update_load(int cpu) ++{ ++ struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); ++ u64 now; ++ u64 now_idle; ++ unsigned int delta_idle; ++ unsigned int delta_time; ++ u64 active_time; ++ ++ now_idle = get_cpu_idle_time_us(cpu, &now); ++ delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); ++ delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); ++ active_time = delta_time - delta_idle; ++ pcpu->cputime_speedadj += active_time * pcpu->policy->cur; ++ ++ pcpu->time_in_idle = now_idle; ++ pcpu->time_in_idle_timestamp = now; ++ return now; ++} ++ ++static void cpufreq_interactive_timer(unsigned long data) ++{ ++ u64 now; ++ unsigned int delta_time; ++ u64 cputime_speedadj; ++ int cpu_load; ++ struct cpufreq_interactive_cpuinfo *pcpu = ++ &per_cpu(cpuinfo, data); ++ unsigned int new_freq; ++ unsigned int loadadjfreq; ++ unsigned int index; ++ unsigned long flags; ++ bool boosted; ++ ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ return; ++ if (!pcpu->governor_enabled) ++ goto exit; ++ ++ spin_lock_irqsave(&pcpu->load_lock, flags); ++ now = update_load(data); ++ delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp); ++ cputime_speedadj = pcpu->cputime_speedadj; ++ spin_unlock_irqrestore(&pcpu->load_lock, flags); ++ ++ if (WARN_ON_ONCE(!delta_time)) ++ goto rearm; ++ ++ do_div(cputime_speedadj, delta_time); ++ loadadjfreq = (unsigned int)cputime_speedadj * 100; ++ cpu_load = loadadjfreq / pcpu->target_freq; ++ boosted = boost_val || now < boostpulse_endtime; ++ ++ if (cpu_load >= go_hispeed_load || boosted) { ++ if (pcpu->target_freq < hispeed_freq) { ++ new_freq = hispeed_freq; ++ } else { ++ new_freq = choose_freq(pcpu, loadadjfreq); ++ ++ if (new_freq < hispeed_freq) ++ new_freq = hispeed_freq; ++ } ++ } else { ++ new_freq = choose_freq(pcpu, loadadjfreq); ++ } ++ ++ if (pcpu->target_freq >= hispeed_freq && ++ new_freq > pcpu->target_freq && ++ now - pcpu->hispeed_validate_time < above_hispeed_delay_val) { ++ trace_cpufreq_interactive_notyet( ++ data, cpu_load, pcpu->target_freq, ++ pcpu->policy->cur, new_freq); ++ goto rearm; ++ } ++ ++ pcpu->hispeed_validate_time = now; ++ ++ if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, ++ new_freq, CPUFREQ_RELATION_L, ++ &index)) { ++ pr_warn_once("timer %d: cpufreq_frequency_table_target error\n", ++ (int) data); ++ goto rearm; ++ } ++ ++ new_freq = pcpu->freq_table[index].frequency; ++ ++ /* ++ * Do not scale below floor_freq unless we have been at or above the ++ * floor frequency for the minimum sample time since last validated. ++ */ ++ if (new_freq < pcpu->floor_freq) { ++ if (now - pcpu->floor_validate_time < min_sample_time) { ++ trace_cpufreq_interactive_notyet( ++ data, cpu_load, pcpu->target_freq, ++ pcpu->policy->cur, new_freq); ++ goto rearm; ++ } ++ } ++ ++ /* ++ * Update the timestamp for checking whether speed has been held at ++ * or above the selected frequency for a minimum of min_sample_time, ++ * if not boosted to hispeed_freq. If boosted to hispeed_freq then we ++ * allow the speed to drop as soon as the boostpulse duration expires ++ * (or the indefinite boost is turned off). ++ */ ++ ++ if (!boosted || new_freq > hispeed_freq) { ++ pcpu->floor_freq = new_freq; ++ pcpu->floor_validate_time = now; ++ } ++ ++ if (pcpu->target_freq == new_freq) { ++ trace_cpufreq_interactive_already( ++ data, cpu_load, pcpu->target_freq, ++ pcpu->policy->cur, new_freq); ++ goto rearm_if_notmax; ++ } ++ ++ trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, ++ pcpu->policy->cur, new_freq); ++ ++ pcpu->target_freq = new_freq; ++ spin_lock_irqsave(&speedchange_cpumask_lock, flags); ++ cpumask_set_cpu(data, &speedchange_cpumask); ++ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); ++ wake_up_process(speedchange_task); ++ ++rearm_if_notmax: ++ /* ++ * Already set max speed and don't see a need to change that, ++ * wait until next idle to re-evaluate, don't need timer. ++ */ ++ if (pcpu->target_freq == pcpu->policy->max) ++ goto exit; ++ ++rearm: ++ if (!timer_pending(&pcpu->cpu_timer)) ++ cpufreq_interactive_timer_resched(pcpu); ++ ++exit: ++ up_read(&pcpu->enable_sem); ++ return; ++} ++ ++static void cpufreq_interactive_idle_start(void) ++{ ++ struct cpufreq_interactive_cpuinfo *pcpu = ++ &per_cpu(cpuinfo, smp_processor_id()); ++ int pending; ++ ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ return; ++ if (!pcpu->governor_enabled) { ++ up_read(&pcpu->enable_sem); ++ return; ++ } ++ ++ pending = timer_pending(&pcpu->cpu_timer); ++ ++ if (pcpu->target_freq != pcpu->policy->min) { ++ /* ++ * Entering idle while not at lowest speed. On some ++ * platforms this can hold the other CPU(s) at that speed ++ * even though the CPU is idle. Set a timer to re-evaluate ++ * speed so this idle CPU doesn't hold the other CPUs above ++ * min indefinitely. This should probably be a quirk of ++ * the CPUFreq driver. ++ */ ++ if (!pending) ++ cpufreq_interactive_timer_resched(pcpu); ++ } ++ ++ up_read(&pcpu->enable_sem); ++} ++ ++static void cpufreq_interactive_idle_end(void) ++{ ++ struct cpufreq_interactive_cpuinfo *pcpu = ++ &per_cpu(cpuinfo, smp_processor_id()); ++ ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ return; ++ if (!pcpu->governor_enabled) { ++ up_read(&pcpu->enable_sem); ++ return; ++ } ++ ++ /* Arm the timer for 1-2 ticks later if not already. */ ++ if (!timer_pending(&pcpu->cpu_timer)) { ++ cpufreq_interactive_timer_resched(pcpu); ++ } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) { ++ del_timer(&pcpu->cpu_timer); ++ del_timer(&pcpu->cpu_slack_timer); ++ cpufreq_interactive_timer(smp_processor_id()); ++ } ++ ++ up_read(&pcpu->enable_sem); ++} ++ ++static int cpufreq_interactive_speedchange_task(void *data) ++{ ++ unsigned int cpu; ++ cpumask_t tmp_mask; ++ unsigned long flags; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ ++ while (1) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ spin_lock_irqsave(&speedchange_cpumask_lock, flags); ++ ++ if (cpumask_empty(&speedchange_cpumask)) { ++ spin_unlock_irqrestore(&speedchange_cpumask_lock, ++ flags); ++ schedule(); ++ ++ if (kthread_should_stop()) ++ break; ++ ++ spin_lock_irqsave(&speedchange_cpumask_lock, flags); ++ } ++ ++ set_current_state(TASK_RUNNING); ++ tmp_mask = speedchange_cpumask; ++ cpumask_clear(&speedchange_cpumask); ++ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); ++ ++ for_each_cpu(cpu, &tmp_mask) { ++ unsigned int j; ++ unsigned int max_freq = 0; ++ ++ pcpu = &per_cpu(cpuinfo, cpu); ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ continue; ++ if (!pcpu->governor_enabled) { ++ up_read(&pcpu->enable_sem); ++ continue; ++ } ++ ++ for_each_cpu(j, pcpu->policy->cpus) { ++ struct cpufreq_interactive_cpuinfo *pjcpu = ++ &per_cpu(cpuinfo, j); ++ ++ if (pjcpu->target_freq > max_freq) ++ max_freq = pjcpu->target_freq; ++ } ++ ++ if (max_freq != pcpu->policy->cur) ++ __cpufreq_driver_target(pcpu->policy, ++ max_freq, ++ CPUFREQ_RELATION_H); ++ trace_cpufreq_interactive_setspeed(cpu, ++ pcpu->target_freq, ++ pcpu->policy->cur); ++ ++ up_read(&pcpu->enable_sem); ++ } ++ } ++ ++ return 0; ++} ++ ++static void cpufreq_interactive_boost(void) ++{ ++ int i; ++ int anyboost = 0; ++ unsigned long flags; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ ++ spin_lock_irqsave(&speedchange_cpumask_lock, flags); ++ ++ for_each_online_cpu(i) { ++ pcpu = &per_cpu(cpuinfo, i); ++ ++ if (pcpu->target_freq < hispeed_freq) { ++ pcpu->target_freq = hispeed_freq; ++ cpumask_set_cpu(i, &speedchange_cpumask); ++ pcpu->hispeed_validate_time = ++ ktime_to_us(ktime_get()); ++ anyboost = 1; ++ } ++ ++ /* ++ * Set floor freq and (re)start timer for when last ++ * validated. ++ */ ++ ++ pcpu->floor_freq = hispeed_freq; ++ pcpu->floor_validate_time = ktime_to_us(ktime_get()); ++ } ++ ++ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); ++ ++ if (anyboost) ++ wake_up_process(speedchange_task); ++} ++ ++static int cpufreq_interactive_notifier( ++ struct notifier_block *nb, unsigned long val, void *data) ++{ ++ struct cpufreq_freqs *freq = data; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ int cpu; ++ unsigned long flags; ++ ++ if (val == CPUFREQ_POSTCHANGE) { ++ pcpu = &per_cpu(cpuinfo, freq->cpu); ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ return 0; ++ if (!pcpu->governor_enabled) { ++ up_read(&pcpu->enable_sem); ++ return 0; ++ } ++ ++ for_each_cpu(cpu, pcpu->policy->cpus) { ++ struct cpufreq_interactive_cpuinfo *pjcpu = ++ &per_cpu(cpuinfo, cpu); ++ spin_lock_irqsave(&pjcpu->load_lock, flags); ++ update_load(cpu); ++ spin_unlock_irqrestore(&pjcpu->load_lock, flags); ++ } ++ ++ up_read(&pcpu->enable_sem); ++ } ++ return 0; ++} ++ ++static struct notifier_block cpufreq_notifier_block = { ++ .notifier_call = cpufreq_interactive_notifier, ++}; ++ ++static ssize_t show_target_loads( ++ struct kobject *kobj, struct attribute *attr, char *buf) ++{ ++ int i; ++ ssize_t ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&target_loads_lock, flags); ++ ++ for (i = 0; i < ntarget_loads; i++) ++ ret += sprintf(buf + ret, "%u%s", target_loads[i], ++ i & 0x1 ? ":" : " "); ++ ++ ret += sprintf(buf + ret, "\n"); ++ spin_unlock_irqrestore(&target_loads_lock, flags); ++ return ret; ++} ++ ++static ssize_t store_target_loads( ++ struct kobject *kobj, struct attribute *attr, const char *buf, ++ size_t count) ++{ ++ int ret; ++ const char *cp; ++ unsigned int *new_target_loads = NULL; ++ int ntokens = 1; ++ int i; ++ unsigned long flags; ++ ++ cp = buf; ++ while ((cp = strpbrk(cp + 1, " :"))) ++ ntokens++; ++ ++ if (!(ntokens & 0x1)) ++ goto err_inval; ++ ++ new_target_loads = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL); ++ if (!new_target_loads) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ cp = buf; ++ i = 0; ++ while (i < ntokens) { ++ if (sscanf(cp, "%u", &new_target_loads[i++]) != 1) ++ goto err_inval; ++ ++ cp = strpbrk(cp, " :"); ++ if (!cp) ++ break; ++ cp++; ++ } ++ ++ if (i != ntokens) ++ goto err_inval; ++ ++ spin_lock_irqsave(&target_loads_lock, flags); ++ if (target_loads != default_target_loads) ++ kfree(target_loads); ++ target_loads = new_target_loads; ++ ntarget_loads = ntokens; ++ spin_unlock_irqrestore(&target_loads_lock, flags); ++ return count; ++ ++err_inval: ++ ret = -EINVAL; ++err: ++ kfree(new_target_loads); ++ return ret; ++} ++ ++static struct global_attr target_loads_attr = ++ __ATTR(target_loads, S_IRUGO | S_IWUSR, ++ show_target_loads, store_target_loads); ++ ++static ssize_t show_hispeed_freq(struct kobject *kobj, ++ struct attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%u\n", hispeed_freq); ++} ++ ++static ssize_t store_hispeed_freq(struct kobject *kobj, ++ struct attribute *attr, const char *buf, ++ size_t count) ++{ ++ int ret; ++ long unsigned int val; ++ ++ ret = strict_strtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ hispeed_freq = val; ++ return count; ++} ++ ++static struct global_attr hispeed_freq_attr = __ATTR(hispeed_freq, 0644, ++ show_hispeed_freq, store_hispeed_freq); ++ ++ ++static ssize_t show_go_hispeed_load(struct kobject *kobj, ++ struct attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%lu\n", go_hispeed_load); ++} ++ ++static ssize_t store_go_hispeed_load(struct kobject *kobj, ++ struct attribute *attr, const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = strict_strtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ go_hispeed_load = val; ++ return count; ++} ++ ++static struct global_attr go_hispeed_load_attr = __ATTR(go_hispeed_load, 0644, ++ show_go_hispeed_load, store_go_hispeed_load); ++ ++static ssize_t show_min_sample_time(struct kobject *kobj, ++ struct attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%lu\n", min_sample_time); ++} ++ ++static ssize_t store_min_sample_time(struct kobject *kobj, ++ struct attribute *attr, const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = strict_strtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ min_sample_time = val; ++ return count; ++} ++ ++static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, ++ show_min_sample_time, store_min_sample_time); ++ ++static ssize_t show_above_hispeed_delay(struct kobject *kobj, ++ struct attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%lu\n", above_hispeed_delay_val); ++} ++ ++static ssize_t store_above_hispeed_delay(struct kobject *kobj, ++ struct attribute *attr, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = strict_strtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ above_hispeed_delay_val = val; ++ return count; ++} ++ ++define_one_global_rw(above_hispeed_delay); ++ ++static ssize_t show_timer_rate(struct kobject *kobj, ++ struct attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%lu\n", timer_rate); ++} ++ ++static ssize_t store_timer_rate(struct kobject *kobj, ++ struct attribute *attr, const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = strict_strtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ timer_rate = val; ++ return count; ++} ++ ++static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, ++ show_timer_rate, store_timer_rate); ++ ++static ssize_t show_timer_slack( ++ struct kobject *kobj, struct attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", timer_slack_val); ++} ++ ++static ssize_t store_timer_slack( ++ struct kobject *kobj, struct attribute *attr, const char *buf, ++ size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtol(buf, 10, &val); ++ if (ret < 0) ++ return ret; ++ ++ timer_slack_val = val; ++ return count; ++} ++ ++define_one_global_rw(timer_slack); ++ ++static ssize_t show_boost(struct kobject *kobj, struct attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", boost_val); ++} ++ ++static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ ++ boost_val = val; ++ ++ if (boost_val) { ++ trace_cpufreq_interactive_boost("on"); ++ cpufreq_interactive_boost(); ++ } else { ++ trace_cpufreq_interactive_unboost("off"); ++ } ++ ++ return count; ++} ++ ++define_one_global_rw(boost); ++ ++static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ ++ boostpulse_endtime = ktime_to_us(ktime_get()) + boostpulse_duration_val; ++ trace_cpufreq_interactive_boost("pulse"); ++ cpufreq_interactive_boost(); ++ return count; ++} ++ ++static struct global_attr boostpulse = ++ __ATTR(boostpulse, 0200, NULL, store_boostpulse); ++ ++static ssize_t show_boostpulse_duration( ++ struct kobject *kobj, struct attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", boostpulse_duration_val); ++} ++ ++static ssize_t store_boostpulse_duration( ++ struct kobject *kobj, struct attribute *attr, const char *buf, ++ size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ ++ boostpulse_duration_val = val; ++ return count; ++} ++ ++define_one_global_rw(boostpulse_duration); ++ ++static struct attribute *interactive_attributes[] = { ++ &target_loads_attr.attr, ++ &hispeed_freq_attr.attr, ++ &go_hispeed_load_attr.attr, ++ &above_hispeed_delay.attr, ++ &min_sample_time_attr.attr, ++ &timer_rate_attr.attr, ++ &timer_slack.attr, ++ &boost.attr, ++ &boostpulse.attr, ++ &boostpulse_duration.attr, ++ NULL, ++}; ++ ++static struct attribute_group interactive_attr_group = { ++ .attrs = interactive_attributes, ++ .name = "interactive", ++}; ++ ++static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, ++ unsigned long val, ++ void *data) ++{ ++ switch (val) { ++ case IDLE_START: ++ cpufreq_interactive_idle_start(); ++ break; ++ case IDLE_END: ++ cpufreq_interactive_idle_end(); ++ break; ++ } ++ ++ return 0; ++} ++ ++static struct notifier_block cpufreq_interactive_idle_nb = { ++ .notifier_call = cpufreq_interactive_idle_notifier, ++}; ++ ++static int cpufreq_governor_interactive(struct cpufreq_policy *policy, ++ unsigned int event) ++{ ++ int rc; ++ unsigned int j; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ struct cpufreq_frequency_table *freq_table; ++ ++ switch (event) { ++ case CPUFREQ_GOV_START: ++ if (!cpu_online(policy->cpu)) ++ return -EINVAL; ++ ++ mutex_lock(&gov_lock); ++ ++ freq_table = ++ cpufreq_frequency_get_table(policy->cpu); ++ if (!hispeed_freq) ++ hispeed_freq = policy->max; ++ ++ for_each_cpu(j, policy->cpus) { ++ unsigned long expires; ++ ++ pcpu = &per_cpu(cpuinfo, j); ++ pcpu->policy = policy; ++ pcpu->target_freq = policy->cur; ++ pcpu->freq_table = freq_table; ++ pcpu->floor_freq = pcpu->target_freq; ++ pcpu->floor_validate_time = ++ ktime_to_us(ktime_get()); ++ pcpu->hispeed_validate_time = ++ pcpu->floor_validate_time; ++ down_write(&pcpu->enable_sem); ++ expires = jiffies + usecs_to_jiffies(timer_rate); ++ pcpu->cpu_timer.expires = expires; ++ add_timer_on(&pcpu->cpu_timer, j); ++ if (timer_slack_val >= 0) { ++ expires += usecs_to_jiffies(timer_slack_val); ++ pcpu->cpu_slack_timer.expires = expires; ++ add_timer_on(&pcpu->cpu_slack_timer, j); ++ } ++ pcpu->governor_enabled = 1; ++ up_write(&pcpu->enable_sem); ++ } ++ ++ /* ++ * Do not register the idle hook and create sysfs ++ * entries if we have already done so. ++ */ ++ if (++active_count > 1) { ++ mutex_unlock(&gov_lock); ++ return 0; ++ } ++ ++ rc = sysfs_create_group(cpufreq_global_kobject, ++ &interactive_attr_group); ++ if (rc) { ++ mutex_unlock(&gov_lock); ++ return rc; ++ } ++ ++ idle_notifier_register(&cpufreq_interactive_idle_nb); ++ cpufreq_register_notifier( ++ &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); ++ mutex_unlock(&gov_lock); ++ break; ++ ++ case CPUFREQ_GOV_STOP: ++ mutex_lock(&gov_lock); ++ for_each_cpu(j, policy->cpus) { ++ pcpu = &per_cpu(cpuinfo, j); ++ down_write(&pcpu->enable_sem); ++ pcpu->governor_enabled = 0; ++ del_timer_sync(&pcpu->cpu_timer); ++ del_timer_sync(&pcpu->cpu_slack_timer); ++ up_write(&pcpu->enable_sem); ++ } ++ ++ if (--active_count > 0) { ++ mutex_unlock(&gov_lock); ++ return 0; ++ } ++ ++ cpufreq_unregister_notifier( ++ &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); ++ idle_notifier_unregister(&cpufreq_interactive_idle_nb); ++ sysfs_remove_group(cpufreq_global_kobject, ++ &interactive_attr_group); ++ mutex_unlock(&gov_lock); ++ ++ break; ++ ++ case CPUFREQ_GOV_LIMITS: ++ if (policy->max < policy->cur) ++ __cpufreq_driver_target(policy, ++ policy->max, CPUFREQ_RELATION_H); ++ else if (policy->min > policy->cur) ++ __cpufreq_driver_target(policy, ++ policy->min, CPUFREQ_RELATION_L); ++ break; ++ } ++ return 0; ++} ++ ++static void cpufreq_interactive_nop_timer(unsigned long data) ++{ ++} ++ ++static int __init cpufreq_interactive_init(void) ++{ ++ unsigned int i; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; ++ ++ /* Initalize per-cpu timers */ ++ for_each_possible_cpu(i) { ++ pcpu = &per_cpu(cpuinfo, i); ++ init_timer_deferrable(&pcpu->cpu_timer); ++ pcpu->cpu_timer.function = cpufreq_interactive_timer; ++ pcpu->cpu_timer.data = i; ++ init_timer(&pcpu->cpu_slack_timer); ++ pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer; ++ spin_lock_init(&pcpu->load_lock); ++ init_rwsem(&pcpu->enable_sem); ++ } ++ ++ spin_lock_init(&target_loads_lock); ++ spin_lock_init(&speedchange_cpumask_lock); ++ mutex_init(&gov_lock); ++ speedchange_task = ++ kthread_create(cpufreq_interactive_speedchange_task, NULL, ++ "cfinteractive"); ++ if (IS_ERR(speedchange_task)) ++ return PTR_ERR(speedchange_task); ++ ++ sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m); ++ get_task_struct(speedchange_task); ++ ++ /* NB: wake up so the thread does not look hung to the freezer */ ++ wake_up_process(speedchange_task); ++ ++ return cpufreq_register_governor(&cpufreq_gov_interactive); ++} ++ ++#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE ++fs_initcall(cpufreq_interactive_init); ++#else ++module_init(cpufreq_interactive_init); ++#endif ++ ++static void __exit cpufreq_interactive_exit(void) ++{ ++ cpufreq_unregister_governor(&cpufreq_gov_interactive); ++ kthread_stop(speedchange_task); ++ put_task_struct(speedchange_task); ++} ++ ++module_exit(cpufreq_interactive_exit); ++ ++MODULE_AUTHOR("Mike Chan "); ++MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for " ++ "Latency sensitive workloads"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c +index b40ee140..72f0093a 100644 +--- a/drivers/cpufreq/cpufreq_stats.c ++++ b/drivers/cpufreq/cpufreq_stats.c +@@ -316,6 +316,27 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb, + return 0; + } + ++static int cpufreq_stats_create_table_cpu(unsigned int cpu) ++{ ++ struct cpufreq_policy *policy; ++ struct cpufreq_frequency_table *table; ++ int ret = -ENODEV; ++ ++ policy = cpufreq_cpu_get(cpu); ++ if (!policy) ++ return -ENODEV; ++ ++ table = cpufreq_frequency_get_table(cpu); ++ if (!table) ++ goto out; ++ ++ ret = cpufreq_stats_create_table(policy, table); ++ ++out: ++ cpufreq_cpu_put(policy); ++ return ret; ++} ++ + static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, + unsigned long action, + void *hcpu) +@@ -334,6 +355,10 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, + case CPU_DEAD_FROZEN: + cpufreq_stats_free_table(cpu); + break; ++ case CPU_DOWN_FAILED: ++ case CPU_DOWN_FAILED_FROZEN: ++ cpufreq_stats_create_table_cpu(cpu); ++ break; + } + return NOTIFY_OK; + } +diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig +index 78a666d1..a76b689e 100644 +--- a/drivers/cpuidle/Kconfig ++++ b/drivers/cpuidle/Kconfig +@@ -18,3 +18,6 @@ config CPU_IDLE_GOV_MENU + bool + depends on CPU_IDLE && NO_HZ + default y ++ ++config ARCH_NEEDS_CPU_IDLE_COUPLED ++ def_bool n +diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile +index 5634f883..38c8f69f 100644 +--- a/drivers/cpuidle/Makefile ++++ b/drivers/cpuidle/Makefile +@@ -3,3 +3,4 @@ + # + + obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ ++obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o +diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c +new file mode 100644 +index 00000000..e95c72e9 +--- /dev/null ++++ b/drivers/cpuidle/coupled.c +@@ -0,0 +1,727 @@ ++/* ++ * coupled.c - helper functions to enter the same idle state on multiple cpus ++ * ++ * Copyright (c) 2011 Google, Inc. ++ * ++ * Author: Colin Cross ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cpuidle.h" ++ ++/** ++ * DOC: Coupled cpuidle states ++ * ++ * On some ARM SMP SoCs (OMAP4460, Tegra 2, and probably more), the ++ * cpus cannot be independently powered down, either due to ++ * sequencing restrictions (on Tegra 2, cpu 0 must be the last to ++ * power down), or due to HW bugs (on OMAP4460, a cpu powering up ++ * will corrupt the gic state unless the other cpu runs a work ++ * around). Each cpu has a power state that it can enter without ++ * coordinating with the other cpu (usually Wait For Interrupt, or ++ * WFI), and one or more "coupled" power states that affect blocks ++ * shared between the cpus (L2 cache, interrupt controller, and ++ * sometimes the whole SoC). Entering a coupled power state must ++ * be tightly controlled on both cpus. ++ * ++ * This file implements a solution, where each cpu will wait in the ++ * WFI state until all cpus are ready to enter a coupled state, at ++ * which point the coupled state function will be called on all ++ * cpus at approximately the same time. ++ * ++ * Once all cpus are ready to enter idle, they are woken by an smp ++ * cross call. At this point, there is a chance that one of the ++ * cpus will find work to do, and choose not to enter idle. A ++ * final pass is needed to guarantee that all cpus will call the ++ * power state enter function at the same time. During this pass, ++ * each cpu will increment the ready counter, and continue once the ++ * ready counter matches the number of online coupled cpus. If any ++ * cpu exits idle, the other cpus will decrement their counter and ++ * retry. ++ * ++ * requested_state stores the deepest coupled idle state each cpu ++ * is ready for. It is assumed that the states are indexed from ++ * shallowest (highest power, lowest exit latency) to deepest ++ * (lowest power, highest exit latency). The requested_state ++ * variable is not locked. It is only written from the cpu that ++ * it stores (or by the on/offlining cpu if that cpu is offline), ++ * and only read after all the cpus are ready for the coupled idle ++ * state are are no longer updating it. ++ * ++ * Three atomic counters are used. alive_count tracks the number ++ * of cpus in the coupled set that are currently or soon will be ++ * online. waiting_count tracks the number of cpus that are in ++ * the waiting loop, in the ready loop, or in the coupled idle state. ++ * ready_count tracks the number of cpus that are in the ready loop ++ * or in the coupled idle state. ++ * ++ * To use coupled cpuidle states, a cpuidle driver must: ++ * ++ * Set struct cpuidle_device.coupled_cpus to the mask of all ++ * coupled cpus, usually the same as cpu_possible_mask if all cpus ++ * are part of the same cluster. The coupled_cpus mask must be ++ * set in the struct cpuidle_device for each cpu. ++ * ++ * Set struct cpuidle_device.safe_state to a state that is not a ++ * coupled state. This is usually WFI. ++ * ++ * Set CPUIDLE_FLAG_COUPLED in struct cpuidle_state.flags for each ++ * state that affects multiple cpus. ++ * ++ * Provide a struct cpuidle_state.enter function for each state ++ * that affects multiple cpus. This function is guaranteed to be ++ * called on all cpus at approximately the same time. The driver ++ * should ensure that the cpus all abort together if any cpu tries ++ * to abort once the function is called. The function should return ++ * with interrupts still disabled. ++ */ ++ ++/** ++ * struct cpuidle_coupled - data for set of cpus that share a coupled idle state ++ * @coupled_cpus: mask of cpus that are part of the coupled set ++ * @requested_state: array of requested states for cpus in the coupled set ++ * @ready_waiting_counts: combined count of cpus in ready or waiting loops ++ * @online_count: count of cpus that are online ++ * @refcnt: reference count of cpuidle devices that are using this struct ++ * @prevent: flag to prevent coupled idle while a cpu is hotplugging ++ */ ++struct cpuidle_coupled { ++ cpumask_t coupled_cpus; ++ int requested_state[NR_CPUS]; ++ atomic_t ready_waiting_counts; ++ int online_count; ++ int refcnt; ++ int prevent; ++}; ++ ++#define WAITING_BITS 16 ++#define MAX_WAITING_CPUS (1 << WAITING_BITS) ++#define WAITING_MASK (MAX_WAITING_CPUS - 1) ++#define READY_MASK (~WAITING_MASK) ++ ++#define CPUIDLE_COUPLED_NOT_IDLE (-1) ++ ++static DEFINE_MUTEX(cpuidle_coupled_lock); ++static DEFINE_PER_CPU(struct call_single_data, cpuidle_coupled_poke_cb); ++ ++/* ++ * The cpuidle_coupled_poked_mask mask is used to avoid calling ++ * __smp_call_function_single with the per cpu call_single_data struct already ++ * in use. This prevents a deadlock where two cpus are waiting for each others ++ * call_single_data struct to be available ++ */ ++static cpumask_t cpuidle_coupled_poked_mask; ++ ++/** ++ * cpuidle_coupled_parallel_barrier - synchronize all online coupled cpus ++ * @dev: cpuidle_device of the calling cpu ++ * @a: atomic variable to hold the barrier ++ * ++ * No caller to this function will return from this function until all online ++ * cpus in the same coupled group have called this function. Once any caller ++ * has returned from this function, the barrier is immediately available for ++ * reuse. ++ * ++ * The atomic variable a must be initialized to 0 before any cpu calls ++ * this function, will be reset to 0 before any cpu returns from this function. ++ * ++ * Must only be called from within a coupled idle state handler ++ * (state.enter when state.flags has CPUIDLE_FLAG_COUPLED set). ++ * ++ * Provides full smp barrier semantics before and after calling. ++ */ ++void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a) ++{ ++ int n = dev->coupled->online_count; ++ ++ smp_mb__before_atomic_inc(); ++ atomic_inc(a); ++ ++ while (atomic_read(a) < n) ++ cpu_relax(); ++ ++ if (atomic_inc_return(a) == n * 2) { ++ atomic_set(a, 0); ++ return; ++ } ++ ++ while (atomic_read(a) > n) ++ cpu_relax(); ++} ++ ++/** ++ * cpuidle_state_is_coupled - check if a state is part of a coupled set ++ * @dev: struct cpuidle_device for the current cpu ++ * @drv: struct cpuidle_driver for the platform ++ * @state: index of the target state in drv->states ++ * ++ * Returns true if the target state is coupled with cpus besides this one ++ */ ++bool cpuidle_state_is_coupled(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int state) ++{ ++ return drv->states[state].flags & CPUIDLE_FLAG_COUPLED; ++} ++ ++/** ++ * cpuidle_coupled_set_ready - mark a cpu as ready ++ * @coupled: the struct coupled that contains the current cpu ++ */ ++static inline void cpuidle_coupled_set_ready(struct cpuidle_coupled *coupled) ++{ ++ atomic_add(MAX_WAITING_CPUS, &coupled->ready_waiting_counts); ++} ++ ++/** ++ * cpuidle_coupled_set_not_ready - mark a cpu as not ready ++ * @coupled: the struct coupled that contains the current cpu ++ * ++ * Decrements the ready counter, unless the ready (and thus the waiting) counter ++ * is equal to the number of online cpus. Prevents a race where one cpu ++ * decrements the waiting counter and then re-increments it just before another ++ * cpu has decremented its ready counter, leading to the ready counter going ++ * down from the number of online cpus without going through the coupled idle ++ * state. ++ * ++ * Returns 0 if the counter was decremented successfully, -EINVAL if the ready ++ * counter was equal to the number of online cpus. ++ */ ++static ++inline int cpuidle_coupled_set_not_ready(struct cpuidle_coupled *coupled) ++{ ++ int all; ++ int ret; ++ ++ all = coupled->online_count || (coupled->online_count << WAITING_BITS); ++ ret = atomic_add_unless(&coupled->ready_waiting_counts, ++ -MAX_WAITING_CPUS, all); ++ ++ return ret ? 0 : -EINVAL; ++} ++ ++/** ++ * cpuidle_coupled_no_cpus_ready - check if no cpus in a coupled set are ready ++ * @coupled: the struct coupled that contains the current cpu ++ * ++ * Returns true if all of the cpus in a coupled set are out of the ready loop. ++ */ ++static inline int cpuidle_coupled_no_cpus_ready(struct cpuidle_coupled *coupled) ++{ ++ int r = atomic_read(&coupled->ready_waiting_counts) >> WAITING_BITS; ++ return r == 0; ++} ++ ++/** ++ * cpuidle_coupled_cpus_ready - check if all cpus in a coupled set are ready ++ * @coupled: the struct coupled that contains the current cpu ++ * ++ * Returns true if all cpus coupled to this target state are in the ready loop ++ */ ++static inline bool cpuidle_coupled_cpus_ready(struct cpuidle_coupled *coupled) ++{ ++ int r = atomic_read(&coupled->ready_waiting_counts) >> WAITING_BITS; ++ return r == coupled->online_count; ++} ++ ++/** ++ * cpuidle_coupled_cpus_waiting - check if all cpus in a coupled set are waiting ++ * @coupled: the struct coupled that contains the current cpu ++ * ++ * Returns true if all cpus coupled to this target state are in the wait loop ++ */ ++static inline bool cpuidle_coupled_cpus_waiting(struct cpuidle_coupled *coupled) ++{ ++ int w = atomic_read(&coupled->ready_waiting_counts) & WAITING_MASK; ++ return w == coupled->online_count; ++} ++ ++/** ++ * cpuidle_coupled_no_cpus_waiting - check if no cpus in coupled set are waiting ++ * @coupled: the struct coupled that contains the current cpu ++ * ++ * Returns true if all of the cpus in a coupled set are out of the waiting loop. ++ */ ++static inline int cpuidle_coupled_no_cpus_waiting(struct cpuidle_coupled *coupled) ++{ ++ int w = atomic_read(&coupled->ready_waiting_counts) & WAITING_MASK; ++ return w == 0; ++} ++ ++/** ++ * cpuidle_coupled_get_state - determine the deepest idle state ++ * @dev: struct cpuidle_device for this cpu ++ * @coupled: the struct coupled that contains the current cpu ++ * ++ * Returns the deepest idle state that all coupled cpus can enter ++ */ ++static inline int cpuidle_coupled_get_state(struct cpuidle_device *dev, ++ struct cpuidle_coupled *coupled) ++{ ++ int i; ++ int state = INT_MAX; ++ ++ /* ++ * Read barrier ensures that read of requested_state is ordered after ++ * reads of ready_count. Matches the write barriers ++ * cpuidle_set_state_waiting. ++ */ ++ smp_rmb(); ++ ++ for_each_cpu_mask(i, coupled->coupled_cpus) ++ if (cpu_online(i) && coupled->requested_state[i] < state) ++ state = coupled->requested_state[i]; ++ ++ return state; ++} ++ ++static void cpuidle_coupled_poked(void *info) ++{ ++ int cpu = (unsigned long)info; ++ cpumask_clear_cpu(cpu, &cpuidle_coupled_poked_mask); ++} ++ ++/** ++ * cpuidle_coupled_poke - wake up a cpu that may be waiting ++ * @cpu: target cpu ++ * ++ * Ensures that the target cpu exits it's waiting idle state (if it is in it) ++ * and will see updates to waiting_count before it re-enters it's waiting idle ++ * state. ++ * ++ * If cpuidle_coupled_poked_mask is already set for the target cpu, that cpu ++ * either has or will soon have a pending IPI that will wake it out of idle, ++ * or it is currently processing the IPI and is not in idle. ++ */ ++static void cpuidle_coupled_poke(int cpu) ++{ ++ struct call_single_data *csd = &per_cpu(cpuidle_coupled_poke_cb, cpu); ++ ++ if (!cpumask_test_and_set_cpu(cpu, &cpuidle_coupled_poked_mask)) ++ __smp_call_function_single(cpu, csd, 0); ++} ++ ++/** ++ * cpuidle_coupled_poke_others - wake up all other cpus that may be waiting ++ * @dev: struct cpuidle_device for this cpu ++ * @coupled: the struct coupled that contains the current cpu ++ * ++ * Calls cpuidle_coupled_poke on all other online cpus. ++ */ ++static void cpuidle_coupled_poke_others(int this_cpu, ++ struct cpuidle_coupled *coupled) ++{ ++ int cpu; ++ ++ for_each_cpu_mask(cpu, coupled->coupled_cpus) ++ if (cpu != this_cpu && cpu_online(cpu)) ++ cpuidle_coupled_poke(cpu); ++} ++ ++/** ++ * cpuidle_coupled_set_waiting - mark this cpu as in the wait loop ++ * @dev: struct cpuidle_device for this cpu ++ * @coupled: the struct coupled that contains the current cpu ++ * @next_state: the index in drv->states of the requested state for this cpu ++ * ++ * Updates the requested idle state for the specified cpuidle device, ++ * poking all coupled cpus out of idle if necessary to let them see the new ++ * state. ++ */ ++static void cpuidle_coupled_set_waiting(int cpu, ++ struct cpuidle_coupled *coupled, int next_state) ++{ ++ int w; ++ ++ coupled->requested_state[cpu] = next_state; ++ ++ /* ++ * If this is the last cpu to enter the waiting state, poke ++ * all the other cpus out of their waiting state so they can ++ * enter a deeper state. This can race with one of the cpus ++ * exiting the waiting state due to an interrupt and ++ * decrementing waiting_count, see comment below. ++ * ++ * The atomic_inc_return provides a write barrier to order the write ++ * to requested_state with the later write that increments ready_count. ++ */ ++ w = atomic_inc_return(&coupled->ready_waiting_counts) & WAITING_MASK; ++ if (w == coupled->online_count) ++ cpuidle_coupled_poke_others(cpu, coupled); ++} ++ ++/** ++ * cpuidle_coupled_set_not_waiting - mark this cpu as leaving the wait loop ++ * @dev: struct cpuidle_device for this cpu ++ * @coupled: the struct coupled that contains the current cpu ++ * ++ * Removes the requested idle state for the specified cpuidle device. ++ */ ++static void cpuidle_coupled_set_not_waiting(int cpu, ++ struct cpuidle_coupled *coupled) ++{ ++ /* ++ * Decrementing waiting count can race with incrementing it in ++ * cpuidle_coupled_set_waiting, but that's OK. Worst case, some ++ * cpus will increment ready_count and then spin until they ++ * notice that this cpu has cleared it's requested_state. ++ */ ++ atomic_dec(&coupled->ready_waiting_counts); ++ ++ coupled->requested_state[cpu] = CPUIDLE_COUPLED_NOT_IDLE; ++} ++ ++/** ++ * cpuidle_coupled_set_done - mark this cpu as leaving the ready loop ++ * @cpu: the current cpu ++ * @coupled: the struct coupled that contains the current cpu ++ * ++ * Marks this cpu as no longer in the ready and waiting loops. Decrements ++ * the waiting count first to prevent another cpu looping back in and seeing ++ * this cpu as waiting just before it exits idle. ++ */ ++static void cpuidle_coupled_set_done(int cpu, struct cpuidle_coupled *coupled) ++{ ++ cpuidle_coupled_set_not_waiting(cpu, coupled); ++ atomic_sub(MAX_WAITING_CPUS, &coupled->ready_waiting_counts); ++} ++ ++/** ++ * cpuidle_coupled_clear_pokes - spin until the poke interrupt is processed ++ * @cpu - this cpu ++ * ++ * Turns on interrupts and spins until any outstanding poke interrupts have ++ * been processed and the poke bit has been cleared. ++ * ++ * Other interrupts may also be processed while interrupts are enabled, so ++ * need_resched() must be tested after turning interrupts off again to make sure ++ * the interrupt didn't schedule work that should take the cpu out of idle. ++ * ++ * Returns 0 if need_resched was false, -EINTR if need_resched was true. ++ */ ++static int cpuidle_coupled_clear_pokes(int cpu) ++{ ++ local_irq_enable(); ++ while (cpumask_test_cpu(cpu, &cpuidle_coupled_poked_mask)) ++ cpu_relax(); ++ local_irq_disable(); ++ ++ return need_resched() ? -EINTR : 0; ++} ++ ++/** ++ * cpuidle_enter_state_coupled - attempt to enter a state with coupled cpus ++ * @dev: struct cpuidle_device for the current cpu ++ * @drv: struct cpuidle_driver for the platform ++ * @next_state: index of the requested state in drv->states ++ * ++ * Coordinate with coupled cpus to enter the target state. This is a two ++ * stage process. In the first stage, the cpus are operating independently, ++ * and may call into cpuidle_enter_state_coupled at completely different times. ++ * To save as much power as possible, the first cpus to call this function will ++ * go to an intermediate state (the cpuidle_device's safe state), and wait for ++ * all the other cpus to call this function. Once all coupled cpus are idle, ++ * the second stage will start. Each coupled cpu will spin until all cpus have ++ * guaranteed that they will call the target_state. ++ * ++ * This function must be called with interrupts disabled. It may enable ++ * interrupts while preparing for idle, and it will always return with ++ * interrupts enabled. ++ */ ++int cpuidle_enter_state_coupled(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int next_state) ++{ ++ int entered_state = -1; ++ struct cpuidle_coupled *coupled = dev->coupled; ++ ++ if (!coupled) ++ return -EINVAL; ++ ++ while (coupled->prevent) { ++ if (cpuidle_coupled_clear_pokes(dev->cpu)) { ++ local_irq_enable(); ++ return entered_state; ++ } ++ entered_state = cpuidle_enter_state(dev, drv, ++ dev->safe_state_index); ++ } ++ ++ /* Read barrier ensures online_count is read after prevent is cleared */ ++ smp_rmb(); ++ ++ cpuidle_coupled_set_waiting(dev->cpu, coupled, next_state); ++ ++retry: ++ /* ++ * Wait for all coupled cpus to be idle, using the deepest state ++ * allowed for a single cpu. ++ */ ++ while (!cpuidle_coupled_cpus_waiting(coupled)) { ++ if (cpuidle_coupled_clear_pokes(dev->cpu)) { ++ cpuidle_coupled_set_not_waiting(dev->cpu, coupled); ++ goto out; ++ } ++ ++ if (coupled->prevent) { ++ cpuidle_coupled_set_not_waiting(dev->cpu, coupled); ++ goto out; ++ } ++ ++ entered_state = cpuidle_enter_state(dev, drv, ++ dev->safe_state_index); ++ } ++ ++ if (cpuidle_coupled_clear_pokes(dev->cpu)) { ++ cpuidle_coupled_set_not_waiting(dev->cpu, coupled); ++ goto out; ++ } ++ ++ /* ++ * All coupled cpus are probably idle. There is a small chance that ++ * one of the other cpus just became active. Increment the ready count, ++ * and spin until all coupled cpus have incremented the counter. Once a ++ * cpu has incremented the ready counter, it cannot abort idle and must ++ * spin until either all cpus have incremented the ready counter, or ++ * another cpu leaves idle and decrements the waiting counter. ++ */ ++ ++ cpuidle_coupled_set_ready(coupled); ++ while (!cpuidle_coupled_cpus_ready(coupled)) { ++ /* Check if any other cpus bailed out of idle. */ ++ if (!cpuidle_coupled_cpus_waiting(coupled)) ++ if (!cpuidle_coupled_set_not_ready(coupled)) ++ goto retry; ++ ++ cpu_relax(); ++ } ++ ++ /* all cpus have acked the coupled state */ ++ next_state = cpuidle_coupled_get_state(dev, coupled); ++ ++ entered_state = cpuidle_enter_state(dev, drv, next_state); ++ ++ cpuidle_coupled_set_done(dev->cpu, coupled); ++ ++out: ++ /* ++ * Normal cpuidle states are expected to return with irqs enabled. ++ * That leads to an inefficiency where a cpu receiving an interrupt ++ * that brings it out of idle will process that interrupt before ++ * exiting the idle enter function and decrementing ready_count. All ++ * other cpus will need to spin waiting for the cpu that is processing ++ * the interrupt. If the driver returns with interrupts disabled, ++ * all other cpus will loop back into the safe idle state instead of ++ * spinning, saving power. ++ * ++ * Calling local_irq_enable here allows coupled states to return with ++ * interrupts disabled, but won't cause problems for drivers that ++ * exit with interrupts enabled. ++ */ ++ local_irq_enable(); ++ ++ /* ++ * Wait until all coupled cpus have exited idle. There is no risk that ++ * a cpu exits and re-enters the ready state because this cpu has ++ * already decremented its waiting_count. ++ */ ++ while (!cpuidle_coupled_no_cpus_ready(coupled)) ++ cpu_relax(); ++ ++ return entered_state; ++} ++ ++static void cpuidle_coupled_update_online_cpus(struct cpuidle_coupled *coupled) ++{ ++ cpumask_t cpus; ++ cpumask_and(&cpus, cpu_online_mask, &coupled->coupled_cpus); ++ coupled->online_count = cpumask_weight(&cpus); ++} ++ ++/** ++ * cpuidle_coupled_register_device - register a coupled cpuidle device ++ * @dev: struct cpuidle_device for the current cpu ++ * ++ * Called from cpuidle_register_device to handle coupled idle init. Finds the ++ * cpuidle_coupled struct for this set of coupled cpus, or creates one if none ++ * exists yet. ++ */ ++int cpuidle_coupled_register_device(struct cpuidle_device *dev) ++{ ++ int cpu; ++ struct cpuidle_device *other_dev; ++ struct call_single_data *csd; ++ struct cpuidle_coupled *coupled; ++ ++ if (cpumask_empty(&dev->coupled_cpus)) ++ return 0; ++ ++ for_each_cpu_mask(cpu, dev->coupled_cpus) { ++ other_dev = per_cpu(cpuidle_devices, cpu); ++ if (other_dev && other_dev->coupled) { ++ coupled = other_dev->coupled; ++ goto have_coupled; ++ } ++ } ++ ++ /* No existing coupled info found, create a new one */ ++ coupled = kzalloc(sizeof(struct cpuidle_coupled), GFP_KERNEL); ++ if (!coupled) ++ return -ENOMEM; ++ ++ coupled->coupled_cpus = dev->coupled_cpus; ++ ++have_coupled: ++ dev->coupled = coupled; ++ if (WARN_ON(!cpumask_equal(&dev->coupled_cpus, &coupled->coupled_cpus))) ++ coupled->prevent++; ++ ++ cpuidle_coupled_update_online_cpus(coupled); ++ ++ coupled->refcnt++; ++ ++ csd = &per_cpu(cpuidle_coupled_poke_cb, dev->cpu); ++ csd->func = cpuidle_coupled_poked; ++ csd->info = (void *)(unsigned long)dev->cpu; ++ ++ return 0; ++} ++ ++/** ++ * cpuidle_coupled_unregister_device - unregister a coupled cpuidle device ++ * @dev: struct cpuidle_device for the current cpu ++ * ++ * Called from cpuidle_unregister_device to tear down coupled idle. Removes the ++ * cpu from the coupled idle set, and frees the cpuidle_coupled_info struct if ++ * this was the last cpu in the set. ++ */ ++void cpuidle_coupled_unregister_device(struct cpuidle_device *dev) ++{ ++ struct cpuidle_coupled *coupled = dev->coupled; ++ ++ if (cpumask_empty(&dev->coupled_cpus)) ++ return; ++ ++ if (--coupled->refcnt) ++ kfree(coupled); ++ dev->coupled = NULL; ++} ++ ++/** ++ * cpuidle_coupled_prevent_idle - prevent cpus from entering a coupled state ++ * @coupled: the struct coupled that contains the cpu that is changing state ++ * ++ * Disables coupled cpuidle on a coupled set of cpus. Used to ensure that ++ * cpu_online_mask doesn't change while cpus are coordinating coupled idle. ++ */ ++static void cpuidle_coupled_prevent_idle(struct cpuidle_coupled *coupled) ++{ ++ int cpu = get_cpu(); ++ ++ /* Force all cpus out of the waiting loop. */ ++ coupled->prevent++; ++ cpuidle_coupled_poke_others(cpu, coupled); ++ put_cpu(); ++ while (!cpuidle_coupled_no_cpus_waiting(coupled)) ++ cpu_relax(); ++} ++ ++/** ++ * cpuidle_coupled_allow_idle - allows cpus to enter a coupled state ++ * @coupled: the struct coupled that contains the cpu that is changing state ++ * ++ * Enables coupled cpuidle on a coupled set of cpus. Used to ensure that ++ * cpu_online_mask doesn't change while cpus are coordinating coupled idle. ++ */ ++static void cpuidle_coupled_allow_idle(struct cpuidle_coupled *coupled) ++{ ++ int cpu = get_cpu(); ++ ++ /* ++ * Write barrier ensures readers see the new online_count when they ++ * see prevent == false. ++ */ ++ smp_wmb(); ++ coupled->prevent--; ++ /* Force cpus out of the prevent loop. */ ++ cpuidle_coupled_poke_others(cpu, coupled); ++ put_cpu(); ++} ++ ++/** ++ * cpuidle_coupled_cpu_notify - notifier called during hotplug transitions ++ * @nb: notifier block ++ * @action: hotplug transition ++ * @hcpu: target cpu number ++ * ++ * Called when a cpu is brought on or offline using hotplug. Updates the ++ * coupled cpu set appropriately ++ */ ++static int cpuidle_coupled_cpu_notify(struct notifier_block *nb, ++ unsigned long action, void *hcpu) ++{ ++ int cpu = (unsigned long)hcpu; ++ struct cpuidle_device *dev; ++ ++ switch (action & ~CPU_TASKS_FROZEN) { ++ case CPU_UP_PREPARE: ++ case CPU_DOWN_PREPARE: ++ case CPU_ONLINE: ++ case CPU_DEAD: ++ case CPU_UP_CANCELED: ++ case CPU_DOWN_FAILED: ++ break; ++ default: ++ return NOTIFY_OK; ++ } ++ ++ mutex_lock(&cpuidle_lock); ++ ++ dev = per_cpu(cpuidle_devices, cpu); ++ if (!dev->coupled) ++ goto out; ++ ++ switch (action & ~CPU_TASKS_FROZEN) { ++ case CPU_UP_PREPARE: ++ case CPU_DOWN_PREPARE: ++ cpuidle_coupled_prevent_idle(dev->coupled); ++ break; ++ case CPU_ONLINE: ++ case CPU_DEAD: ++ cpuidle_coupled_update_online_cpus(dev->coupled); ++ /* Fall through */ ++ case CPU_UP_CANCELED: ++ case CPU_DOWN_FAILED: ++ cpuidle_coupled_allow_idle(dev->coupled); ++ break; ++ } ++ ++out: ++ mutex_unlock(&cpuidle_lock); ++ return NOTIFY_OK; ++} ++ ++static struct notifier_block cpuidle_coupled_cpu_notifier = { ++ .notifier_call = cpuidle_coupled_cpu_notify, ++}; ++ ++static int __init cpuidle_coupled_init(void) ++{ ++ return register_cpu_notifier(&cpuidle_coupled_cpu_notifier); ++} ++core_initcall(cpuidle_coupled_init); +diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c +index 2f0083a5..e81cfda2 100644 +--- a/drivers/cpuidle/cpuidle.c ++++ b/drivers/cpuidle/cpuidle.c +@@ -102,6 +102,34 @@ int cpuidle_play_dead(void) + return -ENODEV; + } + ++/** ++ * cpuidle_enter_state - enter the state and update stats ++ * @dev: cpuidle device for this cpu ++ * @drv: cpuidle driver for this cpu ++ * @next_state: index into drv->states of the state to enter ++ */ ++int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, ++ int next_state) ++{ ++ int entered_state; ++ ++ entered_state = cpuidle_enter_ops(dev, drv, next_state); ++ ++ if (entered_state >= 0) { ++ /* Update cpuidle counters */ ++ /* This can be moved to within driver enter routine ++ * but that results in multiple copies of same code. ++ */ ++ dev->states_usage[entered_state].time += ++ (unsigned long long)dev->last_residency; ++ dev->states_usage[entered_state].usage++; ++ } else { ++ dev->last_residency = 0; ++ } ++ ++ return entered_state; ++} ++ + /** + * cpuidle_idle_call - the main idle loop + * +@@ -143,23 +171,15 @@ int cpuidle_idle_call(void) + trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); + trace_cpu_idle_rcuidle(next_state, dev->cpu); + +- entered_state = cpuidle_enter_ops(dev, drv, next_state); ++ if (cpuidle_state_is_coupled(dev, drv, next_state)) ++ entered_state = cpuidle_enter_state_coupled(dev, drv, ++ next_state); ++ else ++ entered_state = cpuidle_enter_state(dev, drv, next_state); + + trace_power_end_rcuidle(dev->cpu); + trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); + +- if (entered_state >= 0) { +- /* Update cpuidle counters */ +- /* This can be moved to within driver enter routine +- * but that results in multiple copies of same code. +- */ +- dev->states_usage[entered_state].time += +- (unsigned long long)dev->last_residency; +- dev->states_usage[entered_state].usage++; +- } else { +- dev->last_residency = 0; +- } +- + /* give the governor an opportunity to reflect on the outcome */ + if (cpuidle_curr_governor->reflect) + cpuidle_curr_governor->reflect(dev, entered_state); +@@ -387,13 +407,25 @@ static int __cpuidle_register_device(struct cpuidle_device *dev) + + per_cpu(cpuidle_devices, dev->cpu) = dev; + list_add(&dev->device_list, &cpuidle_detected_devices); +- if ((ret = cpuidle_add_sysfs(cpu_dev))) { +- module_put(cpuidle_driver->owner); +- return ret; +- } ++ ret = cpuidle_add_sysfs(cpu_dev); ++ if (ret) ++ goto err_sysfs; ++ ++ ret = cpuidle_coupled_register_device(dev); ++ if (ret) ++ goto err_coupled; + + dev->registered = 1; + return 0; ++ ++err_coupled: ++ cpuidle_remove_sysfs(cpu_dev); ++ wait_for_completion(&dev->kobj_unregister); ++err_sysfs: ++ list_del(&dev->device_list); ++ per_cpu(cpuidle_devices, dev->cpu) = NULL; ++ module_put(cpuidle_driver->owner); ++ return ret; + } + + /** +@@ -443,6 +475,8 @@ void cpuidle_unregister_device(struct cpuidle_device *dev) + wait_for_completion(&dev->kobj_unregister); + per_cpu(cpuidle_devices, dev->cpu) = NULL; + ++ cpuidle_coupled_unregister_device(dev); ++ + cpuidle_resume_and_unlock(); + + module_put(cpuidle_driver->owner); +diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h +index 7db18668..76e7f696 100644 +--- a/drivers/cpuidle/cpuidle.h ++++ b/drivers/cpuidle/cpuidle.h +@@ -14,6 +14,8 @@ extern struct list_head cpuidle_detected_devices; + extern struct mutex cpuidle_lock; + extern spinlock_t cpuidle_driver_lock; + extern int cpuidle_disabled(void); ++extern int cpuidle_enter_state(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int next_state); + + /* idle loop */ + extern void cpuidle_install_idle_handler(void); +@@ -30,4 +32,34 @@ extern void cpuidle_remove_state_sysfs(struct cpuidle_device *device); + extern int cpuidle_add_sysfs(struct device *dev); + extern void cpuidle_remove_sysfs(struct device *dev); + ++#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED ++bool cpuidle_state_is_coupled(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int state); ++int cpuidle_enter_state_coupled(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int next_state); ++int cpuidle_coupled_register_device(struct cpuidle_device *dev); ++void cpuidle_coupled_unregister_device(struct cpuidle_device *dev); ++#else ++static inline bool cpuidle_state_is_coupled(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int state) ++{ ++ return false; ++} ++ ++static inline int cpuidle_enter_state_coupled(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int next_state) ++{ ++ return -1; ++} ++ ++static inline int cpuidle_coupled_register_device(struct cpuidle_device *dev) ++{ ++ return 0; ++} ++ ++static inline void cpuidle_coupled_unregister_device(struct cpuidle_device *dev) ++{ ++} ++#endif ++ + #endif /* __DRIVER_CPUIDLE_H */ +diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c +index 06335756..746f1e6b 100644 +--- a/drivers/cpuidle/governors/menu.c ++++ b/drivers/cpuidle/governors/menu.c +@@ -173,7 +173,12 @@ static inline int performance_multiplier(void) + + /* for higher loadavg, we are more reluctant */ + +- mult += 2 * get_loadavg(); ++ /* ++ * this doesn't work as intended - it is almost always 0, but can ++ * sometimes, depending on workload, spike very high into the hundreds ++ * even when the average cpu load is under 10%. ++ */ ++ /* mult += 2 * get_loadavg(); */ + + /* for IO wait tasks (per cpu!) we add 5x each */ + mult += 10 * nr_iowait_cpu(smp_processor_id()); +diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c +index 574a06b1..af75ddd4 100644 +--- a/drivers/devfreq/governor_performance.c ++++ b/drivers/devfreq/governor_performance.c +@@ -10,6 +10,7 @@ + */ + + #include ++#include "governor.h" + + static int devfreq_performance_func(struct devfreq *df, + unsigned long *freq) +@@ -25,8 +26,14 @@ static int devfreq_performance_func(struct devfreq *df, + return 0; + } + ++static int performance_init(struct devfreq *devfreq) ++{ ++ return update_devfreq(devfreq); ++} ++ + const struct devfreq_governor devfreq_performance = { + .name = "performance", ++ .init = performance_init, + .get_target_freq = devfreq_performance_func, + .no_central_polling = true, + }; +diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c +index d742d4a8..fec0cdbd 100644 +--- a/drivers/devfreq/governor_powersave.c ++++ b/drivers/devfreq/governor_powersave.c +@@ -10,6 +10,7 @@ + */ + + #include ++#include "governor.h" + + static int devfreq_powersave_func(struct devfreq *df, + unsigned long *freq) +@@ -22,8 +23,14 @@ static int devfreq_powersave_func(struct devfreq *df, + return 0; + } + ++static int powersave_init(struct devfreq *devfreq) ++{ ++ return update_devfreq(devfreq); ++} ++ + const struct devfreq_governor devfreq_powersave = { + .name = "powersave", ++ .init = powersave_init, + .get_target_freq = devfreq_powersave_func, + .no_central_polling = true, + }; +diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile +index cc927788..ca2d3b34 100644 +--- a/drivers/gpu/Makefile ++++ b/drivers/gpu/Makefile +@@ -1 +1 @@ +-obj-y += drm/ vga/ stub/ ++obj-y += drm/ vga/ stub/ ion/ +diff --git a/drivers/gpu/ion/Kconfig b/drivers/gpu/ion/Kconfig +new file mode 100644 +index 00000000..cfb11e1e +--- /dev/null ++++ b/drivers/gpu/ion/Kconfig +@@ -0,0 +1,18 @@ ++menuconfig ION ++ tristate "Ion Memory Manager" ++ select GENERIC_ALLOCATOR ++ select DMA_SHARED_BUFFER ++ help ++ Chose this option to enable the ION Memory Manager. ++ ++config ION_TEGRA ++ tristate "Ion for Tegra" ++ depends on ARCH_TEGRA && ION ++ help ++ Choose this option if you wish to use ion on an nVidia Tegra. ++ ++config ION_AK ++ tristate "Ion for AK" ++ depends on ION ++ help ++ Choose this option if you wish to use ion on an AK platform. +diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile +new file mode 100644 +index 00000000..f4a7ac1c +--- /dev/null ++++ b/drivers/gpu/ion/Makefile +@@ -0,0 +1,4 @@ ++obj-$(CONFIG_ION) += ion.o ion_heap.o ion_page_pool.o ion_system_heap.o \ ++ ion_carveout_heap.o ion_chunk_heap.o ++obj-$(CONFIG_ION_TEGRA) += tegra/ ++obj-$(CONFIG_ION_AK) += plat-anyka/ +diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c +new file mode 100644 +index 00000000..cb170959 +--- /dev/null ++++ b/drivers/gpu/ion/ion.c +@@ -0,0 +1,1462 @@ ++/* ++ ++ * drivers/gpu/ion/ion.c ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ion_priv.h" ++ ++/** ++ * struct ion_device - the metadata of the ion device node ++ * @dev: the actual misc device ++ * @buffers: an rb tree of all the existing buffers ++ * @buffer_lock: lock protecting the tree of buffers ++ * @lock: rwsem protecting the tree of heaps and clients ++ * @heaps: list of all the heaps in the system ++ * @user_clients: list of all the clients created from userspace ++ */ ++struct ion_device { ++ struct miscdevice dev; ++ struct rb_root buffers; ++ struct mutex buffer_lock; ++ struct rw_semaphore lock; ++ struct plist_head heaps; ++ long (*custom_ioctl) (struct ion_client *client, unsigned int cmd, ++ unsigned long arg); ++ struct rb_root clients; ++ struct dentry *debug_root; ++}; ++ ++/** ++ * struct ion_client - a process/hw block local address space ++ * @node: node in the tree of all clients ++ * @dev: backpointer to ion device ++ * @handles: an rb tree of all the handles in this client ++ * @lock: lock protecting the tree of handles ++ * @name: used for debugging ++ * @task: used for debugging ++ * ++ * A client represents a list of buffers this client may access. ++ * The mutex stored here is used to protect both handles tree ++ * as well as the handles themselves, and should be held while modifying either. ++ */ ++struct ion_client { ++ struct rb_node node; ++ struct ion_device *dev; ++ struct rb_root handles; ++ struct mutex lock; ++ const char *name; ++ struct task_struct *task; ++ pid_t pid; ++ struct dentry *debug_root; ++}; ++ ++/** ++ * ion_handle - a client local reference to a buffer ++ * @ref: reference count ++ * @client: back pointer to the client the buffer resides in ++ * @buffer: pointer to the buffer ++ * @node: node in the client's handle rbtree ++ * @kmap_cnt: count of times this client has mapped to kernel ++ * @dmap_cnt: count of times this client has mapped for dma ++ * ++ * Modifications to node, map_cnt or mapping should be protected by the ++ * lock in the client. Other fields are never changed after initialization. ++ */ ++struct ion_handle { ++ struct kref ref; ++ struct ion_client *client; ++ struct ion_buffer *buffer; ++ struct rb_node node; ++ unsigned int kmap_cnt; ++}; ++ ++bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer) ++{ ++ return ((buffer->flags & ION_FLAG_CACHED) && ++ !(buffer->flags & ION_FLAG_CACHED_NEEDS_SYNC)); ++} ++ ++bool ion_buffer_cached(struct ion_buffer *buffer) ++{ ++ return !!(buffer->flags & ION_FLAG_CACHED); ++} ++ ++/* this function should only be called while dev->lock is held */ ++static void ion_buffer_add(struct ion_device *dev, ++ struct ion_buffer *buffer) ++{ ++ struct rb_node **p = &dev->buffers.rb_node; ++ struct rb_node *parent = NULL; ++ struct ion_buffer *entry; ++ ++ while (*p) { ++ parent = *p; ++ entry = rb_entry(parent, struct ion_buffer, node); ++ ++ if (buffer < entry) { ++ p = &(*p)->rb_left; ++ } else if (buffer > entry) { ++ p = &(*p)->rb_right; ++ } else { ++ pr_err("%s: buffer already found.", __func__); ++ BUG(); ++ } ++ } ++ ++ rb_link_node(&buffer->node, parent, p); ++ rb_insert_color(&buffer->node, &dev->buffers); ++} ++ ++static int ion_buffer_alloc_dirty(struct ion_buffer *buffer); ++ ++static bool ion_heap_drain_freelist(struct ion_heap *heap); ++/* this function should only be called while dev->lock is held */ ++static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, ++ struct ion_device *dev, ++ unsigned long len, ++ unsigned long align, ++ unsigned long flags) ++{ ++ struct ion_buffer *buffer; ++ struct sg_table *table; ++ struct scatterlist *sg; ++ int i, ret; ++ ++ buffer = kzalloc(sizeof(struct ion_buffer), GFP_KERNEL); ++ if (!buffer) ++ return ERR_PTR(-ENOMEM); ++ ++ buffer->heap = heap; ++ buffer->flags = flags; ++ kref_init(&buffer->ref); ++ ++ ret = heap->ops->allocate(heap, buffer, len, align, flags); ++ ++ if (ret) { ++ if (!(heap->flags & ION_HEAP_FLAG_DEFER_FREE)) ++ goto err2; ++ ++ ion_heap_drain_freelist(heap); ++ ret = heap->ops->allocate(heap, buffer, len, align, ++ flags); ++ if (ret) ++ goto err2; ++ } ++ ++ buffer->dev = dev; ++ buffer->size = len; ++ ++ table = heap->ops->map_dma(heap, buffer); ++ if (IS_ERR_OR_NULL(table)) { ++ heap->ops->free(buffer); ++ kfree(buffer); ++ return ERR_PTR(PTR_ERR(table)); ++ } ++ buffer->sg_table = table; ++ if (ion_buffer_fault_user_mappings(buffer)) { ++ for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, ++ i) { ++ if (sg_dma_len(sg) == PAGE_SIZE) ++ continue; ++ pr_err("%s: cached mappings that will be faulted in " ++ "must have pagewise sg_lists\n", __func__); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ ret = ion_buffer_alloc_dirty(buffer); ++ if (ret) ++ goto err; ++ } ++ ++ buffer->dev = dev; ++ buffer->size = len; ++ INIT_LIST_HEAD(&buffer->vmas); ++ mutex_init(&buffer->lock); ++ /* this will set up dma addresses for the sglist -- it is not ++ technically correct as per the dma api -- a specific ++ device isn't really taking ownership here. However, in practice on ++ our systems the only dma_address space is physical addresses. ++ Additionally, we can't afford the overhead of invalidating every ++ allocation via dma_map_sg. The implicit contract here is that ++ memory comming from the heaps is ready for dma, ie if it has a ++ cached mapping that mapping has been invalidated */ ++ for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) ++ sg_dma_address(sg) = sg_phys(sg); ++ mutex_lock(&dev->buffer_lock); ++ ion_buffer_add(dev, buffer); ++ mutex_unlock(&dev->buffer_lock); ++ return buffer; ++ ++err: ++ heap->ops->unmap_dma(heap, buffer); ++ heap->ops->free(buffer); ++err2: ++ kfree(buffer); ++ return ERR_PTR(ret); ++} ++ ++static void _ion_buffer_destroy(struct ion_buffer *buffer) ++{ ++ if (WARN_ON(buffer->kmap_cnt > 0)) ++ buffer->heap->ops->unmap_kernel(buffer->heap, buffer); ++ buffer->heap->ops->unmap_dma(buffer->heap, buffer); ++ buffer->heap->ops->free(buffer); ++ if (buffer->flags & ION_FLAG_CACHED) ++ kfree(buffer->dirty); ++ kfree(buffer); ++} ++ ++static void ion_buffer_destroy(struct kref *kref) ++{ ++ struct ion_buffer *buffer = container_of(kref, struct ion_buffer, ref); ++ struct ion_heap *heap = buffer->heap; ++ struct ion_device *dev = buffer->dev; ++ ++ mutex_lock(&dev->buffer_lock); ++ rb_erase(&buffer->node, &dev->buffers); ++ mutex_unlock(&dev->buffer_lock); ++ ++ if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) { ++ rt_mutex_lock(&heap->lock); ++ list_add(&buffer->list, &heap->free_list); ++ rt_mutex_unlock(&heap->lock); ++ wake_up(&heap->waitqueue); ++ return; ++ } ++ _ion_buffer_destroy(buffer); ++} ++ ++static void ion_buffer_get(struct ion_buffer *buffer) ++{ ++ kref_get(&buffer->ref); ++} ++ ++static int ion_buffer_put(struct ion_buffer *buffer) ++{ ++ return kref_put(&buffer->ref, ion_buffer_destroy); ++} ++ ++static void ion_buffer_add_to_handle(struct ion_buffer *buffer) ++{ ++ mutex_lock(&buffer->lock); ++ buffer->handle_count++; ++ mutex_unlock(&buffer->lock); ++} ++ ++static void ion_buffer_remove_from_handle(struct ion_buffer *buffer) ++{ ++ /* ++ * when a buffer is removed from a handle, if it is not in ++ * any other handles, copy the taskcomm and the pid of the ++ * process it's being removed from into the buffer. At this ++ * point there will be no way to track what processes this buffer is ++ * being used by, it only exists as a dma_buf file descriptor. ++ * The taskcomm and pid can provide a debug hint as to where this fd ++ * is in the system ++ */ ++ mutex_lock(&buffer->lock); ++ buffer->handle_count--; ++ BUG_ON(buffer->handle_count < 0); ++ if (!buffer->handle_count) { ++ struct task_struct *task; ++ ++ task = current->group_leader; ++ get_task_comm(buffer->task_comm, task); ++ buffer->pid = task_pid_nr(task); ++ } ++ mutex_unlock(&buffer->lock); ++} ++ ++static struct ion_handle *ion_handle_create(struct ion_client *client, ++ struct ion_buffer *buffer) ++{ ++ struct ion_handle *handle; ++ ++ handle = kzalloc(sizeof(struct ion_handle), GFP_KERNEL); ++ if (!handle) ++ return ERR_PTR(-ENOMEM); ++ kref_init(&handle->ref); ++ rb_init_node(&handle->node); ++ handle->client = client; ++ ion_buffer_get(buffer); ++ ion_buffer_add_to_handle(buffer); ++ handle->buffer = buffer; ++ ++ return handle; ++} ++ ++static void ion_handle_kmap_put(struct ion_handle *); ++ ++static void ion_handle_destroy(struct kref *kref) ++{ ++ struct ion_handle *handle = container_of(kref, struct ion_handle, ref); ++ struct ion_client *client = handle->client; ++ struct ion_buffer *buffer = handle->buffer; ++ ++ mutex_lock(&buffer->lock); ++ while (handle->kmap_cnt) ++ ion_handle_kmap_put(handle); ++ mutex_unlock(&buffer->lock); ++ ++ if (!RB_EMPTY_NODE(&handle->node)) ++ rb_erase(&handle->node, &client->handles); ++ ++ ion_buffer_remove_from_handle(buffer); ++ ion_buffer_put(buffer); ++ ++ kfree(handle); ++} ++ ++struct ion_buffer *ion_handle_buffer(struct ion_handle *handle) ++{ ++ return handle->buffer; ++} ++ ++static void ion_handle_get(struct ion_handle *handle) ++{ ++ kref_get(&handle->ref); ++} ++ ++static int ion_handle_put(struct ion_handle *handle) ++{ ++ return kref_put(&handle->ref, ion_handle_destroy); ++} ++ ++static struct ion_handle *ion_handle_lookup(struct ion_client *client, ++ struct ion_buffer *buffer) ++{ ++ struct rb_node *n; ++ ++ for (n = rb_first(&client->handles); n; n = rb_next(n)) { ++ struct ion_handle *handle = rb_entry(n, struct ion_handle, ++ node); ++ if (handle->buffer == buffer) ++ return handle; ++ } ++ return NULL; ++} ++ ++static bool ion_handle_validate(struct ion_client *client, struct ion_handle *handle) ++{ ++ struct rb_node *n = client->handles.rb_node; ++ ++ while (n) { ++ struct ion_handle *handle_node = rb_entry(n, struct ion_handle, ++ node); ++ if (handle < handle_node) ++ n = n->rb_left; ++ else if (handle > handle_node) ++ n = n->rb_right; ++ else ++ return true; ++ } ++ return false; ++} ++ ++static void ion_handle_add(struct ion_client *client, struct ion_handle *handle) ++{ ++ struct rb_node **p = &client->handles.rb_node; ++ struct rb_node *parent = NULL; ++ struct ion_handle *entry; ++ ++ while (*p) { ++ parent = *p; ++ entry = rb_entry(parent, struct ion_handle, node); ++ ++ if (handle < entry) ++ p = &(*p)->rb_left; ++ else if (handle > entry) ++ p = &(*p)->rb_right; ++ else ++ WARN(1, "%s: buffer already found.", __func__); ++ } ++ ++ rb_link_node(&handle->node, parent, p); ++ rb_insert_color(&handle->node, &client->handles); ++} ++ ++struct ion_handle *ion_alloc(struct ion_client *client, size_t len, ++ size_t align, unsigned int heap_id_mask, ++ unsigned int flags) ++{ ++ struct ion_handle *handle; ++ struct ion_device *dev = client->dev; ++ struct ion_buffer *buffer = NULL; ++ struct ion_heap *heap; ++ ++ pr_debug("%s: len %d align %d heap_id_mask %u flags %x\n", __func__, ++ len, align, heap_id_mask, flags); ++ /* ++ * traverse the list of heaps available in this system in priority ++ * order. If the heap type is supported by the client, and matches the ++ * request of the caller allocate from it. Repeat until allocate has ++ * succeeded or all heaps have been tried ++ */ ++ if (WARN_ON(!len)) ++ return ERR_PTR(-EINVAL); ++ ++ len = PAGE_ALIGN(len); ++ ++ down_read(&dev->lock); ++ plist_for_each_entry(heap, &dev->heaps, node) { ++ /* if the caller didn't specify this heap id */ ++ if (!((1 << heap->id) & heap_id_mask)) ++ continue; ++ buffer = ion_buffer_create(heap, dev, len, align, flags); ++ if (!IS_ERR_OR_NULL(buffer)) ++ break; ++ } ++ up_read(&dev->lock); ++ ++ if (buffer == NULL) ++ return ERR_PTR(-ENODEV); ++ ++ if (IS_ERR(buffer)) ++ return ERR_PTR(PTR_ERR(buffer)); ++ ++ handle = ion_handle_create(client, buffer); ++ ++ /* ++ * ion_buffer_create will create a buffer with a ref_cnt of 1, ++ * and ion_handle_create will take a second reference, drop one here ++ */ ++ ion_buffer_put(buffer); ++ ++ if (!IS_ERR(handle)) { ++ mutex_lock(&client->lock); ++ ion_handle_add(client, handle); ++ mutex_unlock(&client->lock); ++ } ++ ++ ++ return handle; ++} ++EXPORT_SYMBOL(ion_alloc); ++ ++void ion_free(struct ion_client *client, struct ion_handle *handle) ++{ ++ bool valid_handle; ++ ++ BUG_ON(client != handle->client); ++ ++ mutex_lock(&client->lock); ++ valid_handle = ion_handle_validate(client, handle); ++ ++ if (!valid_handle) { ++ WARN(1, "%s: invalid handle passed to free.\n", __func__); ++ mutex_unlock(&client->lock); ++ return; ++ } ++ ion_handle_put(handle); ++ mutex_unlock(&client->lock); ++} ++EXPORT_SYMBOL(ion_free); ++ ++int ion_phys(struct ion_client *client, struct ion_handle *handle, ++ ion_phys_addr_t *addr, size_t *len) ++{ ++ struct ion_buffer *buffer; ++ int ret; ++ ++ mutex_lock(&client->lock); ++ if (!ion_handle_validate(client, handle)) { ++ mutex_unlock(&client->lock); ++ return -EINVAL; ++ } ++ ++ buffer = handle->buffer; ++ ++ if (!buffer->heap->ops->phys) { ++ pr_err("%s: ion_phys is not implemented by this heap.\n", ++ __func__); ++ mutex_unlock(&client->lock); ++ return -ENODEV; ++ } ++ mutex_unlock(&client->lock); ++ ret = buffer->heap->ops->phys(buffer->heap, buffer, addr, len); ++ return ret; ++} ++EXPORT_SYMBOL(ion_phys); ++ ++static void *ion_buffer_kmap_get(struct ion_buffer *buffer) ++{ ++ void *vaddr; ++ ++ if (buffer->kmap_cnt) { ++ buffer->kmap_cnt++; ++ return buffer->vaddr; ++ } ++ vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer); ++ if (IS_ERR_OR_NULL(vaddr)) ++ return vaddr; ++ buffer->vaddr = vaddr; ++ buffer->kmap_cnt++; ++ return vaddr; ++} ++ ++static void *ion_handle_kmap_get(struct ion_handle *handle) ++{ ++ struct ion_buffer *buffer = handle->buffer; ++ void *vaddr; ++ ++ if (handle->kmap_cnt) { ++ handle->kmap_cnt++; ++ return buffer->vaddr; ++ } ++ vaddr = ion_buffer_kmap_get(buffer); ++ if (IS_ERR_OR_NULL(vaddr)) ++ return vaddr; ++ handle->kmap_cnt++; ++ return vaddr; ++} ++ ++static void ion_buffer_kmap_put(struct ion_buffer *buffer) ++{ ++ buffer->kmap_cnt--; ++ if (!buffer->kmap_cnt) { ++ buffer->heap->ops->unmap_kernel(buffer->heap, buffer); ++ buffer->vaddr = NULL; ++ } ++} ++ ++static void ion_handle_kmap_put(struct ion_handle *handle) ++{ ++ struct ion_buffer *buffer = handle->buffer; ++ ++ handle->kmap_cnt--; ++ if (!handle->kmap_cnt) ++ ion_buffer_kmap_put(buffer); ++} ++ ++void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle) ++{ ++ struct ion_buffer *buffer; ++ void *vaddr; ++ ++ mutex_lock(&client->lock); ++ if (!ion_handle_validate(client, handle)) { ++ pr_err("%s: invalid handle passed to map_kernel.\n", ++ __func__); ++ mutex_unlock(&client->lock); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ buffer = handle->buffer; ++ ++ if (!handle->buffer->heap->ops->map_kernel) { ++ pr_err("%s: map_kernel is not implemented by this heap.\n", ++ __func__); ++ mutex_unlock(&client->lock); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ mutex_lock(&buffer->lock); ++ vaddr = ion_handle_kmap_get(handle); ++ mutex_unlock(&buffer->lock); ++ mutex_unlock(&client->lock); ++ return vaddr; ++} ++EXPORT_SYMBOL(ion_map_kernel); ++ ++void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle) ++{ ++ struct ion_buffer *buffer; ++ ++ mutex_lock(&client->lock); ++ buffer = handle->buffer; ++ mutex_lock(&buffer->lock); ++ ion_handle_kmap_put(handle); ++ mutex_unlock(&buffer->lock); ++ mutex_unlock(&client->lock); ++} ++EXPORT_SYMBOL(ion_unmap_kernel); ++ ++static int ion_debug_client_show(struct seq_file *s, void *unused) ++{ ++ struct ion_client *client = s->private; ++ struct rb_node *n; ++ size_t sizes[ION_NUM_HEAP_IDS] = {0}; ++ const char *names[ION_NUM_HEAP_IDS] = {0}; ++ int i; ++ ++ mutex_lock(&client->lock); ++ for (n = rb_first(&client->handles); n; n = rb_next(n)) { ++ struct ion_handle *handle = rb_entry(n, struct ion_handle, ++ node); ++ unsigned int id = handle->buffer->heap->id; ++ ++ if (!names[id]) ++ names[id] = handle->buffer->heap->name; ++ sizes[id] += handle->buffer->size; ++ } ++ mutex_unlock(&client->lock); ++ ++ seq_printf(s, "%16.16s: %16.16s\n", "heap_name", "size_in_bytes"); ++ for (i = 0; i < ION_NUM_HEAP_IDS; i++) { ++ if (!names[i]) ++ continue; ++ seq_printf(s, "%16.16s: %16u\n", names[i], sizes[i]); ++ } ++ return 0; ++} ++ ++static int ion_debug_client_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, ion_debug_client_show, inode->i_private); ++} ++ ++static const struct file_operations debug_client_fops = { ++ .open = ion_debug_client_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++struct ion_client *ion_client_create(struct ion_device *dev, ++ const char *name) ++{ ++ struct ion_client *client; ++ struct task_struct *task; ++ struct rb_node **p; ++ struct rb_node *parent = NULL; ++ struct ion_client *entry; ++ char debug_name[64]; ++ pid_t pid; ++ ++ get_task_struct(current->group_leader); ++ task_lock(current->group_leader); ++ pid = task_pid_nr(current->group_leader); ++ /* don't bother to store task struct for kernel threads, ++ they can't be killed anyway */ ++ if (current->group_leader->flags & PF_KTHREAD) { ++ put_task_struct(current->group_leader); ++ task = NULL; ++ } else { ++ task = current->group_leader; ++ } ++ task_unlock(current->group_leader); ++ ++ client = kzalloc(sizeof(struct ion_client), GFP_KERNEL); ++ if (!client) { ++ if (task) ++ put_task_struct(current->group_leader); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ client->dev = dev; ++ client->handles = RB_ROOT; ++ mutex_init(&client->lock); ++ client->name = name; ++ client->task = task; ++ client->pid = pid; ++ ++ down_write(&dev->lock); ++ p = &dev->clients.rb_node; ++ while (*p) { ++ parent = *p; ++ entry = rb_entry(parent, struct ion_client, node); ++ ++ if (client < entry) ++ p = &(*p)->rb_left; ++ else if (client > entry) ++ p = &(*p)->rb_right; ++ } ++ rb_link_node(&client->node, parent, p); ++ rb_insert_color(&client->node, &dev->clients); ++ ++ snprintf(debug_name, 64, "%u", client->pid); ++ client->debug_root = debugfs_create_file(debug_name, 0664, ++ dev->debug_root, client, ++ &debug_client_fops); ++ up_write(&dev->lock); ++ ++ return client; ++} ++EXPORT_SYMBOL(ion_client_create); ++ ++void ion_client_destroy(struct ion_client *client) ++{ ++ struct ion_device *dev = client->dev; ++ struct rb_node *n; ++ ++ pr_debug("%s: %d\n", __func__, __LINE__); ++ while ((n = rb_first(&client->handles))) { ++ struct ion_handle *handle = rb_entry(n, struct ion_handle, ++ node); ++ ion_handle_destroy(&handle->ref); ++ } ++ down_write(&dev->lock); ++ if (client->task) ++ put_task_struct(client->task); ++ rb_erase(&client->node, &dev->clients); ++ debugfs_remove_recursive(client->debug_root); ++ up_write(&dev->lock); ++ ++ kfree(client); ++} ++EXPORT_SYMBOL(ion_client_destroy); ++ ++struct sg_table *ion_sg_table(struct ion_client *client, ++ struct ion_handle *handle) ++{ ++ struct ion_buffer *buffer; ++ struct sg_table *table; ++ ++ mutex_lock(&client->lock); ++ if (!ion_handle_validate(client, handle)) { ++ pr_err("%s: invalid handle passed to map_dma.\n", ++ __func__); ++ mutex_unlock(&client->lock); ++ return ERR_PTR(-EINVAL); ++ } ++ buffer = handle->buffer; ++ table = buffer->sg_table; ++ mutex_unlock(&client->lock); ++ return table; ++} ++EXPORT_SYMBOL(ion_sg_table); ++ ++static void ion_buffer_sync_for_device(struct ion_buffer *buffer, ++ struct device *dev, ++ enum dma_data_direction direction); ++ ++static struct sg_table *ion_map_dma_buf(struct dma_buf_attachment *attachment, ++ enum dma_data_direction direction) ++{ ++ struct dma_buf *dmabuf = attachment->dmabuf; ++ struct ion_buffer *buffer = dmabuf->priv; ++ ++ ion_buffer_sync_for_device(buffer, attachment->dev, direction); ++ return buffer->sg_table; ++} ++ ++static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment, ++ struct sg_table *table, ++ enum dma_data_direction direction) ++{ ++} ++ ++static int ion_buffer_alloc_dirty(struct ion_buffer *buffer) ++{ ++ unsigned long pages = buffer->sg_table->nents; ++ unsigned long length = (pages + BITS_PER_LONG - 1)/BITS_PER_LONG; ++ ++ buffer->dirty = kzalloc(length * sizeof(unsigned long), GFP_KERNEL); ++ if (!buffer->dirty) ++ return -ENOMEM; ++ return 0; ++} ++ ++struct ion_vma_list { ++ struct list_head list; ++ struct vm_area_struct *vma; ++}; ++ ++static void ion_buffer_sync_for_device(struct ion_buffer *buffer, ++ struct device *dev, ++ enum dma_data_direction dir) ++{ ++ struct scatterlist *sg; ++ int i; ++ struct ion_vma_list *vma_list; ++ ++ pr_debug("%s: syncing for device %s\n", __func__, ++ dev ? dev_name(dev) : "null"); ++ ++ if (!ion_buffer_fault_user_mappings(buffer)) ++ return; ++ ++ mutex_lock(&buffer->lock); ++ for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) { ++ if (!test_bit(i, buffer->dirty)) ++ continue; ++ dma_sync_sg_for_device(dev, sg, 1, dir); ++ clear_bit(i, buffer->dirty); ++ } ++ list_for_each_entry(vma_list, &buffer->vmas, list) { ++ struct vm_area_struct *vma = vma_list->vma; ++ ++ zap_page_range(vma, vma->vm_start, vma->vm_end - vma->vm_start, ++ NULL); ++ } ++ mutex_unlock(&buffer->lock); ++} ++ ++int ion_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ++{ ++ struct ion_buffer *buffer = vma->vm_private_data; ++ struct scatterlist *sg; ++ int i; ++ ++ mutex_lock(&buffer->lock); ++ set_bit(vmf->pgoff, buffer->dirty); ++ ++ for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) { ++ if (i != vmf->pgoff) ++ continue; ++ dma_sync_sg_for_cpu(NULL, sg, 1, DMA_BIDIRECTIONAL); ++ vm_insert_page(vma, (unsigned long)vmf->virtual_address, ++ sg_page(sg)); ++ break; ++ } ++ mutex_unlock(&buffer->lock); ++ return VM_FAULT_NOPAGE; ++} ++ ++static void ion_vm_open(struct vm_area_struct *vma) ++{ ++ struct ion_buffer *buffer = vma->vm_private_data; ++ struct ion_vma_list *vma_list; ++ ++ vma_list = kmalloc(sizeof(struct ion_vma_list), GFP_KERNEL); ++ if (!vma_list) ++ return; ++ vma_list->vma = vma; ++ mutex_lock(&buffer->lock); ++ list_add(&vma_list->list, &buffer->vmas); ++ mutex_unlock(&buffer->lock); ++ pr_debug("%s: adding %p\n", __func__, vma); ++} ++ ++static void ion_vm_close(struct vm_area_struct *vma) ++{ ++ struct ion_buffer *buffer = vma->vm_private_data; ++ struct ion_vma_list *vma_list, *tmp; ++ ++ pr_debug("%s\n", __func__); ++ mutex_lock(&buffer->lock); ++ list_for_each_entry_safe(vma_list, tmp, &buffer->vmas, list) { ++ if (vma_list->vma != vma) ++ continue; ++ list_del(&vma_list->list); ++ kfree(vma_list); ++ pr_debug("%s: deleting %p\n", __func__, vma); ++ break; ++ } ++ mutex_unlock(&buffer->lock); ++} ++ ++struct vm_operations_struct ion_vma_ops = { ++ .open = ion_vm_open, ++ .close = ion_vm_close, ++ .fault = ion_vm_fault, ++}; ++ ++static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) ++{ ++ struct ion_buffer *buffer = dmabuf->priv; ++ int ret = 0; ++ ++ if (!buffer->heap->ops->map_user) { ++ pr_err("%s: this heap does not define a method for mapping " ++ "to userspace\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (ion_buffer_fault_user_mappings(buffer)) { ++ vma->vm_private_data = buffer; ++ vma->vm_ops = &ion_vma_ops; ++ ion_vm_open(vma); ++ return 0; ++ } ++ ++ if (!(buffer->flags & ION_FLAG_CACHED)) ++ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); ++ ++ mutex_lock(&buffer->lock); ++ /* now map it to userspace */ ++ ret = buffer->heap->ops->map_user(buffer->heap, buffer, vma); ++ mutex_unlock(&buffer->lock); ++ ++ if (ret) ++ pr_err("%s: failure mapping buffer to userspace\n", ++ __func__); ++ ++ return ret; ++} ++ ++static void ion_dma_buf_release(struct dma_buf *dmabuf) ++{ ++ struct ion_buffer *buffer = dmabuf->priv; ++ ion_buffer_put(buffer); ++} ++ ++static void *ion_dma_buf_kmap(struct dma_buf *dmabuf, unsigned long offset) ++{ ++ struct ion_buffer *buffer = dmabuf->priv; ++ return buffer->vaddr + offset * PAGE_SIZE; ++} ++ ++static void ion_dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long offset, ++ void *ptr) ++{ ++ return; ++} ++ ++static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, size_t start, ++ size_t len, ++ enum dma_data_direction direction) ++{ ++ struct ion_buffer *buffer = dmabuf->priv; ++ void *vaddr; ++ ++ if (!buffer->heap->ops->map_kernel) { ++ pr_err("%s: map kernel is not implemented by this heap.\n", ++ __func__); ++ return -ENODEV; ++ } ++ ++ mutex_lock(&buffer->lock); ++ vaddr = ion_buffer_kmap_get(buffer); ++ mutex_unlock(&buffer->lock); ++ if (IS_ERR(vaddr)) ++ return PTR_ERR(vaddr); ++ if (!vaddr) ++ return -ENOMEM; ++ return 0; ++} ++ ++static void ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, size_t start, ++ size_t len, ++ enum dma_data_direction direction) ++{ ++ struct ion_buffer *buffer = dmabuf->priv; ++ ++ mutex_lock(&buffer->lock); ++ ion_buffer_kmap_put(buffer); ++ mutex_unlock(&buffer->lock); ++} ++ ++struct dma_buf_ops dma_buf_ops = { ++ .map_dma_buf = ion_map_dma_buf, ++ .unmap_dma_buf = ion_unmap_dma_buf, ++ .mmap = ion_mmap, ++ .release = ion_dma_buf_release, ++ .begin_cpu_access = ion_dma_buf_begin_cpu_access, ++ .end_cpu_access = ion_dma_buf_end_cpu_access, ++ .kmap_atomic = ion_dma_buf_kmap, ++ .kunmap_atomic = ion_dma_buf_kunmap, ++ .kmap = ion_dma_buf_kmap, ++ .kunmap = ion_dma_buf_kunmap, ++}; ++ ++struct dma_buf *ion_share_dma_buf(struct ion_client *client, ++ struct ion_handle *handle) ++{ ++ struct ion_buffer *buffer; ++ struct dma_buf *dmabuf; ++ bool valid_handle; ++ ++ mutex_lock(&client->lock); ++ valid_handle = ion_handle_validate(client, handle); ++ mutex_unlock(&client->lock); ++ if (!valid_handle) { ++ WARN(1, "%s: invalid handle passed to share.\n", __func__); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ buffer = handle->buffer; ++ ion_buffer_get(buffer); ++ dmabuf = dma_buf_export(buffer, &dma_buf_ops, buffer->size, O_RDWR); ++ if (IS_ERR(dmabuf)) { ++ ion_buffer_put(buffer); ++ return dmabuf; ++ } ++ ++ return dmabuf; ++} ++EXPORT_SYMBOL(ion_share_dma_buf); ++ ++int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle) ++{ ++ struct dma_buf *dmabuf; ++ int fd; ++ ++ dmabuf = ion_share_dma_buf(client, handle); ++ if (IS_ERR(dmabuf)) ++ return PTR_ERR(dmabuf); ++ ++ fd = dma_buf_fd(dmabuf, O_CLOEXEC); ++ if (fd < 0) ++ dma_buf_put(dmabuf); ++ ++ return fd; ++} ++EXPORT_SYMBOL(ion_share_dma_buf_fd); ++ ++struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) ++{ ++ struct dma_buf *dmabuf; ++ struct ion_buffer *buffer; ++ struct ion_handle *handle; ++ ++ dmabuf = dma_buf_get(fd); ++ if (IS_ERR_OR_NULL(dmabuf)) ++ return ERR_PTR(PTR_ERR(dmabuf)); ++ /* if this memory came from ion */ ++ ++ if (dmabuf->ops != &dma_buf_ops) { ++ pr_err("%s: can not import dmabuf from another exporter\n", ++ __func__); ++ dma_buf_put(dmabuf); ++ return ERR_PTR(-EINVAL); ++ } ++ buffer = dmabuf->priv; ++ ++ mutex_lock(&client->lock); ++ /* if a handle exists for this buffer just take a reference to it */ ++ handle = ion_handle_lookup(client, buffer); ++ if (!IS_ERR_OR_NULL(handle)) { ++ ion_handle_get(handle); ++ goto end; ++ } ++ handle = ion_handle_create(client, buffer); ++ if (IS_ERR_OR_NULL(handle)) ++ goto end; ++ ion_handle_add(client, handle); ++end: ++ mutex_unlock(&client->lock); ++ dma_buf_put(dmabuf); ++ return handle; ++} ++EXPORT_SYMBOL(ion_import_dma_buf); ++ ++static int ion_sync_for_device(struct ion_client *client, int fd) ++{ ++ struct dma_buf *dmabuf; ++ struct ion_buffer *buffer; ++ ++ dmabuf = dma_buf_get(fd); ++ if (IS_ERR_OR_NULL(dmabuf)) ++ return PTR_ERR(dmabuf); ++ ++ /* if this memory came from ion */ ++ if (dmabuf->ops != &dma_buf_ops) { ++ pr_err("%s: can not sync dmabuf from another exporter\n", ++ __func__); ++ dma_buf_put(dmabuf); ++ return -EINVAL; ++ } ++ buffer = dmabuf->priv; ++ ++ dma_sync_sg_for_device(NULL, buffer->sg_table->sgl, ++ buffer->sg_table->nents, DMA_BIDIRECTIONAL); ++ dma_buf_put(dmabuf); ++ return 0; ++} ++ ++static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ struct ion_client *client = filp->private_data; ++ ++ switch (cmd) { ++ case ION_IOC_ALLOC: ++ { ++ struct ion_allocation_data data; ++ ++ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) ++ return -EFAULT; ++ data.handle = ion_alloc(client, data.len, data.align, ++ data.heap_id_mask, data.flags); ++ ++ if (IS_ERR(data.handle)) ++ return PTR_ERR(data.handle); ++ ++ if (copy_to_user((void __user *)arg, &data, sizeof(data))) { ++ ion_free(client, data.handle); ++ return -EFAULT; ++ } ++ break; ++ } ++ case ION_IOC_FREE: ++ { ++ struct ion_handle_data data; ++ bool valid; ++ ++ if (copy_from_user(&data, (void __user *)arg, ++ sizeof(struct ion_handle_data))) ++ return -EFAULT; ++ mutex_lock(&client->lock); ++ valid = ion_handle_validate(client, data.handle); ++ mutex_unlock(&client->lock); ++ if (!valid) ++ return -EINVAL; ++ ion_free(client, data.handle); ++ break; ++ } ++ case ION_IOC_SHARE: ++ case ION_IOC_MAP: ++ { ++ struct ion_fd_data data; ++ ++ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) ++ return -EFAULT; ++ data.fd = ion_share_dma_buf_fd(client, data.handle); ++ if (copy_to_user((void __user *)arg, &data, sizeof(data))) ++ return -EFAULT; ++ if (data.fd < 0) ++ return data.fd; ++ break; ++ } ++ case ION_IOC_IMPORT: ++ { ++ struct ion_fd_data data; ++ int ret = 0; ++ if (copy_from_user(&data, (void __user *)arg, ++ sizeof(struct ion_fd_data))) ++ return -EFAULT; ++ data.handle = ion_import_dma_buf(client, data.fd); ++ if (IS_ERR(data.handle)) { ++ ret = PTR_ERR(data.handle); ++ data.handle = NULL; ++ } ++ if (copy_to_user((void __user *)arg, &data, ++ sizeof(struct ion_fd_data))) ++ return -EFAULT; ++ if (ret < 0) ++ return ret; ++ break; ++ } ++ case ION_IOC_SYNC: ++ { ++ struct ion_fd_data data; ++ if (copy_from_user(&data, (void __user *)arg, ++ sizeof(struct ion_fd_data))) ++ return -EFAULT; ++ ion_sync_for_device(client, data.fd); ++ break; ++ } ++ case ION_IOC_CUSTOM: ++ { ++ struct ion_device *dev = client->dev; ++ struct ion_custom_data data; ++ ++ if (!dev->custom_ioctl) ++ return -ENOTTY; ++ if (copy_from_user(&data, (void __user *)arg, ++ sizeof(struct ion_custom_data))) ++ return -EFAULT; ++ return dev->custom_ioctl(client, data.cmd, data.arg); ++ } ++ default: ++ return -ENOTTY; ++ } ++ return 0; ++} ++ ++static int ion_release(struct inode *inode, struct file *file) ++{ ++ struct ion_client *client = file->private_data; ++ ++ pr_debug("%s: %d\n", __func__, __LINE__); ++ ion_client_destroy(client); ++ return 0; ++} ++ ++static int ion_open(struct inode *inode, struct file *file) ++{ ++ struct miscdevice *miscdev = file->private_data; ++ struct ion_device *dev = container_of(miscdev, struct ion_device, dev); ++ struct ion_client *client; ++ ++ pr_debug("%s: %d\n", __func__, __LINE__); ++ client = ion_client_create(dev, "user"); ++ if (IS_ERR_OR_NULL(client)) ++ return PTR_ERR(client); ++ file->private_data = client; ++ ++ return 0; ++} ++ ++static const struct file_operations ion_fops = { ++ .owner = THIS_MODULE, ++ .open = ion_open, ++ .release = ion_release, ++ .unlocked_ioctl = ion_ioctl, ++}; ++ ++static size_t ion_debug_heap_total(struct ion_client *client, ++ unsigned int id) ++{ ++ size_t size = 0; ++ struct rb_node *n; ++ ++ mutex_lock(&client->lock); ++ for (n = rb_first(&client->handles); n; n = rb_next(n)) { ++ struct ion_handle *handle = rb_entry(n, ++ struct ion_handle, ++ node); ++ if (handle->buffer->heap->id == id) ++ size += handle->buffer->size; ++ } ++ mutex_unlock(&client->lock); ++ return size; ++} ++ ++static int ion_debug_heap_show(struct seq_file *s, void *unused) ++{ ++ struct ion_heap *heap = s->private; ++ struct ion_device *dev = heap->dev; ++ struct rb_node *n; ++ size_t total_size = 0; ++ size_t total_orphaned_size = 0; ++ ++ seq_printf(s, "%16.s %16.s %16.s\n", "client", "pid", "size"); ++ seq_printf(s, "----------------------------------------------------\n"); ++ ++ for (n = rb_first(&dev->clients); n; n = rb_next(n)) { ++ struct ion_client *client = rb_entry(n, struct ion_client, ++ node); ++ size_t size = ion_debug_heap_total(client, heap->id); ++ if (!size) ++ continue; ++ if (client->task) { ++ char task_comm[TASK_COMM_LEN]; ++ ++ get_task_comm(task_comm, client->task); ++ seq_printf(s, "%16.s %16u %16u\n", task_comm, ++ client->pid, size); ++ } else { ++ seq_printf(s, "%16.s %16u %16u\n", client->name, ++ client->pid, size); ++ } ++ } ++ seq_printf(s, "----------------------------------------------------\n"); ++ seq_printf(s, "orphaned allocations (info is from last known client):" ++ "\n"); ++ mutex_lock(&dev->buffer_lock); ++ for (n = rb_first(&dev->buffers); n; n = rb_next(n)) { ++ struct ion_buffer *buffer = rb_entry(n, struct ion_buffer, ++ node); ++ if (buffer->heap->id != heap->id) ++ continue; ++ total_size += buffer->size; ++ if (!buffer->handle_count) { ++ seq_printf(s, "%16.s %16u %16u %d %d\n", buffer->task_comm, ++ buffer->pid, buffer->size, buffer->kmap_cnt, ++ atomic_read(&buffer->ref.refcount)); ++ total_orphaned_size += buffer->size; ++ } ++ } ++ mutex_unlock(&dev->buffer_lock); ++ seq_printf(s, "----------------------------------------------------\n"); ++ seq_printf(s, "%16.s %16u\n", "total orphaned", ++ total_orphaned_size); ++ seq_printf(s, "%16.s %16u\n", "total ", total_size); ++ seq_printf(s, "----------------------------------------------------\n"); ++ ++ if (heap->debug_show) ++ heap->debug_show(heap, s, unused); ++ ++ return 0; ++} ++ ++static int ion_debug_heap_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, ion_debug_heap_show, inode->i_private); ++} ++ ++static const struct file_operations debug_heap_fops = { ++ .open = ion_debug_heap_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static size_t ion_heap_free_list_is_empty(struct ion_heap *heap) ++{ ++ bool is_empty; ++ ++ rt_mutex_lock(&heap->lock); ++ is_empty = list_empty(&heap->free_list); ++ rt_mutex_unlock(&heap->lock); ++ ++ return is_empty; ++} ++ ++static int ion_heap_deferred_free(void *data) ++{ ++ struct ion_heap *heap = data; ++ ++ while (true) { ++ struct ion_buffer *buffer; ++ ++ wait_event_freezable(heap->waitqueue, ++ !ion_heap_free_list_is_empty(heap)); ++ ++ rt_mutex_lock(&heap->lock); ++ if (list_empty(&heap->free_list)) { ++ rt_mutex_unlock(&heap->lock); ++ continue; ++ } ++ buffer = list_first_entry(&heap->free_list, struct ion_buffer, ++ list); ++ list_del(&buffer->list); ++ rt_mutex_unlock(&heap->lock); ++ _ion_buffer_destroy(buffer); ++ } ++ ++ return 0; ++} ++ ++static bool ion_heap_drain_freelist(struct ion_heap *heap) ++{ ++ struct ion_buffer *buffer, *tmp; ++ ++ if (ion_heap_free_list_is_empty(heap)) ++ return false; ++ rt_mutex_lock(&heap->lock); ++ list_for_each_entry_safe(buffer, tmp, &heap->free_list, list) { ++ _ion_buffer_destroy(buffer); ++ list_del(&buffer->list); ++ } ++ BUG_ON(!list_empty(&heap->free_list)); ++ rt_mutex_unlock(&heap->lock); ++ ++ ++ return true; ++} ++ ++void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap) ++{ ++ struct sched_param param = { .sched_priority = 0 }; ++ ++ if (!heap->ops->allocate || !heap->ops->free || !heap->ops->map_dma || ++ !heap->ops->unmap_dma) ++ pr_err("%s: can not add heap with invalid ops struct.\n", ++ __func__); ++ ++ if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) { ++ INIT_LIST_HEAD(&heap->free_list); ++ rt_mutex_init(&heap->lock); ++ init_waitqueue_head(&heap->waitqueue); ++ heap->task = kthread_run(ion_heap_deferred_free, heap, ++ "%s", heap->name); ++ sched_setscheduler(heap->task, SCHED_IDLE, ¶m); ++ if (IS_ERR(heap->task)) ++ pr_err("%s: creating thread for deferred free failed\n", ++ __func__); ++ } ++ ++ heap->dev = dev; ++ down_write(&dev->lock); ++ /* use negative heap->id to reverse the priority -- when traversing ++ the list later attempt higher id numbers first */ ++ plist_node_init(&heap->node, -heap->id); ++ plist_add(&heap->node, &dev->heaps); ++ debugfs_create_file(heap->name, 0664, dev->debug_root, heap, ++ &debug_heap_fops); ++ up_write(&dev->lock); ++} ++ ++struct ion_device *ion_device_create(long (*custom_ioctl) ++ (struct ion_client *client, ++ unsigned int cmd, ++ unsigned long arg)) ++{ ++ struct ion_device *idev; ++ int ret; ++ ++ idev = kzalloc(sizeof(struct ion_device), GFP_KERNEL); ++ if (!idev) ++ return ERR_PTR(-ENOMEM); ++ ++ idev->dev.minor = MISC_DYNAMIC_MINOR; ++ idev->dev.name = "ion"; ++ idev->dev.fops = &ion_fops; ++ idev->dev.parent = NULL; ++ ret = misc_register(&idev->dev); ++ if (ret) { ++ pr_err("ion: failed to register misc device.\n"); ++ return ERR_PTR(ret); ++ } ++ ++ idev->debug_root = debugfs_create_dir("ion", NULL); ++ if (IS_ERR_OR_NULL(idev->debug_root)) ++ pr_err("ion: failed to create debug files.\n"); ++ ++ idev->custom_ioctl = custom_ioctl; ++ idev->buffers = RB_ROOT; ++ mutex_init(&idev->buffer_lock); ++ init_rwsem(&idev->lock); ++ plist_head_init(&idev->heaps); ++ idev->clients = RB_ROOT; ++ return idev; ++} ++ ++void ion_device_destroy(struct ion_device *dev) ++{ ++ misc_deregister(&dev->dev); ++ /* XXX need to free the heaps and clients ? */ ++ kfree(dev); ++} ++ ++void __init ion_reserve(struct ion_platform_data *data) ++{ ++ int i; ++ ++ for (i = 0; i < data->nr; i++) { ++ if (data->heaps[i].size == 0) ++ continue; ++ ++ if (data->heaps[i].base == 0) { ++ phys_addr_t paddr; ++ paddr = memblock_alloc_base(data->heaps[i].size, ++ data->heaps[i].align, ++ MEMBLOCK_ALLOC_ANYWHERE); ++ if (!paddr) { ++ pr_err("%s: error allocating memblock for " ++ "heap %d\n", ++ __func__, i); ++ continue; ++ } ++ data->heaps[i].base = paddr; ++ } else { ++ int ret = memblock_reserve(data->heaps[i].base, ++ data->heaps[i].size); ++ if (ret) ++ pr_err("memblock reserve of %x@%lx failed\n", ++ data->heaps[i].size, ++ data->heaps[i].base); ++ } ++ pr_info("%s: %s reserved base %lx size %d\n", __func__, ++ data->heaps[i].name, ++ data->heaps[i].base, ++ data->heaps[i].size); ++ } ++} +diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c +new file mode 100644 +index 00000000..ce8d3119 +--- /dev/null ++++ b/drivers/gpu/ion/ion_carveout_heap.c +@@ -0,0 +1,182 @@ ++/* ++ * drivers/gpu/ion/ion_carveout_heap.c ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ion_priv.h" ++ ++#include ++ ++struct ion_carveout_heap { ++ struct ion_heap heap; ++ struct gen_pool *pool; ++ ion_phys_addr_t base; ++}; ++ ++ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, ++ unsigned long size, ++ unsigned long align) ++{ ++ struct ion_carveout_heap *carveout_heap = ++ container_of(heap, struct ion_carveout_heap, heap); ++ unsigned long offset = gen_pool_alloc(carveout_heap->pool, size); ++ ++ if (!offset) ++ return ION_CARVEOUT_ALLOCATE_FAIL; ++ ++ return offset; ++} ++ ++void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, ++ unsigned long size) ++{ ++ struct ion_carveout_heap *carveout_heap = ++ container_of(heap, struct ion_carveout_heap, heap); ++ ++ if (addr == ION_CARVEOUT_ALLOCATE_FAIL) ++ return; ++ gen_pool_free(carveout_heap->pool, addr, size); ++} ++ ++static int ion_carveout_heap_phys(struct ion_heap *heap, ++ struct ion_buffer *buffer, ++ ion_phys_addr_t *addr, size_t *len) ++{ ++ *addr = buffer->priv_phys; ++ *len = buffer->size; ++ return 0; ++} ++ ++static int ion_carveout_heap_allocate(struct ion_heap *heap, ++ struct ion_buffer *buffer, ++ unsigned long size, unsigned long align, ++ unsigned long flags) ++{ ++ buffer->priv_phys = ion_carveout_allocate(heap, size, align); ++ return buffer->priv_phys == ION_CARVEOUT_ALLOCATE_FAIL ? -ENOMEM : 0; ++} ++ ++static void ion_carveout_heap_free(struct ion_buffer *buffer) ++{ ++ struct ion_heap *heap = buffer->heap; ++ ++ ion_carveout_free(heap, buffer->priv_phys, buffer->size); ++ buffer->priv_phys = ION_CARVEOUT_ALLOCATE_FAIL; ++} ++ ++struct sg_table *ion_carveout_heap_map_dma(struct ion_heap *heap, ++ struct ion_buffer *buffer) ++{ ++ struct sg_table *table; ++ int ret; ++ ++ table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); ++ if (!table) ++ return ERR_PTR(-ENOMEM); ++ ret = sg_alloc_table(table, 1, GFP_KERNEL); ++ if (ret) { ++ kfree(table); ++ return ERR_PTR(ret); ++ } ++ sg_set_page(table->sgl, phys_to_page(buffer->priv_phys), buffer->size, ++ 0); ++ return table; ++} ++ ++void ion_carveout_heap_unmap_dma(struct ion_heap *heap, ++ struct ion_buffer *buffer) ++{ ++ sg_free_table(buffer->sg_table); ++} ++ ++void *ion_carveout_heap_map_kernel(struct ion_heap *heap, ++ struct ion_buffer *buffer) ++{ ++ int mtype = MT_MEMORY_NONCACHED; ++ ++ if (buffer->flags & ION_FLAG_CACHED) ++ mtype = MT_MEMORY; ++ ++ return __arm_ioremap(buffer->priv_phys, buffer->size, ++ mtype); ++} ++ ++void ion_carveout_heap_unmap_kernel(struct ion_heap *heap, ++ struct ion_buffer *buffer) ++{ ++ __arm_iounmap(buffer->vaddr); ++ buffer->vaddr = NULL; ++ return; ++} ++ ++int ion_carveout_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, ++ struct vm_area_struct *vma) ++{ ++ return remap_pfn_range(vma, vma->vm_start, ++ __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff, ++ vma->vm_end - vma->vm_start, ++ pgprot_noncached(vma->vm_page_prot)); ++} ++ ++static struct ion_heap_ops carveout_heap_ops = { ++ .allocate = ion_carveout_heap_allocate, ++ .free = ion_carveout_heap_free, ++ .phys = ion_carveout_heap_phys, ++ .map_dma = ion_carveout_heap_map_dma, ++ .unmap_dma = ion_carveout_heap_unmap_dma, ++ .map_user = ion_carveout_heap_map_user, ++ .map_kernel = ion_carveout_heap_map_kernel, ++ .unmap_kernel = ion_carveout_heap_unmap_kernel, ++}; ++ ++struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) ++{ ++ struct ion_carveout_heap *carveout_heap; ++ ++ carveout_heap = kzalloc(sizeof(struct ion_carveout_heap), GFP_KERNEL); ++ if (!carveout_heap) ++ return ERR_PTR(-ENOMEM); ++ ++ carveout_heap->pool = gen_pool_create(12, -1); ++ if (!carveout_heap->pool) { ++ kfree(carveout_heap); ++ return ERR_PTR(-ENOMEM); ++ } ++ carveout_heap->base = heap_data->base; ++ gen_pool_add(carveout_heap->pool, carveout_heap->base, heap_data->size, ++ -1); ++ carveout_heap->heap.ops = &carveout_heap_ops; ++ carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT; ++ ++ return &carveout_heap->heap; ++} ++ ++void ion_carveout_heap_destroy(struct ion_heap *heap) ++{ ++ struct ion_carveout_heap *carveout_heap = ++ container_of(heap, struct ion_carveout_heap, heap); ++ ++ gen_pool_destroy(carveout_heap->pool); ++ kfree(carveout_heap); ++ carveout_heap = NULL; ++} +diff --git a/drivers/gpu/ion/ion_chunk_heap.c b/drivers/gpu/ion/ion_chunk_heap.c +new file mode 100644 +index 00000000..7f482b62 +--- /dev/null ++++ b/drivers/gpu/ion/ion_chunk_heap.c +@@ -0,0 +1,178 @@ ++/* ++ * drivers/gpu/ion/ion_chunk_heap.c ++ * ++ * Copyright (C) 2012 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++//#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ion_priv.h" ++ ++#include ++ ++struct ion_chunk_heap { ++ struct ion_heap heap; ++ struct gen_pool *pool; ++ ion_phys_addr_t base; ++ unsigned long chunk_size; ++ unsigned long size; ++ unsigned long allocated; ++}; ++ ++static int ion_chunk_heap_allocate(struct ion_heap *heap, ++ struct ion_buffer *buffer, ++ unsigned long size, unsigned long align, ++ unsigned long flags) ++{ ++ struct ion_chunk_heap *chunk_heap = ++ container_of(heap, struct ion_chunk_heap, heap); ++ struct sg_table *table; ++ struct scatterlist *sg; ++ int ret, i; ++ unsigned long num_chunks; ++ ++ if (ion_buffer_fault_user_mappings(buffer)) ++ return -ENOMEM; ++ ++ num_chunks = ALIGN(size, chunk_heap->chunk_size) / ++ chunk_heap->chunk_size; ++ buffer->size = num_chunks * chunk_heap->chunk_size; ++ ++ if (buffer->size > chunk_heap->size - chunk_heap->allocated) ++ return -ENOMEM; ++ ++ table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); ++ if (!table) ++ return -ENOMEM; ++ ret = sg_alloc_table(table, num_chunks, GFP_KERNEL); ++ if (ret) { ++ kfree(table); ++ return ret; ++ } ++ ++ sg = table->sgl; ++ for (i = 0; i < num_chunks; i++) { ++ unsigned long paddr = gen_pool_alloc(chunk_heap->pool, ++ chunk_heap->chunk_size); ++ if (!paddr) ++ goto err; ++ sg_set_page(sg, phys_to_page(paddr), chunk_heap->chunk_size, 0); ++ sg = sg_next(sg); ++ } ++ ++ buffer->priv_virt = table; ++ chunk_heap->allocated += buffer->size; ++ return 0; ++err: ++ sg = table->sgl; ++ for (i -= 1; i >= 0; i--) { ++ gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)), ++ sg_dma_len(sg)); ++ sg = sg_next(sg); ++ } ++ sg_free_table(table); ++ kfree(table); ++ return -ENOMEM; ++} ++ ++static void ion_chunk_heap_free(struct ion_buffer *buffer) ++{ ++ struct ion_heap *heap = buffer->heap; ++ struct ion_chunk_heap *chunk_heap = ++ container_of(heap, struct ion_chunk_heap, heap); ++ struct sg_table *table = buffer->priv_virt; ++ struct scatterlist *sg; ++ int i; ++ ++ ion_heap_buffer_zero(buffer); ++ ++ for_each_sg(table->sgl, sg, table->nents, i) { ++ if (ion_buffer_cached(buffer)) ++ __dma_page_cpu_to_dev(sg_page(sg), 0, sg_dma_len(sg), ++ DMA_BIDIRECTIONAL); ++ gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)), ++ sg_dma_len(sg)); ++ } ++ chunk_heap->allocated -= buffer->size; ++ sg_free_table(table); ++ kfree(table); ++} ++ ++struct sg_table *ion_chunk_heap_map_dma(struct ion_heap *heap, ++ struct ion_buffer *buffer) ++{ ++ return buffer->priv_virt; ++} ++ ++void ion_chunk_heap_unmap_dma(struct ion_heap *heap, ++ struct ion_buffer *buffer) ++{ ++ return; ++} ++ ++static struct ion_heap_ops chunk_heap_ops = { ++ .allocate = ion_chunk_heap_allocate, ++ .free = ion_chunk_heap_free, ++ .map_dma = ion_chunk_heap_map_dma, ++ .unmap_dma = ion_chunk_heap_unmap_dma, ++ .map_user = ion_heap_map_user, ++ .map_kernel = ion_heap_map_kernel, ++ .unmap_kernel = ion_heap_unmap_kernel, ++}; ++ ++struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data) ++{ ++ struct ion_chunk_heap *chunk_heap; ++ ++ chunk_heap = kzalloc(sizeof(struct ion_chunk_heap), GFP_KERNEL); ++ if (!chunk_heap) ++ return ERR_PTR(-ENOMEM); ++ ++ chunk_heap->chunk_size = (unsigned long)heap_data->priv; ++ chunk_heap->pool = gen_pool_create(get_order(chunk_heap->chunk_size) + ++ PAGE_SHIFT, -1); ++ if (!chunk_heap->pool) { ++ kfree(chunk_heap); ++ return ERR_PTR(-ENOMEM); ++ } ++ chunk_heap->base = heap_data->base; ++ chunk_heap->size = heap_data->size; ++ chunk_heap->allocated = 0; ++ __dma_page_cpu_to_dev(phys_to_page(heap_data->base), 0, heap_data->size, ++ DMA_BIDIRECTIONAL); ++ gen_pool_add(chunk_heap->pool, chunk_heap->base, heap_data->size, -1); ++ chunk_heap->heap.ops = &chunk_heap_ops; ++ chunk_heap->heap.type = ION_HEAP_TYPE_CHUNK; ++ chunk_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE; ++ pr_info("%s: base %lu size %u align %ld\n", __func__, chunk_heap->base, ++ heap_data->size, heap_data->align); ++ ++ return &chunk_heap->heap; ++} ++ ++void ion_chunk_heap_destroy(struct ion_heap *heap) ++{ ++ struct ion_chunk_heap *chunk_heap = ++ container_of(heap, struct ion_chunk_heap, heap); ++ ++ gen_pool_destroy(chunk_heap->pool); ++ kfree(chunk_heap); ++ chunk_heap = NULL; ++} +diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c +new file mode 100644 +index 00000000..225ef946 +--- /dev/null ++++ b/drivers/gpu/ion/ion_heap.c +@@ -0,0 +1,190 @@ ++/* ++ * drivers/gpu/ion/ion_heap.c ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "ion_priv.h" ++ ++void *ion_heap_map_kernel(struct ion_heap *heap, ++ struct ion_buffer *buffer) ++{ ++ struct scatterlist *sg; ++ int i, j; ++ void *vaddr; ++ pgprot_t pgprot; ++ struct sg_table *table = buffer->sg_table; ++ int npages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; ++ struct page **pages = vmalloc(sizeof(struct page *) * npages); ++ struct page **tmp = pages; ++ ++ if (!pages) ++ return 0; ++ ++ if (buffer->flags & ION_FLAG_CACHED) ++ pgprot = PAGE_KERNEL; ++ else ++ pgprot = pgprot_writecombine(PAGE_KERNEL); ++ ++ for_each_sg(table->sgl, sg, table->nents, i) { ++ int npages_this_entry = PAGE_ALIGN(sg_dma_len(sg)) / PAGE_SIZE; ++ struct page *page = sg_page(sg); ++ BUG_ON(i >= npages); ++ for (j = 0; j < npages_this_entry; j++) { ++ *(tmp++) = page++; ++ } ++ } ++ vaddr = vmap(pages, npages, VM_MAP, pgprot); ++ vfree(pages); ++ ++ return vaddr; ++} ++ ++void ion_heap_unmap_kernel(struct ion_heap *heap, ++ struct ion_buffer *buffer) ++{ ++ vunmap(buffer->vaddr); ++} ++ ++int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, ++ struct vm_area_struct *vma) ++{ ++ struct sg_table *table = buffer->sg_table; ++ unsigned long addr = vma->vm_start; ++ unsigned long offset = vma->vm_pgoff * PAGE_SIZE; ++ struct scatterlist *sg; ++ int i; ++ ++ for_each_sg(table->sgl, sg, table->nents, i) { ++ struct page *page = sg_page(sg); ++ unsigned long remainder = vma->vm_end - addr; ++ unsigned long len = sg_dma_len(sg); ++ ++ if (offset >= sg_dma_len(sg)) { ++ offset -= sg_dma_len(sg); ++ continue; ++ } else if (offset) { ++ page += offset / PAGE_SIZE; ++ len = sg_dma_len(sg) - offset; ++ offset = 0; ++ } ++ len = min(len, remainder); ++ remap_pfn_range(vma, addr, page_to_pfn(page), len, ++ vma->vm_page_prot); ++ addr += len; ++ if (addr >= vma->vm_end) ++ return 0; ++ } ++ return 0; ++} ++ ++int ion_heap_buffer_zero(struct ion_buffer *buffer) ++{ ++ struct sg_table *table = buffer->sg_table; ++ pgprot_t pgprot; ++ struct scatterlist *sg; ++ struct vm_struct *vm_struct; ++ int i, j, ret = 0; ++ ++ if (buffer->flags & ION_FLAG_CACHED) ++ pgprot = PAGE_KERNEL; ++ else ++ pgprot = pgprot_writecombine(PAGE_KERNEL); ++ ++ vm_struct = get_vm_area(PAGE_SIZE, VM_ALLOC); ++ if (!vm_struct) ++ return -ENOMEM; ++ ++ for_each_sg(table->sgl, sg, table->nents, i) { ++ struct page *page = sg_page(sg); ++ unsigned long len = sg_dma_len(sg); ++ ++ for (j = 0; j < len / PAGE_SIZE; j++) { ++ struct page *sub_page = page + j; ++ struct page **pages = &sub_page; ++ ret = map_vm_area(vm_struct, pgprot, &pages); ++ if (ret) ++ goto end; ++ memset(vm_struct->addr, 0, PAGE_SIZE); ++ unmap_kernel_range((unsigned long)vm_struct->addr, ++ PAGE_SIZE); ++ } ++ } ++end: ++ free_vm_area(vm_struct); ++ return ret; ++} ++ ++struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) ++{ ++ struct ion_heap *heap = NULL; ++ ++ switch (heap_data->type) { ++ case ION_HEAP_TYPE_SYSTEM_CONTIG: ++ heap = ion_system_contig_heap_create(heap_data); ++ break; ++ case ION_HEAP_TYPE_SYSTEM: ++ heap = ion_system_heap_create(heap_data); ++ break; ++ case ION_HEAP_TYPE_CARVEOUT: ++ heap = ion_carveout_heap_create(heap_data); ++ break; ++ case ION_HEAP_TYPE_CHUNK: ++ heap = ion_chunk_heap_create(heap_data); ++ break; ++ default: ++ pr_err("%s: Invalid heap type %d\n", __func__, ++ heap_data->type); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ if (IS_ERR_OR_NULL(heap)) { ++ pr_err("%s: error creating heap %s type %d base %lu size %u\n", ++ __func__, heap_data->name, heap_data->type, ++ heap_data->base, heap_data->size); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ heap->name = heap_data->name; ++ heap->id = heap_data->id; ++ return heap; ++} ++ ++void ion_heap_destroy(struct ion_heap *heap) ++{ ++ if (!heap) ++ return; ++ ++ switch (heap->type) { ++ case ION_HEAP_TYPE_SYSTEM_CONTIG: ++ ion_system_contig_heap_destroy(heap); ++ break; ++ case ION_HEAP_TYPE_SYSTEM: ++ ion_system_heap_destroy(heap); ++ break; ++ case ION_HEAP_TYPE_CARVEOUT: ++ ion_carveout_heap_destroy(heap); ++ break; ++ case ION_HEAP_TYPE_CHUNK: ++ ion_chunk_heap_destroy(heap); ++ break; ++ default: ++ pr_err("%s: Invalid heap type %d\n", __func__, ++ heap->type); ++ } ++} +diff --git a/drivers/gpu/ion/ion_page_pool.c b/drivers/gpu/ion/ion_page_pool.c +new file mode 100644 +index 00000000..cd57b30e +--- /dev/null ++++ b/drivers/gpu/ion/ion_page_pool.c +@@ -0,0 +1,281 @@ ++/* ++ * drivers/gpu/ion/ion_mem_pool.c ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ion_priv.h" ++ ++/* #define DEBUG_PAGE_POOL_SHRINKER */ ++ ++static struct plist_head pools = PLIST_HEAD_INIT(pools); ++static struct shrinker shrinker; ++ ++struct ion_page_pool_item { ++ struct page *page; ++ struct list_head list; ++}; ++ ++static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool) ++{ ++ struct page *page = alloc_pages(pool->gfp_mask, pool->order); ++ ++ if (!page) ++ return NULL; ++ /* this is only being used to flush the page for dma, ++ this api is not really suitable for calling from a driver ++ but no better way to flush a page for dma exist at this time */ ++ __dma_page_cpu_to_dev(page, 0, PAGE_SIZE << pool->order, ++ DMA_BIDIRECTIONAL); ++ return page; ++} ++ ++static void ion_page_pool_free_pages(struct ion_page_pool *pool, ++ struct page *page) ++{ ++ __free_pages(page, pool->order); ++} ++ ++static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page) ++{ ++ struct ion_page_pool_item *item; ++ ++ item = kmalloc(sizeof(struct ion_page_pool_item), GFP_KERNEL); ++ if (!item) ++ return -ENOMEM; ++ ++ mutex_lock(&pool->mutex); ++ item->page = page; ++ if (PageHighMem(page)) { ++ list_add_tail(&item->list, &pool->high_items); ++ pool->high_count++; ++ } else { ++ list_add_tail(&item->list, &pool->low_items); ++ pool->low_count++; ++ } ++ mutex_unlock(&pool->mutex); ++ return 0; ++} ++ ++static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high) ++{ ++ struct ion_page_pool_item *item; ++ struct page *page; ++ ++ if (high) { ++ BUG_ON(!pool->high_count); ++ item = list_first_entry(&pool->high_items, ++ struct ion_page_pool_item, list); ++ pool->high_count--; ++ } else { ++ BUG_ON(!pool->low_count); ++ item = list_first_entry(&pool->low_items, ++ struct ion_page_pool_item, list); ++ pool->low_count--; ++ } ++ ++ list_del(&item->list); ++ page = item->page; ++ kfree(item); ++ return page; ++} ++ ++void *ion_page_pool_alloc(struct ion_page_pool *pool) ++{ ++ struct page *page = NULL; ++ ++ BUG_ON(!pool); ++ ++ mutex_lock(&pool->mutex); ++ if (pool->high_count) ++ page = ion_page_pool_remove(pool, true); ++ else if (pool->low_count) ++ page = ion_page_pool_remove(pool, false); ++ mutex_unlock(&pool->mutex); ++ ++ if (!page) ++ page = ion_page_pool_alloc_pages(pool); ++ ++ return page; ++} ++ ++void ion_page_pool_free(struct ion_page_pool *pool, struct page* page) ++{ ++ int ret; ++ ++ ret = ion_page_pool_add(pool, page); ++ if (ret) ++ ion_page_pool_free_pages(pool, page); ++} ++ ++#ifdef DEBUG_PAGE_POOL_SHRINKER ++static int debug_drop_pools_set(void *data, u64 val) ++{ ++ struct shrink_control sc; ++ int objs; ++ ++ sc.gfp_mask = -1; ++ sc.nr_to_scan = 0; ++ ++ if (!val) ++ return 0; ++ ++ objs = shrinker.shrink(&shrinker, &sc); ++ sc.nr_to_scan = objs; ++ ++ shrinker.shrink(&shrinker, &sc); ++ return 0; ++} ++ ++static int debug_drop_pools_get(void *data, u64 *val) ++{ ++ struct shrink_control sc; ++ int objs; ++ ++ sc.gfp_mask = -1; ++ sc.nr_to_scan = 0; ++ ++ objs = shrinker.shrink(&shrinker, &sc); ++ *val = objs; ++ return 0; ++} ++ ++DEFINE_SIMPLE_ATTRIBUTE(debug_drop_pools_fops, debug_drop_pools_get, ++ debug_drop_pools_set, "%llu\n"); ++ ++static int debug_grow_pools_set(void *data, u64 val) ++{ ++ struct ion_page_pool *pool; ++ struct page *page; ++ ++ plist_for_each_entry(pool, &pools, list) { ++ if (val != pool->list.prio) ++ continue; ++ page = ion_page_pool_alloc_pages(pool); ++ if (page) ++ ion_page_pool_add(pool, page); ++ } ++ ++ return 0; ++} ++ ++DEFINE_SIMPLE_ATTRIBUTE(debug_grow_pools_fops, debug_drop_pools_get, ++ debug_grow_pools_set, "%llu\n"); ++#endif ++ ++static int ion_page_pool_total(bool high) ++{ ++ struct ion_page_pool *pool; ++ int total = 0; ++ ++ plist_for_each_entry(pool, &pools, list) { ++ total += high ? (pool->high_count + pool->low_count) * ++ (1 << pool->order) : ++ pool->low_count * (1 << pool->order); ++ } ++ return total; ++} ++ ++static int ion_page_pool_shrink(struct shrinker *shrinker, ++ struct shrink_control *sc) ++{ ++ struct ion_page_pool *pool; ++ int nr_freed = 0; ++ int i; ++ bool high; ++ int nr_to_scan = sc->nr_to_scan; ++ ++ if (sc->gfp_mask & __GFP_HIGHMEM) ++ high = true; ++ ++ if (nr_to_scan == 0) ++ return ion_page_pool_total(high); ++ ++ plist_for_each_entry(pool, &pools, list) { ++ for (i = 0; i < nr_to_scan; i++) { ++ struct page *page; ++ ++ mutex_lock(&pool->mutex); ++ if (high && pool->high_count) { ++ page = ion_page_pool_remove(pool, true); ++ } else if (pool->low_count) { ++ page = ion_page_pool_remove(pool, false); ++ } else { ++ mutex_unlock(&pool->mutex); ++ break; ++ } ++ mutex_unlock(&pool->mutex); ++ ion_page_pool_free_pages(pool, page); ++ nr_freed += (1 << pool->order); ++ } ++ nr_to_scan -= i; ++ } ++ ++ return ion_page_pool_total(high); ++} ++ ++struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order) ++{ ++ struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool), ++ GFP_KERNEL); ++ if (!pool) ++ return NULL; ++ pool->high_count = 0; ++ pool->low_count = 0; ++ INIT_LIST_HEAD(&pool->low_items); ++ INIT_LIST_HEAD(&pool->high_items); ++ pool->gfp_mask = gfp_mask; ++ pool->order = order; ++ mutex_init(&pool->mutex); ++ plist_node_init(&pool->list, order); ++ plist_add(&pool->list, &pools); ++ ++ return pool; ++} ++ ++void ion_page_pool_destroy(struct ion_page_pool *pool) ++{ ++ plist_del(&pool->list, &pools); ++ kfree(pool); ++} ++ ++static int __init ion_page_pool_init(void) ++{ ++ shrinker.shrink = ion_page_pool_shrink; ++ shrinker.seeks = DEFAULT_SEEKS; ++ shrinker.batch = 0; ++ register_shrinker(&shrinker); ++#ifdef DEBUG_PAGE_POOL_SHRINKER ++ debugfs_create_file("ion_pools_shrink", 0644, NULL, NULL, ++ &debug_drop_pools_fops); ++ debugfs_create_file("ion_pools_grow", 0644, NULL, NULL, ++ &debug_grow_pools_fops); ++#endif ++ return 0; ++} ++ ++static void __exit ion_page_pool_exit(void) ++{ ++ unregister_shrinker(&shrinker); ++} ++ ++module_init(ion_page_pool_init); ++module_exit(ion_page_pool_exit); +diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h +new file mode 100644 +index 00000000..50568147 +--- /dev/null ++++ b/drivers/gpu/ion/ion_priv.h +@@ -0,0 +1,288 @@ ++/* ++ * drivers/gpu/ion/ion_priv.h ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _ION_PRIV_H ++#define _ION_PRIV_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct ion_buffer *ion_handle_buffer(struct ion_handle *handle); ++ ++/** ++ * struct ion_buffer - metadata for a particular buffer ++ * @ref: refernce count ++ * @node: node in the ion_device buffers tree ++ * @dev: back pointer to the ion_device ++ * @heap: back pointer to the heap the buffer came from ++ * @flags: buffer specific flags ++ * @size: size of the buffer ++ * @priv_virt: private data to the buffer representable as ++ * a void * ++ * @priv_phys: private data to the buffer representable as ++ * an ion_phys_addr_t (and someday a phys_addr_t) ++ * @lock: protects the buffers cnt fields ++ * @kmap_cnt: number of times the buffer is mapped to the kernel ++ * @vaddr: the kenrel mapping if kmap_cnt is not zero ++ * @dmap_cnt: number of times the buffer is mapped for dma ++ * @sg_table: the sg table for the buffer if dmap_cnt is not zero ++ * @dirty: bitmask representing which pages of this buffer have ++ * been dirtied by the cpu and need cache maintenance ++ * before dma ++ * @vmas: list of vma's mapping this buffer ++ * @handle_count: count of handles referencing this buffer ++ * @task_comm: taskcomm of last client to reference this buffer in a ++ * handle, used for debugging ++ * @pid: pid of last client to reference this buffer in a ++ * handle, used for debugging ++*/ ++struct ion_buffer { ++ struct kref ref; ++ union { ++ struct rb_node node; ++ struct list_head list; ++ }; ++ struct ion_device *dev; ++ struct ion_heap *heap; ++ unsigned long flags; ++ size_t size; ++ union { ++ void *priv_virt; ++ ion_phys_addr_t priv_phys; ++ }; ++ struct mutex lock; ++ int kmap_cnt; ++ void *vaddr; ++ int dmap_cnt; ++ struct sg_table *sg_table; ++ unsigned long *dirty; ++ struct list_head vmas; ++ /* used to track orphaned buffers */ ++ int handle_count; ++ char task_comm[TASK_COMM_LEN]; ++ pid_t pid; ++}; ++ ++/** ++ * struct ion_heap_ops - ops to operate on a given heap ++ * @allocate: allocate memory ++ * @free: free memory ++ * @phys get physical address of a buffer (only define on ++ * physically contiguous heaps) ++ * @map_dma map the memory for dma to a scatterlist ++ * @unmap_dma unmap the memory for dma ++ * @map_kernel map memory to the kernel ++ * @unmap_kernel unmap memory to the kernel ++ * @map_user map memory to userspace ++ */ ++struct ion_heap_ops { ++ int (*allocate) (struct ion_heap *heap, ++ struct ion_buffer *buffer, unsigned long len, ++ unsigned long align, unsigned long flags); ++ void (*free) (struct ion_buffer *buffer); ++ int (*phys) (struct ion_heap *heap, struct ion_buffer *buffer, ++ ion_phys_addr_t *addr, size_t *len); ++ struct sg_table *(*map_dma) (struct ion_heap *heap, ++ struct ion_buffer *buffer); ++ void (*unmap_dma) (struct ion_heap *heap, struct ion_buffer *buffer); ++ void * (*map_kernel) (struct ion_heap *heap, struct ion_buffer *buffer); ++ void (*unmap_kernel) (struct ion_heap *heap, struct ion_buffer *buffer); ++ int (*map_user) (struct ion_heap *mapper, struct ion_buffer *buffer, ++ struct vm_area_struct *vma); ++}; ++ ++/** ++ * heap flags - flags between the heaps and core ion code ++ */ ++#define ION_HEAP_FLAG_DEFER_FREE (1 << 0) ++ ++/** ++ * struct ion_heap - represents a heap in the system ++ * @node: rb node to put the heap on the device's tree of heaps ++ * @dev: back pointer to the ion_device ++ * @type: type of heap ++ * @ops: ops struct as above ++ * @flags: flags ++ * @id: id of heap, also indicates priority of this heap when ++ * allocating. These are specified by platform data and ++ * MUST be unique ++ * @name: used for debugging ++ * @free_list: free list head if deferred free is used ++ * @lock: protects the free list ++ * @waitqueue: queue to wait on from deferred free thread ++ * @task: task struct of deferred free thread ++ * @debug_show: called when heap debug file is read to add any ++ * heap specific debug info to output ++ * ++ * Represents a pool of memory from which buffers can be made. In some ++ * systems the only heap is regular system memory allocated via vmalloc. ++ * On others, some blocks might require large physically contiguous buffers ++ * that are allocated from a specially reserved heap. ++ */ ++struct ion_heap { ++ struct plist_node node; ++ struct ion_device *dev; ++ enum ion_heap_type type; ++ struct ion_heap_ops *ops; ++ unsigned long flags; ++ unsigned int id; ++ const char *name; ++ struct list_head free_list; ++ struct rt_mutex lock; ++ wait_queue_head_t waitqueue; ++ struct task_struct *task; ++ int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *); ++}; ++ ++/** ++ * ion_buffer_cached - this ion buffer is cached ++ * @buffer: buffer ++ * ++ * indicates whether this ion buffer is cached ++ */ ++bool ion_buffer_cached(struct ion_buffer *buffer); ++ ++/** ++ * ion_buffer_fault_user_mappings - fault in user mappings of this buffer ++ * @buffer: buffer ++ * ++ * indicates whether userspace mappings of this buffer will be faulted ++ * in, this can affect how buffers are allocated from the heap. ++ */ ++bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer); ++ ++/** ++ * ion_device_create - allocates and returns an ion device ++ * @custom_ioctl: arch specific ioctl function if applicable ++ * ++ * returns a valid device or -PTR_ERR ++ */ ++struct ion_device *ion_device_create(long (*custom_ioctl) ++ (struct ion_client *client, ++ unsigned int cmd, ++ unsigned long arg)); ++ ++/** ++ * ion_device_destroy - free and device and it's resource ++ * @dev: the device ++ */ ++void ion_device_destroy(struct ion_device *dev); ++ ++/** ++ * ion_device_add_heap - adds a heap to the ion device ++ * @dev: the device ++ * @heap: the heap to add ++ */ ++void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap); ++ ++/** ++ * some helpers for common operations on buffers using the sg_table ++ * and vaddr fields ++ */ ++void *ion_heap_map_kernel(struct ion_heap *, struct ion_buffer *); ++void ion_heap_unmap_kernel(struct ion_heap *, struct ion_buffer *); ++int ion_heap_map_user(struct ion_heap *, struct ion_buffer *, ++ struct vm_area_struct *); ++int ion_heap_buffer_zero(struct ion_buffer *buffer); ++ ++ ++/** ++ * functions for creating and destroying the built in ion heaps. ++ * architectures can add their own custom architecture specific ++ * heaps as appropriate. ++ */ ++ ++struct ion_heap *ion_heap_create(struct ion_platform_heap *); ++void ion_heap_destroy(struct ion_heap *); ++struct ion_heap *ion_system_heap_create(struct ion_platform_heap *); ++void ion_system_heap_destroy(struct ion_heap *); ++ ++struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *); ++void ion_system_contig_heap_destroy(struct ion_heap *); ++ ++struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *); ++void ion_carveout_heap_destroy(struct ion_heap *); ++ ++struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *); ++void ion_chunk_heap_destroy(struct ion_heap *); ++/** ++ * kernel api to allocate/free from carveout -- used when carveout is ++ * used to back an architecture specific custom heap ++ */ ++ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, unsigned long size, ++ unsigned long align); ++void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, ++ unsigned long size); ++/** ++ * The carveout heap returns physical addresses, since 0 may be a valid ++ * physical address, this is used to indicate allocation failed ++ */ ++#define ION_CARVEOUT_ALLOCATE_FAIL -1 ++ ++/** ++ * functions for creating and destroying a heap pool -- allows you ++ * to keep a pool of pre allocated memory to use from your heap. Keeping ++ * a pool of memory that is ready for dma, ie any cached mapping have been ++ * invalidated from the cache, provides a significant peformance benefit on ++ * many systems */ ++ ++/** ++ * struct ion_page_pool - pagepool struct ++ * @high_count: number of highmem items in the pool ++ * @low_count: number of lowmem items in the pool ++ * @high_items: list of highmem items ++ * @low_items: list of lowmem items ++ * @shrinker: a shrinker for the items ++ * @mutex: lock protecting this struct and especially the count ++ * item list ++ * @alloc: function to be used to allocate pageory when the pool ++ * is empty ++ * @free: function to be used to free pageory back to the system ++ * when the shrinker fires ++ * @gfp_mask: gfp_mask to use from alloc ++ * @order: order of pages in the pool ++ * @list: plist node for list of pools ++ * ++ * Allows you to keep a pool of pre allocated pages to use from your heap. ++ * Keeping a pool of pages that is ready for dma, ie any cached mapping have ++ * been invalidated from the cache, provides a significant peformance benefit ++ * on many systems ++ */ ++struct ion_page_pool { ++ int high_count; ++ int low_count; ++ struct list_head high_items; ++ struct list_head low_items; ++ struct mutex mutex; ++ void *(*alloc)(struct ion_page_pool *pool); ++ void (*free)(struct ion_page_pool *pool, struct page *page); ++ gfp_t gfp_mask; ++ unsigned int order; ++ struct plist_node list; ++}; ++ ++struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order); ++void ion_page_pool_destroy(struct ion_page_pool *); ++void *ion_page_pool_alloc(struct ion_page_pool *); ++void ion_page_pool_free(struct ion_page_pool *, struct page *); ++ ++#endif /* _ION_PRIV_H */ +diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c +new file mode 100644 +index 00000000..6369fe8f +--- /dev/null ++++ b/drivers/gpu/ion/ion_system_heap.c +@@ -0,0 +1,417 @@ ++/* ++ * drivers/gpu/ion/ion_system_heap.c ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ion_priv.h" ++ ++static unsigned int high_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | ++ __GFP_NOWARN | __GFP_NORETRY | ++ __GFP_NO_KSWAPD) & ~__GFP_WAIT; ++static unsigned int low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | ++ __GFP_NOWARN); ++static const unsigned int orders[] = {8, 4, 0}; ++static const int num_orders = ARRAY_SIZE(orders); ++static int order_to_index(unsigned int order) ++{ ++ int i; ++ for (i = 0; i < num_orders; i++) ++ if (order == orders[i]) ++ return i; ++ BUG(); ++ return -1; ++} ++ ++static unsigned int order_to_size(int order) ++{ ++ return PAGE_SIZE << order; ++} ++ ++struct ion_system_heap { ++ struct ion_heap heap; ++ struct ion_page_pool **pools; ++}; ++ ++struct page_info { ++ struct page *page; ++ unsigned int order; ++ struct list_head list; ++}; ++ ++static struct page *alloc_buffer_page(struct ion_system_heap *heap, ++ struct ion_buffer *buffer, ++ unsigned long order) ++{ ++ bool cached = ion_buffer_cached(buffer); ++ bool split_pages = ion_buffer_fault_user_mappings(buffer); ++ struct ion_page_pool *pool = heap->pools[order_to_index(order)]; ++ struct page *page; ++ ++ if (!cached) { ++ page = ion_page_pool_alloc(pool); ++ } else { ++ gfp_t gfp_flags = low_order_gfp_flags; ++ ++ if (order > 4) ++ gfp_flags = high_order_gfp_flags; ++ page = alloc_pages(gfp_flags, order); ++ if (!page) ++ return 0; ++ __dma_page_cpu_to_dev(page, 0, PAGE_SIZE << order, ++ DMA_BIDIRECTIONAL); ++ } ++ if (!page) ++ return 0; ++ ++ if (split_pages) ++ split_page(page, order); ++ return page; ++} ++ ++static void free_buffer_page(struct ion_system_heap *heap, ++ struct ion_buffer *buffer, struct page *page, ++ unsigned int order) ++{ ++ bool cached = ion_buffer_cached(buffer); ++ bool split_pages = ion_buffer_fault_user_mappings(buffer); ++ int i; ++ ++ if (!cached) { ++ struct ion_page_pool *pool = heap->pools[order_to_index(order)]; ++ ion_page_pool_free(pool, page); ++ } else if (split_pages) { ++ for (i = 0; i < (1 << order); i++) ++ __free_page(page + i); ++ } else { ++ __free_pages(page, order); ++ } ++} ++ ++ ++static struct page_info *alloc_largest_available(struct ion_system_heap *heap, ++ struct ion_buffer *buffer, ++ unsigned long size, ++ unsigned int max_order) ++{ ++ struct page *page; ++ struct page_info *info; ++ int i; ++ ++ for (i = 0; i < num_orders; i++) { ++ if (size < order_to_size(orders[i])) ++ continue; ++ if (max_order < orders[i]) ++ continue; ++ ++ page = alloc_buffer_page(heap, buffer, orders[i]); ++ if (!page) ++ continue; ++ ++ info = kmalloc(sizeof(struct page_info), GFP_KERNEL); ++ info->page = page; ++ info->order = orders[i]; ++ return info; ++ } ++ return NULL; ++} ++ ++static int ion_system_heap_allocate(struct ion_heap *heap, ++ struct ion_buffer *buffer, ++ unsigned long size, unsigned long align, ++ unsigned long flags) ++{ ++ struct ion_system_heap *sys_heap = container_of(heap, ++ struct ion_system_heap, ++ heap); ++ struct sg_table *table; ++ struct scatterlist *sg; ++ int ret; ++ struct list_head pages; ++ struct page_info *info, *tmp_info; ++ int i = 0; ++ long size_remaining = PAGE_ALIGN(size); ++ unsigned int max_order = orders[0]; ++ bool split_pages = ion_buffer_fault_user_mappings(buffer); ++ ++ INIT_LIST_HEAD(&pages); ++ while (size_remaining > 0) { ++ info = alloc_largest_available(sys_heap, buffer, size_remaining, max_order); ++ if (!info) ++ goto err; ++ list_add_tail(&info->list, &pages); ++ size_remaining -= (1 << info->order) * PAGE_SIZE; ++ max_order = info->order; ++ i++; ++ } ++ ++ table = kmalloc(sizeof(struct sg_table), GFP_KERNEL); ++ if (!table) ++ goto err; ++ ++ if (split_pages) ++ ret = sg_alloc_table(table, PAGE_ALIGN(size) / PAGE_SIZE, ++ GFP_KERNEL); ++ else ++ ret = sg_alloc_table(table, i, GFP_KERNEL); ++ ++ if (ret) ++ goto err1; ++ ++ sg = table->sgl; ++ list_for_each_entry_safe(info, tmp_info, &pages, list) { ++ struct page *page = info->page; ++ if (split_pages) { ++ for (i = 0; i < (1 << info->order); i++) { ++ sg_set_page(sg, page + i, PAGE_SIZE, 0); ++ sg = sg_next(sg); ++ } ++ } else { ++ sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE, ++ 0); ++ sg = sg_next(sg); ++ } ++ list_del(&info->list); ++ kfree(info); ++ } ++ ++ buffer->priv_virt = table; ++ return 0; ++err1: ++ kfree(table); ++err: ++ list_for_each_entry(info, &pages, list) { ++ free_buffer_page(sys_heap, buffer, info->page, info->order); ++ kfree(info); ++ } ++ return -ENOMEM; ++} ++ ++void ion_system_heap_free(struct ion_buffer *buffer) ++{ ++ struct ion_heap *heap = buffer->heap; ++ struct ion_system_heap *sys_heap = container_of(heap, ++ struct ion_system_heap, ++ heap); ++ struct sg_table *table = buffer->sg_table; ++ bool cached = ion_buffer_cached(buffer); ++ struct scatterlist *sg; ++ LIST_HEAD(pages); ++ int i; ++ ++ /* uncached pages come from the page pools, zero them before returning ++ for security purposes (other allocations are zerod at alloc time */ ++ if (!cached) ++ ion_heap_buffer_zero(buffer); ++ ++ for_each_sg(table->sgl, sg, table->nents, i) ++ free_buffer_page(sys_heap, buffer, sg_page(sg), ++ get_order(sg_dma_len(sg))); ++ sg_free_table(table); ++ kfree(table); ++} ++ ++struct sg_table *ion_system_heap_map_dma(struct ion_heap *heap, ++ struct ion_buffer *buffer) ++{ ++ return buffer->priv_virt; ++} ++ ++void ion_system_heap_unmap_dma(struct ion_heap *heap, ++ struct ion_buffer *buffer) ++{ ++ return; ++} ++ ++static struct ion_heap_ops system_heap_ops = { ++ .allocate = ion_system_heap_allocate, ++ .free = ion_system_heap_free, ++ .map_dma = ion_system_heap_map_dma, ++ .unmap_dma = ion_system_heap_unmap_dma, ++ .map_kernel = ion_heap_map_kernel, ++ .unmap_kernel = ion_heap_unmap_kernel, ++ .map_user = ion_heap_map_user, ++}; ++ ++static int ion_system_heap_debug_show(struct ion_heap *heap, struct seq_file *s, ++ void *unused) ++{ ++ ++ struct ion_system_heap *sys_heap = container_of(heap, ++ struct ion_system_heap, ++ heap); ++ int i; ++ for (i = 0; i < num_orders; i++) { ++ struct ion_page_pool *pool = sys_heap->pools[i]; ++ seq_printf(s, "%d order %u highmem pages in pool = %lu total\n", ++ pool->high_count, pool->order, ++ (1 << pool->order) * PAGE_SIZE * pool->high_count); ++ seq_printf(s, "%d order %u lowmem pages in pool = %lu total\n", ++ pool->low_count, pool->order, ++ (1 << pool->order) * PAGE_SIZE * pool->low_count); ++ } ++ return 0; ++} ++ ++struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused) ++{ ++ struct ion_system_heap *heap; ++ int i; ++ ++ heap = kzalloc(sizeof(struct ion_system_heap), GFP_KERNEL); ++ if (!heap) ++ return ERR_PTR(-ENOMEM); ++ heap->heap.ops = &system_heap_ops; ++ heap->heap.type = ION_HEAP_TYPE_SYSTEM; ++ heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE; ++ heap->pools = kzalloc(sizeof(struct ion_page_pool *) * num_orders, ++ GFP_KERNEL); ++ if (!heap->pools) ++ goto err_alloc_pools; ++ for (i = 0; i < num_orders; i++) { ++ struct ion_page_pool *pool; ++ gfp_t gfp_flags = low_order_gfp_flags; ++ ++ if (orders[i] > 4) ++ gfp_flags = high_order_gfp_flags; ++ pool = ion_page_pool_create(gfp_flags, orders[i]); ++ if (!pool) ++ goto err_create_pool; ++ heap->pools[i] = pool; ++ } ++ heap->heap.debug_show = ion_system_heap_debug_show; ++ return &heap->heap; ++err_create_pool: ++ for (i = 0; i < num_orders; i++) ++ if (heap->pools[i]) ++ ion_page_pool_destroy(heap->pools[i]); ++ kfree(heap->pools); ++err_alloc_pools: ++ kfree(heap); ++ return ERR_PTR(-ENOMEM); ++} ++ ++void ion_system_heap_destroy(struct ion_heap *heap) ++{ ++ struct ion_system_heap *sys_heap = container_of(heap, ++ struct ion_system_heap, ++ heap); ++ int i; ++ ++ for (i = 0; i < num_orders; i++) ++ ion_page_pool_destroy(sys_heap->pools[i]); ++ kfree(sys_heap->pools); ++ kfree(sys_heap); ++} ++ ++static int ion_system_contig_heap_allocate(struct ion_heap *heap, ++ struct ion_buffer *buffer, ++ unsigned long len, ++ unsigned long align, ++ unsigned long flags) ++{ ++ buffer->priv_virt = kzalloc(len, GFP_KERNEL); ++ if (!buffer->priv_virt) ++ return -ENOMEM; ++ return 0; ++} ++ ++void ion_system_contig_heap_free(struct ion_buffer *buffer) ++{ ++ kfree(buffer->priv_virt); ++} ++ ++static int ion_system_contig_heap_phys(struct ion_heap *heap, ++ struct ion_buffer *buffer, ++ ion_phys_addr_t *addr, size_t *len) ++{ ++ *addr = virt_to_phys(buffer->priv_virt); ++ *len = buffer->size; ++ return 0; ++} ++ ++struct sg_table *ion_system_contig_heap_map_dma(struct ion_heap *heap, ++ struct ion_buffer *buffer) ++{ ++ struct sg_table *table; ++ int ret; ++ ++ table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); ++ if (!table) ++ return ERR_PTR(-ENOMEM); ++ ret = sg_alloc_table(table, 1, GFP_KERNEL); ++ if (ret) { ++ kfree(table); ++ return ERR_PTR(ret); ++ } ++ sg_set_page(table->sgl, virt_to_page(buffer->priv_virt), buffer->size, ++ 0); ++ return table; ++} ++ ++void ion_system_contig_heap_unmap_dma(struct ion_heap *heap, ++ struct ion_buffer *buffer) ++{ ++ sg_free_table(buffer->sg_table); ++ kfree(buffer->sg_table); ++} ++ ++int ion_system_contig_heap_map_user(struct ion_heap *heap, ++ struct ion_buffer *buffer, ++ struct vm_area_struct *vma) ++{ ++ unsigned long pfn = __phys_to_pfn(virt_to_phys(buffer->priv_virt)); ++ return remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff, ++ vma->vm_end - vma->vm_start, ++ vma->vm_page_prot); ++ ++} ++ ++static struct ion_heap_ops kmalloc_ops = { ++ .allocate = ion_system_contig_heap_allocate, ++ .free = ion_system_contig_heap_free, ++ .phys = ion_system_contig_heap_phys, ++ .map_dma = ion_system_contig_heap_map_dma, ++ .unmap_dma = ion_system_contig_heap_unmap_dma, ++ .map_kernel = ion_heap_map_kernel, ++ .unmap_kernel = ion_heap_unmap_kernel, ++ .map_user = ion_system_contig_heap_map_user, ++}; ++ ++struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *unused) ++{ ++ struct ion_heap *heap; ++ ++ heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL); ++ if (!heap) ++ return ERR_PTR(-ENOMEM); ++ heap->ops = &kmalloc_ops; ++ heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG; ++ return heap; ++} ++ ++void ion_system_contig_heap_destroy(struct ion_heap *heap) ++{ ++ kfree(heap); ++} ++ +diff --git a/drivers/gpu/ion/ion_system_mapper.c b/drivers/gpu/ion/ion_system_mapper.c +new file mode 100644 +index 00000000..692458e0 +--- /dev/null ++++ b/drivers/gpu/ion/ion_system_mapper.c +@@ -0,0 +1,114 @@ ++/* ++ * drivers/gpu/ion/ion_system_mapper.c ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ion_priv.h" ++/* ++ * This mapper is valid for any heap that allocates memory that already has ++ * a kernel mapping, this includes vmalloc'd memory, kmalloc'd memory, ++ * pages obtained via io_remap, etc. ++ */ ++static void *ion_kernel_mapper_map(struct ion_mapper *mapper, ++ struct ion_buffer *buffer, ++ struct ion_mapping **mapping) ++{ ++ if (!((1 << buffer->heap->type) & mapper->heap_mask)) { ++ pr_err("%s: attempting to map an unsupported heap\n", __func__); ++ return ERR_PTR(-EINVAL); ++ } ++ /* XXX REVISIT ME!!! */ ++ *((unsigned long *)mapping) = (unsigned long)buffer->priv; ++ return buffer->priv; ++} ++ ++static void ion_kernel_mapper_unmap(struct ion_mapper *mapper, ++ struct ion_buffer *buffer, ++ struct ion_mapping *mapping) ++{ ++ if (!((1 << buffer->heap->type) & mapper->heap_mask)) ++ pr_err("%s: attempting to unmap an unsupported heap\n", ++ __func__); ++} ++ ++static void *ion_kernel_mapper_map_kernel(struct ion_mapper *mapper, ++ struct ion_buffer *buffer, ++ struct ion_mapping *mapping) ++{ ++ if (!((1 << buffer->heap->type) & mapper->heap_mask)) { ++ pr_err("%s: attempting to unmap an unsupported heap\n", ++ __func__); ++ return ERR_PTR(-EINVAL); ++ } ++ return buffer->priv; ++} ++ ++static int ion_kernel_mapper_map_user(struct ion_mapper *mapper, ++ struct ion_buffer *buffer, ++ struct vm_area_struct *vma, ++ struct ion_mapping *mapping) ++{ ++ int ret; ++ ++ switch (buffer->heap->type) { ++ case ION_HEAP_KMALLOC: ++ { ++ unsigned long pfn = __phys_to_pfn(virt_to_phys(buffer->priv)); ++ ret = remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff, ++ vma->vm_end - vma->vm_start, ++ vma->vm_page_prot); ++ break; ++ } ++ case ION_HEAP_VMALLOC: ++ ret = remap_vmalloc_range(vma, buffer->priv, vma->vm_pgoff); ++ break; ++ default: ++ pr_err("%s: attempting to map unsupported heap to userspace\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static struct ion_mapper_ops ops = { ++ .map = ion_kernel_mapper_map, ++ .map_kernel = ion_kernel_mapper_map_kernel, ++ .map_user = ion_kernel_mapper_map_user, ++ .unmap = ion_kernel_mapper_unmap, ++}; ++ ++struct ion_mapper *ion_system_mapper_create(void) ++{ ++ struct ion_mapper *mapper; ++ mapper = kzalloc(sizeof(struct ion_mapper), GFP_KERNEL); ++ if (!mapper) ++ return ERR_PTR(-ENOMEM); ++ mapper->type = ION_SYSTEM_MAPPER; ++ mapper->ops = &ops; ++ mapper->heap_mask = (1 << ION_HEAP_VMALLOC) | (1 << ION_HEAP_KMALLOC); ++ return mapper; ++} ++ ++void ion_system_mapper_destroy(struct ion_mapper *mapper) ++{ ++ kfree(mapper); ++} ++ +diff --git a/drivers/gpu/ion/plat-anyka/Makefile b/drivers/gpu/ion/plat-anyka/Makefile +new file mode 100755 +index 00000000..5d2c60ec +--- /dev/null ++++ b/drivers/gpu/ion/plat-anyka/Makefile +@@ -0,0 +1 @@ ++obj-y += ak_ion.o +diff --git a/drivers/gpu/ion/plat-anyka/ak_ion.c b/drivers/gpu/ion/plat-anyka/ak_ion.c +new file mode 100755 +index 00000000..9cc80298 +--- /dev/null ++++ b/drivers/gpu/ion/plat-anyka/ak_ion.c +@@ -0,0 +1,96 @@ ++/* ++ * drivers/gpu/plat-anyka/ak_ion.c ++ * ++ * Copyright (C) 2011 Anyka, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include "../ion_priv.h" ++ ++struct ion_device *idev; ++struct ion_mapper *anyka_user_mapper; ++int num_heaps; ++struct ion_heap **heaps; ++ ++int ak_ion_probe(struct platform_device *pdev) ++{ ++ struct ion_platform_data *pdata = pdev->dev.platform_data; ++ int err; ++ int i; ++ ++ num_heaps = pdata->nr; ++ ++ heaps = kzalloc(sizeof(struct ion_heap *) * pdata->nr, GFP_KERNEL); ++ ++ idev = ion_device_create(NULL); ++ if (IS_ERR_OR_NULL(idev)) { ++ kfree(heaps); ++ return PTR_ERR(idev); ++ } ++ ++ /* create the heaps as specified in the board file */ ++ for (i = 0; i < num_heaps; i++) { ++ struct ion_platform_heap *heap_data = &pdata->heaps[i]; ++ ++ heaps[i] = ion_heap_create(heap_data); ++ if (IS_ERR_OR_NULL(heaps[i])) { ++ err = PTR_ERR(heaps[i]); ++ goto err; ++ } ++ ion_device_add_heap(idev, heaps[i]); ++ } ++ platform_set_drvdata(pdev, idev); ++ return 0; ++err: ++ for (i = 0; i < num_heaps; i++) { ++ if (heaps[i]) ++ ion_heap_destroy(heaps[i]); ++ } ++ kfree(heaps); ++ return err; ++} ++ ++int ak_ion_remove(struct platform_device *pdev) ++{ ++ struct ion_device *idev = platform_get_drvdata(pdev); ++ int i; ++ ++ ion_device_destroy(idev); ++ for (i = 0; i < num_heaps; i++) ++ ion_heap_destroy(heaps[i]); ++ kfree(heaps); ++ return 0; ++} ++ ++static struct platform_driver ak_ion_driver = { ++ .probe = ak_ion_probe, ++ .remove = ak_ion_remove, ++ .driver = { .name = "ion-ak" } ++}; ++ ++static int __init ak_ion_init(void) ++{ ++ return platform_driver_register(&ak_ion_driver); ++} ++ ++static void __exit ak_ion_exit(void) ++{ ++ platform_driver_unregister(&ak_ion_driver); ++} ++ ++module_init(ak_ion_init); ++module_exit(ak_ion_exit); ++ +diff --git a/drivers/gpu/ion/tegra/Makefile b/drivers/gpu/ion/tegra/Makefile +new file mode 100644 +index 00000000..11cd003f +--- /dev/null ++++ b/drivers/gpu/ion/tegra/Makefile +@@ -0,0 +1 @@ ++obj-y += tegra_ion.o +diff --git a/drivers/gpu/ion/tegra/tegra_ion.c b/drivers/gpu/ion/tegra/tegra_ion.c +new file mode 100644 +index 00000000..7af6e168 +--- /dev/null ++++ b/drivers/gpu/ion/tegra/tegra_ion.c +@@ -0,0 +1,96 @@ ++/* ++ * drivers/gpu/tegra/tegra_ion.c ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include "../ion_priv.h" ++ ++struct ion_device *idev; ++struct ion_mapper *tegra_user_mapper; ++int num_heaps; ++struct ion_heap **heaps; ++ ++int tegra_ion_probe(struct platform_device *pdev) ++{ ++ struct ion_platform_data *pdata = pdev->dev.platform_data; ++ int err; ++ int i; ++ ++ num_heaps = pdata->nr; ++ ++ heaps = kzalloc(sizeof(struct ion_heap *) * pdata->nr, GFP_KERNEL); ++ ++ idev = ion_device_create(NULL); ++ if (IS_ERR_OR_NULL(idev)) { ++ kfree(heaps); ++ return PTR_ERR(idev); ++ } ++ ++ /* create the heaps as specified in the board file */ ++ for (i = 0; i < num_heaps; i++) { ++ struct ion_platform_heap *heap_data = &pdata->heaps[i]; ++ ++ heaps[i] = ion_heap_create(heap_data); ++ if (IS_ERR_OR_NULL(heaps[i])) { ++ err = PTR_ERR(heaps[i]); ++ goto err; ++ } ++ ion_device_add_heap(idev, heaps[i]); ++ } ++ platform_set_drvdata(pdev, idev); ++ return 0; ++err: ++ for (i = 0; i < num_heaps; i++) { ++ if (heaps[i]) ++ ion_heap_destroy(heaps[i]); ++ } ++ kfree(heaps); ++ return err; ++} ++ ++int tegra_ion_remove(struct platform_device *pdev) ++{ ++ struct ion_device *idev = platform_get_drvdata(pdev); ++ int i; ++ ++ ion_device_destroy(idev); ++ for (i = 0; i < num_heaps; i++) ++ ion_heap_destroy(heaps[i]); ++ kfree(heaps); ++ return 0; ++} ++ ++static struct platform_driver ion_driver = { ++ .probe = tegra_ion_probe, ++ .remove = tegra_ion_remove, ++ .driver = { .name = "ion-tegra" } ++}; ++ ++static int __init ion_init(void) ++{ ++ return platform_driver_register(&ion_driver); ++} ++ ++static void __exit ion_exit(void) ++{ ++ platform_driver_unregister(&ion_driver); ++} ++ ++module_init(ion_init); ++module_exit(ion_exit); ++ +diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig +index ffddcba3..1283fa3b 100644 +--- a/drivers/hid/Kconfig ++++ b/drivers/hid/Kconfig +@@ -55,6 +55,27 @@ config HIDRAW + + If unsure, say Y. + ++config UHID ++ tristate "User-space I/O driver support for HID subsystem" ++ depends on HID ++ default n ++ ---help--- ++ Say Y here if you want to provide HID I/O Drivers from user-space. ++ This allows to write I/O drivers in user-space and feed the data from ++ the device into the kernel. The kernel parses the HID reports, loads the ++ corresponding HID Device Driver or provides input devices on top of your ++ user-space device. ++ ++ This driver cannot be used to parse HID-reports in user-space and write ++ special HID-drivers. You should use hidraw for that. ++ Instead, this driver allows to write the transport-layer driver in ++ user-space like USB-HID and Bluetooth-HID do in kernel-space. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called uhid. ++ + source "drivers/hid/usbhid/Kconfig" + + menu "Special HID drivers" +diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile +index 22f1d16c..9dca8459 100644 +--- a/drivers/hid/Makefile ++++ b/drivers/hid/Makefile +@@ -8,6 +8,7 @@ ifdef CONFIG_DEBUG_FS + endif + + obj-$(CONFIG_HID) += hid.o ++obj-$(CONFIG_UHID) += uhid.o + + hid-$(CONFIG_HIDRAW) += hidraw.o + +diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c +index 21e473e7..43669731 100644 +--- a/drivers/hid/hid-input.c ++++ b/drivers/hid/hid-input.c +@@ -1204,6 +1204,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) + * UGCI) cram a lot of unrelated inputs into the + * same interface. */ + hidinput->report = report; ++ if (hid->driver->input_register && ++ hid->driver->input_register(hid, hidinput)) ++ goto out_cleanup; + if (input_register_device(hidinput->input)) + goto out_cleanup; + hidinput = NULL; +@@ -1218,6 +1221,10 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) + goto out_unwind; + } + ++ if (hidinput && hid->driver->input_register && ++ hid->driver->input_register(hid, hidinput)) ++ goto out_cleanup; ++ + if (hidinput && input_register_device(hidinput->input)) + goto out_cleanup; + +diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c +index 7cf3ffe4..8427463b 100644 +--- a/drivers/hid/hid-magicmouse.c ++++ b/drivers/hid/hid-magicmouse.c +@@ -387,8 +387,10 @@ static int magicmouse_raw_event(struct hid_device *hdev, + return 1; + } + +-static void magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev) ++static int magicmouse_setup_input(struct hid_device *hdev, struct hid_input *hi) + { ++ struct input_dev *input = hi->input; ++ + __set_bit(EV_KEY, input->evbit); + + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { +@@ -471,6 +473,8 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h + __set_bit(EV_MSC, input->evbit); + __set_bit(MSC_RAW, input->mscbit); + } ++ ++ return 0; + } + + static int magicmouse_input_mapping(struct hid_device *hdev, +@@ -523,12 +527,6 @@ static int magicmouse_probe(struct hid_device *hdev, + goto err_free; + } + +- /* We do this after hid-input is done parsing reports so that +- * hid-input uses the most natural button and axis IDs. +- */ +- if (msc->input) +- magicmouse_setup_input(msc->input, hdev); +- + if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE) + report = hid_register_report(hdev, HID_INPUT_REPORT, + MOUSE_REPORT_ID); +@@ -593,6 +591,7 @@ static struct hid_driver magicmouse_driver = { + .remove = magicmouse_remove, + .raw_event = magicmouse_raw_event, + .input_mapping = magicmouse_input_mapping, ++ .input_register = magicmouse_setup_input, + }; + + static int __init magicmouse_init(void) +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index e754dff1..7a180b99 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -330,6 +330,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, + if (field->physical == HID_DG_STYLUS) + return -1; + ++ /* Only map fields from TouchScreen or TouchPad collections. ++ * We need to ignore fields that belong to other collections ++ * such as Mouse that might have the same GenericDesktop usages. */ ++ if (field->application == HID_DG_TOUCHSCREEN) ++ set_bit(INPUT_PROP_DIRECT, hi->input->propbit); ++ else if (field->application == HID_DG_TOUCHPAD) ++ set_bit(INPUT_PROP_POINTER, hi->input->propbit); ++ else ++ return 0; ++ + switch (usage->hid & HID_USAGE_PAGE) { + + case HID_UP_GENDESK: +diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c +new file mode 100644 +index 00000000..714cd8cc +--- /dev/null ++++ b/drivers/hid/uhid.c +@@ -0,0 +1,572 @@ ++/* ++ * User-space I/O driver support for HID subsystem ++ * Copyright (c) 2012 David Herrmann ++ */ ++ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at your option) ++ * any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define UHID_NAME "uhid" ++#define UHID_BUFSIZE 32 ++ ++struct uhid_device { ++ struct mutex devlock; ++ bool running; ++ ++ __u8 *rd_data; ++ uint rd_size; ++ ++ struct hid_device *hid; ++ struct uhid_event input_buf; ++ ++ wait_queue_head_t waitq; ++ spinlock_t qlock; ++ __u8 head; ++ __u8 tail; ++ struct uhid_event *outq[UHID_BUFSIZE]; ++ ++ struct mutex report_lock; ++ wait_queue_head_t report_wait; ++ atomic_t report_done; ++ atomic_t report_id; ++ struct uhid_event report_buf; ++}; ++ ++static struct miscdevice uhid_misc; ++ ++static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) ++{ ++ __u8 newhead; ++ ++ newhead = (uhid->head + 1) % UHID_BUFSIZE; ++ ++ if (newhead != uhid->tail) { ++ uhid->outq[uhid->head] = ev; ++ uhid->head = newhead; ++ wake_up_interruptible(&uhid->waitq); ++ } else { ++ hid_warn(uhid->hid, "Output queue is full\n"); ++ kfree(ev); ++ } ++} ++ ++static int uhid_queue_event(struct uhid_device *uhid, __u32 event) ++{ ++ unsigned long flags; ++ struct uhid_event *ev; ++ ++ ev = kzalloc(sizeof(*ev), GFP_KERNEL); ++ if (!ev) ++ return -ENOMEM; ++ ++ ev->type = event; ++ ++ spin_lock_irqsave(&uhid->qlock, flags); ++ uhid_queue(uhid, ev); ++ spin_unlock_irqrestore(&uhid->qlock, flags); ++ ++ return 0; ++} ++ ++static int uhid_hid_start(struct hid_device *hid) ++{ ++ struct uhid_device *uhid = hid->driver_data; ++ ++ return uhid_queue_event(uhid, UHID_START); ++} ++ ++static void uhid_hid_stop(struct hid_device *hid) ++{ ++ struct uhid_device *uhid = hid->driver_data; ++ ++ hid->claimed = 0; ++ uhid_queue_event(uhid, UHID_STOP); ++} ++ ++static int uhid_hid_open(struct hid_device *hid) ++{ ++ struct uhid_device *uhid = hid->driver_data; ++ ++ return uhid_queue_event(uhid, UHID_OPEN); ++} ++ ++static void uhid_hid_close(struct hid_device *hid) ++{ ++ struct uhid_device *uhid = hid->driver_data; ++ ++ uhid_queue_event(uhid, UHID_CLOSE); ++} ++ ++static int uhid_hid_input(struct input_dev *input, unsigned int type, ++ unsigned int code, int value) ++{ ++ struct hid_device *hid = input_get_drvdata(input); ++ struct uhid_device *uhid = hid->driver_data; ++ unsigned long flags; ++ struct uhid_event *ev; ++ ++ ev = kzalloc(sizeof(*ev), GFP_ATOMIC); ++ if (!ev) ++ return -ENOMEM; ++ ++ ev->type = UHID_OUTPUT_EV; ++ ev->u.output_ev.type = type; ++ ev->u.output_ev.code = code; ++ ev->u.output_ev.value = value; ++ ++ spin_lock_irqsave(&uhid->qlock, flags); ++ uhid_queue(uhid, ev); ++ spin_unlock_irqrestore(&uhid->qlock, flags); ++ ++ return 0; ++} ++ ++static int uhid_hid_parse(struct hid_device *hid) ++{ ++ struct uhid_device *uhid = hid->driver_data; ++ ++ return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); ++} ++ ++static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, ++ __u8 *buf, size_t count, unsigned char rtype) ++{ ++ struct uhid_device *uhid = hid->driver_data; ++ __u8 report_type; ++ struct uhid_event *ev; ++ unsigned long flags; ++ int ret; ++ size_t uninitialized_var(len); ++ struct uhid_feature_answer_req *req; ++ ++ if (!uhid->running) ++ return -EIO; ++ ++ switch (rtype) { ++ case HID_FEATURE_REPORT: ++ report_type = UHID_FEATURE_REPORT; ++ break; ++ case HID_OUTPUT_REPORT: ++ report_type = UHID_OUTPUT_REPORT; ++ break; ++ case HID_INPUT_REPORT: ++ report_type = UHID_INPUT_REPORT; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = mutex_lock_interruptible(&uhid->report_lock); ++ if (ret) ++ return ret; ++ ++ ev = kzalloc(sizeof(*ev), GFP_KERNEL); ++ if (!ev) { ++ ret = -ENOMEM; ++ goto unlock; ++ } ++ ++ spin_lock_irqsave(&uhid->qlock, flags); ++ ev->type = UHID_FEATURE; ++ ev->u.feature.id = atomic_inc_return(&uhid->report_id); ++ ev->u.feature.rnum = rnum; ++ ev->u.feature.rtype = report_type; ++ ++ atomic_set(&uhid->report_done, 0); ++ uhid_queue(uhid, ev); ++ spin_unlock_irqrestore(&uhid->qlock, flags); ++ ++ ret = wait_event_interruptible_timeout(uhid->report_wait, ++ atomic_read(&uhid->report_done), 5 * HZ); ++ ++ /* ++ * Make sure "uhid->running" is cleared on shutdown before ++ * "uhid->report_done" is set. ++ */ ++ smp_rmb(); ++ if (!ret || !uhid->running) { ++ ret = -EIO; ++ } else if (ret < 0) { ++ ret = -ERESTARTSYS; ++ } else { ++ spin_lock_irqsave(&uhid->qlock, flags); ++ req = &uhid->report_buf.u.feature_answer; ++ ++ if (req->err) { ++ ret = -EIO; ++ } else { ++ ret = 0; ++ len = min(count, ++ min_t(size_t, req->size, UHID_DATA_MAX)); ++ memcpy(buf, req->data, len); ++ } ++ ++ spin_unlock_irqrestore(&uhid->qlock, flags); ++ } ++ ++ atomic_set(&uhid->report_done, 1); ++ ++unlock: ++ mutex_unlock(&uhid->report_lock); ++ return ret ? ret : len; ++} ++ ++static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, ++ unsigned char report_type) ++{ ++ struct uhid_device *uhid = hid->driver_data; ++ __u8 rtype; ++ unsigned long flags; ++ struct uhid_event *ev; ++ ++ switch (report_type) { ++ case HID_FEATURE_REPORT: ++ rtype = UHID_FEATURE_REPORT; ++ break; ++ case HID_OUTPUT_REPORT: ++ rtype = UHID_OUTPUT_REPORT; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (count < 1 || count > UHID_DATA_MAX) ++ return -EINVAL; ++ ++ ev = kzalloc(sizeof(*ev), GFP_KERNEL); ++ if (!ev) ++ return -ENOMEM; ++ ++ ev->type = UHID_OUTPUT; ++ ev->u.output.size = count; ++ ev->u.output.rtype = rtype; ++ memcpy(ev->u.output.data, buf, count); ++ ++ spin_lock_irqsave(&uhid->qlock, flags); ++ uhid_queue(uhid, ev); ++ spin_unlock_irqrestore(&uhid->qlock, flags); ++ ++ return count; ++} ++ ++static struct hid_ll_driver uhid_hid_driver = { ++ .start = uhid_hid_start, ++ .stop = uhid_hid_stop, ++ .open = uhid_hid_open, ++ .close = uhid_hid_close, ++ .hidinput_input_event = uhid_hid_input, ++ .parse = uhid_hid_parse, ++}; ++ ++static int uhid_dev_create(struct uhid_device *uhid, ++ const struct uhid_event *ev) ++{ ++ struct hid_device *hid; ++ int ret; ++ ++ if (uhid->running) ++ return -EALREADY; ++ ++ uhid->rd_size = ev->u.create.rd_size; ++ if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) ++ return -EINVAL; ++ ++ uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL); ++ if (!uhid->rd_data) ++ return -ENOMEM; ++ ++ if (copy_from_user(uhid->rd_data, ev->u.create.rd_data, ++ uhid->rd_size)) { ++ ret = -EFAULT; ++ goto err_free; ++ } ++ ++ hid = hid_allocate_device(); ++ if (IS_ERR(hid)) { ++ ret = PTR_ERR(hid); ++ goto err_free; ++ } ++ ++ strncpy(hid->name, ev->u.create.name, 127); ++ hid->name[127] = 0; ++ strncpy(hid->phys, ev->u.create.phys, 63); ++ hid->phys[63] = 0; ++ strncpy(hid->uniq, ev->u.create.uniq, 63); ++ hid->uniq[63] = 0; ++ ++ hid->ll_driver = &uhid_hid_driver; ++ hid->hid_get_raw_report = uhid_hid_get_raw; ++ hid->hid_output_raw_report = uhid_hid_output_raw; ++ hid->bus = ev->u.create.bus; ++ hid->vendor = ev->u.create.vendor; ++ hid->product = ev->u.create.product; ++ hid->version = ev->u.create.version; ++ hid->country = ev->u.create.country; ++ hid->driver_data = uhid; ++ hid->dev.parent = uhid_misc.this_device; ++ ++ uhid->hid = hid; ++ uhid->running = true; ++ ++ ret = hid_add_device(hid); ++ if (ret) { ++ hid_err(hid, "Cannot register HID device\n"); ++ goto err_hid; ++ } ++ ++ return 0; ++ ++err_hid: ++ hid_destroy_device(hid); ++ uhid->hid = NULL; ++ uhid->running = false; ++err_free: ++ kfree(uhid->rd_data); ++ return ret; ++} ++ ++static int uhid_dev_destroy(struct uhid_device *uhid) ++{ ++ if (!uhid->running) ++ return -EINVAL; ++ ++ /* clear "running" before setting "report_done" */ ++ uhid->running = false; ++ smp_wmb(); ++ atomic_set(&uhid->report_done, 1); ++ wake_up_interruptible(&uhid->report_wait); ++ ++ hid_destroy_device(uhid->hid); ++ kfree(uhid->rd_data); ++ ++ return 0; ++} ++ ++static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) ++{ ++ if (!uhid->running) ++ return -EINVAL; ++ ++ hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, ++ min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); ++ ++ return 0; ++} ++ ++static int uhid_dev_feature_answer(struct uhid_device *uhid, ++ struct uhid_event *ev) ++{ ++ unsigned long flags; ++ ++ if (!uhid->running) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&uhid->qlock, flags); ++ ++ /* id for old report; drop it silently */ ++ if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id) ++ goto unlock; ++ if (atomic_read(&uhid->report_done)) ++ goto unlock; ++ ++ memcpy(&uhid->report_buf, ev, sizeof(*ev)); ++ atomic_set(&uhid->report_done, 1); ++ wake_up_interruptible(&uhid->report_wait); ++ ++unlock: ++ spin_unlock_irqrestore(&uhid->qlock, flags); ++ return 0; ++} ++ ++static int uhid_char_open(struct inode *inode, struct file *file) ++{ ++ struct uhid_device *uhid; ++ ++ uhid = kzalloc(sizeof(*uhid), GFP_KERNEL); ++ if (!uhid) ++ return -ENOMEM; ++ ++ mutex_init(&uhid->devlock); ++ mutex_init(&uhid->report_lock); ++ spin_lock_init(&uhid->qlock); ++ init_waitqueue_head(&uhid->waitq); ++ init_waitqueue_head(&uhid->report_wait); ++ uhid->running = false; ++ atomic_set(&uhid->report_done, 1); ++ ++ file->private_data = uhid; ++ nonseekable_open(inode, file); ++ ++ return 0; ++} ++ ++static int uhid_char_release(struct inode *inode, struct file *file) ++{ ++ struct uhid_device *uhid = file->private_data; ++ unsigned int i; ++ ++ uhid_dev_destroy(uhid); ++ ++ for (i = 0; i < UHID_BUFSIZE; ++i) ++ kfree(uhid->outq[i]); ++ ++ kfree(uhid); ++ ++ return 0; ++} ++ ++static ssize_t uhid_char_read(struct file *file, char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ struct uhid_device *uhid = file->private_data; ++ int ret; ++ unsigned long flags; ++ size_t len; ++ ++ /* they need at least the "type" member of uhid_event */ ++ if (count < sizeof(__u32)) ++ return -EINVAL; ++ ++try_again: ++ if (file->f_flags & O_NONBLOCK) { ++ if (uhid->head == uhid->tail) ++ return -EAGAIN; ++ } else { ++ ret = wait_event_interruptible(uhid->waitq, ++ uhid->head != uhid->tail); ++ if (ret) ++ return ret; ++ } ++ ++ ret = mutex_lock_interruptible(&uhid->devlock); ++ if (ret) ++ return ret; ++ ++ if (uhid->head == uhid->tail) { ++ mutex_unlock(&uhid->devlock); ++ goto try_again; ++ } else { ++ len = min(count, sizeof(**uhid->outq)); ++ if (copy_to_user(buffer, uhid->outq[uhid->tail], len)) { ++ ret = -EFAULT; ++ } else { ++ kfree(uhid->outq[uhid->tail]); ++ uhid->outq[uhid->tail] = NULL; ++ ++ spin_lock_irqsave(&uhid->qlock, flags); ++ uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE; ++ spin_unlock_irqrestore(&uhid->qlock, flags); ++ } ++ } ++ ++ mutex_unlock(&uhid->devlock); ++ return ret ? ret : len; ++} ++ ++static ssize_t uhid_char_write(struct file *file, const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ struct uhid_device *uhid = file->private_data; ++ int ret; ++ size_t len; ++ ++ /* we need at least the "type" member of uhid_event */ ++ if (count < sizeof(__u32)) ++ return -EINVAL; ++ ++ ret = mutex_lock_interruptible(&uhid->devlock); ++ if (ret) ++ return ret; ++ ++ memset(&uhid->input_buf, 0, sizeof(uhid->input_buf)); ++ len = min(count, sizeof(uhid->input_buf)); ++ if (copy_from_user(&uhid->input_buf, buffer, len)) { ++ ret = -EFAULT; ++ goto unlock; ++ } ++ ++ switch (uhid->input_buf.type) { ++ case UHID_CREATE: ++ ret = uhid_dev_create(uhid, &uhid->input_buf); ++ break; ++ case UHID_DESTROY: ++ ret = uhid_dev_destroy(uhid); ++ break; ++ case UHID_INPUT: ++ ret = uhid_dev_input(uhid, &uhid->input_buf); ++ break; ++ case UHID_FEATURE_ANSWER: ++ ret = uhid_dev_feature_answer(uhid, &uhid->input_buf); ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ } ++ ++unlock: ++ mutex_unlock(&uhid->devlock); ++ ++ /* return "count" not "len" to not confuse the caller */ ++ return ret ? ret : count; ++} ++ ++static unsigned int uhid_char_poll(struct file *file, poll_table *wait) ++{ ++ struct uhid_device *uhid = file->private_data; ++ ++ poll_wait(file, &uhid->waitq, wait); ++ ++ if (uhid->head != uhid->tail) ++ return POLLIN | POLLRDNORM; ++ ++ return 0; ++} ++ ++static const struct file_operations uhid_fops = { ++ .owner = THIS_MODULE, ++ .open = uhid_char_open, ++ .release = uhid_char_release, ++ .read = uhid_char_read, ++ .write = uhid_char_write, ++ .poll = uhid_char_poll, ++ .llseek = no_llseek, ++}; ++ ++static struct miscdevice uhid_misc = { ++ .fops = &uhid_fops, ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = UHID_NAME, ++}; ++ ++static int __init uhid_init(void) ++{ ++ return misc_register(&uhid_misc); ++} ++ ++static void __exit uhid_exit(void) ++{ ++ misc_deregister(&uhid_misc); ++} ++ ++module_init(uhid_init); ++module_exit(uhid_exit); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Herrmann "); ++MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); +diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index ea8736bc..af663a88 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -723,6 +723,30 @@ config I2C_XLR + This driver can also be built as a module. If so, the module + will be called i2c-xlr. + ++config I2C_ANYKA ++ bool "Anyka I2C drivers support" ++ depends on ARCH_AK39 ++ help ++ Say Y here to enable support for I2C driver for Anyka chip. ++ ++choice ++ bool "I2C driver" ++ depends on I2C_ANYKA ++ ++config I2C_AK39_HW ++ bool "i2c controller" ++ depends on I2C_ANYKA ++ help ++ Say Y here to enable support for I2C driver by chip controller. ++ ++config I2C_GPIO_SOFT ++ bool "i2c software simulate by gpios" ++ depends on I2C_ANYKA && I2C_ALGOBIT ++ help ++ Say Y here to enable support I2C driver by GPIOs simulate ++ ++endchoice ++ + comment "External I2C/SMBus adapter drivers" + + config I2C_DIOLAN_U2C +diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile +index 2f05d7b6..969fdc8b 100644 +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -72,6 +72,8 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o + obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o + obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o + obj-$(CONFIG_I2C_XLR) += i2c-xlr.o ++obj-$(CONFIG_I2C_AK39_HW) += i2c-ak39.o ++obj-$(CONFIG_I2C_GPIO_SOFT) += i2c-gpio-soft.o + + # External I2C/SMBus adapter drivers + obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o +diff --git a/drivers/i2c/busses/i2c-ak39.c b/drivers/i2c/busses/i2c-ak39.c +new file mode 100755 +index 00000000..3a88d6f1 +--- /dev/null ++++ b/drivers/i2c/busses/i2c-ak39.c +@@ -0,0 +1,931 @@ ++/* linux/drivers/i2c/busses/i2c-ak39.c ++ * ++ * Copyright (C) 2010 Anyka ++ * ++ * AK39xx I2C Controller ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define AK39_I2C_INTERRUPT_MODE ++//#define AK39_I2C_POLL_MODE ++ ++/* i2c controller state */ ++enum ak39_i2c_state { ++ STATE_READ, ++ STATE_WRITE ++}; ++ ++enum read_data_addr { ++ READ_ADDR, ++ READ_DATA ++}; ++ ++struct ak39_i2c { ++ spinlock_t lock; ++ wait_queue_head_t wait; ++ unsigned int suspended:1; ++ ++ struct i2c_msg *msg; ++ unsigned int msg_num; ++ unsigned int msg_idx; ++ unsigned int msg_ptr; ++ ++ unsigned int irq; ++ unsigned long clkrate; ++ ++ enum ak39_i2c_state state; ++ enum read_data_addr read_value; ++ ++ void __iomem *regs; ++ struct clk *clk; ++ struct device *dev; ++ struct resource *ioarea; ++ struct i2c_adapter adap; ++ ++#ifdef CONFIG_CPU_FREQ ++ struct notifier_block freq_transition; ++#endif ++}; ++ ++static struct semaphore xfer_sem; ++ ++/* ~~~~~~~~~~~ poll mode ~~~~~~~~~~~~~~~~~~~~~~~~~ */ ++ ++#ifdef AK39_I2C_POLL_MODE ++ ++/* ++ * Poll the i2c status register until the specified bit is set. ++ * Return 1 if transfer finished. ++ */ ++static short ak39_poll_status(void) ++{ ++ do { ++ } while (!(__raw_readl(AK39_I2C_CTRL) & INT_PEND_FLAG)); ++ ++ return 1; ++} ++ ++static int xfer_read(struct ak39_i2c *i2c, unsigned char *buf, int length) ++{ ++ int i,j, ctrl_value; ++ unsigned long ret, reg_value; ++ unsigned char *p = buf; ++ int idx = 0; ++ ++ if((length - 1) > 15) ++ ctrl_value = 16; ++ else ++ ctrl_value = length; ++ ++ ret = __raw_readl(AK39_I2C_CTRL); ++ ret |= (AK39_I2C_START | AK39_I2C_ACKEN | AK39_I2C_TXRXSEL | AK39_I2C_CLR_DELAY); ++ ret &= ~((0xf) << AK39_I2C_TRX_BYTE); ++ ret |= ((ctrl_value - 1) << AK39_I2C_TRX_BYTE); ++ ret &= ~(INT_PEND_FLAG); ++ __raw_writel(ret, AK39_I2C_CTRL); ++ ++ ++ for (i = 0; i < length / 16; i++) { ++ if (!ak39_poll_status()) { ++ dev_dbg(&i2c->adap.dev, "RXRDY timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ++ for (j = 0; j < 4; j++) { ++ reg_value = __raw_readl(AK39_I2C_DATA0 + j * 4); ++ *(unsigned long *)p = reg_value; ++ p +=4; ++ } ++ } ++ ++ if (!ak39_poll_status()) { ++ dev_dbg(&i2c->adap.dev, "RXRDY timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ++ idx = 0; ++ for (; idx < (length % 16) / 4; idx++) { ++ unsigned long regval = __raw_readl(AK39_I2C_DATA0 + idx * 4); ++ *(unsigned long *)p = regval; ++ p +=4; ++ } ++ ++ if (length % 4) { ++ unsigned long regval; ++ if (!ak39_poll_status()) { ++ dev_dbg(&i2c->adap.dev, "RXRDY timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ++ regval = __raw_readl(AK39_I2C_DATA0 + idx / 4); ++ for (i = 0; i < length % 4; i++) { ++ *p++ = (regval >> (i * 8)) & 0xFF; ++ } ++ } ++ ++ return 0; ++} ++ ++static void wait_xfer_write(struct ak39_i2c *i2c, int length) ++{ ++ unsigned long ret; ++ ++ ret = __raw_readl(AK39_I2C_CTRL); ++ ret |= (AK39_I2C_START); ++ ret &= ~(AK39_I2C_TXRXSEL | INT_PEND_FLAG); ++ ret &= ~((0xf) << AK39_I2C_TRX_BYTE); ++ ret |= ((length - 1)<< AK39_I2C_TRX_BYTE); ++ __raw_writel(ret, AK39_I2C_CTRL); ++ ++ if (!ak39_poll_status()) { ++ dev_info(&i2c->adap.dev, "TXRDY timeout\n"); ++ return ; ++ } ++} ++ ++static int xfer_write(struct ak39_i2c *i2c, unsigned char *buf, int length) ++{ ++ unsigned int i; ++ unsigned long reg_value; ++ unsigned int num_count, num_char; ++ int ctrl_value, num_bck = length; ++ unsigned char *p = buf; ++ ++ if((length - 1) > 15) ++ ctrl_value = 16; ++ else ++ ctrl_value = length; ++ ++ num_count = num_bck / 16; ++ num_char = num_bck % 16; ++ ++ while(num_count--) { ++ for(i = 0; i < 4; i++) { ++ reg_value = *(unsigned long *)p; ++ p += 4; ++ __raw_writel(reg_value, AK39_I2C_DATA0 + i * 4); ++ } ++ ++ wait_xfer_write(i2c, 16); ++ } ++ if(num_char > 0) { ++ unsigned long value = 0; ++ for(i = 0; i < num_char; i+=4) { ++ value = *(unsigned long *)p; ++ p +=4; ++ __raw_writel(value, AK39_I2C_DATA0 + i); ++ } ++ wait_xfer_write(i2c, num_char); ++ } ++ ++ return 0; ++} ++ ++static int ak39_i2c_doxfer(struct ak39_i2c *i2c, struct i2c_msg *msgs, int num) ++{ ++ unsigned int addr = (msgs->addr & 0x7f) << 1; ++ int i, ret, rw_flag; ++ ++ dev_dbg(&i2c->adap.dev, "ak39_i2c_doxfer: processing %d messages:\n", num); ++ ++ for (i = 0; i < num; i++) { ++ if (msgs->flags & I2C_M_RD) { ++ addr |= AK39_I2C_READ; ++ rw_flag = 1; ++ } else { ++ rw_flag = 0; ++ } ++ if (msgs->flags & I2C_M_REV_DIR_ADDR) ++ addr ^= 1; ++ ++ __raw_writel((AK39_I2C_CMD_EN | AK39_I2C_START_BIT)|addr, AK39_I2C_CMD1); ++ if(msgs->len && msgs->buf) { ++ if (rw_flag == 1) ++ ret = xfer_read(i2c, msgs->buf, msgs->len); ++ else ++ ret = xfer_write(i2c, msgs->buf, msgs->len); ++ ++ if (ret) ++ return ret; ++ } ++ dev_dbg(&i2c->adap.dev, "transfer complete\n"); ++ msgs++; /* next message */ ++ } ++ return i; ++} ++#endif ++ ++/* ~~~~~~~~~~~ INTER MODE ~~~~~~~~~~~~~~~~~~~ */ ++ ++#ifdef AK39_I2C_INTERRUPT_MODE ++ ++static void clear_int_flag(void) ++{ ++ unsigned long tmp; ++ ++ tmp = __raw_readl(AK39_I2C_CTRL); ++ tmp &= ~INT_PEND_FLAG; ++ __raw_writel(tmp, AK39_I2C_CTRL); ++} ++ ++/* irq disable functions */ ++static void ak39_i2c_disable_irq(struct ak39_i2c *i2c) ++{ ++ unsigned long tmp; ++ ++ tmp = __raw_readl(AK39_I2C_CTRL); ++ __raw_writel(tmp & ~AK39_I2C_INTEN, AK39_I2C_CTRL); ++} ++ ++static inline void ak39_i2c_master_complete(struct ak39_i2c *i2c, int ret) ++{ ++ dev_dbg(i2c->dev, "master_complete %d\n", ret); ++ ++ i2c->msg_ptr = 0; ++ i2c->msg = NULL; ++ i2c->msg_num = 0; ++ if (ret) ++ i2c->msg_idx = ret; ++ ++ wake_up(&i2c->wait); ++} ++ ++static inline void ak39_i2c_stop(struct ak39_i2c *i2c, int ret) ++{ ++ dev_dbg(i2c->dev, "STOP\n"); ++ ++ ak39_i2c_master_complete(i2c, ret); ++ ak39_i2c_disable_irq(i2c); ++} ++ ++static int ak39_i2c_irq_transfer(struct ak39_i2c *i2c) ++{ ++ unsigned int addr = (i2c->msg->addr & 0x7f) << 1; ++ unsigned int num_char, i, length; ++ unsigned long stat, regval = 0; ++ unsigned char *p = i2c->msg->buf; ++ ++read_next: ++ if(i2c->msg_idx < i2c->msg_num) { ++ if (i2c->msg->len == 0) { ++ ak39_i2c_stop(i2c, 0); ++ return 0; ++ } ++ ++ stat = __raw_readl(AK39_I2C_CTRL); ++ stat &= ~(0xf << 9); ++ if (i2c->msg->flags & I2C_M_RD) { ++ addr |= AK39_I2C_READ ; ++ i2c->state = STATE_READ ; ++ stat |= AK39_I2C_TXRXSEL ; ++ } ++ else { ++ i2c->state = STATE_WRITE ; ++ stat &= ~AK39_I2C_TXRXSEL ; ++ } ++ ++ if (i2c->msg->flags & I2C_M_REV_DIR_ADDR) ++ addr ^= 1; ++ ++ __raw_writel((AK39_I2C_CMD_EN | AK39_I2C_START_BIT)|addr, AK39_I2C_CMD1); ++ ++ switch(i2c->state) { ++ case STATE_WRITE: ++ ++ if (i2c->msg->len > 16) { ++ printk("Error, needed debug more data transmitted.\n"); ++ return 0; ++ } ++ if (i2c->msg_ptr < i2c->msg->len) { ++ ++ num_char = i2c->msg->len % 16; ++ ++ if (num_char > 0) { ++ __raw_writel(stat | ((num_char - 1) << 9), AK39_I2C_CTRL); ++ for(i = 0; i < num_char; i+=4) { ++ regval = *(unsigned long *)p; ++ p += 4; ++ __raw_writel(regval, AK39_I2C_DATA0 + i); ++ } ++ i2c->msg_ptr += num_char; ++ } ++ ++ if(i2c->msg_ptr >= i2c->msg->len){ ++ i2c->msg_ptr = 0; ++ i2c->msg_idx++; ++ i2c->msg++; ++ } ++ ++ stat = __raw_readl(AK39_I2C_CTRL); ++ __raw_writel(stat | AK39_I2C_START, AK39_I2C_CTRL); ++ } ++ break; ++ ++ case STATE_READ: ++ length = i2c->msg->len; ++ ++ if (i2c->msg->len > 16) { ++ printk("Error, needed debug more data transmitted.\n"); ++ return 0; ++ } ++ __raw_writel(stat | ((length - 1) << 9), AK39_I2C_CTRL); ++ ++ if(i2c->read_value == READ_ADDR) { ++ stat = __raw_readl(AK39_I2C_CTRL); ++ __raw_writel(stat | AK39_I2C_START, AK39_I2C_CTRL); ++ i2c->read_value = READ_DATA; ++ } else { ++ if (i2c->msg_ptr < i2c->msg->len) { ++ ++ num_char = i2c->msg->len % 16; ++ ++ if (num_char > 0) { ++ for (i = 0; i < num_char; i+=4) { ++ regval = __raw_readl(AK39_I2C_DATA0 + i); ++ *(unsigned long *)p = regval; ++ p += 4; ++ } ++ i2c->msg_ptr += num_char; ++ } ++ ++ if (i2c->msg_ptr >= i2c->msg->len) { ++ /* we need to go to the next i2c message */ ++ dev_dbg(i2c->dev, "READ: Next Message\n"); ++ ++ i2c->msg_ptr = 0; ++ i2c->msg_idx++; ++ i2c->msg++; ++ ++ if(i2c->msg_idx >= i2c->msg_num) { ++ ak39_i2c_stop(i2c, 0); ++ return 0; ++ } ++ } ++ i2c->read_value = READ_ADDR; ++ goto read_next; ++ } ++ } ++ break; ++ } ++ } ++ else { ++ ak39_i2c_stop(i2c, 0); ++ } ++ return 0; ++} ++ ++/* ak39_i2c_irq ++ * ++ * top level IRQ servicing routine ++*/ ++static irqreturn_t ak39_i2c_irq(int irqno, void *dev_id) ++{ ++ struct ak39_i2c *i2c = dev_id; ++ ++ clear_int_flag(); ++ ++ /* pretty much this leaves us with the fact that we've ++ * transmitted or received whatever byte we last sent */ ++ ++ ak39_i2c_irq_transfer(i2c); ++ ++ return IRQ_HANDLED; ++} ++ ++static int ak39_i2c_doxfer(struct ak39_i2c *i2c, struct i2c_msg *msgs, int num) ++{ ++ unsigned long timeout, stat; ++ int ret; ++ ++ if (i2c->suspended) ++ return -EIO; ++ ++ spin_lock_irq(&i2c->lock); ++ i2c->msg = msgs; ++ i2c->msg_num = num; ++ i2c->msg_ptr = 0; ++ i2c->msg_idx = 0; ++ i2c->read_value = READ_ADDR; ++ ++ ak39_i2c_irq_transfer(i2c); ++ stat = __raw_readl(AK39_I2C_CTRL); ++ stat |= AK39_I2C_INTEN | AK39_I2C_ACKEN; ++ __raw_writel(stat, AK39_I2C_CTRL); ++ ++ spin_unlock_irq(&i2c->lock); ++ timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); ++ ++ ret = i2c->msg_idx; ++ ++ /* having these next two as dev_err() makes life very ++ * noisy when doing an i2cdetect */ ++ ++ if (timeout == 0) ++ dev_info(i2c->dev, "timeout\n"); ++ else if (ret != num) ++ dev_info(i2c->dev, "incomplete xfer (%d)\n", ret); ++ ++ return ret; ++} ++#endif ++ ++ ++/* ak39_i2c_xfer ++ * ++ * first port of call from the i2c bus code when an message needs ++ * transferring across the i2c bus. ++*/ ++static int ak39_i2c_xfer(struct i2c_adapter *adap, ++ struct i2c_msg *msgs, int num) ++{ ++ struct ak39_i2c *i2c = (struct ak39_i2c *)adap->algo_data; ++ int retry; ++ int ret; ++ ++ down(&xfer_sem); ++ for (retry = 0; retry < adap->retries; retry++) { ++ ++ ret = ak39_i2c_doxfer(i2c, msgs, num); ++ ++ if (ret != -EAGAIN) ++ { ++ up(&xfer_sem); ++ return ret; ++ } ++ ++ dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry); ++ ++ udelay(100); ++ } ++ ++ up(&xfer_sem); ++ return -EREMOTEIO; ++} ++ ++/* declare our i2c functionality */ ++static u32 ak39_i2c_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING; ++} ++ ++/* i2c bus registration info */ ++ ++static const struct i2c_algorithm ak39_i2c_algorithm = { ++ .master_xfer = ak39_i2c_xfer, ++ .functionality = ak39_i2c_func, ++}; ++ ++/* ak39_i2c_calcdivisor ++ * ++ * return the divisor settings for a given frequency ++*/ ++static int ak39_i2c_calcdivisor(unsigned long clkin, unsigned int wanted, ++ unsigned int *div1, unsigned int *divs) ++{ ++ unsigned int calc_divs = clkin / wanted / 2; ++ unsigned int calc_div1; ++ ++ if (calc_divs > (16*16)) ++ calc_div1 = 512; ++ else ++ calc_div1 = 16; ++ ++ calc_divs /= calc_div1; ++ ++ if (calc_divs == 0) ++ calc_divs = 1; ++ if (calc_divs > 16) ++ calc_divs = 16; ++ ++ *divs = calc_divs; ++ *div1 = calc_div1; ++ ++ return clkin / ((calc_divs * 2)* calc_div1); ++} ++ ++/* ak39_i2c_clockrate ++ * ++ * work out a divisor for the user requested frequency setting, ++ * either by the requested frequency, or scanning the acceptable ++ * range of frequencies until something is found ++*/ ++static int ak39_i2c_clockrate(struct ak39_i2c *i2c, unsigned int *got) ++{ ++ struct ak39_platform_i2c *pdata = i2c->dev->platform_data; ++ unsigned long clkin = ak_get_asic_clk(); ++ unsigned int divs, div1; ++ unsigned long target_frequency; ++ u32 i2c_val, sda_delay; ++ int freq; ++ ++ i2c->clkrate = clkin; ++ clkin /= 1000; /* clkin now in KHz */ ++ ++ dev_dbg(i2c->dev, "pdata desired frequency %lu\n", pdata->frequency); ++ ++ target_frequency = pdata->frequency ? pdata->frequency : 100000; ++ //target_frequency *= 4; ++ target_frequency /= 1000; /* Target frequency now in KHz */ ++ ++ freq = ak39_i2c_calcdivisor(clkin, target_frequency, &div1, &divs); ++ if (freq > target_frequency) { ++ dev_err(i2c->dev, ++ "Unable to achieve desired frequency %luKHz." \ ++ " Lowest achievable %dKHz\n", target_frequency, freq); ++ // return -EINVAL; ++ } ++ ++ *got = freq; ++ ++ i2c_val = __raw_readl(AK39_I2C_CTRL); ++ i2c_val &= ~(AK39_TX_CLK_DIV | AK39_I2C_TXDIV_512); ++ i2c_val |= (divs-1); ++ ++ if (div1 == 512) ++ i2c_val |= AK39_I2C_TXDIV_512; ++ ++ __raw_writel(i2c_val, AK39_I2C_CTRL); ++ ++ if (pdata->sda_delay) { ++ sda_delay = (freq / 1000) * pdata->sda_delay; ++ sda_delay /= 1000000; ++ sda_delay = DIV_ROUND_UP(sda_delay, 5); ++ if (sda_delay > 3) ++ sda_delay = AK39_I2C_CLR_DELAY; ++ ++ sda_delay |= AK39_I2C_SDA_DELAY; ++ } else ++ sda_delay = ~AK39_I2C_CLR_DELAY; ++ ++ i2c_val |= sda_delay; ++ __raw_writel(i2c_val, AK39_I2C_CTRL); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_CPU_FREQ ++ ++#define freq_to_i2c(_n) container_of(_n, struct ak39_i2c, freq_transition) ++ ++static int ak39_i2c_cpufreq_transition(struct notifier_block *nb, ++ unsigned long val, void *data) ++{ ++ struct ak39_i2c *i2c = freq_to_i2c(nb); ++ struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; ++ unsigned int old_clk = freqs->old_cpufreq.asic_clk; ++ unsigned int new_clk = freqs->new_cpufreq.asic_clk; ++ unsigned long flags; ++ unsigned int got; ++ int ret; ++ ++ if (val == CPUFREQ_PRECHANGE) ++ { ++ down(&xfer_sem); ++ } ++ else if (val == CPUFREQ_POSTCHANGE) ++ { ++ if (old_clk != new_clk) ++ { ++ spin_lock_irqsave(&i2c->lock, flags); ++ ret = ak39_i2c_clockrate(i2c, &got); ++ //printk("setting freq %u\n", got); ++ spin_unlock_irqrestore(&i2c->lock, flags); ++ ++ if (ret < 0) ++ dev_err(i2c->dev, "cannot find frequency\n"); ++ else ++ dev_info(i2c->dev, "setting freq %d\n", got); ++ ++ } ++ up(&xfer_sem); ++ } ++ ++ return 0; ++} ++ ++static inline int ak39_i2c_register_cpufreq(struct ak39_i2c *i2c) ++{ ++ i2c->freq_transition.notifier_call = ak39_i2c_cpufreq_transition; ++ return cpufreq_register_notifier(&i2c->freq_transition, ++ CPUFREQ_TRANSITION_NOTIFIER); ++} ++ ++static inline void ak39_i2c_deregister_cpufreq(struct ak39_i2c *i2c) ++{ ++ cpufreq_unregister_notifier(&i2c->freq_transition, ++ CPUFREQ_TRANSITION_NOTIFIER); ++} ++#else ++static inline int ak39_i2c_register_cpufreq(struct ak39_i2c *i2c) ++{ ++ return 0; ++} ++static inline void ak39_i2c_deregister_cpufreq(struct ak39_i2c *i2c) ++{ ++} ++#endif ++ ++/* ak39_i2c_init ++ * ++ * initialise the controller, set the IO lines and frequency ++*/ ++static int ak39_i2c_init(struct ak39_i2c *i2c) ++{ ++ struct ak39_platform_i2c *pdata; ++ unsigned int freq ; ++ ++ /* ++ * Reset I2C Controller ++ */ ++ ak39_soft_reset(AK39_SRESET_I2C); ++ ++ /* get the plafrom data */ ++ pdata = i2c->dev->platform_data; ++ ++ /* inititalise the gpio */ ++ ak_group_config(ePIN_AS_I2C); ++ ++ __raw_writel(AK39_I2C_INTEN | AK39_I2C_ACKEN, AK39_I2C_CTRL); ++ ++ /* we need to work out the divisors for the clock... */ ++ if (ak39_i2c_clockrate(i2c, &freq) != 0) { ++ __raw_writel(0, AK39_I2C_CTRL); ++ dev_err(i2c->dev, "cannot meet bus frequency required\n"); ++ return -EINVAL; ++ } ++ ++ /* todo - check that the i2c lines aren't being dragged anywhere */ ++ ++ dev_dbg(i2c->dev, "bus frequency set to %d KHz\n", freq); ++ return 0; ++} ++ ++ ++/* ak39_i2c_probe ++ * ++ * called by the bus driver when a suitable device is found ++*/ ++static int ak39_i2c_probe(struct platform_device *pdev) ++{ ++ struct ak39_i2c *i2c; ++ struct ak39_platform_i2c *pdata; ++ struct resource *res; ++ int ret; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) { ++ dev_err(&pdev->dev, "no platform data\n"); ++ return -EINVAL; ++ } ++ ++ i2c = kzalloc(sizeof(struct ak39_i2c), GFP_KERNEL); ++ if (!i2c) { ++ dev_err(&pdev->dev, "no memory for state\n"); ++ return -ENOMEM; ++ } ++ ++ strlcpy(i2c->adap.name, "ak39-i2c", sizeof(i2c->adap.name)); ++ i2c->adap.owner = THIS_MODULE; ++ i2c->adap.algo = &ak39_i2c_algorithm; ++ i2c->adap.retries = 2; ++ i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; ++ ++ spin_lock_init(&i2c->lock); ++ init_waitqueue_head(&i2c->wait); ++ ++ /* find the clock and enable it */ ++ ++ i2c->dev = &pdev->dev; ++ i2c->clk = clk_get(&pdev->dev, "i2c"); ++ if (IS_ERR(i2c->clk)) { ++ dev_err(&pdev->dev, "cannot get clock\n"); ++ ret = -ENOENT; ++ goto err_noclk; ++ } ++ ++ dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); ++ ++ clk_enable(i2c->clk); ++ ++ /* map the registers */ ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ dev_err(&pdev->dev, "cannot find IO resource\n"); ++ ret = -ENOENT; ++ goto err_clk; ++ } ++ ++ i2c->ioarea = request_mem_region(res->start, resource_size(res), pdev->name); ++ if (i2c->ioarea == NULL) { ++ dev_err(&pdev->dev, "cannot request IO\n"); ++ ret = -ENXIO; ++ goto err_clk; ++ } ++ ++ i2c->regs = ioremap(res->start, resource_size(res)); ++ if (i2c->regs == NULL) { ++ dev_err(&pdev->dev, "cannot map IO\n"); ++ ret = -ENXIO; ++ goto err_ioarea; ++ } ++ ++ dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res); ++ ++ /* setup info block for the i2c core */ ++ ++ i2c->adap.algo_data = i2c; ++ i2c->adap.dev.parent = &pdev->dev; ++ ++ /* initialise the i2c controller */ ++ ++ ret = ak39_i2c_init(i2c); ++ if (ret != 0) ++ goto err_iomap; ++ ++ /* find the IRQ for this unit (note, this relies on the init call to ++ * ensure no current IRQs pending ++ */ ++ i2c->irq = ret = platform_get_irq(pdev, 0); ++ if (ret <= 0) { ++ dev_err(&pdev->dev, "cannot find IRQ\n"); ++ goto err_iomap; ++ } ++ ++#ifdef AK39_I2C_INTERRUPT_MODE ++ ret = request_irq(i2c->irq, ak39_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c); ++ if (ret != 0) { ++ dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); ++ goto err_iomap; ++ } ++#endif ++ ++ sema_init(&xfer_sem, 1); ++ ret = ak39_i2c_register_cpufreq(i2c); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); ++ goto err_irq; ++ } ++ ++ i2c->adap.nr = pdata->bus_num; ++ ret = i2c_add_numbered_adapter(&i2c->adap); ++ ++ if (ret < 0) { ++ dev_err(&pdev->dev, "failed to add bus to i2c core\n"); ++ goto err_cpufreq; ++ } ++ ++ platform_set_drvdata(pdev, i2c); ++ ++ dev_info(&pdev->dev, "%s: AK39 I2C adapter\n", dev_name(&i2c->adap.dev)); ++ ++ return 0; ++ ++ err_cpufreq: ++ ak39_i2c_deregister_cpufreq(i2c); ++ ++ err_irq: ++ free_irq(i2c->irq, i2c); ++ ++ err_iomap: ++ iounmap(i2c->regs); ++ ++ err_ioarea: ++ release_resource(i2c->ioarea); ++ kfree(i2c->ioarea); ++ ++ err_clk: ++ clk_disable(i2c->clk); ++ clk_put(i2c->clk); ++ ++ err_noclk: ++ kfree(i2c); ++ return ret; ++} ++ ++ ++/* ak39_i2c_remove ++ * ++ * called when device is removed from the bus ++*/ ++static int ak39_i2c_remove(struct platform_device *pdev) ++{ ++ struct ak39_i2c *i2c = platform_get_drvdata(pdev); ++ ++ ak39_i2c_deregister_cpufreq(i2c); ++ ++ i2c_del_adapter(&i2c->adap); ++ free_irq(i2c->irq, i2c); ++ ++ clk_disable(i2c->clk); ++ clk_put(i2c->clk); ++ ++ iounmap(i2c->regs); ++ ++ release_resource(i2c->ioarea); ++ ++ kfree(i2c->ioarea); ++ kfree(i2c); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int ak39_i2c_suspend_noirq(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct ak39_i2c *i2c = platform_get_drvdata(pdev); ++ struct ak39_platform_i2c *pdata = pdev->dev.platform_data; ++ int i; ++ ++ down(&xfer_sem); ++ i2c->suspended = 1; ++ ++ clk_disable(i2c->clk); ++ clk_put(i2c->clk); ++ ++ for (i=0; inpins; i++) ++ ak_gpio_set(&(pdata->gpios[i])); ++ ++ return 0; ++} ++ ++static int ak39_i2c_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct ak39_i2c *i2c = platform_get_drvdata(pdev); ++ ++ i2c->suspended = 0; ++ clk_enable(i2c->clk); ++ ak39_i2c_init(i2c); ++ up(&xfer_sem); ++ return 0; ++} ++ ++static struct dev_pm_ops ak39_i2c_dev_pm_ops = { ++ .suspend_noirq = ak39_i2c_suspend_noirq, ++ .resume = ak39_i2c_resume, ++}; ++ ++#define AK39_DEV_PM_OPS (&ak39_i2c_dev_pm_ops) ++#else ++#define AK39_DEV_PM_OPS NULL ++#endif ++ ++/* device driver for platform bus bits */ ++static struct platform_driver ak39_i2c_driver = { ++ .probe = ak39_i2c_probe, ++ .remove = ak39_i2c_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "i2c-ak39", ++ .pm = AK39_DEV_PM_OPS, ++ }, ++}; ++ ++static int __init i2c_adap_ak39_init(void) ++{ ++ return platform_driver_register(&ak39_i2c_driver); ++} ++subsys_initcall(i2c_adap_ak39_init); ++ ++static void __exit i2c_adap_ak39_exit(void) ++{ ++ platform_driver_unregister(&ak39_i2c_driver); ++} ++module_exit(i2c_adap_ak39_exit); ++ ++MODULE_DESCRIPTION("AK39 I2C Bus driver"); ++MODULE_AUTHOR("Anaka, "); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/i2c/busses/i2c-gpio-soft.c b/drivers/i2c/busses/i2c-gpio-soft.c +new file mode 100644 +index 00000000..37984874 +--- /dev/null ++++ b/drivers/i2c/busses/i2c-gpio-soft.c +@@ -0,0 +1,287 @@ ++/* ++ * Bitbanging I2C bus driver using the GPIO API ++ * ++ * Copyright (C) 2007 Atmel Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++struct i2c_gpio_private_data { ++ struct i2c_adapter adap; ++ struct i2c_algo_bit_data bit_data; ++ struct i2c_gpio_platform_data pdata; ++}; ++ ++/* Toggle SDA by changing the direction of the pin */ ++static void i2c_gpio_setsda_dir(void *data, int state) ++{ ++ struct i2c_gpio_platform_data *pdata = data; ++ ++ if (state) ++ ak_gpio_dircfg(pdata->sda_pin, AK_GPIO_DIR_INPUT); ++ else { ++ ak_gpio_dircfg(pdata->sda_pin, AK_GPIO_DIR_OUTPUT); ++ ak_gpio_setpin(pdata->sda_pin, AK_GPIO_OUT_LOW); ++ } ++} ++ ++/* ++ * Toggle SDA by changing the output value of the pin. This is only ++ * valid for pins configured as open drain (i.e. setting the value ++ * high effectively turns off the output driver.) ++ */ ++static void i2c_gpio_setsda_val(void *data, int state) ++{ ++ struct i2c_gpio_platform_data *pdata = data; ++ ++ ak_gpio_setpin(pdata->sda_pin, state); ++} ++ ++/* Toggle SCL by changing the direction of the pin. */ ++static void i2c_gpio_setscl_dir(void *data, int state) ++{ ++ struct i2c_gpio_platform_data *pdata = data; ++ ++ if (state) ++ ak_gpio_dircfg(pdata->scl_pin, AK_GPIO_DIR_INPUT); ++ else { ++ ak_gpio_dircfg(pdata->scl_pin, AK_GPIO_DIR_OUTPUT); ++ ak_gpio_setpin(pdata->sda_pin, AK_GPIO_OUT_LOW); ++ } ++} ++ ++/* ++ * Toggle SCL by changing the output value of the pin. This is used ++ * for pins that are configured as open drain and for output-only ++ * pins. The latter case will break the i2c protocol, but it will ++ * often work in practice. ++ */ ++static void i2c_gpio_setscl_val(void *data, int state) ++{ ++ struct i2c_gpio_platform_data *pdata = data; ++ ++ ak_gpio_setpin(pdata->scl_pin, state); ++} ++ ++static int i2c_gpio_getsda(void *data) ++{ ++ struct i2c_gpio_platform_data *pdata = data; ++ ++ return ak_gpio_getpin(pdata->sda_pin); ++} ++ ++static int i2c_gpio_getscl(void *data) ++{ ++ struct i2c_gpio_platform_data *pdata = data; ++ ++ return ak_gpio_getpin(pdata->scl_pin); ++} ++ ++static int __devinit of_i2c_gpio_probe(struct device_node *np, ++ struct i2c_gpio_platform_data *pdata) ++{ ++ u32 reg; ++ ++ if (of_gpio_count(np) < 2) ++ return -ENODEV; ++ ++ pdata->sda_pin = of_get_gpio(np, 0); ++ pdata->scl_pin = of_get_gpio(np, 1); ++ ++ if (!gpio_is_valid(pdata->sda_pin) || !gpio_is_valid(pdata->scl_pin)) { ++ pr_err("%s: invalid GPIO pins, sda=%d/scl=%d\n", ++ np->full_name, pdata->sda_pin, pdata->scl_pin); ++ return -ENODEV; ++ } ++ ++ of_property_read_u32(np, "i2c-gpio,delay-us", &pdata->udelay); ++ ++ if (!of_property_read_u32(np, "i2c-gpio,timeout-ms", ®)) ++ pdata->timeout = msecs_to_jiffies(reg); ++ ++ pdata->sda_is_open_drain = ++ of_property_read_bool(np, "i2c-gpio,sda-open-drain"); ++ pdata->scl_is_open_drain = ++ of_property_read_bool(np, "i2c-gpio,scl-open-drain"); ++ pdata->scl_is_output_only = ++ of_property_read_bool(np, "i2c-gpio,scl-output-only"); ++ ++ return 0; ++} ++ ++static int __devinit i2c_gpio_probe(struct platform_device *pdev) ++{ ++ struct i2c_gpio_private_data *priv; ++ struct i2c_gpio_platform_data *pdata; ++ struct i2c_algo_bit_data *bit_data; ++ struct i2c_adapter *adap; ++ int ret; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ adap = &priv->adap; ++ bit_data = &priv->bit_data; ++ pdata = &priv->pdata; ++ ++ if (pdev->dev.of_node) { ++ ret = of_i2c_gpio_probe(pdev->dev.of_node, pdata); ++ if (ret) ++ return ret; ++ } else { ++ if (!pdev->dev.platform_data) ++ return -ENXIO; ++ memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata)); ++ } ++ ++ ret = ak_gpio_request(pdata->sda_pin, "sda"); ++ if (ret) ++ goto err_request_sda; ++ ret = ak_gpio_request(pdata->scl_pin, "scl"); ++ if (ret) ++ goto err_request_scl; ++ ++ if (pdata->sda_is_open_drain) { ++ ak_gpio_dircfg(pdata->sda_pin, AK_GPIO_DIR_OUTPUT); ++ ak_gpio_setpin(pdata->sda_pin, AK_GPIO_OUT_HIGH); ++ bit_data->setsda = i2c_gpio_setsda_val; ++ } else { ++ ak_gpio_dircfg(pdata->sda_pin, AK_GPIO_DIR_INPUT); ++ bit_data->setsda = i2c_gpio_setsda_dir; ++ } ++ ++ if (pdata->scl_is_open_drain || pdata->scl_is_output_only) { ++ ak_gpio_dircfg(pdata->sda_pin, AK_GPIO_DIR_OUTPUT); ++ ak_gpio_setpin(pdata->sda_pin, AK_GPIO_OUT_HIGH); ++ bit_data->setscl = i2c_gpio_setscl_val; ++ } else { ++ ak_gpio_dircfg(pdata->sda_pin, AK_GPIO_DIR_INPUT); ++ bit_data->setscl = i2c_gpio_setscl_dir; ++ } ++ ++ if (!pdata->scl_is_output_only) ++ bit_data->getscl = i2c_gpio_getscl; ++ bit_data->getsda = i2c_gpio_getsda; ++ ++ if (pdata->udelay) ++ bit_data->udelay = pdata->udelay; ++ else if (pdata->scl_is_output_only) ++ bit_data->udelay = 50; /* 10 kHz */ ++ else ++ bit_data->udelay = 5; /* 100 kHz */ ++ ++ if (pdata->timeout) ++ bit_data->timeout = pdata->timeout; ++ else ++ bit_data->timeout = HZ / 10; /* 100 ms */ ++ ++ bit_data->data = pdata; ++ ++ adap->owner = THIS_MODULE; ++ snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id); ++ adap->algo_data = bit_data; ++ adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; ++ adap->dev.parent = &pdev->dev; ++ adap->dev.of_node = pdev->dev.of_node; ++ ++ /* ++ * If "dev->id" is negative we consider it as zero. ++ * The reason to do so is to avoid sysfs names that only make ++ * sense when there are multiple adapters. ++ */ ++ adap->nr = (pdev->id != -1) ? pdev->id : 0; ++ ret = i2c_bit_add_numbered_bus(adap); ++ if (ret) ++ goto err_add_bus; ++ ++ of_i2c_register_devices(adap); ++ ++ platform_set_drvdata(pdev, priv); ++ ++ dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)\n", ++ pdata->sda_pin, pdata->scl_pin, ++ pdata->scl_is_output_only ++ ? ", no clock stretching" : ""); ++ ++ return 0; ++ ++err_add_bus: ++ ak_gpio_free(pdata->scl_pin); ++err_request_scl: ++ ak_gpio_free(pdata->sda_pin); ++err_request_sda: ++ return ret; ++} ++ ++static int __devexit i2c_gpio_remove(struct platform_device *pdev) ++{ ++ struct i2c_gpio_private_data *priv; ++ struct i2c_gpio_platform_data *pdata; ++ struct i2c_adapter *adap; ++ ++ priv = platform_get_drvdata(pdev); ++ adap = &priv->adap; ++ pdata = &priv->pdata; ++ ++ i2c_del_adapter(adap); ++ ak_gpio_free(pdata->scl_pin); ++ ak_gpio_free(pdata->sda_pin); ++ ++ return 0; ++} ++ ++#if defined(CONFIG_OF) ++static const struct of_device_id i2c_gpio_dt_ids[] = { ++ { .compatible = "i2c-gpio", }, ++ { /* sentinel */ } ++}; ++ ++MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids); ++#endif ++ ++static struct platform_driver i2c_gpio_driver = { ++ .driver = { ++ .name = "i2c-gpio", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(i2c_gpio_dt_ids), ++ }, ++ .probe = i2c_gpio_probe, ++ .remove = __devexit_p(i2c_gpio_remove), ++}; ++ ++static int __init i2c_gpio_init(void) ++{ ++ int ret; ++ ++ ret = platform_driver_register(&i2c_gpio_driver); ++ if (ret) ++ printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret); ++ ++ return ret; ++} ++subsys_initcall(i2c_gpio_init); ++ ++static void __exit i2c_gpio_exit(void) ++{ ++ platform_driver_unregister(&i2c_gpio_driver); ++} ++module_exit(i2c_gpio_exit); ++ ++MODULE_AUTHOR("Anyka ltd company"); ++MODULE_DESCRIPTION("Platform-independent bitbanging I2C driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:i2c-gpio"); +diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig +index 33259798..69d36863 100644 +--- a/drivers/input/Kconfig ++++ b/drivers/input/Kconfig +@@ -165,6 +165,15 @@ config INPUT_APMPOWER + To compile this driver as a module, choose M here: the + module will be called apm-power. + ++config INPUT_KEYRESET ++ tristate "Reset key" ++ depends on INPUT ++ ---help--- ++ Say Y here if you want to reboot when some keys are pressed; ++ ++ To compile this driver as a module, choose M here: the ++ module will be called keyreset. ++ + comment "Input Device Drivers" + + source "drivers/input/keyboard/Kconfig" +diff --git a/drivers/input/Makefile b/drivers/input/Makefile +index b173a13a..cf643bee 100644 +--- a/drivers/input/Makefile ++++ b/drivers/input/Makefile +@@ -25,3 +25,4 @@ obj-$(CONFIG_INPUT_MISC) += misc/ + + obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o + obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o ++obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o +diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c +index 4b2e10d5..a9374387 100644 +--- a/drivers/input/evdev.c ++++ b/drivers/input/evdev.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include "input-compat.h" + + struct evdev { +@@ -43,6 +44,9 @@ struct evdev_client { + unsigned int tail; + unsigned int packet_head; /* [future] position of the first element of next packet */ + spinlock_t buffer_lock; /* protects access to buffer, head and tail */ ++ struct wake_lock wake_lock; ++ bool use_wake_lock; ++ char name[28]; + struct fasync_struct *fasync; + struct evdev *evdev; + struct list_head node; +@@ -80,10 +84,14 @@ static void evdev_pass_event(struct evdev_client *client, + client->buffer[client->tail].value = 0; + + client->packet_head = client->tail; ++ if (client->use_wake_lock) ++ wake_unlock(&client->wake_lock); + } + + if (event->type == EV_SYN && event->code == SYN_REPORT) { + client->packet_head = client->head; ++ if (client->use_wake_lock) ++ wake_lock(&client->wake_lock); + kill_fasync(&client->fasync, SIGIO, POLL_IN); + } + +@@ -264,6 +272,8 @@ static int evdev_release(struct inode *inode, struct file *file) + mutex_unlock(&evdev->mutex); + + evdev_detach_client(evdev, client); ++ if (client->use_wake_lock) ++ wake_lock_destroy(&client->wake_lock); + kfree(client); + + evdev_close_device(evdev); +@@ -315,6 +325,8 @@ static int evdev_open(struct inode *inode, struct file *file) + + client->bufsize = bufsize; + spin_lock_init(&client->buffer_lock); ++ snprintf(client->name, sizeof(client->name), "%s-%d", ++ dev_name(&evdev->dev), task_tgid_vnr(current)); + client->evdev = evdev; + evdev_attach_client(evdev, client); + +@@ -382,6 +394,9 @@ static int evdev_fetch_next_event(struct evdev_client *client, + if (have_event) { + *event = client->buffer[client->tail++]; + client->tail &= client->bufsize - 1; ++ if (client->use_wake_lock && ++ client->packet_head == client->tail) ++ wake_unlock(&client->wake_lock); + } + + spin_unlock_irq(&client->buffer_lock); +@@ -654,6 +669,35 @@ static int evdev_handle_mt_request(struct input_dev *dev, + return 0; + } + ++static int evdev_enable_suspend_block(struct evdev *evdev, ++ struct evdev_client *client) ++{ ++ if (client->use_wake_lock) ++ return 0; ++ ++ spin_lock_irq(&client->buffer_lock); ++ wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name); ++ client->use_wake_lock = true; ++ if (client->packet_head != client->tail) ++ wake_lock(&client->wake_lock); ++ spin_unlock_irq(&client->buffer_lock); ++ return 0; ++} ++ ++static int evdev_disable_suspend_block(struct evdev *evdev, ++ struct evdev_client *client) ++{ ++ if (!client->use_wake_lock) ++ return 0; ++ ++ spin_lock_irq(&client->buffer_lock); ++ client->use_wake_lock = false; ++ wake_lock_destroy(&client->wake_lock); ++ spin_unlock_irq(&client->buffer_lock); ++ ++ return 0; ++} ++ + static long evdev_do_ioctl(struct file *file, unsigned int cmd, + void __user *p, int compat_mode) + { +@@ -735,6 +779,15 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, + + case EVIOCSKEYCODE_V2: + return evdev_handle_set_keycode_v2(dev, p); ++ ++ case EVIOCGSUSPENDBLOCK: ++ return put_user(client->use_wake_lock, ip); ++ ++ case EVIOCSSUSPENDBLOCK: ++ if (p) ++ return evdev_enable_suspend_block(evdev, client); ++ else ++ return evdev_disable_suspend_block(evdev, client); + } + + size = _IOC_SIZE(cmd); +diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig +index f354813a..3e362083 100644 +--- a/drivers/input/keyboard/Kconfig ++++ b/drivers/input/keyboard/Kconfig +@@ -580,4 +580,35 @@ config KEYBOARD_W90P910 + To compile this driver as a module, choose M here: the + module will be called w90p910_keypad. + ++config KEYBOARD_AKKEY ++ tristate "Anyka gpio-keys support" ++ depends on ARCH_AK39 ++ help ++ This driver implements support for buttons connected ++ to GPIO pins of various CPUs (and some other chips). ++ ++ To compile this driver as a module, choose M here: the ++ module will be called akgpio_keys. ++ ++config KEYBOARD_AK_KEYPAD ++ tristate "Anyka matrix-keypad support" ++ depends on ARCH_AK39 ++ help ++ This driver implements support for buttons connected ++ to GPIO pins of various CPUs (and some other chips). ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ak_matrix_keypad. ++ ++config KEYBOARD_ADKEY ++ tristate "ADC simulate gpio keys support" ++ depends on ARCH_AK39 ++# select INPUT_POLLDEV ++ help ++ This driver implements support for AD-KEY connected ++ to ADC pin. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called akad-key. ++ + endif +diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile +index df7061f1..30cf1c52 100644 +--- a/drivers/input/keyboard/Makefile ++++ b/drivers/input/keyboard/Makefile +@@ -52,3 +52,6 @@ obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o + obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o + obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o + obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o ++obj-$(CONFIG_KEYBOARD_AKKEY) += akgpio_keys.o ++obj-$(CONFIG_KEYBOARD_AK_KEYPAD) += akmatrix_keypad.o ++obj-$(CONFIG_KEYBOARD_ADKEY) += akad-keys.o +diff --git a/drivers/input/keyboard/akad-keys.c b/drivers/input/keyboard/akad-keys.c +new file mode 100755 +index 00000000..a8eec235 +--- /dev/null ++++ b/drivers/input/keyboard/akad-keys.c +@@ -0,0 +1,358 @@ ++/* ++ * drivers/input/keyboard/akad-keys.c ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++//#define ADKEY_DEBUG ++ ++#ifdef ADKEY_DEBUG ++#define pr_dbg(fmt...) printk(fmt) ++#else ++#define pr_dbg(fmt...) ++#endif ++ ++#define DRV_NAME "ad-keys" ++ ++struct adgpio_key_data { ++ struct input_dev *input; ++ struct adgpio_key *keys; ++ struct timer_list timer; ++ /* private: */ ++ struct delayed_work work; ++ ++ struct semaphore sem; ++ ++ struct multi_addetect *addet; ++ int naddet; ++ ++ int poll_interval; ++ int debounce_interval; ++ int keycode; ++ int nkey; ++ ++ unsigned char loop; ++ unsigned char press; ++ unsigned char sync_me; ++}; ++ ++static void adkeys_poll(struct adgpio_key_data *adkey); ++ ++static void adkey_input_queue_work(struct adgpio_key_data *adkey) ++{ ++ unsigned long delay; ++ ++ delay = msecs_to_jiffies(adkey->poll_interval); ++ if (delay >= HZ) ++ delay = round_jiffies_relative(delay); ++ ++ queue_delayed_work(system_freezable_wq, &adkey->work, delay); ++} ++ ++static void adkey_input_device_work(struct work_struct *work) ++{ ++ struct adgpio_key_data *adkey = ++ container_of(work, struct adgpio_key_data, work.work); ++ ++ adkeys_poll(adkey); ++ adkey_input_queue_work(adkey); ++} ++ ++static void addetect_plugin_notify(T_ad_check state) ++{ ++ static bool dev1_flag = false; ++ static bool dev2_flag = false; ++ static bool dev3_flag = false; ++ static bool dev4_flag = false; ++ ++ pr_dbg("adkey: plugin device state: 0x%0x\n", state); ++ ++ if ((state & PLUGIN_DEV1) && (!dev1_flag)) { ++ addetect_notifier_call_chain(ADDEDECT_DEV1_PLUGIN, NULL); ++ dev1_flag = true; ++ } else if ((!(state & PLUGIN_DEV1)) && dev1_flag){ ++ addetect_notifier_call_chain(ADDEDECT_DEV1_PLUGOUT, NULL); ++ dev1_flag = false; ++ } ++ ++ if ((state & PLUGIN_DEV2) && (!dev2_flag)) { ++ addetect_notifier_call_chain(ADDEDECT_DEV2_PLUGIN, NULL); ++ dev2_flag = true; ++ } else if ((!(state & PLUGIN_DEV2)) && dev2_flag){ ++ addetect_notifier_call_chain(ADDEDECT_DEV2_PLUGOUT, NULL); ++ dev2_flag = false; ++ } ++ ++ if ((state & PLUGIN_DEV3) && (!dev3_flag)) { ++ addetect_notifier_call_chain(ADDEDECT_DEV3_PLUGIN, NULL); ++ dev3_flag = true; ++ } else if ((!(state & PLUGIN_DEV3)) && dev3_flag) { ++ addetect_notifier_call_chain(ADDEDECT_DEV3_PLUGOUT, NULL); ++ dev3_flag = false; ++ } ++ ++ if ((state & PLUGIN_DEV4) && (!dev4_flag)) { ++ addetect_notifier_call_chain(ADDEDECT_DEV4_PLUGIN, NULL); ++ dev4_flag = true; ++ } else if ((!(state & PLUGIN_DEV4)) && dev4_flag) { ++ addetect_notifier_call_chain(ADDEDECT_DEV4_PLUGOUT, NULL); ++ dev4_flag = false; ++ } ++} ++ ++static void adkeys_timer(unsigned long _data) ++{ ++ struct adgpio_key_data *adkey = (struct adgpio_key_data *)_data; ++ struct input_dev *input_dev = adkey->input; ++ int advol, i, code; ++ ++ if (!adkey->press) { ++ adkey->sync_me = 0; ++ } else { ++ advol = adc1_read_ad5(); ++ pr_dbg("timer: volatge: %d\n", advol); ++ ++ i = 0; ++ do { ++ if ((advol > adkey->keys[i].min)&&(advol < adkey->keys[i].max)) { ++ pr_dbg("timer: volatge: %d\n", advol); ++ code = adkey->keys[i].code; ++ if ((code == adkey->keycode)&&(i == adkey->loop)) ++ break; ++ } ++ i++; ++ } while(i < adkey->nkey); ++ } ++ ++ /* report the key */ ++ input_event(input_dev, EV_KEY, adkey->keycode, adkey->press); ++ input_sync(input_dev); ++} ++ ++static void adkeys_poll(struct adgpio_key_data *adkey) ++{ ++ int advol, i; ++ static struct adgpio_key *prev_keys = NULL; ++ static T_ad_check state = PLUGIN_INVALID; ++ static int is_change_flag = 0; ++ ++ advol = adc1_read_ad5(); ++ pr_dbg("poll: volatge: %d\n", advol); ++ ++ for (i = 0; i < adkey->naddet; i++) { ++ if ((advol > adkey->addet[i].unpress_min) ++ &&(advol < adkey->addet[i].unpress_max)) { ++ ++ adkey->keys = adkey->addet[i].fixkeys; ++ if ((prev_keys != adkey->keys) || (state != adkey->addet[i].plugdev)) { ++ prev_keys = adkey->keys; ++ state = adkey->addet[i].plugdev; ++ is_change_flag = 1; ++ } else { ++ is_change_flag = 0; ++ } ++ break; ++ } ++ } ++ ++ if (is_change_flag) { ++ addetect_plugin_notify(state); ++ } ++ ++ i = 0; ++ do { ++ if ((adkey->keys != NULL) ++ && (advol > adkey->keys[i].min) && (advol < adkey->keys[i].max)) { ++ adkey->press = 1; ++ adkey->sync_me = 1; ++ ++ adkey->keycode = adkey->keys[i].code; ++ adkey->loop = i; ++ break; ++ } else ++ adkey->press = 0; ++ ++ i++; ++ } while(i < adkey->nkey); ++ ++ if (adkey->sync_me) { ++ mod_timer(&adkey->timer, ++ jiffies + msecs_to_jiffies(adkey->debounce_interval)); ++ } ++} ++ ++static int analog_gpio_probe(struct platform_device *pdev) ++{ ++ struct adgpio_key_data *adkey; ++ struct input_dev *input_dev; ++ struct analog_gpio_key *pdata = NULL; ++ int i, err; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) ++ return -ENODEV; ++ ++ adkey = kzalloc(sizeof(struct adgpio_key_data)+ ++ pdata->naddet*sizeof(struct multi_addetect)+ ++ pdata->nkey*sizeof(struct adgpio_key), GFP_KERNEL); ++ if (!adkey) ++ return -ENOMEM; ++ ++ input_dev = input_allocate_device(); ++ if (!input_dev) { ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ platform_set_drvdata(pdev, adkey); ++ ++ adkey->input = input_dev; ++ if ((pdata->addet) && (pdata->naddet > 0)) { ++ adkey->addet = pdata->addet; ++ adkey->naddet = pdata->naddet; ++ adkey->keys = pdata->addet[0].fixkeys; ++ } ++ adkey->nkey = pdata->nkey; ++ ++ if (pdata->interval <= 0) ++ adkey->poll_interval = 500; ++ else ++ adkey->poll_interval = pdata->interval; ++ ++ if (pdata->debounce_interval <= 0) ++ adkey->debounce_interval = 20; ++ else ++ adkey->debounce_interval = pdata->debounce_interval; ++ ++ adkey->press = 0; ++ adkey->sync_me = 0; ++ ++ input_dev->name = pdev->name; ++ input_dev->phys = DRV_NAME"/input0"; ++ input_dev->id.bustype = BUS_HOST; ++ input_dev->id.vendor = 0x0001; ++ input_dev->id.product = 0x0010; ++ input_dev->id.version = 0x00100; ++ input_dev->dev.parent = &pdev->dev; ++ ++ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); ++ input_dev->keycode = adkey->keys; ++ input_dev->keycodesize = sizeof(struct adgpio_key); ++ input_dev->keycodemax = adkey->nkey; ++ ++ for (i = 0; i < adkey->nkey; i++) ++ set_bit(adkey->keys[i].code, input_dev->keybit); ++ clear_bit(KEY_RESERVED, input_dev->keybit); ++ ++ input_set_capability(input_dev, EV_MSC, MSC_SCAN); ++ ++ INIT_DELAYED_WORK(&adkey->work, adkey_input_device_work); ++ ++ /* Only start polling if polling is enabled */ ++ adkey_input_queue_work(adkey); ++ ++ setup_timer(&adkey->timer, adkeys_timer, (unsigned long)adkey); ++ ++ err = input_register_device(adkey->input); ++ if (err) ++ goto fail; ++ ++ return 0; ++ ++fail: ++ printk(KERN_ERR "Adkey: failed to register driver, error: %d\n", err); ++ platform_set_drvdata(pdev, NULL); ++ input_free_device(input_dev); ++ kfree(adkey); ++ return err; ++} ++ ++static int analog_gpio_remove(struct platform_device *pdev) ++{ ++ struct adgpio_key_data *adkey = platform_get_drvdata(pdev); ++ ++ cancel_delayed_work_sync(&adkey->work); ++ ++ input_unregister_device(adkey->input); ++ ++ input_free_device(adkey->input); ++ ++ del_timer_sync(&adkey->timer); ++ kfree(adkey); ++ ++ return 0; ++} ++ ++ ++#ifdef CONFIG_PM ++static int analog_gpio_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct adgpio_key_data *adkey = platform_get_drvdata(pdev); ++ struct analog_gpio_key *pdata = pdev->dev.platform_data; ++ ++ if (pdata->wakeup > 0) ++ adkey_wakeup_enable(true); ++ ++ del_timer_sync(&adkey->timer); ++ ++ return 0; ++} ++ ++static int analog_gpio_resume(struct platform_device *pdev) ++{ ++ //struct adgpio_key_data *adkey = platform_get_drvdata(pdev); ++ struct analog_gpio_key *pdata = pdev->dev.platform_data; ++ ++ if (pdata->wakeup > 0) ++ adkey_wakeup_enable(false); ++ ++ return 0; ++} ++#else ++#define analog_gpio_suspend NULL ++#define analog_gpio_resume NULL ++#endif ++ ++static struct platform_driver analog_ops = { ++ .probe = analog_gpio_probe, ++ .remove = __devexit_p(analog_gpio_remove), ++ .suspend = analog_gpio_suspend, ++ .resume = analog_gpio_resume, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = DRV_NAME, ++ }, ++}; ++ ++static int __init analog_gpio_key_init(void) ++{ ++ printk("Init ADC simulate gpio keys driver.\n"); ++ return platform_driver_register(&analog_ops); ++} ++ ++static void __exit analog_gpio_key_exit(void) ++{ ++ platform_driver_unregister(&analog_ops); ++} ++ ++late_initcall(analog_gpio_key_init); ++module_exit(analog_gpio_key_exit); ++ ++MODULE_AUTHOR("Anyka Microelectronic Ltd."); ++MODULE_DESCRIPTION("Anyka ADC simulate gpio keys driver"); ++MODULE_ALIAS("platform:" DRV_NAME); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/input/keyboard/akgpio_keys.c b/drivers/input/keyboard/akgpio_keys.c +new file mode 100755 +index 00000000..923775a1 +--- /dev/null ++++ b/drivers/input/keyboard/akgpio_keys.c +@@ -0,0 +1,1003 @@ ++/* ++ * Driver for keys on GPIO lines capable of generating interrupts. ++ * ++ * Copyright 2005 Phil Blundell ++ * Copyright 2010, 2011 David Jander ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ /* ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++struct gpio_button_data { ++ const struct gpio_keys_button *button; ++ struct input_dev *input; ++ struct timer_list timer; ++ struct work_struct work; ++ unsigned int timer_debounce; /* in msecs */ ++ unsigned int irq; ++ spinlock_t lock; ++ bool disabled; ++ bool key_pressed; ++}; ++ ++struct gpio_keys_drvdata { ++ struct input_dev *input; ++ struct mutex disable_lock; ++ unsigned int n_buttons; ++ int (*enable)(struct device *dev); ++ void (*disable)(struct device *dev); ++ struct gpio_button_data data[0]; ++}; ++ ++/* ++ * SYSFS interface for enabling/disabling keys and switches: ++ * ++ * There are 4 attributes under /sys/devices/platform/gpio-keys/ ++ * keys [ro] - bitmap of keys (EV_KEY) which can be ++ * disabled ++ * switches [ro] - bitmap of switches (EV_SW) which can be ++ * disabled ++ * disabled_keys [rw] - bitmap of keys currently disabled ++ * disabled_switches [rw] - bitmap of switches currently disabled ++ * ++ * Userland can change these values and hence disable event generation ++ * for each key (or switch). Disabling a key means its interrupt line ++ * is disabled. ++ * ++ * For example, if we have following switches set up as gpio-keys: ++ * SW_DOCK = 5 ++ * SW_CAMERA_LENS_COVER = 9 ++ * SW_KEYPAD_SLIDE = 10 ++ * SW_FRONT_PROXIMITY = 11 ++ * This is read from switches: ++ * 11-9,5 ++ * Next we want to disable proximity (11) and dock (5), we write: ++ * 11,5 ++ * to file disabled_switches. Now proximity and dock IRQs are disabled. ++ * This can be verified by reading the file disabled_switches: ++ * 11,5 ++ * If we now want to enable proximity (11) switch we write: ++ * 5 ++ * to disabled_switches. ++ * ++ * We can disable only those keys which don't allow sharing the irq. ++ */ ++ ++/** ++ * get_n_events_by_type() - returns maximum number of events per @type ++ * @type: type of button (%EV_KEY, %EV_SW) ++ * ++ * Return value of this function can be used to allocate bitmap ++ * large enough to hold all bits for given type. ++ */ ++static inline int get_n_events_by_type(int type) ++{ ++ BUG_ON(type != EV_SW && type != EV_KEY); ++ ++ return (type == EV_KEY) ? KEY_CNT : SW_CNT; ++} ++ ++/** ++ * gpio_keys_disable_button() - disables given GPIO button ++ * @bdata: button data for button to be disabled ++ * ++ * Disables button pointed by @bdata. This is done by masking ++ * IRQ line. After this function is called, button won't generate ++ * input events anymore. Note that one can only disable buttons ++ * that don't share IRQs. ++ * ++ * Make sure that @bdata->disable_lock is locked when entering ++ * this function to avoid races when concurrent threads are ++ * disabling buttons at the same time. ++ */ ++static void gpio_keys_disable_button(struct gpio_button_data *bdata) ++{ ++ if (!bdata->disabled) { ++ /* ++ * Disable IRQ and possible debouncing timer. ++ */ ++ disable_irq(bdata->irq); ++ if (bdata->timer_debounce) ++ del_timer_sync(&bdata->timer); ++ ++ bdata->disabled = true; ++ } ++} ++ ++/** ++ * gpio_keys_enable_button() - enables given GPIO button ++ * @bdata: button data for button to be disabled ++ * ++ * Enables given button pointed by @bdata. ++ * ++ * Make sure that @bdata->disable_lock is locked when entering ++ * this function to avoid races with concurrent threads trying ++ * to enable the same button at the same time. ++ */ ++static void gpio_keys_enable_button(struct gpio_button_data *bdata) ++{ ++ if (bdata->disabled) { ++ enable_irq(bdata->irq); ++ bdata->disabled = false; ++ } ++} ++ ++/** ++ * gpio_keys_attr_show_helper() - fill in stringified bitmap of buttons ++ * @ddata: pointer to drvdata ++ * @buf: buffer where stringified bitmap is written ++ * @type: button type (%EV_KEY, %EV_SW) ++ * @only_disabled: does caller want only those buttons that are ++ * currently disabled or all buttons that can be ++ * disabled ++ * ++ * This function writes buttons that can be disabled to @buf. If ++ * @only_disabled is true, then @buf contains only those buttons ++ * that are currently disabled. Returns 0 on success or negative ++ * errno on failure. ++ */ ++static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata, ++ char *buf, unsigned int type, ++ bool only_disabled) ++{ ++ int n_events = get_n_events_by_type(type); ++ unsigned long *bits; ++ ssize_t ret; ++ int i; ++ ++ bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL); ++ if (!bits) ++ return -ENOMEM; ++ ++ for (i = 0; i < ddata->n_buttons; i++) { ++ struct gpio_button_data *bdata = &ddata->data[i]; ++ ++ if (bdata->button->type != type) ++ continue; ++ ++ if (only_disabled && !bdata->disabled) ++ continue; ++ ++ __set_bit(bdata->button->code, bits); ++ } ++ ++ ret = bitmap_scnlistprintf(buf, PAGE_SIZE - 2, bits, n_events); ++ buf[ret++] = '\n'; ++ buf[ret] = '\0'; ++ ++ kfree(bits); ++ ++ return ret; ++} ++ ++/** ++ * gpio_keys_attr_store_helper() - enable/disable buttons based on given bitmap ++ * @ddata: pointer to drvdata ++ * @buf: buffer from userspace that contains stringified bitmap ++ * @type: button type (%EV_KEY, %EV_SW) ++ * ++ * This function parses stringified bitmap from @buf and disables/enables ++ * GPIO buttons accordingly. Returns 0 on success and negative error ++ * on failure. ++ */ ++static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, ++ const char *buf, unsigned int type) ++{ ++ int n_events = get_n_events_by_type(type); ++ unsigned long *bits; ++ ssize_t error; ++ int i; ++ ++ bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL); ++ if (!bits) ++ return -ENOMEM; ++ ++ error = bitmap_parselist(buf, bits, n_events); ++ if (error) ++ goto out; ++ ++ /* First validate */ ++ for (i = 0; i < ddata->n_buttons; i++) { ++ struct gpio_button_data *bdata = &ddata->data[i]; ++ ++ if (bdata->button->type != type) ++ continue; ++ ++ if (test_bit(bdata->button->code, bits) && ++ !bdata->button->can_disable) { ++ error = -EINVAL; ++ goto out; ++ } ++ } ++ ++ mutex_lock(&ddata->disable_lock); ++ ++ for (i = 0; i < ddata->n_buttons; i++) { ++ struct gpio_button_data *bdata = &ddata->data[i]; ++ ++ if (bdata->button->type != type) ++ continue; ++ ++ if (test_bit(bdata->button->code, bits)) ++ gpio_keys_disable_button(bdata); ++ else ++ gpio_keys_enable_button(bdata); ++ } ++ ++ mutex_unlock(&ddata->disable_lock); ++ ++out: ++ kfree(bits); ++ return error; ++} ++ ++#define ATTR_SHOW_FN(name, type, only_disabled) \ ++static ssize_t gpio_keys_show_##name(struct device *dev, \ ++ struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ struct platform_device *pdev = to_platform_device(dev); \ ++ struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); \ ++ \ ++ return gpio_keys_attr_show_helper(ddata, buf, \ ++ type, only_disabled); \ ++} ++ ++ATTR_SHOW_FN(keys, EV_KEY, false); ++ATTR_SHOW_FN(switches, EV_SW, false); ++ATTR_SHOW_FN(disabled_keys, EV_KEY, true); ++ATTR_SHOW_FN(disabled_switches, EV_SW, true); ++ ++/* ++ * ATTRIBUTES: ++ * ++ * /sys/devices/platform/gpio-keys/keys [ro] ++ * /sys/devices/platform/gpio-keys/switches [ro] ++ */ ++static DEVICE_ATTR(keys, S_IRUGO, gpio_keys_show_keys, NULL); ++static DEVICE_ATTR(switches, S_IRUGO, gpio_keys_show_switches, NULL); ++ ++#define ATTR_STORE_FN(name, type) \ ++static ssize_t gpio_keys_store_##name(struct device *dev, \ ++ struct device_attribute *attr, \ ++ const char *buf, \ ++ size_t count) \ ++{ \ ++ struct platform_device *pdev = to_platform_device(dev); \ ++ struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); \ ++ ssize_t error; \ ++ \ ++ error = gpio_keys_attr_store_helper(ddata, buf, type); \ ++ if (error) \ ++ return error; \ ++ \ ++ return count; \ ++} ++ ++ATTR_STORE_FN(disabled_keys, EV_KEY); ++ATTR_STORE_FN(disabled_switches, EV_SW); ++ ++/* ++ * ATTRIBUTES: ++ * ++ * /sys/devices/platform/gpio-keys/disabled_keys [rw] ++ * /sys/devices/platform/gpio-keys/disables_switches [rw] ++ */ ++static DEVICE_ATTR(disabled_keys, S_IWUSR | S_IRUGO, ++ gpio_keys_show_disabled_keys, ++ gpio_keys_store_disabled_keys); ++static DEVICE_ATTR(disabled_switches, S_IWUSR | S_IRUGO, ++ gpio_keys_show_disabled_switches, ++ gpio_keys_store_disabled_switches); ++ ++static struct attribute *gpio_keys_attrs[] = { ++ &dev_attr_keys.attr, ++ &dev_attr_switches.attr, ++ &dev_attr_disabled_keys.attr, ++ &dev_attr_disabled_switches.attr, ++ NULL, ++}; ++ ++static struct attribute_group gpio_keys_attr_group = { ++ .attrs = gpio_keys_attrs, ++}; ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief init gpio ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] *button ++* @return fail or not ++*/ ++static int gpio_init_data(const struct gpio_keys_button *button) ++{ ++ ak_setpin_as_gpio(button->gpio); ++ ak_gpio_cfgpin(button->gpio, button->dir); ++ if (button->pullup == AK_PULLUP_ENABLE || button->pullup == AK_PULLUP_DISABLE) ++ ak_gpio_pullup(button->gpio, button->pullup); ++ if (button->pulldown == AK_PULLDOWN_ENABLE || button->pulldown == AK_PULLDOWN_DISABLE) ++ ak_gpio_pulldown(button->gpio, button->pulldown); ++ ak_gpio_intpol(button->gpio, button->int_pol); ++ ++ return 0; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief revert irq polarity ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] irq, int_pol ++* @return none ++*/ ++static void revert_irq_polarity(unsigned int irq, char int_pol) ++{ ++ ak_gpio_intpol(ak_irq_to_gpio(irq), int_pol); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief report event to user ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] *bdata ++* @return none ++*/ ++static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) ++{ ++ const struct gpio_keys_button *button = bdata->button; ++ struct input_dev *input = bdata->input; ++ unsigned int type = button->type ?: EV_KEY; ++ int state = (ak_gpio_getpin(button->gpio) ? 1 : 0) ^ button->active_low; ++ ++ if (type == EV_ABS) { ++ if (state) ++ input_event(input, type, button->code, button->value); ++ } else { ++ input_event(input, type, button->code, !!state); ++ } ++ input_sync(input); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief keys work function ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] *work ++* @return none ++*/ ++static void gpio_keys_gpio_work_func(struct work_struct *work) ++{ ++ struct gpio_button_data *bdata = ++ container_of(work, struct gpio_button_data, work); ++ ++ gpio_keys_gpio_report_event(bdata); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief keys timer ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] _data ++* @return none ++*/ ++static void gpio_keys_gpio_timer(unsigned long _data) ++{ ++ struct gpio_button_data *bdata = (struct gpio_button_data *)_data; ++ ++ schedule_work(&bdata->work); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief keys isr ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] int irq, void *dev_id ++* @return IRQ_HANDLED ++*/ ++static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) ++{ ++ struct gpio_button_data *bdata = dev_id; ++ const struct gpio_keys_button *button = bdata->button; ++ int gpio_level; ++ ++ BUG_ON(irq != bdata->irq); ++ ++ gpio_level = ak_gpio_getpin(button->gpio); ++ //disalbe gpio_irq when the button down ++ if ((gpio_level && button->int_pol) || (!gpio_level && !button->int_pol)) ++ revert_irq_polarity(irq, !button->int_pol); ++ ++ //enable gpiot_irq when the button up ++ if ((!gpio_level && button->int_pol) || (gpio_level && !button->int_pol)) ++ revert_irq_polarity(irq, button->int_pol); ++ ++ if (bdata->timer_debounce) ++ mod_timer(&bdata->timer, ++ jiffies + msecs_to_jiffies(bdata->timer_debounce)); ++ else ++ schedule_work(&bdata->work); ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief keys timer isr ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] _data ++* @return none ++*/ ++static void gpio_keys_irq_timer(unsigned long _data) ++{ ++ struct gpio_button_data *bdata = (struct gpio_button_data *)_data; ++ struct input_dev *input = bdata->input; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&bdata->lock, flags); ++ if (bdata->key_pressed) { ++ input_event(input, EV_KEY, bdata->button->code, 0); ++ input_sync(input); ++ bdata->key_pressed = false; ++ } ++ spin_unlock_irqrestore(&bdata->lock, flags); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief keys irq isr ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] int irq, void *dev_id ++* @return IRQ_HANDLED ++*/ ++static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id) ++{ ++ struct gpio_button_data *bdata = dev_id; ++ const struct gpio_keys_button *button = bdata->button; ++ struct input_dev *input = bdata->input; ++ unsigned long flags; ++ ++ BUG_ON(irq != bdata->irq); ++ ++ spin_lock_irqsave(&bdata->lock, flags); ++ ++ if (!bdata->key_pressed) { ++ input_event(input, EV_KEY, button->code, 1); ++ input_sync(input); ++ ++ if (!bdata->timer_debounce) { ++ input_event(input, EV_KEY, button->code, 0); ++ input_sync(input); ++ goto out; ++ } ++ ++ bdata->key_pressed = true; ++ } ++ ++ if (bdata->timer_debounce) ++ mod_timer(&bdata->timer, ++ jiffies + msecs_to_jiffies(bdata->timer_debounce)); ++out: ++ spin_unlock_irqrestore(&bdata->lock, flags); ++ return IRQ_HANDLED; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief setup key ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] *pdev,... ++* @return fail or not ++*/ ++static int __devinit gpio_keys_setup_key(struct platform_device *pdev, ++ struct input_dev *input, ++ struct gpio_button_data *bdata, ++ const struct gpio_keys_button *button) ++{ ++ const char *desc = button->desc ? button->desc : "gpio_keys"; ++ struct device *dev = &pdev->dev; ++ irq_handler_t isr; ++ unsigned long irqflags; ++ int irq, error; ++ ++ bdata->input = input; ++ bdata->button = button; ++ spin_lock_init(&bdata->lock); ++ ++ if (gpio_is_valid(button->gpio)) { ++ ++ error = gpio_init_data(button); ++ if (error < 0) { ++ dev_err(dev, "gpio-keys: failed to configure input" ++ " direction and pull up/down for GPIO %d, error %d\n", ++ button->gpio, error); ++ gpio_free(button->gpio); ++ goto fail; ++ } ++ ++ if (button->debounce_interval) { ++ error = gpio_set_debounce(button->gpio, ++ button->debounce_interval * 1000); ++ /* use timer if gpiolib doesn't provide debounce */ ++ if (error < 0) ++ bdata->timer_debounce = button->debounce_interval; ++ } ++ ++ irq = ak_gpio_to_irq(button->gpio); ++ if (irq < 0) { ++ error = irq; ++ dev_err(dev, ++ "Unable to get irq number for GPIO %d, error %d\n", ++ button->gpio, error); ++ goto fail; ++ } ++ bdata->irq = irq; ++ ++ INIT_WORK(&bdata->work, gpio_keys_gpio_work_func); ++ setup_timer(&bdata->timer, ++ gpio_keys_gpio_timer, (unsigned long)bdata); ++ ++ isr = gpio_keys_gpio_isr; ++ irqflags = (button->active_low)?(IRQF_TRIGGER_LOW):(IRQF_TRIGGER_HIGH); ++ ++ } else { ++ if (!button->irq) { ++ dev_err(dev, "No IRQ specified\n"); ++ return -EINVAL; ++ } ++ bdata->irq = button->irq; ++ ++ if (button->type && button->type != EV_KEY) { ++ dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n"); ++ return -EINVAL; ++ } ++ ++ bdata->timer_debounce = button->debounce_interval; ++ setup_timer(&bdata->timer, ++ gpio_keys_irq_timer, (unsigned long)bdata); ++ ++ isr = gpio_keys_irq_isr; ++ irqflags = 0; ++ } ++ ++ input_set_capability(input, button->type ?: EV_KEY, button->code); ++ ++ /* ++ * If platform has specified that the button can be disabled, ++ * we don't want it to share the interrupt line. ++ */ ++ if (!button->can_disable) ++ irqflags |= IRQF_SHARED; ++ ++ error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata); ++ if (error < 0) { ++ dev_err(dev, "Unable to claim irq %d; error %d\n", ++ bdata->irq, error); ++ goto fail; ++ } ++ ++ return 0; ++ ++fail: ++ if (gpio_is_valid(button->gpio)) ++ gpio_free(button->gpio); ++ ++ return error; ++} ++ ++static int gpio_keys_open(struct input_dev *input) ++{ ++ struct gpio_keys_drvdata *ddata = input_get_drvdata(input); ++ ++ return ddata->enable ? ddata->enable(input->dev.parent) : 0; ++} ++ ++static void gpio_keys_close(struct input_dev *input) ++{ ++ struct gpio_keys_drvdata *ddata = input_get_drvdata(input); ++ ++ if (ddata->disable) ++ ddata->disable(input->dev.parent); ++} ++ ++/* ++ * Handlers for alternative sources of platform_data ++ */ ++#ifdef CONFIG_OF ++/* ++ * Translate OpenFirmware node properties into platform_data ++ */ ++static int gpio_keys_get_devtree_pdata(struct device *dev, ++ struct akgpio_keys_platform_data *pdata) ++{ ++ struct device_node *node, *pp; ++ int i; ++ struct gpio_keys_button *buttons; ++ u32 reg; ++ ++ node = dev->of_node; ++ if (node == NULL) ++ return -ENODEV; ++ ++ memset(pdata, 0, sizeof *pdata); ++ ++ pdata->rep = !!of_get_property(node, "autorepeat", NULL); ++ ++ /* First count the subnodes */ ++ pdata->nbuttons = 0; ++ pp = NULL; ++ while ((pp = of_get_next_child(node, pp))) ++ pdata->nbuttons++; ++ ++ if (pdata->nbuttons == 0) ++ return -ENODEV; ++ ++ buttons = kzalloc(pdata->nbuttons * (sizeof *buttons), GFP_KERNEL); ++ if (!buttons) ++ return -ENOMEM; ++ ++ pp = NULL; ++ i = 0; ++ while ((pp = of_get_next_child(node, pp))) { ++ enum of_gpio_flags flags; ++ ++ if (!of_find_property(pp, "gpios", NULL)) { ++ pdata->nbuttons--; ++ dev_warn(dev, "Found button without gpios\n"); ++ continue; ++ } ++ buttons[i].gpio = of_get_gpio_flags(pp, 0, &flags); ++ buttons[i].active_low = flags & OF_GPIO_ACTIVE_LOW; ++ ++ if (of_property_read_u32(pp, "linux,code", ®)) { ++ dev_err(dev, "Button without keycode: 0x%x\n", buttons[i].gpio); ++ goto out_fail; ++ } ++ buttons[i].code = reg; ++ ++ buttons[i].desc = of_get_property(pp, "label", NULL); ++ ++ if (of_property_read_u32(pp, "linux,input-type", ®) == 0) ++ buttons[i].type = reg; ++ else ++ buttons[i].type = EV_KEY; ++ ++ buttons[i].wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL); ++ ++ if (of_property_read_u32(pp, "debounce-interval", ®) == 0) ++ buttons[i].debounce_interval = reg; ++ else ++ buttons[i].debounce_interval = 5; ++ ++ i++; ++ } ++ ++ pdata->buttons = buttons; ++ ++ return 0; ++ ++out_fail: ++ kfree(buttons); ++ return -ENODEV; ++} ++ ++static struct of_device_id gpio_keys_of_match[] = { ++ { .compatible = "gpio-keys", }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, gpio_keys_of_match); ++ ++#else ++ ++static int gpio_keys_get_devtree_pdata(struct device *dev, ++ struct akgpio_keys_platform_data *altp) ++{ ++ return -ENODEV; ++} ++ ++#define gpio_keys_of_match NULL ++ ++#endif ++ ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief gpio_remove_key ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] *bdata ++* @return none ++*/ ++static void gpio_remove_key(struct gpio_button_data *bdata) ++{ ++ free_irq(bdata->irq, bdata); ++ if (bdata->timer_debounce) ++ del_timer_sync(&bdata->timer); ++ cancel_work_sync(&bdata->work); ++ if (gpio_is_valid(bdata->button->gpio)) ++ gpio_free(bdata->button->gpio); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief gpio_keys_probe ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] *pdev ++* @return fail or not ++*/ ++static int __devinit gpio_keys_probe(struct platform_device *pdev) ++{ ++ const struct akgpio_keys_platform_data *pdata = pdev->dev.platform_data; ++ struct gpio_keys_drvdata *ddata; ++ struct device *dev = &pdev->dev; ++ struct akgpio_keys_platform_data alt_pdata; ++ struct input_dev *input; ++ int i, error; ++ int wakeup = 0; ++ ++ if (!pdata) { ++ error = gpio_keys_get_devtree_pdata(dev, &alt_pdata); ++ if (error) ++ return error; ++ pdata = &alt_pdata; ++ } ++ ++ ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + ++ pdata->nbuttons * sizeof(struct gpio_button_data), ++ GFP_KERNEL); ++ input = input_allocate_device(); ++ if (!ddata || !input) { ++ dev_err(dev, "failed to allocate state\n"); ++ error = -ENOMEM; ++ goto fail1; ++ } ++ ++ ddata->input = input; ++ ddata->n_buttons = pdata->nbuttons; ++ ddata->enable = pdata->enable; ++ ddata->disable = pdata->disable; ++ mutex_init(&ddata->disable_lock); ++ ++ platform_set_drvdata(pdev, ddata); ++ input_set_drvdata(input, ddata); ++ ++ input->name = pdata->name ? : pdev->name; ++ input->phys = "gpio-keys/input0"; ++ input->dev.parent = &pdev->dev; ++ input->open = gpio_keys_open; ++ input->close = gpio_keys_close; ++ ++ input->id.bustype = BUS_HOST; ++ input->id.vendor = 0x0001; ++ input->id.product = 0x0001; ++ input->id.version = 0x0100; ++ ++ /* Enable auto repeat feature of Linux input subsystem */ ++ if (pdata->rep) ++ __set_bit(EV_REP, input->evbit); ++ ++ for (i = 0; i < pdata->nbuttons; i++) { ++ const struct gpio_keys_button *button = &pdata->buttons[i]; ++ struct gpio_button_data *bdata = &ddata->data[i]; ++ ++ error = gpio_keys_setup_key(pdev, input, bdata, button); ++ if (error) ++ goto fail2; ++ ++ if (button->wakeup) ++ wakeup = 1; ++ } ++ ++ error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); ++ if (error) { ++ dev_err(dev, "Unable to export keys/switches, error: %d\n", ++ error); ++ goto fail2; ++ } ++ ++ error = input_register_device(input); ++ if (error) { ++ dev_err(dev, "Unable to register input device, error: %d\n", ++ error); ++ goto fail3; ++ } ++ ++ /* get current state of buttons that are connected to GPIOs */ ++ for (i = 0; i < pdata->nbuttons; i++) { ++ struct gpio_button_data *bdata = &ddata->data[i]; ++ if (gpio_is_valid(bdata->button->gpio)) ++ gpio_keys_gpio_report_event(bdata); ++ } ++ input_sync(input); ++ ++ device_init_wakeup(&pdev->dev, wakeup); ++ ++ return 0; ++ ++ fail3: ++ sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); ++ fail2: ++ while (--i >= 0) ++ gpio_remove_key(&ddata->data[i]); ++ ++ platform_set_drvdata(pdev, NULL); ++ fail1: ++ input_free_device(input); ++ kfree(ddata); ++ /* If we have no platform_data, we allocated buttons dynamically. */ ++ if (!pdev->dev.platform_data) ++ kfree(pdata->buttons); ++ ++ return error; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief gpio keys remove ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] *pdev ++* @return fail or not ++*/ ++static int __devexit gpio_keys_remove(struct platform_device *pdev) ++{ ++ struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); ++ struct input_dev *input = ddata->input; ++ int i; ++ ++ sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); ++ ++ device_init_wakeup(&pdev->dev, 0); ++ ++ for (i = 0; i < ddata->n_buttons; i++) ++ gpio_remove_key(&ddata->data[i]); ++ ++ input_unregister_device(input); ++ ++ /* ++ * If we had no platform_data, we allocated buttons dynamically, and ++ * must free them here. ddata->data[0].button is the pointer to the ++ * beginning of the allocated array. ++ */ ++ if (!pdev->dev.platform_data) ++ kfree(ddata->data[0].button); ++ ++ kfree(ddata); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++/** ++* @Copyright (C) Anyka 2012 ++* @brief gpio_keys_suspend ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] *dev ++* @return 0 ++*/ ++static int gpio_keys_suspend(struct device *dev) ++{ ++ struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); ++ int i; ++ ++ if (device_may_wakeup(dev)) { ++ for (i = 0; i < ddata->n_buttons; i++) { ++ struct gpio_button_data *bdata = &ddata->data[i]; ++ if (bdata->button->wakeup) ++ enable_irq_wake(bdata->irq); ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief gpio_keys_resume ++* @date 2013-5-14 ++* @param[out] void ++* @param[in] *dev ++* @return 0 ++*/ ++static int gpio_keys_resume(struct device *dev) ++{ ++ struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); ++ int i; ++ ++ for (i = 0; i < ddata->n_buttons; i++) { ++ struct gpio_button_data *bdata = &ddata->data[i]; ++ if (bdata->button->wakeup && device_may_wakeup(dev)) ++ disable_irq_wake(bdata->irq); ++ ++ if (gpio_is_valid(bdata->button->gpio)) ++ gpio_keys_gpio_report_event(bdata); ++ } ++ input_sync(ddata->input); ++ ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume); ++ ++static struct platform_driver gpio_keys_device_driver = { ++ .probe = gpio_keys_probe, ++ .remove = __devexit_p(gpio_keys_remove), ++ .driver = { ++ .name = "akgpio-keys", ++ .owner = THIS_MODULE, ++ .pm = &gpio_keys_pm_ops, ++ .of_match_table = gpio_keys_of_match, ++ } ++}; ++ ++static int __init gpio_keys_init(void) ++{ ++ return platform_driver_register(&gpio_keys_device_driver); ++} ++ ++static void __exit gpio_keys_exit(void) ++{ ++ platform_driver_unregister(&gpio_keys_device_driver); ++} ++ ++module_init(gpio_keys_init); ++module_exit(gpio_keys_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Phil Blundell "); ++MODULE_DESCRIPTION("Keyboard driver for GPIOs"); ++MODULE_ALIAS("platform:gpio-keys"); +diff --git a/drivers/input/keyboard/akmatrix_keypad.c b/drivers/input/keyboard/akmatrix_keypad.c +new file mode 100755 +index 00000000..2978f8d7 +--- /dev/null ++++ b/drivers/input/keyboard/akmatrix_keypad.c +@@ -0,0 +1,708 @@ ++/** ++* @file ak_matrix_keypad.c ++* @brief GPIO driven matrix keyboard driver ++* Copyright C 2011 Anyka CO.,LTD ++* Based on matrix_keypad.c ++* ++* This program is free software; you can redistribute it and/or modify ++* it under the terms of the GNU General Public License version 2 as ++* published by the Free Software Foundation. ++* ++* @author zhou wenyong ++* @date 2011-08-05 ++* @note 2010-12-24 created by cao_lianming ++* @note 2011-06-25 integrate and optimize matrix keypad driver -- ++* only one copy code exists now. we use the same code whether ++* or not keypad is connected aw9523, whether or not there is a ++* grounding line. ++* @note 2011-06-27 fix a bug imported from previous version, which is that ++* driver can not work properly if there is grounding line in 3X3 ++* or 4X4 matrix keypad ++* @note 2011-07-08 fix the issue of incorrect responsing to long press ++* of menu key ++* ++* Notes: if the comments look like inaesthetic, try to press ++* Alt+F12(source insight) ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ structure used to present the keypad ++*/ ++struct matrix_keypad { ++ struct matrix_keypad_platform_data *pdata; ++ struct input_dev *input_dev; ++ unsigned short *keycodes; ++ unsigned int row_shift; ++ ++ uint32_t last_key_state[MATRIX_MAX_COLS]; ++ struct delayed_work work; ++ bool scan_pending; ++ bool stopped; ++ spinlock_t lock; ++ bool start_close_int; ++}; ++ ++/** ++* @brief get the value of gpio (this function may sleep) ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] gpio ++* @return int ++*/ ++static inline int ak_gpio_get_value_cansleep(unsigned gpio) ++{ ++ might_sleep(); ++ return ak_gpio_getpin(gpio); ++} ++/** ++* @brief set the value of gpio (this function may sleep) ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] gpio ++* @param[in] value ++* @return void ++*/ ++static inline void ak_gpio_set_value_cansleep(unsigned gpio, int value) ++{ ++ might_sleep(); ++ ak_gpio_setpin(gpio, value); ++} ++ ++/** ++* @brief called by activate_other_col (refer to it) ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] *pdata ++* @param[in] col ++* @param[in] on ++* @return void ++*/ ++static void __activate_col(const struct matrix_keypad_platform_data *pdata, ++ int col, bool on) ++{ ++ bool level_on = pdata->active_low; ++ int i; ++ ++ for (i=0; inum_col_gpios;i++) ++ { ++ if (i == col) ++ continue; ++ if (on) ++ { ++ ak_gpio_setpin(pdata->col_gpios[i], level_on); ++ } ++ else ++ { ++ ak_gpio_setpin(pdata->col_gpios[i], !level_on); ++ } ++ } ++} ++ ++/** ++* @brief activate other colums ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] *pdata ++* @param[in] col -- the column excluded ++* @param[in] on ++* @return void ++*/ ++static void activate_other_col(const struct matrix_keypad_platform_data *pdata, ++ int col, bool on) ++{ ++ __activate_col(pdata, col, on); ++ ++ if (on && pdata->col_scan_delay_us) ++ udelay(pdata->col_scan_delay_us); ++} ++ ++/** ++* @brief get the value of gpio (can sleep) ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] *pdata ++* @param[in] row ++* @return bool ++*/ ++static bool row_asserted(const struct matrix_keypad_platform_data *pdata, ++ int row) ++{ ++ return ak_gpio_get_value_cansleep(pdata->row_gpios[row]) ? ++ !pdata->active_low : pdata->active_low; ++} ++ ++/** ++* @brief enable_row_irqs ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] *keypad ++* @return void ++*/ ++static void enable_row_irqs(struct matrix_keypad *keypad) ++{ ++ const struct matrix_keypad_platform_data *pdata = keypad->pdata; ++ int i; ++ ++ ++ for (i = 0; i < pdata->num_row_gpios; i++) ++ enable_irq(ak_gpio_to_irq(pdata->row_gpios[i])); ++} ++ ++/** ++* @brief disable_row_irqs ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] *keypad ++* @return void ++*/ ++static void disable_row_irqs(struct matrix_keypad *keypad) ++{ ++ const struct matrix_keypad_platform_data *pdata = keypad->pdata; ++ int i; ++ ++ ++ for (i = 0; i < pdata->num_row_gpios; i++) ++ disable_irq_nosync(ak_gpio_to_irq(pdata->row_gpios[i])); ++} ++ ++/** ++* @brief print the CODE of button pressed -- for debugging ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] code ++* @param[in] state ++* @return void ++*/ ++static void print_code(int code, int state) ++{ ++ switch(code) ++ { ++ case KEY_VOLUMEUP: ++ printk("KEY_VOLUMEUP "); break; ++ case KEY_RIGHT: ++ printk("KEY_RIGHT "); break; ++ case KEY_MENU: ++ printk("KEY_MENU "); break; ++ case KEY_UP: ++ printk("KEY_UP "); break; ++ case KEY_REPLY: ++ printk("KEY_CENTER "); break; ++ case KEY_DOWN: ++ printk("KEY_DOWN "); break; ++ case KEY_VOLUMEDOWN: ++ printk("KEY_VOLUMEDOWN "); break; ++ case KEY_LEFT: ++ printk("KEY_LEFT "); break; ++ case KEY_HOME: ++ printk("KEY_HOME "); break; ++ case KEY_BACK: ++ printk("KEY_BACK "); break; ++ } ++ printk("%s \n", state?"Down":"Up"); ++ ++ ++} ++ ++EXPORT_SYMBOL(print_code); ++ ++/** ++* @brief This gets the keys from keyboard and reports it to input ++* subsystem ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[out] *work ++* @return void ++*/ ++static void matrix_keypad_scan(struct work_struct *work) ++{ ++ struct matrix_keypad *keypad = ++ container_of(work, struct matrix_keypad, work.work); ++ struct input_dev *input_dev = keypad->input_dev; ++ const struct matrix_keypad_platform_data *pdata = keypad->pdata; ++ uint32_t new_state[MATRIX_MAX_COLS]; ++ int row, col, code, num_cols; ++ ++ memset(new_state, 0, sizeof(new_state)); ++ ++ ++ num_cols = pdata->num_col_gpios; ++ if (pdata->grounding) ++ num_cols++; ++ for (col = 0; col < num_cols; col++) ++ { ++ activate_other_col(pdata, col, true); ++ for (row = 0; row < pdata->num_row_gpios; row++) ++ { ++ new_state[col] |= ++ row_asserted(pdata, row) ? (1 << row) : 0; ++ ++ } ++ ++ activate_other_col(pdata, col, false); ++ } ++ ++ /* ++ * if the button pressed is connected to the grounding line, the state of ++ * the row input line connected to the button pressed will keep low level ++ * when we activate other lines ++ */ ++ if (pdata->grounding) ++ { ++ for (row=0; row < pdata->num_row_gpios; row++) ++ { ++ for (col=0; colrow_gpios[0]); ++ ++ num_cols = pdata->num_col_gpios; ++ if (pdata->grounding) ++ num_cols++; ++ for (col = 0; col < num_cols; col++) { ++ uint32_t bits_changed; ++ ++ bits_changed = (keypad->last_key_state[col] ^ new_state[col]); ++ ++ ++ if (bits_changed == 0) ++ continue; ++ ++ for (row = 0; row < pdata->num_row_gpios; row++) { ++ if ((bits_changed & (1 << row)) == 0) ++ continue; ++ ++ code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); ++ input_event(input_dev, EV_MSC, MSC_SCAN, code); ++ input_report_key(input_dev, ++ keypad->keycodes[code], ++ new_state[col] & (1 << row)); ++ //print_code(keypad->keycodes[code], new_state[col] & (1 << row)); ++ } ++ } ++ input_sync(input_dev); ++ ++ memcpy(keypad->last_key_state, new_state, sizeof(new_state)); ++ ++ ++ /* Enable IRQs again */ ++ spin_lock_irq(&keypad->lock); ++ keypad->scan_pending = false; ++ enable_row_irqs(keypad); ++ spin_unlock_irq(&keypad->lock); ++} ++ ++/** ++* @brief matrix keypad interrupt routine ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] irq ++* @param[out] *id ++* @return irqreturn_t ++*/ ++static irqreturn_t matrix_keypad_interrupt(int irq, void *id) ++{ ++ struct matrix_keypad *keypad = id; ++ unsigned long flags; ++ ++ ++ spin_lock_irqsave(&keypad->lock, flags); ++ ++ /* ++ * See if another IRQ beaten us to it and scheduled the ++ * scan already. In that case we should not try to ++ * disable IRQs again. ++ */ ++ if (unlikely(keypad->scan_pending || keypad->stopped)) ++ goto out; ++ ++ disable_row_irqs(keypad); ++ keypad->scan_pending = true; ++ schedule_delayed_work(&keypad->work, ++ msecs_to_jiffies(keypad->pdata->debounce_ms)); ++ ++out: ++ /* ++ * if disable_row_irqs(keypad); in init_matrix_gpio is not reached, then ++ * we need excuting it here. otherwise system can not start successfully if ++ * one key is pressed on OS starts. ++ */ ++ if (!keypad->start_close_int) ++ { ++ disable_row_irqs(keypad); ++ keypad->start_close_int = true; ++ } ++ spin_unlock_irqrestore(&keypad->lock, flags); ++ return IRQ_HANDLED; ++} ++ ++/** ++* @brief start the keypad ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] *dev ++* @return int ++*/ ++static int matrix_keypad_start(struct input_dev *dev) ++{ ++ struct matrix_keypad *keypad = input_get_drvdata(dev); ++ ++ keypad->stopped = false; ++ mb(); ++ ++ /* ++ * Schedule an immediate key scan to capture current key state; ++ * columns will be activated and IRQs be enabled after the scan. ++ */ ++ schedule_delayed_work(&keypad->work, 0); ++ ++ return 0; ++} ++ ++/** ++* @brief stop the keypad ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[out] *dev ++* @return void ++*/ ++static void matrix_keypad_stop(struct input_dev *dev) ++{ ++ struct matrix_keypad *keypad = input_get_drvdata(dev); ++ ++ keypad->stopped = true; ++ mb(); ++ flush_work(&keypad->work.work); ++ /* ++ * matrix_keypad_scan() will leave IRQs enabled; ++ * we should disable them now. ++ */ ++ disable_row_irqs(keypad); ++} ++ ++#ifdef CONFIG_PM ++/** ++* @brief matrix_keypad_suspend ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] *pdev ++* @param[in] state ++* @return int ++*/ ++static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct matrix_keypad *keypad = platform_get_drvdata(pdev); ++ const struct matrix_keypad_platform_data *pdata = keypad->pdata; ++ int i; ++ ++ matrix_keypad_stop(keypad->input_dev); ++ ++ if (device_may_wakeup(&pdev->dev)) ++ for (i = 0; i < pdata->num_row_gpios; i++) ++ enable_irq_wake(ak_gpio_to_irq(pdata->row_gpios[i])); ++ ++ return 0; ++} ++ ++/** ++* @brief matrix_keypad_resume ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] *pdev ++* @return int ++*/ ++static int matrix_keypad_resume(struct platform_device *pdev) ++{ ++ struct matrix_keypad *keypad = platform_get_drvdata(pdev); ++ const struct matrix_keypad_platform_data *pdata = keypad->pdata; ++ int i; ++ ++ if (device_may_wakeup(&pdev->dev)) ++ for (i = 0; i < pdata->num_row_gpios; i++) ++ disable_irq_wake(ak_gpio_to_irq(pdata->row_gpios[i])); ++ ++ matrix_keypad_start(keypad->input_dev); ++ ++ return 0; ++} ++#else ++#define matrix_keypad_suspend NULL ++#define matrix_keypad_resume NULL ++#endif ++ ++/** ++* @brief init_matrix_gpio ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] *pdev ++* @param[in] *keypad ++* @return int __devinit ++*/ ++static int __devinit init_matrix_gpio(struct platform_device *pdev, ++ struct matrix_keypad *keypad) ++{ ++ struct matrix_keypad_platform_data *pdata = keypad->pdata; ++ int i, err = -(EINVAL); ++ unsigned long flags; ++ ++ /* to fix the bug: can not start OS if one key is pressed on OS starts */ ++ keypad->start_close_int = false; ++ /* initialized strobe lines as outputs, activated */ ++ for (i = 0; i < pdata->num_col_gpios; i++) { ++ ++ err = ak_gpio_request(pdata->col_gpios[i], "matrix_kbd_col"); ++ if (err) { ++ dev_err(&pdev->dev, ++ "failed to request GPIO%d for COL%d\n", ++ pdata->col_gpios[i], i); ++ goto err_free_cols; ++ } ++ pdata->col_gpios_cfginfo.pin = pdata->col_gpios[i]; ++ ak_gpio_set(&(pdata->col_gpios_cfginfo)); ++ ++ } ++ ++ for (i = 0; i < pdata->num_row_gpios; i++) { ++ ++ err = ak_gpio_request(pdata->row_gpios[i], "matrix_kbd_row"); ++ if (err) { ++ dev_err(&pdev->dev, ++ "failed to request GPIO%d for ROW%d\n", ++ pdata->row_gpios[i], i); ++ goto err_free_rows; ++ } ++ pdata->row_gpios_cfginfo.pin = pdata->row_gpios[i]; ++ ak_gpio_set(&(pdata->row_gpios_cfginfo)); ++ ak_gpio_intcfg(pdata->row_gpios[i], AK_GPIO_INT_ENABLE); ++ } ++ ++ ++ for (i = 0; i < pdata->num_row_gpios; i++) { ++ err = request_irq(ak_gpio_to_irq(pdata->row_gpios[i]), ++ matrix_keypad_interrupt, ++ IRQF_DISABLED, ++ "matrix-keypad", keypad); ++ if (err) { ++ dev_err(&pdev->dev, ++ "Unable to acquire interrupt for GPIO line %i\n", ++ pdata->row_gpios[i]); ++ goto err_free_irqs; ++ } ++ } ++ ++ /* update input status, needed if keypad is connected to AW9523 */ ++ ak_gpio_getpin(pdata->row_gpios[0]); ++ ++ spin_lock_irqsave(&keypad->lock, flags); ++ /* initialized as disabled - enabled by input->open */ ++ if (!keypad->start_close_int) ++ { ++ disable_row_irqs(keypad); ++ keypad->start_close_int = true; ++ } ++ spin_unlock_irqrestore(&keypad->lock, flags); ++ return 0; ++ ++err_free_irqs: ++ while (--i >= 0) ++ free_irq(ak_gpio_to_irq(pdata->row_gpios[i]), keypad); ++ i = pdata->num_row_gpios; ++err_free_rows: ++ while (--i >= 0) ++ ak_gpio_free(pdata->row_gpios[i]); ++ i = pdata->num_col_gpios; ++err_free_cols: ++ while (--i >= 0) ++ ak_gpio_free(pdata->col_gpios[i]); ++ ++ return err; ++} ++ ++/** ++* @brief matrix_keypad_probe ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] *pdev ++* @return int __devinit ++*/ ++static int __devinit matrix_keypad_probe(struct platform_device *pdev) ++{ ++ struct matrix_keypad_platform_data *pdata; ++ const struct matrix_keymap_data *keymap_data; ++ struct matrix_keypad *keypad; ++ struct input_dev *input_dev; ++ unsigned short *keycodes; ++ unsigned int row_shift; ++ int err; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) { ++ dev_err(&pdev->dev, "no platform data defined\n"); ++ return -EINVAL; ++ } ++ ++ keymap_data = pdata->keymap_data; ++ if (!keymap_data) { ++ dev_err(&pdev->dev, "no keymap data defined\n"); ++ return -EINVAL; ++ } ++ ++ row_shift = get_count_order(pdata->num_col_gpios); ++ if (pdata->grounding) ++ row_shift++; ++ ++ keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL); ++ keycodes = kzalloc((pdata->num_row_gpios << (row_shift)) * ++ sizeof(*keycodes), ++ GFP_KERNEL); ++ input_dev = input_allocate_device(); ++ if (!keypad || !keycodes || !input_dev) { ++ err = -ENOMEM; ++ goto err_free_mem; ++ } ++ ++ keypad->input_dev = input_dev; ++ keypad->pdata = pdata; ++ keypad->keycodes = keycodes; ++ ++ keypad->row_shift = row_shift; ++ keypad->stopped = true; ++ INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan); ++ spin_lock_init(&keypad->lock); ++ ++ input_dev->name = pdev->name; ++ input_dev->id.bustype = BUS_HOST; ++ input_dev->dev.parent = &pdev->dev; ++ input_dev->evbit[0] = BIT_MASK(EV_KEY);// | BIT_MASK(EV_REP); ++ input_dev->open = matrix_keypad_start; ++ input_dev->close = matrix_keypad_stop; ++ ++ input_dev->keycode = keycodes; ++ input_dev->keycodesize = sizeof(*keycodes); ++ input_dev->keycodemax = pdata->num_row_gpios << (row_shift); ++ ++ matrix_keypad_build_keymap(keymap_data, row_shift, ++ input_dev->keycode, input_dev->keybit); ++ ++ input_set_capability(input_dev, EV_MSC, MSC_SCAN); ++ input_set_drvdata(input_dev, keypad); ++ ++ err = init_matrix_gpio(pdev, keypad); ++ if (err) ++ goto err_free_mem; ++ ++ err = input_register_device(keypad->input_dev); ++ if (err) ++ goto err_free_mem; ++ ++ device_init_wakeup(&pdev->dev, pdata->wakeup); ++ platform_set_drvdata(pdev, keypad); ++ ++ return 0; ++ ++err_free_mem: ++ input_free_device(input_dev); ++ kfree(keycodes); ++ kfree(keypad); ++ return err; ++} ++ ++/** ++* @brief called when the device is removed ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] *pdev ++* @return int __devexit ++*/ ++static int __devexit matrix_keypad_remove(struct platform_device *pdev) ++{ ++ struct matrix_keypad *keypad = platform_get_drvdata(pdev); ++ const struct matrix_keypad_platform_data *pdata = keypad->pdata; ++ int i; ++ ++ device_init_wakeup(&pdev->dev, 0); ++ ++ for (i = 0; i < pdata->num_row_gpios; i++) { ++ free_irq(ak_gpio_to_irq(pdata->row_gpios[i]), keypad); ++ ak_gpio_free(pdata->row_gpios[i]); ++ } ++ ++ for (i = 0; i < pdata->num_col_gpios; i++) ++ ak_gpio_free(pdata->col_gpios[i]); ++ ++ input_unregister_device(keypad->input_dev); ++ platform_set_drvdata(pdev, NULL); ++ kfree(keypad->keycodes); ++ kfree(keypad); ++ ++ return 0; ++} ++ ++static struct platform_driver matrix_keypad_driver = { ++ .probe = matrix_keypad_probe, ++ .remove = __devexit_p(matrix_keypad_remove), ++ .suspend = matrix_keypad_suspend, ++ .resume = matrix_keypad_resume, ++ .driver = { ++ .name = "matrix-keypad", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++/** ++* @brief matrix_keypad_init ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] void ++* @return int __init ++*/ ++static int __init matrix_keypad_init(void) ++{ ++ return platform_driver_register(&matrix_keypad_driver); ++} ++ ++/** ++* @brief matrix_keypad_exit ++* @author zhou wenyong ++* @date 2011-08-05 ++* @param[in] void ++* @return void __exit ++*/ ++static void __exit matrix_keypad_exit(void) ++{ ++ platform_driver_unregister(&matrix_keypad_driver); ++} ++ ++module_init(matrix_keypad_init); ++module_exit(matrix_keypad_exit); ++ ++MODULE_AUTHOR("Anyka "); ++MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:matrix-keypad"); ++ +diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c +new file mode 100644 +index 00000000..36208fe0 +--- /dev/null ++++ b/drivers/input/keyreset.c +@@ -0,0 +1,239 @@ ++/* drivers/input/keyreset.c ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++struct keyreset_state { ++ struct input_handler input_handler; ++ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; ++ unsigned long upbit[BITS_TO_LONGS(KEY_CNT)]; ++ unsigned long key[BITS_TO_LONGS(KEY_CNT)]; ++ spinlock_t lock; ++ int key_down_target; ++ int key_down; ++ int key_up; ++ int restart_disabled; ++ int (*reset_fn)(void); ++}; ++ ++int restart_requested; ++static void deferred_restart(struct work_struct *dummy) ++{ ++ restart_requested = 2; ++ sys_sync(); ++ restart_requested = 3; ++ kernel_restart(NULL); ++} ++static DECLARE_WORK(restart_work, deferred_restart); ++ ++static void keyreset_event(struct input_handle *handle, unsigned int type, ++ unsigned int code, int value) ++{ ++ unsigned long flags; ++ struct keyreset_state *state = handle->private; ++ ++ if (type != EV_KEY) ++ return; ++ ++ if (code >= KEY_MAX) ++ return; ++ ++ if (!test_bit(code, state->keybit)) ++ return; ++ ++ spin_lock_irqsave(&state->lock, flags); ++ if (!test_bit(code, state->key) == !value) ++ goto done; ++ __change_bit(code, state->key); ++ if (test_bit(code, state->upbit)) { ++ if (value) { ++ state->restart_disabled = 1; ++ state->key_up++; ++ } else ++ state->key_up--; ++ } else { ++ if (value) ++ state->key_down++; ++ else ++ state->key_down--; ++ } ++ if (state->key_down == 0 && state->key_up == 0) ++ state->restart_disabled = 0; ++ ++ pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value, ++ state->key_down, state->key_up, state->restart_disabled); ++ ++ if (value && !state->restart_disabled && ++ state->key_down == state->key_down_target) { ++ state->restart_disabled = 1; ++ if (restart_requested) ++ panic("keyboard reset failed, %d", restart_requested); ++ if (state->reset_fn) { ++ restart_requested = state->reset_fn(); ++ } else { ++ pr_info("keyboard reset\n"); ++ schedule_work(&restart_work); ++ restart_requested = 1; ++ } ++ } ++done: ++ spin_unlock_irqrestore(&state->lock, flags); ++} ++ ++static int keyreset_connect(struct input_handler *handler, ++ struct input_dev *dev, ++ const struct input_device_id *id) ++{ ++ int i; ++ int ret; ++ struct input_handle *handle; ++ struct keyreset_state *state = ++ container_of(handler, struct keyreset_state, input_handler); ++ ++ for (i = 0; i < KEY_MAX; i++) { ++ if (test_bit(i, state->keybit) && test_bit(i, dev->keybit)) ++ break; ++ } ++ if (i == KEY_MAX) ++ return -ENODEV; ++ ++ handle = kzalloc(sizeof(*handle), GFP_KERNEL); ++ if (!handle) ++ return -ENOMEM; ++ ++ handle->dev = dev; ++ handle->handler = handler; ++ handle->name = "keyreset"; ++ handle->private = state; ++ ++ ret = input_register_handle(handle); ++ if (ret) ++ goto err_input_register_handle; ++ ++ ret = input_open_device(handle); ++ if (ret) ++ goto err_input_open_device; ++ ++ pr_info("using input dev %s for key reset\n", dev->name); ++ ++ return 0; ++ ++err_input_open_device: ++ input_unregister_handle(handle); ++err_input_register_handle: ++ kfree(handle); ++ return ret; ++} ++ ++static void keyreset_disconnect(struct input_handle *handle) ++{ ++ input_close_device(handle); ++ input_unregister_handle(handle); ++ kfree(handle); ++} ++ ++static const struct input_device_id keyreset_ids[] = { ++ { ++ .flags = INPUT_DEVICE_ID_MATCH_EVBIT, ++ .evbit = { BIT_MASK(EV_KEY) }, ++ }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(input, keyreset_ids); ++ ++static int keyreset_probe(struct platform_device *pdev) ++{ ++ int ret; ++ int key, *keyp; ++ struct keyreset_state *state; ++ struct keyreset_platform_data *pdata = pdev->dev.platform_data; ++ ++ if (!pdata) ++ return -EINVAL; ++ ++ state = kzalloc(sizeof(*state), GFP_KERNEL); ++ if (!state) ++ return -ENOMEM; ++ ++ spin_lock_init(&state->lock); ++ keyp = pdata->keys_down; ++ while ((key = *keyp++)) { ++ if (key >= KEY_MAX) ++ continue; ++ state->key_down_target++; ++ __set_bit(key, state->keybit); ++ } ++ if (pdata->keys_up) { ++ keyp = pdata->keys_up; ++ while ((key = *keyp++)) { ++ if (key >= KEY_MAX) ++ continue; ++ __set_bit(key, state->keybit); ++ __set_bit(key, state->upbit); ++ } ++ } ++ ++ if (pdata->reset_fn) ++ state->reset_fn = pdata->reset_fn; ++ ++ state->input_handler.event = keyreset_event; ++ state->input_handler.connect = keyreset_connect; ++ state->input_handler.disconnect = keyreset_disconnect; ++ state->input_handler.name = KEYRESET_NAME; ++ state->input_handler.id_table = keyreset_ids; ++ ret = input_register_handler(&state->input_handler); ++ if (ret) { ++ kfree(state); ++ return ret; ++ } ++ platform_set_drvdata(pdev, state); ++ return 0; ++} ++ ++int keyreset_remove(struct platform_device *pdev) ++{ ++ struct keyreset_state *state = platform_get_drvdata(pdev); ++ input_unregister_handler(&state->input_handler); ++ kfree(state); ++ return 0; ++} ++ ++ ++struct platform_driver keyreset_driver = { ++ .driver.name = KEYRESET_NAME, ++ .probe = keyreset_probe, ++ .remove = keyreset_remove, ++}; ++ ++static int __init keyreset_init(void) ++{ ++ return platform_driver_register(&keyreset_driver); ++} ++ ++static void __exit keyreset_exit(void) ++{ ++ return platform_driver_unregister(&keyreset_driver); ++} ++ ++module_init(keyreset_init); ++module_exit(keyreset_exit); +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index 7faf4a7f..66550c2b 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -279,6 +279,17 @@ config INPUT_ATI_REMOTE2 + To compile this driver as a module, choose M here: the module will be + called ati_remote2. + ++config INPUT_KEYCHORD ++ tristate "Key chord input driver support" ++ help ++ Say Y here if you want to enable the key chord driver ++ accessible at /dev/keychord. This driver can be used ++ for receiving notifications when client specified key ++ combinations are pressed. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called keychord. ++ + config INPUT_KEYSPAN_REMOTE + tristate "Keyspan DMR USB remote control (EXPERIMENTAL)" + depends on EXPERIMENTAL +@@ -407,6 +418,11 @@ config INPUT_SGI_BTNS + To compile this driver as a module, choose M here: the + module will be called sgi_btns. + ++config INPUT_GPIO ++ tristate "GPIO driver support" ++ help ++ Say Y here if you want to support gpio based keys, wheels etc... ++ + config HP_SDC_RTC + tristate "HP SDC Real Time Clock" + depends on (GSC || HP300) && SERIO +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +index f55cdf49..f8cb5221 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -25,8 +25,10 @@ obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o + obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o + obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o + obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o ++obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o + obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o + obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o ++obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o + obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o + obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o + obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o +diff --git a/drivers/input/misc/gpio_axis.c b/drivers/input/misc/gpio_axis.c +new file mode 100644 +index 00000000..0acf4a57 +--- /dev/null ++++ b/drivers/input/misc/gpio_axis.c +@@ -0,0 +1,192 @@ ++/* drivers/input/misc/gpio_axis.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct gpio_axis_state { ++ struct gpio_event_input_devs *input_devs; ++ struct gpio_event_axis_info *info; ++ uint32_t pos; ++}; ++ ++uint16_t gpio_axis_4bit_gray_map_table[] = { ++ [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */ ++ [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */ ++ [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */ ++ [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */ ++ [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */ ++ [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */ ++ [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */ ++ [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */ ++}; ++uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in) ++{ ++ return gpio_axis_4bit_gray_map_table[in]; ++} ++ ++uint16_t gpio_axis_5bit_singletrack_map_table[] = { ++ [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */ ++ [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */ ++ [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */ ++ [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */ ++ [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */ ++ [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */ ++ [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */ ++ [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */ ++ [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */ ++ [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */ ++}; ++uint16_t gpio_axis_5bit_singletrack_map( ++ struct gpio_event_axis_info *info, uint16_t in) ++{ ++ return gpio_axis_5bit_singletrack_map_table[in]; ++} ++ ++static void gpio_event_update_axis(struct gpio_axis_state *as, int report) ++{ ++ struct gpio_event_axis_info *ai = as->info; ++ int i; ++ int change; ++ uint16_t state = 0; ++ uint16_t pos; ++ uint16_t old_pos = as->pos; ++ for (i = ai->count - 1; i >= 0; i--) ++ state = (state << 1) | gpio_get_value(ai->gpio[i]); ++ pos = ai->map(ai, state); ++ if (ai->flags & GPIOEAF_PRINT_RAW) ++ pr_info("axis %d-%d raw %x, pos %d -> %d\n", ++ ai->type, ai->code, state, old_pos, pos); ++ if (report && pos != old_pos) { ++ if (ai->type == EV_REL) { ++ change = (ai->decoded_size + pos - old_pos) % ++ ai->decoded_size; ++ if (change > ai->decoded_size / 2) ++ change -= ai->decoded_size; ++ if (change == ai->decoded_size / 2) { ++ if (ai->flags & GPIOEAF_PRINT_EVENT) ++ pr_info("axis %d-%d unknown direction, " ++ "pos %d -> %d\n", ai->type, ++ ai->code, old_pos, pos); ++ change = 0; /* no closest direction */ ++ } ++ if (ai->flags & GPIOEAF_PRINT_EVENT) ++ pr_info("axis %d-%d change %d\n", ++ ai->type, ai->code, change); ++ input_report_rel(as->input_devs->dev[ai->dev], ++ ai->code, change); ++ } else { ++ if (ai->flags & GPIOEAF_PRINT_EVENT) ++ pr_info("axis %d-%d now %d\n", ++ ai->type, ai->code, pos); ++ input_event(as->input_devs->dev[ai->dev], ++ ai->type, ai->code, pos); ++ } ++ input_sync(as->input_devs->dev[ai->dev]); ++ } ++ as->pos = pos; ++} ++ ++static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id) ++{ ++ struct gpio_axis_state *as = dev_id; ++ gpio_event_update_axis(as, 1); ++ return IRQ_HANDLED; ++} ++ ++int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func) ++{ ++ int ret; ++ int i; ++ int irq; ++ struct gpio_event_axis_info *ai; ++ struct gpio_axis_state *as; ++ ++ ai = container_of(info, struct gpio_event_axis_info, info); ++ if (func == GPIO_EVENT_FUNC_SUSPEND) { ++ for (i = 0; i < ai->count; i++) ++ disable_irq(gpio_to_irq(ai->gpio[i])); ++ return 0; ++ } ++ if (func == GPIO_EVENT_FUNC_RESUME) { ++ for (i = 0; i < ai->count; i++) ++ enable_irq(gpio_to_irq(ai->gpio[i])); ++ return 0; ++ } ++ ++ if (func == GPIO_EVENT_FUNC_INIT) { ++ *data = as = kmalloc(sizeof(*as), GFP_KERNEL); ++ if (as == NULL) { ++ ret = -ENOMEM; ++ goto err_alloc_axis_state_failed; ++ } ++ as->input_devs = input_devs; ++ as->info = ai; ++ if (ai->dev >= input_devs->count) { ++ pr_err("gpio_event_axis: bad device index %d >= %d " ++ "for %d:%d\n", ai->dev, input_devs->count, ++ ai->type, ai->code); ++ ret = -EINVAL; ++ goto err_bad_device_index; ++ } ++ ++ input_set_capability(input_devs->dev[ai->dev], ++ ai->type, ai->code); ++ if (ai->type == EV_ABS) { ++ input_set_abs_params(input_devs->dev[ai->dev], ai->code, ++ 0, ai->decoded_size - 1, 0, 0); ++ } ++ for (i = 0; i < ai->count; i++) { ++ ret = gpio_request(ai->gpio[i], "gpio_event_axis"); ++ if (ret < 0) ++ goto err_request_gpio_failed; ++ ret = gpio_direction_input(ai->gpio[i]); ++ if (ret < 0) ++ goto err_gpio_direction_input_failed; ++ ret = irq = gpio_to_irq(ai->gpio[i]); ++ if (ret < 0) ++ goto err_get_irq_num_failed; ++ ret = request_irq(irq, gpio_axis_irq_handler, ++ IRQF_TRIGGER_RISING | ++ IRQF_TRIGGER_FALLING, ++ "gpio_event_axis", as); ++ if (ret < 0) ++ goto err_request_irq_failed; ++ } ++ gpio_event_update_axis(as, 0); ++ return 0; ++ } ++ ++ ret = 0; ++ as = *data; ++ for (i = ai->count - 1; i >= 0; i--) { ++ free_irq(gpio_to_irq(ai->gpio[i]), as); ++err_request_irq_failed: ++err_get_irq_num_failed: ++err_gpio_direction_input_failed: ++ gpio_free(ai->gpio[i]); ++err_request_gpio_failed: ++ ; ++ } ++err_bad_device_index: ++ kfree(as); ++ *data = NULL; ++err_alloc_axis_state_failed: ++ return ret; ++} +diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c +new file mode 100644 +index 00000000..c7f396ab +--- /dev/null ++++ b/drivers/input/misc/gpio_event.c +@@ -0,0 +1,239 @@ ++/* drivers/input/misc/gpio_event.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct gpio_event { ++ struct gpio_event_input_devs *input_devs; ++ const struct gpio_event_platform_data *info; ++ void *state[0]; ++}; ++ ++static int gpio_input_event( ++ struct input_dev *dev, unsigned int type, unsigned int code, int value) ++{ ++ int i; ++ int devnr; ++ int ret = 0; ++ int tmp_ret; ++ struct gpio_event_info **ii; ++ struct gpio_event *ip = input_get_drvdata(dev); ++ ++ for (devnr = 0; devnr < ip->input_devs->count; devnr++) ++ if (ip->input_devs->dev[devnr] == dev) ++ break; ++ if (devnr == ip->input_devs->count) { ++ pr_err("gpio_input_event: unknown device %p\n", dev); ++ return -EIO; ++ } ++ ++ for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) { ++ if ((*ii)->event) { ++ tmp_ret = (*ii)->event(ip->input_devs, *ii, ++ &ip->state[i], ++ devnr, type, code, value); ++ if (tmp_ret) ++ ret = tmp_ret; ++ } ++ } ++ return ret; ++} ++ ++static int gpio_event_call_all_func(struct gpio_event *ip, int func) ++{ ++ int i; ++ int ret; ++ struct gpio_event_info **ii; ++ ++ if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) { ++ ii = ip->info->info; ++ for (i = 0; i < ip->info->info_count; i++, ii++) { ++ if ((*ii)->func == NULL) { ++ ret = -ENODEV; ++ pr_err("gpio_event_probe: Incomplete pdata, " ++ "no function\n"); ++ goto err_no_func; ++ } ++ if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend) ++ continue; ++ ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i], ++ func); ++ if (ret) { ++ pr_err("gpio_event_probe: function failed\n"); ++ goto err_func_failed; ++ } ++ } ++ return 0; ++ } ++ ++ ret = 0; ++ i = ip->info->info_count; ++ ii = ip->info->info + i; ++ while (i > 0) { ++ i--; ++ ii--; ++ if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend) ++ continue; ++ (*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1); ++err_func_failed: ++err_no_func: ++ ; ++ } ++ return ret; ++} ++ ++static void __maybe_unused gpio_event_suspend(struct gpio_event *ip) ++{ ++ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); ++ if (ip->info->power) ++ ip->info->power(ip->info, 0); ++} ++ ++static void __maybe_unused gpio_event_resume(struct gpio_event *ip) ++{ ++ if (ip->info->power) ++ ip->info->power(ip->info, 1); ++ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); ++} ++ ++static int gpio_event_probe(struct platform_device *pdev) ++{ ++ int err; ++ struct gpio_event *ip; ++ struct gpio_event_platform_data *event_info; ++ int dev_count = 1; ++ int i; ++ int registered = 0; ++ ++ event_info = pdev->dev.platform_data; ++ if (event_info == NULL) { ++ pr_err("gpio_event_probe: No pdata\n"); ++ return -ENODEV; ++ } ++ if ((!event_info->name && !event_info->names[0]) || ++ !event_info->info || !event_info->info_count) { ++ pr_err("gpio_event_probe: Incomplete pdata\n"); ++ return -ENODEV; ++ } ++ if (!event_info->name) ++ while (event_info->names[dev_count]) ++ dev_count++; ++ ip = kzalloc(sizeof(*ip) + ++ sizeof(ip->state[0]) * event_info->info_count + ++ sizeof(*ip->input_devs) + ++ sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL); ++ if (ip == NULL) { ++ err = -ENOMEM; ++ pr_err("gpio_event_probe: Failed to allocate private data\n"); ++ goto err_kp_alloc_failed; ++ } ++ ip->input_devs = (void*)&ip->state[event_info->info_count]; ++ platform_set_drvdata(pdev, ip); ++ ++ for (i = 0; i < dev_count; i++) { ++ struct input_dev *input_dev = input_allocate_device(); ++ if (input_dev == NULL) { ++ err = -ENOMEM; ++ pr_err("gpio_event_probe: " ++ "Failed to allocate input device\n"); ++ goto err_input_dev_alloc_failed; ++ } ++ input_set_drvdata(input_dev, ip); ++ input_dev->name = event_info->name ? ++ event_info->name : event_info->names[i]; ++ input_dev->event = gpio_input_event; ++ ip->input_devs->dev[i] = input_dev; ++ } ++ ip->input_devs->count = dev_count; ++ ip->info = event_info; ++ if (event_info->power) ++ ip->info->power(ip->info, 1); ++ ++ err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); ++ if (err) ++ goto err_call_all_func_failed; ++ ++ for (i = 0; i < dev_count; i++) { ++ err = input_register_device(ip->input_devs->dev[i]); ++ if (err) { ++ pr_err("gpio_event_probe: Unable to register %s " ++ "input device\n", ip->input_devs->dev[i]->name); ++ goto err_input_register_device_failed; ++ } ++ registered++; ++ } ++ ++ return 0; ++ ++err_input_register_device_failed: ++ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); ++err_call_all_func_failed: ++ if (event_info->power) ++ ip->info->power(ip->info, 0); ++ for (i = 0; i < registered; i++) ++ input_unregister_device(ip->input_devs->dev[i]); ++ for (i = dev_count - 1; i >= registered; i--) { ++ input_free_device(ip->input_devs->dev[i]); ++err_input_dev_alloc_failed: ++ ; ++ } ++ kfree(ip); ++err_kp_alloc_failed: ++ return err; ++} ++ ++static int gpio_event_remove(struct platform_device *pdev) ++{ ++ struct gpio_event *ip = platform_get_drvdata(pdev); ++ int i; ++ ++ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); ++ if (ip->info->power) ++ ip->info->power(ip->info, 0); ++ for (i = 0; i < ip->input_devs->count; i++) ++ input_unregister_device(ip->input_devs->dev[i]); ++ kfree(ip); ++ return 0; ++} ++ ++static struct platform_driver gpio_event_driver = { ++ .probe = gpio_event_probe, ++ .remove = gpio_event_remove, ++ .driver = { ++ .name = GPIO_EVENT_DEV_NAME, ++ }, ++}; ++ ++static int __devinit gpio_event_init(void) ++{ ++ return platform_driver_register(&gpio_event_driver); ++} ++ ++static void __exit gpio_event_exit(void) ++{ ++ platform_driver_unregister(&gpio_event_driver); ++} ++ ++module_init(gpio_event_init); ++module_exit(gpio_event_exit); ++ ++MODULE_DESCRIPTION("GPIO Event Driver"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c +new file mode 100644 +index 00000000..eefd0272 +--- /dev/null ++++ b/drivers/input/misc/gpio_input.c +@@ -0,0 +1,390 @@ ++/* drivers/input/misc/gpio_input.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++enum { ++ DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ ++ DEBOUNCE_PRESSED = BIT(1), ++ DEBOUNCE_NOTPRESSED = BIT(2), ++ DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */ ++ DEBOUNCE_POLL = BIT(4), /* Stable polling state */ ++ ++ DEBOUNCE_UNKNOWN = ++ DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED, ++}; ++ ++struct gpio_key_state { ++ struct gpio_input_state *ds; ++ uint8_t debounce; ++}; ++ ++struct gpio_input_state { ++ struct gpio_event_input_devs *input_devs; ++ const struct gpio_event_input_info *info; ++ struct hrtimer timer; ++ int use_irq; ++ int debounce_count; ++ spinlock_t irq_lock; ++ struct wakeup_source *ws; ++ struct gpio_key_state key_state[0]; ++}; ++ ++static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) ++{ ++ int i; ++ int pressed; ++ struct gpio_input_state *ds = ++ container_of(timer, struct gpio_input_state, timer); ++ unsigned gpio_flags = ds->info->flags; ++ unsigned npolarity; ++ int nkeys = ds->info->keymap_size; ++ const struct gpio_event_direct_entry *key_entry; ++ struct gpio_key_state *key_state; ++ unsigned long irqflags; ++ uint8_t debounce; ++ bool sync_needed; ++ ++#if 0 ++ key_entry = kp->keys_info->keymap; ++ key_state = kp->key_state; ++ for (i = 0; i < nkeys; i++, key_entry++, key_state++) ++ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, ++ gpio_read_detect_status(key_entry->gpio)); ++#endif ++ key_entry = ds->info->keymap; ++ key_state = ds->key_state; ++ sync_needed = false; ++ spin_lock_irqsave(&ds->irq_lock, irqflags); ++ for (i = 0; i < nkeys; i++, key_entry++, key_state++) { ++ debounce = key_state->debounce; ++ if (debounce & DEBOUNCE_WAIT_IRQ) ++ continue; ++ if (key_state->debounce & DEBOUNCE_UNSTABLE) { ++ debounce = key_state->debounce = DEBOUNCE_UNKNOWN; ++ enable_irq(gpio_to_irq(key_entry->gpio)); ++ if (gpio_flags & GPIOEDF_PRINT_KEY_UNSTABLE) ++ pr_info("gpio_keys_scan_keys: key %x-%x, %d " ++ "(%d) continue debounce\n", ++ ds->info->type, key_entry->code, ++ i, key_entry->gpio); ++ } ++ npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH); ++ pressed = gpio_get_value(key_entry->gpio) ^ npolarity; ++ if (debounce & DEBOUNCE_POLL) { ++ if (pressed == !(debounce & DEBOUNCE_PRESSED)) { ++ ds->debounce_count++; ++ key_state->debounce = DEBOUNCE_UNKNOWN; ++ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) ++ pr_info("gpio_keys_scan_keys: key %x-" ++ "%x, %d (%d) start debounce\n", ++ ds->info->type, key_entry->code, ++ i, key_entry->gpio); ++ } ++ continue; ++ } ++ if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) { ++ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) ++ pr_info("gpio_keys_scan_keys: key %x-%x, %d " ++ "(%d) debounce pressed 1\n", ++ ds->info->type, key_entry->code, ++ i, key_entry->gpio); ++ key_state->debounce = DEBOUNCE_PRESSED; ++ continue; ++ } ++ if (!pressed && (debounce & DEBOUNCE_PRESSED)) { ++ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) ++ pr_info("gpio_keys_scan_keys: key %x-%x, %d " ++ "(%d) debounce pressed 0\n", ++ ds->info->type, key_entry->code, ++ i, key_entry->gpio); ++ key_state->debounce = DEBOUNCE_NOTPRESSED; ++ continue; ++ } ++ /* key is stable */ ++ ds->debounce_count--; ++ if (ds->use_irq) ++ key_state->debounce |= DEBOUNCE_WAIT_IRQ; ++ else ++ key_state->debounce |= DEBOUNCE_POLL; ++ if (gpio_flags & GPIOEDF_PRINT_KEYS) ++ pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) " ++ "changed to %d\n", ds->info->type, ++ key_entry->code, i, key_entry->gpio, pressed); ++ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, ++ key_entry->code, pressed); ++ sync_needed = true; ++ } ++ if (sync_needed) { ++ for (i = 0; i < ds->input_devs->count; i++) ++ input_sync(ds->input_devs->dev[i]); ++ } ++ ++#if 0 ++ key_entry = kp->keys_info->keymap; ++ key_state = kp->key_state; ++ for (i = 0; i < nkeys; i++, key_entry++, key_state++) { ++ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, ++ gpio_read_detect_status(key_entry->gpio)); ++ } ++#endif ++ ++ if (ds->debounce_count) ++ hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL); ++ else if (!ds->use_irq) ++ hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL); ++ else ++ __pm_relax(ds->ws); ++ ++ spin_unlock_irqrestore(&ds->irq_lock, irqflags); ++ ++ return HRTIMER_NORESTART; ++} ++ ++static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) ++{ ++ struct gpio_key_state *ks = dev_id; ++ struct gpio_input_state *ds = ks->ds; ++ int keymap_index = ks - ds->key_state; ++ const struct gpio_event_direct_entry *key_entry; ++ unsigned long irqflags; ++ int pressed; ++ ++ if (!ds->use_irq) ++ return IRQ_HANDLED; ++ ++ key_entry = &ds->info->keymap[keymap_index]; ++ ++ if (ds->info->debounce_time.tv64) { ++ spin_lock_irqsave(&ds->irq_lock, irqflags); ++ if (ks->debounce & DEBOUNCE_WAIT_IRQ) { ++ ks->debounce = DEBOUNCE_UNKNOWN; ++ if (ds->debounce_count++ == 0) { ++ __pm_stay_awake(ds->ws); ++ hrtimer_start( ++ &ds->timer, ds->info->debounce_time, ++ HRTIMER_MODE_REL); ++ } ++ if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE) ++ pr_info("gpio_event_input_irq_handler: " ++ "key %x-%x, %d (%d) start debounce\n", ++ ds->info->type, key_entry->code, ++ keymap_index, key_entry->gpio); ++ } else { ++ disable_irq_nosync(irq); ++ ks->debounce = DEBOUNCE_UNSTABLE; ++ } ++ spin_unlock_irqrestore(&ds->irq_lock, irqflags); ++ } else { ++ pressed = gpio_get_value(key_entry->gpio) ^ ++ !(ds->info->flags & GPIOEDF_ACTIVE_HIGH); ++ if (ds->info->flags & GPIOEDF_PRINT_KEYS) ++ pr_info("gpio_event_input_irq_handler: key %x-%x, %d " ++ "(%d) changed to %d\n", ++ ds->info->type, key_entry->code, keymap_index, ++ key_entry->gpio, pressed); ++ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, ++ key_entry->code, pressed); ++ input_sync(ds->input_devs->dev[key_entry->dev]); ++ } ++ return IRQ_HANDLED; ++} ++ ++static int gpio_event_input_request_irqs(struct gpio_input_state *ds) ++{ ++ int i; ++ int err; ++ unsigned int irq; ++ unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ ++ for (i = 0; i < ds->info->keymap_size; i++) { ++ err = irq = gpio_to_irq(ds->info->keymap[i].gpio); ++ if (err < 0) ++ goto err_gpio_get_irq_num_failed; ++ err = request_irq(irq, gpio_event_input_irq_handler, ++ req_flags, "gpio_keys", &ds->key_state[i]); ++ if (err) { ++ pr_err("gpio_event_input_request_irqs: request_irq " ++ "failed for input %d, irq %d\n", ++ ds->info->keymap[i].gpio, irq); ++ goto err_request_irq_failed; ++ } ++ if (ds->info->info.no_suspend) { ++ err = enable_irq_wake(irq); ++ if (err) { ++ pr_err("gpio_event_input_request_irqs: " ++ "enable_irq_wake failed for input %d, " ++ "irq %d\n", ++ ds->info->keymap[i].gpio, irq); ++ goto err_enable_irq_wake_failed; ++ } ++ } ++ } ++ return 0; ++ ++ for (i = ds->info->keymap_size - 1; i >= 0; i--) { ++ irq = gpio_to_irq(ds->info->keymap[i].gpio); ++ if (ds->info->info.no_suspend) ++ disable_irq_wake(irq); ++err_enable_irq_wake_failed: ++ free_irq(irq, &ds->key_state[i]); ++err_request_irq_failed: ++err_gpio_get_irq_num_failed: ++ ; ++ } ++ return err; ++} ++ ++int gpio_event_input_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func) ++{ ++ int ret; ++ int i; ++ unsigned long irqflags; ++ struct gpio_event_input_info *di; ++ struct gpio_input_state *ds = *data; ++ char *wlname; ++ ++ di = container_of(info, struct gpio_event_input_info, info); ++ ++ if (func == GPIO_EVENT_FUNC_SUSPEND) { ++ if (ds->use_irq) ++ for (i = 0; i < di->keymap_size; i++) ++ disable_irq(gpio_to_irq(di->keymap[i].gpio)); ++ hrtimer_cancel(&ds->timer); ++ return 0; ++ } ++ if (func == GPIO_EVENT_FUNC_RESUME) { ++ spin_lock_irqsave(&ds->irq_lock, irqflags); ++ if (ds->use_irq) ++ for (i = 0; i < di->keymap_size; i++) ++ enable_irq(gpio_to_irq(di->keymap[i].gpio)); ++ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); ++ spin_unlock_irqrestore(&ds->irq_lock, irqflags); ++ return 0; ++ } ++ ++ if (func == GPIO_EVENT_FUNC_INIT) { ++ if (ktime_to_ns(di->poll_time) <= 0) ++ di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC); ++ ++ *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) * ++ di->keymap_size, GFP_KERNEL); ++ if (ds == NULL) { ++ ret = -ENOMEM; ++ pr_err("gpio_event_input_func: " ++ "Failed to allocate private data\n"); ++ goto err_ds_alloc_failed; ++ } ++ ds->debounce_count = di->keymap_size; ++ ds->input_devs = input_devs; ++ ds->info = di; ++ wlname = kasprintf(GFP_KERNEL, "gpio_input:%s%s", ++ input_devs->dev[0]->name, ++ (input_devs->count > 1) ? "..." : ""); ++ ++ ds->ws = wakeup_source_register(wlname); ++ kfree(wlname); ++ if (!ds->ws) { ++ ret = -ENOMEM; ++ pr_err("gpio_event_input_func: " ++ "Failed to allocate wakeup source\n"); ++ goto err_ws_failed; ++ } ++ ++ spin_lock_init(&ds->irq_lock); ++ ++ for (i = 0; i < di->keymap_size; i++) { ++ int dev = di->keymap[i].dev; ++ if (dev >= input_devs->count) { ++ pr_err("gpio_event_input_func: bad device " ++ "index %d >= %d for key code %d\n", ++ dev, input_devs->count, ++ di->keymap[i].code); ++ ret = -EINVAL; ++ goto err_bad_keymap; ++ } ++ input_set_capability(input_devs->dev[dev], di->type, ++ di->keymap[i].code); ++ ds->key_state[i].ds = ds; ++ ds->key_state[i].debounce = DEBOUNCE_UNKNOWN; ++ } ++ ++ for (i = 0; i < di->keymap_size; i++) { ++ ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in"); ++ if (ret) { ++ pr_err("gpio_event_input_func: gpio_request " ++ "failed for %d\n", di->keymap[i].gpio); ++ goto err_gpio_request_failed; ++ } ++ ret = gpio_direction_input(di->keymap[i].gpio); ++ if (ret) { ++ pr_err("gpio_event_input_func: " ++ "gpio_direction_input failed for %d\n", ++ di->keymap[i].gpio); ++ goto err_gpio_configure_failed; ++ } ++ } ++ ++ ret = gpio_event_input_request_irqs(ds); ++ ++ spin_lock_irqsave(&ds->irq_lock, irqflags); ++ ds->use_irq = ret == 0; ++ ++ pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s " ++ "mode\n", input_devs->dev[0]->name, ++ (input_devs->count > 1) ? "..." : "", ++ ret == 0 ? "interrupt" : "polling"); ++ ++ hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ ds->timer.function = gpio_event_input_timer_func; ++ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); ++ spin_unlock_irqrestore(&ds->irq_lock, irqflags); ++ return 0; ++ } ++ ++ ret = 0; ++ spin_lock_irqsave(&ds->irq_lock, irqflags); ++ hrtimer_cancel(&ds->timer); ++ if (ds->use_irq) { ++ for (i = di->keymap_size - 1; i >= 0; i--) { ++ int irq = gpio_to_irq(di->keymap[i].gpio); ++ if (ds->info->info.no_suspend) ++ disable_irq_wake(irq); ++ free_irq(irq, &ds->key_state[i]); ++ } ++ } ++ spin_unlock_irqrestore(&ds->irq_lock, irqflags); ++ ++ for (i = di->keymap_size - 1; i >= 0; i--) { ++err_gpio_configure_failed: ++ gpio_free(di->keymap[i].gpio); ++err_gpio_request_failed: ++ ; ++ } ++err_bad_keymap: ++ wakeup_source_unregister(ds->ws); ++err_ws_failed: ++ kfree(ds); ++err_ds_alloc_failed: ++ return ret; ++} +diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c +new file mode 100644 +index 00000000..eaa9e89d +--- /dev/null ++++ b/drivers/input/misc/gpio_matrix.c +@@ -0,0 +1,441 @@ ++/* drivers/input/misc/gpio_matrix.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct gpio_kp { ++ struct gpio_event_input_devs *input_devs; ++ struct gpio_event_matrix_info *keypad_info; ++ struct hrtimer timer; ++ struct wake_lock wake_lock; ++ int current_output; ++ unsigned int use_irq:1; ++ unsigned int key_state_changed:1; ++ unsigned int last_key_state_changed:1; ++ unsigned int some_keys_pressed:2; ++ unsigned int disabled_irq:1; ++ unsigned long keys_pressed[0]; ++}; ++ ++static void clear_phantom_key(struct gpio_kp *kp, int out, int in) ++{ ++ struct gpio_event_matrix_info *mi = kp->keypad_info; ++ int key_index = out * mi->ninputs + in; ++ unsigned short keyentry = mi->keymap[key_index]; ++ unsigned short keycode = keyentry & MATRIX_KEY_MASK; ++ unsigned short dev = keyentry >> MATRIX_CODE_BITS; ++ ++ if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) { ++ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) ++ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " ++ "cleared\n", keycode, out, in, ++ mi->output_gpios[out], mi->input_gpios[in]); ++ __clear_bit(key_index, kp->keys_pressed); ++ } else { ++ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) ++ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " ++ "not cleared\n", keycode, out, in, ++ mi->output_gpios[out], mi->input_gpios[in]); ++ } ++} ++ ++static int restore_keys_for_input(struct gpio_kp *kp, int out, int in) ++{ ++ int rv = 0; ++ int key_index; ++ ++ key_index = out * kp->keypad_info->ninputs + in; ++ while (out < kp->keypad_info->noutputs) { ++ if (test_bit(key_index, kp->keys_pressed)) { ++ rv = 1; ++ clear_phantom_key(kp, out, in); ++ } ++ key_index += kp->keypad_info->ninputs; ++ out++; ++ } ++ return rv; ++} ++ ++static void remove_phantom_keys(struct gpio_kp *kp) ++{ ++ int out, in, inp; ++ int key_index; ++ ++ if (kp->some_keys_pressed < 3) ++ return; ++ ++ for (out = 0; out < kp->keypad_info->noutputs; out++) { ++ inp = -1; ++ key_index = out * kp->keypad_info->ninputs; ++ for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) { ++ if (test_bit(key_index, kp->keys_pressed)) { ++ if (inp == -1) { ++ inp = in; ++ continue; ++ } ++ if (inp >= 0) { ++ if (!restore_keys_for_input(kp, out + 1, ++ inp)) ++ break; ++ clear_phantom_key(kp, out, inp); ++ inp = -2; ++ } ++ restore_keys_for_input(kp, out, in); ++ } ++ } ++ } ++} ++ ++static void report_key(struct gpio_kp *kp, int key_index, int out, int in) ++{ ++ struct gpio_event_matrix_info *mi = kp->keypad_info; ++ int pressed = test_bit(key_index, kp->keys_pressed); ++ unsigned short keyentry = mi->keymap[key_index]; ++ unsigned short keycode = keyentry & MATRIX_KEY_MASK; ++ unsigned short dev = keyentry >> MATRIX_CODE_BITS; ++ ++ if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) { ++ if (keycode == KEY_RESERVED) { ++ if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS) ++ pr_info("gpiomatrix: unmapped key, %d-%d " ++ "(%d-%d) changed to %d\n", ++ out, in, mi->output_gpios[out], ++ mi->input_gpios[in], pressed); ++ } else { ++ if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS) ++ pr_info("gpiomatrix: key %x, %d-%d (%d-%d) " ++ "changed to %d\n", keycode, ++ out, in, mi->output_gpios[out], ++ mi->input_gpios[in], pressed); ++ input_report_key(kp->input_devs->dev[dev], keycode, pressed); ++ } ++ } ++} ++ ++static void report_sync(struct gpio_kp *kp) ++{ ++ int i; ++ ++ for (i = 0; i < kp->input_devs->count; i++) ++ input_sync(kp->input_devs->dev[i]); ++} ++ ++static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) ++{ ++ int out, in; ++ int key_index; ++ int gpio; ++ struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer); ++ struct gpio_event_matrix_info *mi = kp->keypad_info; ++ unsigned gpio_keypad_flags = mi->flags; ++ unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH); ++ ++ out = kp->current_output; ++ if (out == mi->noutputs) { ++ out = 0; ++ kp->last_key_state_changed = kp->key_state_changed; ++ kp->key_state_changed = 0; ++ kp->some_keys_pressed = 0; ++ } else { ++ key_index = out * mi->ninputs; ++ for (in = 0; in < mi->ninputs; in++, key_index++) { ++ gpio = mi->input_gpios[in]; ++ if (gpio_get_value(gpio) ^ !polarity) { ++ if (kp->some_keys_pressed < 3) ++ kp->some_keys_pressed++; ++ kp->key_state_changed |= !__test_and_set_bit( ++ key_index, kp->keys_pressed); ++ } else ++ kp->key_state_changed |= __test_and_clear_bit( ++ key_index, kp->keys_pressed); ++ } ++ gpio = mi->output_gpios[out]; ++ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) ++ gpio_set_value(gpio, !polarity); ++ else ++ gpio_direction_input(gpio); ++ out++; ++ } ++ kp->current_output = out; ++ if (out < mi->noutputs) { ++ gpio = mi->output_gpios[out]; ++ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) ++ gpio_set_value(gpio, polarity); ++ else ++ gpio_direction_output(gpio, polarity); ++ hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL); ++ return HRTIMER_NORESTART; ++ } ++ if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) { ++ if (kp->key_state_changed) { ++ hrtimer_start(&kp->timer, mi->debounce_delay, ++ HRTIMER_MODE_REL); ++ return HRTIMER_NORESTART; ++ } ++ kp->key_state_changed = kp->last_key_state_changed; ++ } ++ if (kp->key_state_changed) { ++ if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS) ++ remove_phantom_keys(kp); ++ key_index = 0; ++ for (out = 0; out < mi->noutputs; out++) ++ for (in = 0; in < mi->ninputs; in++, key_index++) ++ report_key(kp, key_index, out, in); ++ report_sync(kp); ++ } ++ if (!kp->use_irq || kp->some_keys_pressed) { ++ hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL); ++ return HRTIMER_NORESTART; ++ } ++ ++ /* No keys are pressed, reenable interrupt */ ++ for (out = 0; out < mi->noutputs; out++) { ++ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) ++ gpio_set_value(mi->output_gpios[out], polarity); ++ else ++ gpio_direction_output(mi->output_gpios[out], polarity); ++ } ++ for (in = 0; in < mi->ninputs; in++) ++ enable_irq(gpio_to_irq(mi->input_gpios[in])); ++ wake_unlock(&kp->wake_lock); ++ return HRTIMER_NORESTART; ++} ++ ++static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) ++{ ++ int i; ++ struct gpio_kp *kp = dev_id; ++ struct gpio_event_matrix_info *mi = kp->keypad_info; ++ unsigned gpio_keypad_flags = mi->flags; ++ ++ if (!kp->use_irq) { ++ /* ignore interrupt while registering the handler */ ++ kp->disabled_irq = 1; ++ disable_irq_nosync(irq_in); ++ return IRQ_HANDLED; ++ } ++ ++ for (i = 0; i < mi->ninputs; i++) ++ disable_irq_nosync(gpio_to_irq(mi->input_gpios[i])); ++ for (i = 0; i < mi->noutputs; i++) { ++ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) ++ gpio_set_value(mi->output_gpios[i], ++ !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH)); ++ else ++ gpio_direction_input(mi->output_gpios[i]); ++ } ++ wake_lock(&kp->wake_lock); ++ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); ++ return IRQ_HANDLED; ++} ++ ++static int gpio_keypad_request_irqs(struct gpio_kp *kp) ++{ ++ int i; ++ int err; ++ unsigned int irq; ++ unsigned long request_flags; ++ struct gpio_event_matrix_info *mi = kp->keypad_info; ++ ++ switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) { ++ default: ++ request_flags = IRQF_TRIGGER_FALLING; ++ break; ++ case GPIOKPF_ACTIVE_HIGH: ++ request_flags = IRQF_TRIGGER_RISING; ++ break; ++ case GPIOKPF_LEVEL_TRIGGERED_IRQ: ++ request_flags = IRQF_TRIGGER_LOW; ++ break; ++ case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH: ++ request_flags = IRQF_TRIGGER_HIGH; ++ break; ++ } ++ ++ for (i = 0; i < mi->ninputs; i++) { ++ err = irq = gpio_to_irq(mi->input_gpios[i]); ++ if (err < 0) ++ goto err_gpio_get_irq_num_failed; ++ err = request_irq(irq, gpio_keypad_irq_handler, request_flags, ++ "gpio_kp", kp); ++ if (err) { ++ pr_err("gpiomatrix: request_irq failed for input %d, " ++ "irq %d\n", mi->input_gpios[i], irq); ++ goto err_request_irq_failed; ++ } ++ err = enable_irq_wake(irq); ++ if (err) { ++ pr_err("gpiomatrix: set_irq_wake failed for input %d, " ++ "irq %d\n", mi->input_gpios[i], irq); ++ } ++ disable_irq(irq); ++ if (kp->disabled_irq) { ++ kp->disabled_irq = 0; ++ enable_irq(irq); ++ } ++ } ++ return 0; ++ ++ for (i = mi->noutputs - 1; i >= 0; i--) { ++ free_irq(gpio_to_irq(mi->input_gpios[i]), kp); ++err_request_irq_failed: ++err_gpio_get_irq_num_failed: ++ ; ++ } ++ return err; ++} ++ ++int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func) ++{ ++ int i; ++ int err; ++ int key_count; ++ struct gpio_kp *kp; ++ struct gpio_event_matrix_info *mi; ++ ++ mi = container_of(info, struct gpio_event_matrix_info, info); ++ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) { ++ /* TODO: disable scanning */ ++ return 0; ++ } ++ ++ if (func == GPIO_EVENT_FUNC_INIT) { ++ if (mi->keymap == NULL || ++ mi->input_gpios == NULL || ++ mi->output_gpios == NULL) { ++ err = -ENODEV; ++ pr_err("gpiomatrix: Incomplete pdata\n"); ++ goto err_invalid_platform_data; ++ } ++ key_count = mi->ninputs * mi->noutputs; ++ ++ *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) * ++ BITS_TO_LONGS(key_count), GFP_KERNEL); ++ if (kp == NULL) { ++ err = -ENOMEM; ++ pr_err("gpiomatrix: Failed to allocate private data\n"); ++ goto err_kp_alloc_failed; ++ } ++ kp->input_devs = input_devs; ++ kp->keypad_info = mi; ++ for (i = 0; i < key_count; i++) { ++ unsigned short keyentry = mi->keymap[i]; ++ unsigned short keycode = keyentry & MATRIX_KEY_MASK; ++ unsigned short dev = keyentry >> MATRIX_CODE_BITS; ++ if (dev >= input_devs->count) { ++ pr_err("gpiomatrix: bad device index %d >= " ++ "%d for key code %d\n", ++ dev, input_devs->count, keycode); ++ err = -EINVAL; ++ goto err_bad_keymap; ++ } ++ if (keycode && keycode <= KEY_MAX) ++ input_set_capability(input_devs->dev[dev], ++ EV_KEY, keycode); ++ } ++ ++ for (i = 0; i < mi->noutputs; i++) { ++ err = gpio_request(mi->output_gpios[i], "gpio_kp_out"); ++ if (err) { ++ pr_err("gpiomatrix: gpio_request failed for " ++ "output %d\n", mi->output_gpios[i]); ++ goto err_request_output_gpio_failed; ++ } ++ if (gpio_cansleep(mi->output_gpios[i])) { ++ pr_err("gpiomatrix: unsupported output gpio %d," ++ " can sleep\n", mi->output_gpios[i]); ++ err = -EINVAL; ++ goto err_output_gpio_configure_failed; ++ } ++ if (mi->flags & GPIOKPF_DRIVE_INACTIVE) ++ err = gpio_direction_output(mi->output_gpios[i], ++ !(mi->flags & GPIOKPF_ACTIVE_HIGH)); ++ else ++ err = gpio_direction_input(mi->output_gpios[i]); ++ if (err) { ++ pr_err("gpiomatrix: gpio_configure failed for " ++ "output %d\n", mi->output_gpios[i]); ++ goto err_output_gpio_configure_failed; ++ } ++ } ++ for (i = 0; i < mi->ninputs; i++) { ++ err = gpio_request(mi->input_gpios[i], "gpio_kp_in"); ++ if (err) { ++ pr_err("gpiomatrix: gpio_request failed for " ++ "input %d\n", mi->input_gpios[i]); ++ goto err_request_input_gpio_failed; ++ } ++ err = gpio_direction_input(mi->input_gpios[i]); ++ if (err) { ++ pr_err("gpiomatrix: gpio_direction_input failed" ++ " for input %d\n", mi->input_gpios[i]); ++ goto err_gpio_direction_input_failed; ++ } ++ } ++ kp->current_output = mi->noutputs; ++ kp->key_state_changed = 1; ++ ++ hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ kp->timer.function = gpio_keypad_timer_func; ++ wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp"); ++ err = gpio_keypad_request_irqs(kp); ++ kp->use_irq = err == 0; ++ ++ pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for " ++ "%s%s in %s mode\n", input_devs->dev[0]->name, ++ (input_devs->count > 1) ? "..." : "", ++ kp->use_irq ? "interrupt" : "polling"); ++ ++ if (kp->use_irq) ++ wake_lock(&kp->wake_lock); ++ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); ++ ++ return 0; ++ } ++ ++ err = 0; ++ kp = *data; ++ ++ if (kp->use_irq) ++ for (i = mi->noutputs - 1; i >= 0; i--) ++ free_irq(gpio_to_irq(mi->input_gpios[i]), kp); ++ ++ hrtimer_cancel(&kp->timer); ++ wake_lock_destroy(&kp->wake_lock); ++ for (i = mi->noutputs - 1; i >= 0; i--) { ++err_gpio_direction_input_failed: ++ gpio_free(mi->input_gpios[i]); ++err_request_input_gpio_failed: ++ ; ++ } ++ for (i = mi->noutputs - 1; i >= 0; i--) { ++err_output_gpio_configure_failed: ++ gpio_free(mi->output_gpios[i]); ++err_request_output_gpio_failed: ++ ; ++ } ++err_bad_keymap: ++ kfree(kp); ++err_kp_alloc_failed: ++err_invalid_platform_data: ++ return err; ++} +diff --git a/drivers/input/misc/gpio_output.c b/drivers/input/misc/gpio_output.c +new file mode 100644 +index 00000000..2aac2fad +--- /dev/null ++++ b/drivers/input/misc/gpio_output.c +@@ -0,0 +1,97 @@ ++/* drivers/input/misc/gpio_output.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++ ++int gpio_event_output_event( ++ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, ++ void **data, unsigned int dev, unsigned int type, ++ unsigned int code, int value) ++{ ++ int i; ++ struct gpio_event_output_info *oi; ++ oi = container_of(info, struct gpio_event_output_info, info); ++ if (type != oi->type) ++ return 0; ++ if (!(oi->flags & GPIOEDF_ACTIVE_HIGH)) ++ value = !value; ++ for (i = 0; i < oi->keymap_size; i++) ++ if (dev == oi->keymap[i].dev && code == oi->keymap[i].code) ++ gpio_set_value(oi->keymap[i].gpio, value); ++ return 0; ++} ++ ++int gpio_event_output_func( ++ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, ++ void **data, int func) ++{ ++ int ret; ++ int i; ++ struct gpio_event_output_info *oi; ++ oi = container_of(info, struct gpio_event_output_info, info); ++ ++ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) ++ return 0; ++ ++ if (func == GPIO_EVENT_FUNC_INIT) { ++ int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH); ++ ++ for (i = 0; i < oi->keymap_size; i++) { ++ int dev = oi->keymap[i].dev; ++ if (dev >= input_devs->count) { ++ pr_err("gpio_event_output_func: bad device " ++ "index %d >= %d for key code %d\n", ++ dev, input_devs->count, ++ oi->keymap[i].code); ++ ret = -EINVAL; ++ goto err_bad_keymap; ++ } ++ input_set_capability(input_devs->dev[dev], oi->type, ++ oi->keymap[i].code); ++ } ++ ++ for (i = 0; i < oi->keymap_size; i++) { ++ ret = gpio_request(oi->keymap[i].gpio, ++ "gpio_event_output"); ++ if (ret) { ++ pr_err("gpio_event_output_func: gpio_request " ++ "failed for %d\n", oi->keymap[i].gpio); ++ goto err_gpio_request_failed; ++ } ++ ret = gpio_direction_output(oi->keymap[i].gpio, ++ output_level); ++ if (ret) { ++ pr_err("gpio_event_output_func: " ++ "gpio_direction_output failed for %d\n", ++ oi->keymap[i].gpio); ++ goto err_gpio_direction_output_failed; ++ } ++ } ++ return 0; ++ } ++ ++ ret = 0; ++ for (i = oi->keymap_size - 1; i >= 0; i--) { ++err_gpio_direction_output_failed: ++ gpio_free(oi->keymap[i].gpio); ++err_gpio_request_failed: ++ ; ++ } ++err_bad_keymap: ++ return ret; ++} ++ +diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c +new file mode 100644 +index 00000000..a5ea27ad +--- /dev/null ++++ b/drivers/input/misc/keychord.c +@@ -0,0 +1,391 @@ ++/* ++ * drivers/input/misc/keychord.c ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define KEYCHORD_NAME "keychord" ++#define BUFFER_SIZE 16 ++ ++MODULE_AUTHOR("Mike Lockwood "); ++MODULE_DESCRIPTION("Key chord input driver"); ++MODULE_SUPPORTED_DEVICE("keychord"); ++MODULE_LICENSE("GPL"); ++ ++#define NEXT_KEYCHORD(kc) ((struct input_keychord *) \ ++ ((char *)kc + sizeof(struct input_keychord) + \ ++ kc->count * sizeof(kc->keycodes[0]))) ++ ++struct keychord_device { ++ struct input_handler input_handler; ++ int registered; ++ ++ /* list of keychords to monitor */ ++ struct input_keychord *keychords; ++ int keychord_count; ++ ++ /* bitmask of keys contained in our keychords */ ++ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; ++ /* current state of the keys */ ++ unsigned long keystate[BITS_TO_LONGS(KEY_CNT)]; ++ /* number of keys that are currently pressed */ ++ int key_down; ++ ++ /* second input_device_id is needed for null termination */ ++ struct input_device_id device_ids[2]; ++ ++ spinlock_t lock; ++ wait_queue_head_t waitq; ++ unsigned char head; ++ unsigned char tail; ++ __u16 buff[BUFFER_SIZE]; ++}; ++ ++static int check_keychord(struct keychord_device *kdev, ++ struct input_keychord *keychord) ++{ ++ int i; ++ ++ if (keychord->count != kdev->key_down) ++ return 0; ++ ++ for (i = 0; i < keychord->count; i++) { ++ if (!test_bit(keychord->keycodes[i], kdev->keystate)) ++ return 0; ++ } ++ ++ /* we have a match */ ++ return 1; ++} ++ ++static void keychord_event(struct input_handle *handle, unsigned int type, ++ unsigned int code, int value) ++{ ++ struct keychord_device *kdev = handle->private; ++ struct input_keychord *keychord; ++ unsigned long flags; ++ int i, got_chord = 0; ++ ++ if (type != EV_KEY || code >= KEY_MAX) ++ return; ++ ++ spin_lock_irqsave(&kdev->lock, flags); ++ /* do nothing if key state did not change */ ++ if (!test_bit(code, kdev->keystate) == !value) ++ goto done; ++ __change_bit(code, kdev->keystate); ++ if (value) ++ kdev->key_down++; ++ else ++ kdev->key_down--; ++ ++ /* don't notify on key up */ ++ if (!value) ++ goto done; ++ /* ignore this event if it is not one of the keys we are monitoring */ ++ if (!test_bit(code, kdev->keybit)) ++ goto done; ++ ++ keychord = kdev->keychords; ++ if (!keychord) ++ goto done; ++ ++ /* check to see if the keyboard state matches any keychords */ ++ for (i = 0; i < kdev->keychord_count; i++) { ++ if (check_keychord(kdev, keychord)) { ++ kdev->buff[kdev->head] = keychord->id; ++ kdev->head = (kdev->head + 1) % BUFFER_SIZE; ++ got_chord = 1; ++ break; ++ } ++ /* skip to next keychord */ ++ keychord = NEXT_KEYCHORD(keychord); ++ } ++ ++done: ++ spin_unlock_irqrestore(&kdev->lock, flags); ++ ++ if (got_chord) { ++ pr_info("keychord: got keychord id %d. Any tasks: %d\n", ++ keychord->id, ++ !list_empty_careful(&kdev->waitq.task_list)); ++ wake_up_interruptible(&kdev->waitq); ++ } ++} ++ ++static int keychord_connect(struct input_handler *handler, ++ struct input_dev *dev, ++ const struct input_device_id *id) ++{ ++ int i, ret; ++ struct input_handle *handle; ++ struct keychord_device *kdev = ++ container_of(handler, struct keychord_device, input_handler); ++ ++ /* ++ * ignore this input device if it does not contain any keycodes ++ * that we are monitoring ++ */ ++ for (i = 0; i < KEY_MAX; i++) { ++ if (test_bit(i, kdev->keybit) && test_bit(i, dev->keybit)) ++ break; ++ } ++ if (i == KEY_MAX) ++ return -ENODEV; ++ ++ handle = kzalloc(sizeof(*handle), GFP_KERNEL); ++ if (!handle) ++ return -ENOMEM; ++ ++ handle->dev = dev; ++ handle->handler = handler; ++ handle->name = KEYCHORD_NAME; ++ handle->private = kdev; ++ ++ ret = input_register_handle(handle); ++ if (ret) ++ goto err_input_register_handle; ++ ++ ret = input_open_device(handle); ++ if (ret) ++ goto err_input_open_device; ++ ++ pr_info("keychord: using input dev %s for fevent\n", dev->name); ++ ++ return 0; ++ ++err_input_open_device: ++ input_unregister_handle(handle); ++err_input_register_handle: ++ kfree(handle); ++ return ret; ++} ++ ++static void keychord_disconnect(struct input_handle *handle) ++{ ++ input_close_device(handle); ++ input_unregister_handle(handle); ++ kfree(handle); ++} ++ ++/* ++ * keychord_read is used to read keychord events from the driver ++ */ ++static ssize_t keychord_read(struct file *file, char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ struct keychord_device *kdev = file->private_data; ++ __u16 id; ++ int retval; ++ unsigned long flags; ++ ++ if (count < sizeof(id)) ++ return -EINVAL; ++ count = sizeof(id); ++ ++ if (kdev->head == kdev->tail && (file->f_flags & O_NONBLOCK)) ++ return -EAGAIN; ++ ++ retval = wait_event_interruptible(kdev->waitq, ++ kdev->head != kdev->tail); ++ if (retval) ++ return retval; ++ ++ spin_lock_irqsave(&kdev->lock, flags); ++ /* pop a keychord ID off the queue */ ++ id = kdev->buff[kdev->tail]; ++ kdev->tail = (kdev->tail + 1) % BUFFER_SIZE; ++ spin_unlock_irqrestore(&kdev->lock, flags); ++ ++ if (copy_to_user(buffer, &id, count)) ++ return -EFAULT; ++ ++ return count; ++} ++ ++/* ++ * keychord_write is used to configure the driver ++ */ ++static ssize_t keychord_write(struct file *file, const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ struct keychord_device *kdev = file->private_data; ++ struct input_keychord *keychords = 0; ++ struct input_keychord *keychord, *next, *end; ++ int ret, i, key; ++ unsigned long flags; ++ ++ if (count < sizeof(struct input_keychord)) ++ return -EINVAL; ++ keychords = kzalloc(count, GFP_KERNEL); ++ if (!keychords) ++ return -ENOMEM; ++ ++ /* read list of keychords from userspace */ ++ if (copy_from_user(keychords, buffer, count)) { ++ kfree(keychords); ++ return -EFAULT; ++ } ++ ++ /* unregister handler before changing configuration */ ++ if (kdev->registered) { ++ input_unregister_handler(&kdev->input_handler); ++ kdev->registered = 0; ++ } ++ ++ spin_lock_irqsave(&kdev->lock, flags); ++ /* clear any existing configuration */ ++ kfree(kdev->keychords); ++ kdev->keychords = 0; ++ kdev->keychord_count = 0; ++ kdev->key_down = 0; ++ memset(kdev->keybit, 0, sizeof(kdev->keybit)); ++ memset(kdev->keystate, 0, sizeof(kdev->keystate)); ++ kdev->head = kdev->tail = 0; ++ ++ keychord = keychords; ++ end = (struct input_keychord *)((char *)keychord + count); ++ ++ while (keychord < end) { ++ next = NEXT_KEYCHORD(keychord); ++ if (keychord->count <= 0 || next > end) { ++ pr_err("keychord: invalid keycode count %d\n", ++ keychord->count); ++ goto err_unlock_return; ++ } ++ if (keychord->version != KEYCHORD_VERSION) { ++ pr_err("keychord: unsupported version %d\n", ++ keychord->version); ++ goto err_unlock_return; ++ } ++ ++ /* keep track of the keys we are monitoring in keybit */ ++ for (i = 0; i < keychord->count; i++) { ++ key = keychord->keycodes[i]; ++ if (key < 0 || key >= KEY_CNT) { ++ pr_err("keychord: keycode %d out of range\n", ++ key); ++ goto err_unlock_return; ++ } ++ __set_bit(key, kdev->keybit); ++ } ++ ++ kdev->keychord_count++; ++ keychord = next; ++ } ++ ++ kdev->keychords = keychords; ++ spin_unlock_irqrestore(&kdev->lock, flags); ++ ++ ret = input_register_handler(&kdev->input_handler); ++ if (ret) { ++ kfree(keychords); ++ kdev->keychords = 0; ++ return ret; ++ } ++ kdev->registered = 1; ++ ++ return count; ++ ++err_unlock_return: ++ spin_unlock_irqrestore(&kdev->lock, flags); ++ kfree(keychords); ++ return -EINVAL; ++} ++ ++static unsigned int keychord_poll(struct file *file, poll_table *wait) ++{ ++ struct keychord_device *kdev = file->private_data; ++ ++ poll_wait(file, &kdev->waitq, wait); ++ ++ if (kdev->head != kdev->tail) ++ return POLLIN | POLLRDNORM; ++ ++ return 0; ++} ++ ++static int keychord_open(struct inode *inode, struct file *file) ++{ ++ struct keychord_device *kdev; ++ ++ kdev = kzalloc(sizeof(struct keychord_device), GFP_KERNEL); ++ if (!kdev) ++ return -ENOMEM; ++ ++ spin_lock_init(&kdev->lock); ++ init_waitqueue_head(&kdev->waitq); ++ ++ kdev->input_handler.event = keychord_event; ++ kdev->input_handler.connect = keychord_connect; ++ kdev->input_handler.disconnect = keychord_disconnect; ++ kdev->input_handler.name = KEYCHORD_NAME; ++ kdev->input_handler.id_table = kdev->device_ids; ++ ++ kdev->device_ids[0].flags = INPUT_DEVICE_ID_MATCH_EVBIT; ++ __set_bit(EV_KEY, kdev->device_ids[0].evbit); ++ ++ file->private_data = kdev; ++ ++ return 0; ++} ++ ++static int keychord_release(struct inode *inode, struct file *file) ++{ ++ struct keychord_device *kdev = file->private_data; ++ ++ if (kdev->registered) ++ input_unregister_handler(&kdev->input_handler); ++ kfree(kdev); ++ ++ return 0; ++} ++ ++static const struct file_operations keychord_fops = { ++ .owner = THIS_MODULE, ++ .open = keychord_open, ++ .release = keychord_release, ++ .read = keychord_read, ++ .write = keychord_write, ++ .poll = keychord_poll, ++}; ++ ++static struct miscdevice keychord_misc = { ++ .fops = &keychord_fops, ++ .name = KEYCHORD_NAME, ++ .minor = MISC_DYNAMIC_MINOR, ++}; ++ ++static int __init keychord_init(void) ++{ ++ return misc_register(&keychord_misc); ++} ++ ++static void __exit keychord_exit(void) ++{ ++ misc_deregister(&keychord_misc); ++} ++ ++module_init(keychord_init); ++module_exit(keychord_exit); +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 2a214191..f102a924 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -451,6 +451,12 @@ config TOUCHSCREEN_TNETV107X + To compile this driver as a module, choose M here: the + module will be called tnetv107x-ts. + ++config TOUCHSCREEN_SYNAPTICS_I2C_RMI ++ tristate "Synaptics i2c touchscreen" ++ depends on I2C ++ help ++ This enables support for Synaptics RMI over I2C based touchscreens. ++ + config TOUCHSCREEN_TOUCHRIGHT + tristate "Touchright serial touchscreen" + select SERIO +@@ -792,6 +798,35 @@ config TOUCHSCREEN_TSC2007 + To compile this driver as a module, choose M here: the + module will be called tsc2007. + ++config TOUCHSCREEN_ANYKA ++ bool "Anyka extra touchscreen" ++ depends on ARCH_AK39 ++ help ++ This item enable extra touchscreen interface for Anyka platform. ++ ++config TOUCHSCREEN_CP2007 ++ tristate "cp2007 based touchscreen" ++ depends on TOUCHSCREEN_ANYKA ++ default y ++ help ++ Say Y here if you have a CP2007 based touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tscp2007. ++ ++config TOUCHSCREEN_CP2017 ++ tristate "cp2017 based touchscreen" ++ depends on TOUCHSCREEN_ANYKA ++ help ++ Say Y here if you have a CP2017 based touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tscp2017. ++ + config TOUCHSCREEN_W90X900 + tristate "W90P910 touchscreen driver" + depends on HAVE_CLK +diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile +index 3d5cf8cb..55706d47 100644 +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -51,12 +51,14 @@ obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o + obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o + obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o + obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o ++obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI) += synaptics_i2c_rmi.o + obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o + obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o + obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o + obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o + obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o + obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o ++obj-$(CONFIG_TOUCHSCREEN_CP2007) += tscp2007.o + obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o + obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o + obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o +diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi.c b/drivers/input/touchscreen/synaptics_i2c_rmi.c +new file mode 100644 +index 00000000..5729602c +--- /dev/null ++++ b/drivers/input/touchscreen/synaptics_i2c_rmi.c +@@ -0,0 +1,675 @@ ++/* drivers/input/keyboard/synaptics_i2c_rmi.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct workqueue_struct *synaptics_wq; ++ ++struct synaptics_ts_data { ++ uint16_t addr; ++ struct i2c_client *client; ++ struct input_dev *input_dev; ++ int use_irq; ++ bool has_relative_report; ++ struct hrtimer timer; ++ struct work_struct work; ++ uint16_t max[2]; ++ int snap_state[2][2]; ++ int snap_down_on[2]; ++ int snap_down_off[2]; ++ int snap_up_on[2]; ++ int snap_up_off[2]; ++ int snap_down[2]; ++ int snap_up[2]; ++ uint32_t flags; ++ int reported_finger_count; ++ int8_t sensitivity_adjust; ++ int (*power)(int on); ++ struct early_suspend early_suspend; ++}; ++ ++#ifdef CONFIG_HAS_EARLYSUSPEND ++static void synaptics_ts_early_suspend(struct early_suspend *h); ++static void synaptics_ts_late_resume(struct early_suspend *h); ++#endif ++ ++static int synaptics_init_panel(struct synaptics_ts_data *ts) ++{ ++ int ret; ++ ++ ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */ ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); ++ goto err_page_select_failed; ++ } ++ ret = i2c_smbus_write_byte_data(ts->client, 0x41, 0x04); /* Set "No Clip Z" */ ++ if (ret < 0) ++ printk(KERN_ERR "i2c_smbus_write_byte_data failed for No Clip Z\n"); ++ ++ ret = i2c_smbus_write_byte_data(ts->client, 0x44, ++ ts->sensitivity_adjust); ++ if (ret < 0) ++ pr_err("synaptics_ts: failed to set Sensitivity Adjust\n"); ++ ++err_page_select_failed: ++ ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x04); /* page select = 0x04 */ ++ if (ret < 0) ++ printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); ++ ret = i2c_smbus_write_byte_data(ts->client, 0xf0, 0x81); /* normal operation, 80 reports per second */ ++ if (ret < 0) ++ printk(KERN_ERR "synaptics_ts_resume: i2c_smbus_write_byte_data failed\n"); ++ return ret; ++} ++ ++static void synaptics_ts_work_func(struct work_struct *work) ++{ ++ int i; ++ int ret; ++ int bad_data = 0; ++ struct i2c_msg msg[2]; ++ uint8_t start_reg; ++ uint8_t buf[15]; ++ struct synaptics_ts_data *ts = container_of(work, struct synaptics_ts_data, work); ++ int buf_len = ts->has_relative_report ? 15 : 13; ++ ++ msg[0].addr = ts->client->addr; ++ msg[0].flags = 0; ++ msg[0].len = 1; ++ msg[0].buf = &start_reg; ++ start_reg = 0x00; ++ msg[1].addr = ts->client->addr; ++ msg[1].flags = I2C_M_RD; ++ msg[1].len = buf_len; ++ msg[1].buf = buf; ++ ++ /* printk("synaptics_ts_work_func\n"); */ ++ for (i = 0; i < ((ts->use_irq && !bad_data) ? 1 : 10); i++) { ++ ret = i2c_transfer(ts->client->adapter, msg, 2); ++ if (ret < 0) { ++ printk(KERN_ERR "synaptics_ts_work_func: i2c_transfer failed\n"); ++ bad_data = 1; ++ } else { ++ /* printk("synaptics_ts_work_func: %x %x %x %x %x %x" */ ++ /* " %x %x %x %x %x %x %x %x %x, ret %d\n", */ ++ /* buf[0], buf[1], buf[2], buf[3], */ ++ /* buf[4], buf[5], buf[6], buf[7], */ ++ /* buf[8], buf[9], buf[10], buf[11], */ ++ /* buf[12], buf[13], buf[14], ret); */ ++ if ((buf[buf_len - 1] & 0xc0) != 0x40) { ++ printk(KERN_WARNING "synaptics_ts_work_func:" ++ " bad read %x %x %x %x %x %x %x %x %x" ++ " %x %x %x %x %x %x, ret %d\n", ++ buf[0], buf[1], buf[2], buf[3], ++ buf[4], buf[5], buf[6], buf[7], ++ buf[8], buf[9], buf[10], buf[11], ++ buf[12], buf[13], buf[14], ret); ++ if (bad_data) ++ synaptics_init_panel(ts); ++ bad_data = 1; ++ continue; ++ } ++ bad_data = 0; ++ if ((buf[buf_len - 1] & 1) == 0) { ++ /* printk("read %d coordinates\n", i); */ ++ break; ++ } else { ++ int pos[2][2]; ++ int f, a; ++ int base; ++ /* int x = buf[3] | (uint16_t)(buf[2] & 0x1f) << 8; */ ++ /* int y = buf[5] | (uint16_t)(buf[4] & 0x1f) << 8; */ ++ int z = buf[1]; ++ int w = buf[0] >> 4; ++ int finger = buf[0] & 7; ++ ++ /* int x2 = buf[3+6] | (uint16_t)(buf[2+6] & 0x1f) << 8; */ ++ /* int y2 = buf[5+6] | (uint16_t)(buf[4+6] & 0x1f) << 8; */ ++ /* int z2 = buf[1+6]; */ ++ /* int w2 = buf[0+6] >> 4; */ ++ /* int finger2 = buf[0+6] & 7; */ ++ ++ /* int dx = (int8_t)buf[12]; */ ++ /* int dy = (int8_t)buf[13]; */ ++ int finger2_pressed; ++ ++ /* printk("x %4d, y %4d, z %3d, w %2d, F %d, 2nd: x %4d, y %4d, z %3d, w %2d, F %d, dx %4d, dy %4d\n", */ ++ /* x, y, z, w, finger, */ ++ /* x2, y2, z2, w2, finger2, */ ++ /* dx, dy); */ ++ ++ base = 2; ++ for (f = 0; f < 2; f++) { ++ uint32_t flip_flag = SYNAPTICS_FLIP_X; ++ for (a = 0; a < 2; a++) { ++ int p = buf[base + 1]; ++ p |= (uint16_t)(buf[base] & 0x1f) << 8; ++ if (ts->flags & flip_flag) ++ p = ts->max[a] - p; ++ if (ts->flags & SYNAPTICS_SNAP_TO_INACTIVE_EDGE) { ++ if (ts->snap_state[f][a]) { ++ if (p <= ts->snap_down_off[a]) ++ p = ts->snap_down[a]; ++ else if (p >= ts->snap_up_off[a]) ++ p = ts->snap_up[a]; ++ else ++ ts->snap_state[f][a] = 0; ++ } else { ++ if (p <= ts->snap_down_on[a]) { ++ p = ts->snap_down[a]; ++ ts->snap_state[f][a] = 1; ++ } else if (p >= ts->snap_up_on[a]) { ++ p = ts->snap_up[a]; ++ ts->snap_state[f][a] = 1; ++ } ++ } ++ } ++ pos[f][a] = p; ++ base += 2; ++ flip_flag <<= 1; ++ } ++ base += 2; ++ if (ts->flags & SYNAPTICS_SWAP_XY) ++ swap(pos[f][0], pos[f][1]); ++ } ++ if (z) { ++ input_report_abs(ts->input_dev, ABS_X, pos[0][0]); ++ input_report_abs(ts->input_dev, ABS_Y, pos[0][1]); ++ } ++ input_report_abs(ts->input_dev, ABS_PRESSURE, z); ++ input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, w); ++ input_report_key(ts->input_dev, BTN_TOUCH, finger); ++ finger2_pressed = finger > 1 && finger != 7; ++ input_report_key(ts->input_dev, BTN_2, finger2_pressed); ++ if (finger2_pressed) { ++ input_report_abs(ts->input_dev, ABS_HAT0X, pos[1][0]); ++ input_report_abs(ts->input_dev, ABS_HAT0Y, pos[1][1]); ++ } ++ ++ if (!finger) ++ z = 0; ++ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z); ++ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); ++ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[0][0]); ++ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[0][1]); ++ input_mt_sync(ts->input_dev); ++ if (finger2_pressed) { ++ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z); ++ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); ++ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[1][0]); ++ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[1][1]); ++ input_mt_sync(ts->input_dev); ++ } else if (ts->reported_finger_count > 1) { ++ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); ++ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); ++ input_mt_sync(ts->input_dev); ++ } ++ ts->reported_finger_count = finger; ++ input_sync(ts->input_dev); ++ } ++ } ++ } ++ if (ts->use_irq) ++ enable_irq(ts->client->irq); ++} ++ ++static enum hrtimer_restart synaptics_ts_timer_func(struct hrtimer *timer) ++{ ++ struct synaptics_ts_data *ts = container_of(timer, struct synaptics_ts_data, timer); ++ /* printk("synaptics_ts_timer_func\n"); */ ++ ++ queue_work(synaptics_wq, &ts->work); ++ ++ hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); ++ return HRTIMER_NORESTART; ++} ++ ++static irqreturn_t synaptics_ts_irq_handler(int irq, void *dev_id) ++{ ++ struct synaptics_ts_data *ts = dev_id; ++ ++ /* printk("synaptics_ts_irq_handler\n"); */ ++ disable_irq_nosync(ts->client->irq); ++ queue_work(synaptics_wq, &ts->work); ++ return IRQ_HANDLED; ++} ++ ++static int synaptics_ts_probe( ++ struct i2c_client *client, const struct i2c_device_id *id) ++{ ++ struct synaptics_ts_data *ts; ++ uint8_t buf0[4]; ++ uint8_t buf1[8]; ++ struct i2c_msg msg[2]; ++ int ret = 0; ++ uint16_t max_x, max_y; ++ int fuzz_x, fuzz_y, fuzz_p, fuzz_w; ++ struct synaptics_i2c_rmi_platform_data *pdata; ++ unsigned long irqflags; ++ int inactive_area_left; ++ int inactive_area_right; ++ int inactive_area_top; ++ int inactive_area_bottom; ++ int snap_left_on; ++ int snap_left_off; ++ int snap_right_on; ++ int snap_right_off; ++ int snap_top_on; ++ int snap_top_off; ++ int snap_bottom_on; ++ int snap_bottom_off; ++ uint32_t panel_version; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ printk(KERN_ERR "synaptics_ts_probe: need I2C_FUNC_I2C\n"); ++ ret = -ENODEV; ++ goto err_check_functionality_failed; ++ } ++ ++ ts = kzalloc(sizeof(*ts), GFP_KERNEL); ++ if (ts == NULL) { ++ ret = -ENOMEM; ++ goto err_alloc_data_failed; ++ } ++ INIT_WORK(&ts->work, synaptics_ts_work_func); ++ ts->client = client; ++ i2c_set_clientdata(client, ts); ++ pdata = client->dev.platform_data; ++ if (pdata) ++ ts->power = pdata->power; ++ if (ts->power) { ++ ret = ts->power(1); ++ if (ret < 0) { ++ printk(KERN_ERR "synaptics_ts_probe power on failed\n"); ++ goto err_power_failed; ++ } ++ } ++ ++ ret = i2c_smbus_write_byte_data(ts->client, 0xf4, 0x01); /* device command = reset */ ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_smbus_write_byte_data failed\n"); ++ /* fail? */ ++ } ++ { ++ int retry = 10; ++ while (retry-- > 0) { ++ ret = i2c_smbus_read_byte_data(ts->client, 0xe4); ++ if (ret >= 0) ++ break; ++ msleep(100); ++ } ++ } ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); ++ goto err_detect_failed; ++ } ++ printk(KERN_INFO "synaptics_ts_probe: Product Major Version %x\n", ret); ++ panel_version = ret << 8; ++ ret = i2c_smbus_read_byte_data(ts->client, 0xe5); ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); ++ goto err_detect_failed; ++ } ++ printk(KERN_INFO "synaptics_ts_probe: Product Minor Version %x\n", ret); ++ panel_version |= ret; ++ ++ ret = i2c_smbus_read_byte_data(ts->client, 0xe3); ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); ++ goto err_detect_failed; ++ } ++ printk(KERN_INFO "synaptics_ts_probe: product property %x\n", ret); ++ ++ if (pdata) { ++ while (pdata->version > panel_version) ++ pdata++; ++ ts->flags = pdata->flags; ++ ts->sensitivity_adjust = pdata->sensitivity_adjust; ++ irqflags = pdata->irqflags; ++ inactive_area_left = pdata->inactive_left; ++ inactive_area_right = pdata->inactive_right; ++ inactive_area_top = pdata->inactive_top; ++ inactive_area_bottom = pdata->inactive_bottom; ++ snap_left_on = pdata->snap_left_on; ++ snap_left_off = pdata->snap_left_off; ++ snap_right_on = pdata->snap_right_on; ++ snap_right_off = pdata->snap_right_off; ++ snap_top_on = pdata->snap_top_on; ++ snap_top_off = pdata->snap_top_off; ++ snap_bottom_on = pdata->snap_bottom_on; ++ snap_bottom_off = pdata->snap_bottom_off; ++ fuzz_x = pdata->fuzz_x; ++ fuzz_y = pdata->fuzz_y; ++ fuzz_p = pdata->fuzz_p; ++ fuzz_w = pdata->fuzz_w; ++ } else { ++ irqflags = 0; ++ inactive_area_left = 0; ++ inactive_area_right = 0; ++ inactive_area_top = 0; ++ inactive_area_bottom = 0; ++ snap_left_on = 0; ++ snap_left_off = 0; ++ snap_right_on = 0; ++ snap_right_off = 0; ++ snap_top_on = 0; ++ snap_top_off = 0; ++ snap_bottom_on = 0; ++ snap_bottom_off = 0; ++ fuzz_x = 0; ++ fuzz_y = 0; ++ fuzz_p = 0; ++ fuzz_w = 0; ++ } ++ ++ ret = i2c_smbus_read_byte_data(ts->client, 0xf0); ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); ++ goto err_detect_failed; ++ } ++ printk(KERN_INFO "synaptics_ts_probe: device control %x\n", ret); ++ ++ ret = i2c_smbus_read_byte_data(ts->client, 0xf1); ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); ++ goto err_detect_failed; ++ } ++ printk(KERN_INFO "synaptics_ts_probe: interrupt enable %x\n", ret); ++ ++ ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */ ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_smbus_write_byte_data failed\n"); ++ goto err_detect_failed; ++ } ++ ++ msg[0].addr = ts->client->addr; ++ msg[0].flags = 0; ++ msg[0].len = 1; ++ msg[0].buf = buf0; ++ buf0[0] = 0xe0; ++ msg[1].addr = ts->client->addr; ++ msg[1].flags = I2C_M_RD; ++ msg[1].len = 8; ++ msg[1].buf = buf1; ++ ret = i2c_transfer(ts->client->adapter, msg, 2); ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_transfer failed\n"); ++ goto err_detect_failed; ++ } ++ printk(KERN_INFO "synaptics_ts_probe: 0xe0: %x %x %x %x %x %x %x %x\n", ++ buf1[0], buf1[1], buf1[2], buf1[3], ++ buf1[4], buf1[5], buf1[6], buf1[7]); ++ ++ ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */ ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); ++ goto err_detect_failed; ++ } ++ ret = i2c_smbus_read_word_data(ts->client, 0x02); ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); ++ goto err_detect_failed; ++ } ++ ts->has_relative_report = !(ret & 0x100); ++ printk(KERN_INFO "synaptics_ts_probe: Sensor properties %x\n", ret); ++ ret = i2c_smbus_read_word_data(ts->client, 0x04); ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); ++ goto err_detect_failed; ++ } ++ ts->max[0] = max_x = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8); ++ ret = i2c_smbus_read_word_data(ts->client, 0x06); ++ if (ret < 0) { ++ printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); ++ goto err_detect_failed; ++ } ++ ts->max[1] = max_y = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8); ++ if (ts->flags & SYNAPTICS_SWAP_XY) ++ swap(max_x, max_y); ++ ++ ret = synaptics_init_panel(ts); /* will also switch back to page 0x04 */ ++ if (ret < 0) { ++ printk(KERN_ERR "synaptics_init_panel failed\n"); ++ goto err_detect_failed; ++ } ++ ++ ts->input_dev = input_allocate_device(); ++ if (ts->input_dev == NULL) { ++ ret = -ENOMEM; ++ printk(KERN_ERR "synaptics_ts_probe: Failed to allocate input device\n"); ++ goto err_input_dev_alloc_failed; ++ } ++ ts->input_dev->name = "synaptics-rmi-touchscreen"; ++ set_bit(EV_SYN, ts->input_dev->evbit); ++ set_bit(EV_KEY, ts->input_dev->evbit); ++ set_bit(BTN_TOUCH, ts->input_dev->keybit); ++ set_bit(BTN_2, ts->input_dev->keybit); ++ set_bit(EV_ABS, ts->input_dev->evbit); ++ inactive_area_left = inactive_area_left * max_x / 0x10000; ++ inactive_area_right = inactive_area_right * max_x / 0x10000; ++ inactive_area_top = inactive_area_top * max_y / 0x10000; ++ inactive_area_bottom = inactive_area_bottom * max_y / 0x10000; ++ snap_left_on = snap_left_on * max_x / 0x10000; ++ snap_left_off = snap_left_off * max_x / 0x10000; ++ snap_right_on = snap_right_on * max_x / 0x10000; ++ snap_right_off = snap_right_off * max_x / 0x10000; ++ snap_top_on = snap_top_on * max_y / 0x10000; ++ snap_top_off = snap_top_off * max_y / 0x10000; ++ snap_bottom_on = snap_bottom_on * max_y / 0x10000; ++ snap_bottom_off = snap_bottom_off * max_y / 0x10000; ++ fuzz_x = fuzz_x * max_x / 0x10000; ++ fuzz_y = fuzz_y * max_y / 0x10000; ++ ts->snap_down[!!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_left; ++ ts->snap_up[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x + inactive_area_right; ++ ts->snap_down[!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_top; ++ ts->snap_up[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y + inactive_area_bottom; ++ ts->snap_down_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_on; ++ ts->snap_down_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_off; ++ ts->snap_up_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_on; ++ ts->snap_up_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_off; ++ ts->snap_down_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_on; ++ ts->snap_down_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_off; ++ ts->snap_up_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_on; ++ ts->snap_up_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_off; ++ printk(KERN_INFO "synaptics_ts_probe: max_x %d, max_y %d\n", max_x, max_y); ++ printk(KERN_INFO "synaptics_ts_probe: inactive_x %d %d, inactive_y %d %d\n", ++ inactive_area_left, inactive_area_right, ++ inactive_area_top, inactive_area_bottom); ++ printk(KERN_INFO "synaptics_ts_probe: snap_x %d-%d %d-%d, snap_y %d-%d %d-%d\n", ++ snap_left_on, snap_left_off, snap_right_on, snap_right_off, ++ snap_top_on, snap_top_off, snap_bottom_on, snap_bottom_off); ++ input_set_abs_params(ts->input_dev, ABS_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); ++ input_set_abs_params(ts->input_dev, ABS_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); ++ input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, fuzz_p, 0); ++ input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, 0, 15, fuzz_w, 0); ++ input_set_abs_params(ts->input_dev, ABS_HAT0X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); ++ input_set_abs_params(ts->input_dev, ABS_HAT0Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); ++ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); ++ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); ++ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, fuzz_p, 0); ++ input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, fuzz_w, 0); ++ /* ts->input_dev->name = ts->keypad_info->name; */ ++ ret = input_register_device(ts->input_dev); ++ if (ret) { ++ printk(KERN_ERR "synaptics_ts_probe: Unable to register %s input device\n", ts->input_dev->name); ++ goto err_input_register_device_failed; ++ } ++ if (client->irq) { ++ ret = request_irq(client->irq, synaptics_ts_irq_handler, irqflags, client->name, ts); ++ if (ret == 0) { ++ ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */ ++ if (ret) ++ free_irq(client->irq, ts); ++ } ++ if (ret == 0) ++ ts->use_irq = 1; ++ else ++ dev_err(&client->dev, "request_irq failed\n"); ++ } ++ if (!ts->use_irq) { ++ hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ ts->timer.function = synaptics_ts_timer_func; ++ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); ++ } ++#ifdef CONFIG_HAS_EARLYSUSPEND ++ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; ++ ts->early_suspend.suspend = synaptics_ts_early_suspend; ++ ts->early_suspend.resume = synaptics_ts_late_resume; ++ register_early_suspend(&ts->early_suspend); ++#endif ++ ++ printk(KERN_INFO "synaptics_ts_probe: Start touchscreen %s in %s mode\n", ts->input_dev->name, ts->use_irq ? "interrupt" : "polling"); ++ ++ return 0; ++ ++err_input_register_device_failed: ++ input_free_device(ts->input_dev); ++ ++err_input_dev_alloc_failed: ++err_detect_failed: ++err_power_failed: ++ kfree(ts); ++err_alloc_data_failed: ++err_check_functionality_failed: ++ return ret; ++} ++ ++static int synaptics_ts_remove(struct i2c_client *client) ++{ ++ struct synaptics_ts_data *ts = i2c_get_clientdata(client); ++ unregister_early_suspend(&ts->early_suspend); ++ if (ts->use_irq) ++ free_irq(client->irq, ts); ++ else ++ hrtimer_cancel(&ts->timer); ++ input_unregister_device(ts->input_dev); ++ kfree(ts); ++ return 0; ++} ++ ++static int synaptics_ts_suspend(struct i2c_client *client, pm_message_t mesg) ++{ ++ int ret; ++ struct synaptics_ts_data *ts = i2c_get_clientdata(client); ++ ++ if (ts->use_irq) ++ disable_irq(client->irq); ++ else ++ hrtimer_cancel(&ts->timer); ++ ret = cancel_work_sync(&ts->work); ++ if (ret && ts->use_irq) /* if work was pending disable-count is now 2 */ ++ enable_irq(client->irq); ++ ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */ ++ if (ret < 0) ++ printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n"); ++ ++ ret = i2c_smbus_write_byte_data(client, 0xf0, 0x86); /* deep sleep */ ++ if (ret < 0) ++ printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n"); ++ if (ts->power) { ++ ret = ts->power(0); ++ if (ret < 0) ++ printk(KERN_ERR "synaptics_ts_resume power off failed\n"); ++ } ++ return 0; ++} ++ ++static int synaptics_ts_resume(struct i2c_client *client) ++{ ++ int ret; ++ struct synaptics_ts_data *ts = i2c_get_clientdata(client); ++ ++ if (ts->power) { ++ ret = ts->power(1); ++ if (ret < 0) ++ printk(KERN_ERR "synaptics_ts_resume power on failed\n"); ++ } ++ ++ synaptics_init_panel(ts); ++ ++ if (ts->use_irq) ++ enable_irq(client->irq); ++ ++ if (!ts->use_irq) ++ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); ++ else ++ i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */ ++ ++ return 0; ++} ++ ++#ifdef CONFIG_HAS_EARLYSUSPEND ++static void synaptics_ts_early_suspend(struct early_suspend *h) ++{ ++ struct synaptics_ts_data *ts; ++ ts = container_of(h, struct synaptics_ts_data, early_suspend); ++ synaptics_ts_suspend(ts->client, PMSG_SUSPEND); ++} ++ ++static void synaptics_ts_late_resume(struct early_suspend *h) ++{ ++ struct synaptics_ts_data *ts; ++ ts = container_of(h, struct synaptics_ts_data, early_suspend); ++ synaptics_ts_resume(ts->client); ++} ++#endif ++ ++static const struct i2c_device_id synaptics_ts_id[] = { ++ { SYNAPTICS_I2C_RMI_NAME, 0 }, ++ { } ++}; ++ ++static struct i2c_driver synaptics_ts_driver = { ++ .probe = synaptics_ts_probe, ++ .remove = synaptics_ts_remove, ++#ifndef CONFIG_HAS_EARLYSUSPEND ++ .suspend = synaptics_ts_suspend, ++ .resume = synaptics_ts_resume, ++#endif ++ .id_table = synaptics_ts_id, ++ .driver = { ++ .name = SYNAPTICS_I2C_RMI_NAME, ++ }, ++}; ++ ++static int __devinit synaptics_ts_init(void) ++{ ++ synaptics_wq = create_singlethread_workqueue("synaptics_wq"); ++ if (!synaptics_wq) ++ return -ENOMEM; ++ return i2c_add_driver(&synaptics_ts_driver); ++} ++ ++static void __exit synaptics_ts_exit(void) ++{ ++ i2c_del_driver(&synaptics_ts_driver); ++ if (synaptics_wq) ++ destroy_workqueue(synaptics_wq); ++} ++ ++module_init(synaptics_ts_init); ++module_exit(synaptics_ts_exit); ++ ++MODULE_DESCRIPTION("Synaptics Touchscreen Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/input/touchscreen/tscp2007.c b/drivers/input/touchscreen/tscp2007.c +new file mode 100755 +index 00000000..5fe2e0ba +--- /dev/null ++++ b/drivers/input/touchscreen/tscp2007.c +@@ -0,0 +1,409 @@ ++/* ++ * drivers/input/touchscreen/tscp2007.c ++ * ++ * Copyright (c) 2008 MtekVision Co., Ltd. ++ * Kwangwoo Lee ++ * ++ * Using code from: ++ * - ads7846.c ++ * Copyright (c) 2005 David Brownell ++ * Copyright (c) 2006 Nokia Corporation ++ * - corgi_ts.c ++ * Copyright (C) 2004-2005 Richard Purdie ++ * - omap_ts.[hc], ads7846.h, ts_osk.c ++ * Copyright (C) 2002 MontaVista Software ++ * Copyright (C) 2004 Texas Instruments ++ * Copyright (C) 2005 Dirk Behme ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define tscp2007_MEASURE_TEMP0 (0x0 << 4) ++#define tscp2007_MEASURE_AUX (0x2 << 4) ++#define tscp2007_MEASURE_TEMP1 (0x4 << 4) ++#define tscp2007_ACTIVATE_XN (0x8 << 4) ++#define tscp2007_ACTIVATE_YN (0x9 << 4) ++#define tscp2007_ACTIVATE_YP_XN (0xa << 4) ++#define tscp2007_SETUP (0xb << 4) ++#define tscp2007_MEASURE_X (0xc << 4) ++#define tscp2007_MEASURE_Y (0xd << 4) ++#define tscp2007_MEASURE_Z1 (0xe << 4) ++#define tscp2007_MEASURE_Z2 (0xf << 4) ++ ++#define tscp2007_POWER_OFF_IRQ_EN (0x0 << 2) ++#define tscp2007_ADC_ON_IRQ_DIS0 (0x1 << 2) ++#define tscp2007_ADC_OFF_IRQ_EN (0x2 << 2) ++#define tscp2007_ADC_ON_IRQ_DIS1 (0x3 << 2) ++ ++#define tscp2007_12BIT (0x0 << 1) ++#define tscp2007_8BIT (0x1 << 1) ++ ++#define MAX_12BIT ((1 << 12) - 1) ++ ++#define ADC_ON_12BIT (tscp2007_12BIT | tscp2007_ADC_ON_IRQ_DIS0) ++ ++#define READ_Y (ADC_ON_12BIT | tscp2007_MEASURE_Y) ++#define READ_Z1 (ADC_ON_12BIT | tscp2007_MEASURE_Z1) ++#define READ_Z2 (ADC_ON_12BIT | tscp2007_MEASURE_Z2) ++#define READ_X (ADC_ON_12BIT | tscp2007_MEASURE_X) ++#define PWRDOWN (tscp2007_12BIT | tscp2007_POWER_OFF_IRQ_EN) ++ ++struct ts_event { ++ u16 x; ++ u16 y; ++ u16 z1, z2; ++}; ++ ++struct tscp2007 { ++ struct input_dev *input; ++ char phys[32]; ++ ++ struct i2c_client *client; ++ ++ u16 model; ++ u16 x_plate_ohms; ++ u16 max_rt; ++ unsigned long poll_delay; ++ unsigned long poll_period; ++ ++ int irq; ++ ++ wait_queue_head_t wait; ++ bool stopped; ++ ++ unsigned int intpin; ++ int (*get_pendown_state)(unsigned int pin); ++ void (*clear_penirq)(void); ++}; ++ ++static inline int tscp2007_xfer(struct tscp2007 *tsc, u8 cmd) ++{ ++ s32 data; ++ u16 val; ++ ++ data = i2c_smbus_read_word_data(tsc->client, cmd); ++ if (data < 0) { ++ dev_err(&tsc->client->dev, "i2c io error: %d\n", data); ++ return data; ++ } ++ ++ /* The protocol and raw data format from i2c interface: ++ * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P ++ * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit]. ++ */ ++ val = swab16(data) >> 4; ++ ++ dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val); ++ ++ return val; ++} ++ ++static void tscp2007_read_values(struct tscp2007 *tsc, struct ts_event *tc) ++{ ++ /* y- still on; turn on only y+ (and ADC) */ ++ tc->y = tscp2007_xfer(tsc, READ_Y); ++ ++ /* turn y- off, x+ on, then leave in lowpower */ ++ tc->x = tscp2007_xfer(tsc, READ_X); ++ ++ /* turn y+ off, x- on; we'll use formula #1 */ ++ tc->z1 = tscp2007_xfer(tsc, READ_Z1); ++ tc->z2 = tscp2007_xfer(tsc, READ_Z2); ++ ++ /* Prepare for next touch reading - power down ADC, enable PENIRQ */ ++ tscp2007_xfer(tsc, PWRDOWN); ++} ++ ++static u32 tscp2007_calculate_pressure(struct tscp2007 *tsc, struct ts_event *tc) ++{ ++ u32 rt = 0; ++ ++ /* range filtering */ ++ if (tc->x == MAX_12BIT) ++ tc->x = 0; ++ ++ if (likely(tc->x && tc->z1)) { ++ /* compute touch pressure resistance using equation #1 */ ++ rt = tc->z2 - tc->z1; ++ rt *= tc->x; ++ rt *= tsc->x_plate_ohms; ++ rt /= tc->z1; ++ rt = (rt + 2047) >> 12; ++ } ++ ++ return rt; ++} ++ ++static bool tscp2007_is_pen_down(struct tscp2007 *ts) ++{ ++ /* ++ * NOTE: We can't rely on the pressure to determine the pen down ++ * state, even though this controller has a pressure sensor. ++ * The pressure value can fluctuate for quite a while after ++ * lifting the pen and in some cases may not even settle at the ++ * expected value. ++ * ++ * The only safe way to check for the pen up condition is in the ++ * work function by reading the pen signal state (it's a GPIO ++ * and IRQ). Unfortunately such callback is not always available, ++ * in that case we assume that the pen is down and expect caller ++ * to fall back on the pressure reading. ++ */ ++ ++ if (!ts->get_pendown_state) ++ return true; ++ ++ return ts->get_pendown_state(ts->intpin); ++} ++ ++static irqreturn_t tscp2007_soft_irq(int irq, void *handle) ++{ ++ struct tscp2007 *ts = handle; ++ struct input_dev *input = ts->input; ++ struct ts_event tc; ++ u32 rt; ++ ++ while (!ts->stopped && tscp2007_is_pen_down(ts)) { ++ ++ /* pen is down, continue with the measurement */ ++ tscp2007_read_values(ts, &tc); ++ ++ rt = tscp2007_calculate_pressure(ts, &tc); ++ ++ if (rt == 0 && !ts->get_pendown_state) { ++ /* ++ * If pressure reported is 0 and we don't have ++ * callback to check pendown state, we have to ++ * assume that pen was lifted up. ++ */ ++ break; ++ } ++ ++ if (rt <= ts->max_rt) { ++ dev_dbg(&ts->client->dev, ++ "DOWN point(%4d,%4d), pressure (%4u)\n", ++ tc.x, tc.y, rt); ++ ++ input_report_key(input, BTN_TOUCH, 1); ++ input_report_abs(input, ABS_X, tc.x); ++ input_report_abs(input, ABS_Y, tc.y); ++ input_report_abs(input, ABS_PRESSURE, rt); ++ ++ input_sync(input); ++ ++ } else { ++ /* ++ * Sample found inconsistent by debouncing or pressure is ++ * beyond the maximum. Don't report it to user space, ++ * repeat at least once more the measurement. ++ */ ++ dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); ++ } ++ ++ wait_event_timeout(ts->wait, ts->stopped, ++ msecs_to_jiffies(ts->poll_period)); ++ } ++ ++ dev_dbg(&ts->client->dev, "UP\n"); ++ ++ input_report_key(input, BTN_TOUCH, 0); ++ input_report_abs(input, ABS_PRESSURE, 0); ++ input_sync(input); ++ ++ if (ts->clear_penirq) ++ ts->clear_penirq(); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t tscp2007_hard_irq(int irq, void *handle) ++{ ++ struct tscp2007 *ts = handle; ++ ++ if (!ts->get_pendown_state || likely(ts->get_pendown_state(ts->intpin))) ++ return IRQ_WAKE_THREAD; ++ ++ if (ts->clear_penirq) ++ ts->clear_penirq(); ++ ++ return IRQ_HANDLED; ++} ++ ++static void tscp2007_stop(struct tscp2007 *ts) ++{ ++ ts->stopped = true; ++ mb(); ++ wake_up(&ts->wait); ++ ++ disable_irq(ts->irq); ++} ++ ++static int tscp2007_open(struct input_dev *input_dev) ++{ ++ struct tscp2007 *ts = input_get_drvdata(input_dev); ++ int err; ++ ++ ts->stopped = false; ++ mb(); ++ ++ enable_irq(ts->irq); ++ ++ /* Prepare for touch readings - power down ADC and enable PENIRQ */ ++ err = tscp2007_xfer(ts, PWRDOWN); ++ if (err < 0) { ++ tscp2007_stop(ts); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static void tscp2007_close(struct input_dev *input_dev) ++{ ++ struct tscp2007 *ts = input_get_drvdata(input_dev); ++ ++ tscp2007_stop(ts); ++} ++ ++static int __devinit tscp2007_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct tscp2007 *ts; ++ struct tscp2007_platform_data *pdata = client->dev.platform_data; ++ struct input_dev *input_dev; ++ int err; ++ ++ if (!pdata) { ++ dev_err(&client->dev, "platform data is required!\n"); ++ return -EINVAL; ++ } ++ ++ if (!i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_READ_WORD_DATA)) ++ return -EIO; ++ ++ ts = kzalloc(sizeof(struct tscp2007), GFP_KERNEL); ++ input_dev = input_allocate_device(); ++ if (!ts || !input_dev) { ++ err = -ENOMEM; ++ goto err_free_mem; ++ } ++ ++ ts->client = client; ++ ts->irq = client->irq; ++ ts->input = input_dev; ++ init_waitqueue_head(&ts->wait); ++ ++ ts->model = pdata->model; ++ ts->x_plate_ohms = pdata->x_plate_ohms; ++ ts->max_rt = pdata->max_rt ? : MAX_12BIT; ++ ts->poll_delay = pdata->poll_delay ? : 1; ++ ts->poll_period = pdata->poll_period ? : 1; ++ ts->get_pendown_state = pdata->get_pendown_state; ++ ts->clear_penirq = pdata->clear_penirq; ++ ts->intpin = pdata->intpin_info.pin; ++ ++ if (pdata->x_plate_ohms == 0) { ++ dev_err(&client->dev, "x_plate_ohms is not set up in platform data"); ++ err = -EINVAL; ++ goto err_free_mem; ++ } ++ ++ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&client->dev)); ++ ++ input_dev->name = "tscp2007 Touchscreen"; ++ input_dev->phys = ts->phys; ++ input_dev->id.bustype = BUS_I2C; ++ ++ input_dev->open = tscp2007_open; ++ input_dev->close = tscp2007_close; ++ ++ input_set_drvdata(input_dev, ts); ++ ++ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); ++ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); ++ ++ input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, pdata->fuzzx, 0); ++ input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 0); ++ input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, ++ pdata->fuzzz, 0); ++ ++ if (pdata->init_platform_hw) ++ pdata->init_platform_hw(&pdata->intpin_info); ++ ++ err = request_threaded_irq(ts->irq, tscp2007_hard_irq, tscp2007_soft_irq, ++ IRQF_ONESHOT, client->dev.driver->name, ts); ++ if (err < 0) { ++ dev_err(&client->dev, "irq %d busy?\n", ts->irq); ++ goto err_free_mem; ++ } ++ ++ tscp2007_stop(ts); ++ ++ err = input_register_device(input_dev); ++ if (err) ++ goto err_free_irq; ++ ++ i2c_set_clientdata(client, ts); ++ ++ return 0; ++ ++ err_free_irq: ++ free_irq(ts->irq, ts); ++ if (pdata->exit_platform_hw) ++ pdata->exit_platform_hw(); ++ err_free_mem: ++ input_free_device(input_dev); ++ kfree(ts); ++ return err; ++} ++ ++static int __devexit tscp2007_remove(struct i2c_client *client) ++{ ++ struct tscp2007 *ts = i2c_get_clientdata(client); ++ struct tscp2007_platform_data *pdata = client->dev.platform_data; ++ ++ free_irq(ts->irq, ts); ++ ++ if (pdata->exit_platform_hw) ++ pdata->exit_platform_hw(); ++ ++ input_unregister_device(ts->input); ++ kfree(ts); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id tscp2007_idtable[] = { ++ { "tscp2007", 0 }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(i2c, tscp2007_idtable); ++ ++static struct i2c_driver tscp2007_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "tscp2007" ++ }, ++ .id_table = tscp2007_idtable, ++ .probe = tscp2007_probe, ++ .remove = __devexit_p(tscp2007_remove), ++}; ++ ++module_i2c_driver(tscp2007_driver); ++ ++MODULE_AUTHOR("Kwangwoo Lee "); ++MODULE_DESCRIPTION("tscp2007 TouchScreen Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig +index ff4b8cfd..19820ee5 100644 +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -289,6 +289,12 @@ config LEDS_BD2802 + This option enables support for BD2802GU RGB LED driver chips + accessed via the I2C bus. + ++config LEDS_AK39 ++ tristate "LED Support for the AK39 LEDS" ++ depends on LEDS_CLASS && ARCH_AK39 ++ help ++ This option enables support for the AK39 LEDS ++ + config LEDS_INTEL_SS4200 + tristate "LED driver for Intel NAS SS4200 series" + depends on LEDS_CLASS +@@ -468,6 +474,12 @@ config LEDS_TRIGGER_DEFAULT_ON + This allows LEDs to be initialised in the ON state. + If unsure, say Y. + ++config LEDS_TRIGGER_SLEEP ++ tristate "LED Sleep Mode Trigger" ++ depends on LEDS_TRIGGERS && HAS_EARLYSUSPEND ++ help ++ This turns LEDs on when the screen is off but the cpu still running. ++ + comment "iptables trigger is under Netfilter config (LED target)" + depends on LEDS_TRIGGERS + +diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile +index 890481cb..b054e8a7 100644 +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -45,6 +45,7 @@ obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o + obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o + obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o + obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o ++obj-$(CONFIG_LEDS_AK39) += leds-ak39.o + + # LED SPI Drivers + obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o +@@ -56,3 +57,4 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o + obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o + obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o + obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o ++obj-$(CONFIG_LEDS_TRIGGER_SLEEP) += ledtrig-sleep.o +diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c +index 46b4c766..a85ce094 100644 +--- a/drivers/leds/led-triggers.c ++++ b/drivers/leds/led-triggers.c +@@ -102,6 +102,12 @@ EXPORT_SYMBOL_GPL(led_trigger_show); + void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger) + { + unsigned long flags; ++ char *event = NULL; ++ char *envp[2]; ++ const char *name; ++ ++ name = trigger ? trigger->name : "none"; ++ event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name); + + /* Remove any existing trigger */ + if (led_cdev->trigger) { +@@ -122,6 +128,13 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger) + if (trigger->activate) + trigger->activate(led_cdev); + } ++ ++ if (event) { ++ envp[0] = event; ++ envp[1] = NULL; ++ kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp); ++ kfree(event); ++ } + } + EXPORT_SYMBOL_GPL(led_trigger_set); + +diff --git a/drivers/leds/leds-ak39.c b/drivers/leds/leds-ak39.c +new file mode 100755 +index 00000000..9b9882e0 +--- /dev/null ++++ b/drivers/leds/leds-ak39.c +@@ -0,0 +1,127 @@ ++/* drivers/leds/leds-ak98-factory.c ++ * ++ * Copyright (c) 2012 anyka ++ * ++ * AK98- LEDs GPIO driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++struct ak_led_instance { ++ struct led_classdev cdev; ++ struct ak_led_data *led; ++}; ++ ++struct ak_led_class { ++ struct ak_led_instance *instance; ++ int nr_instance; ++}; ++ ++static void ak_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) ++{ ++ unsigned int active_level = 0; ++ ++ struct ak_led_instance *instance = container_of(led_cdev, struct ak_led_instance, cdev); ++ struct ak_led_data *led = instance->led; ++ ++ active_level = (value ? (led->gpio.value == AK_GPIO_OUT_HIGH ? AK_GPIO_OUT_LOW : AK_GPIO_OUT_HIGH) ++ : led->gpio.value); ++ ak_gpio_setpin(led->gpio.pin, active_level); ++ ++} ++ ++static int ak_led_remove(struct platform_device *dev) ++{ ++ int i; ++ struct ak_led_class *class = platform_get_drvdata(dev); ++ struct ak_led_instance *instance = class->instance; ++ ++ for (i = 0; i < class->nr_instance; i++) ++ led_classdev_unregister(&(instance + i)->cdev); ++ ++ kfree(instance); ++ kfree(class); ++ ++ return 0; ++} ++ ++static int ak_led_probe(struct platform_device *dev) ++{ ++ int i, ret; ++ struct ak_led_pdata *pdata = dev->dev.platform_data; ++ struct ak_led_data *leds = pdata->leds; ++ struct ak_led_instance *instance; ++ struct ak_led_class *class; ++ ++ class = kzalloc(sizeof(struct ak_led_class), GFP_KERNEL); ++ instance = kzalloc(sizeof(struct ak_led_instance)*(pdata->nr_led), GFP_KERNEL); ++ if (!class || !instance) { ++ dev_err(&dev->dev, "No memory for device\n"); ++ return -ENOMEM; ++ } ++ ++ class->instance = instance; ++ class->nr_instance = pdata->nr_led; ++ platform_set_drvdata(dev, class); ++ ++ for (i = 0; i < class->nr_instance; i++) { ++ /* Default GPIO configure according to board defined */ ++ ak_gpio_set(&((leds + i)->gpio)); ++ (instance + i)->cdev.name = (leds + i)->name; ++ (instance + i)->cdev.default_trigger = (leds + i)->def_trigger; ++ (instance + i)->cdev.brightness_set = ak_led_brightness_set; ++ (instance + i)->cdev.flags |= LED_CORE_SUSPENDRESUME; ++ (instance + i)->led = (leds + i); ++ ++ ret = led_classdev_register(&dev->dev, &(instance + i)->cdev); ++ if (ret < 0) { ++ dev_err(&dev->dev, "led_classdev_register failed\n"); ++ kfree(instance); ++ kfree(class); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver ak_led_driver = { ++ .probe = ak_led_probe, ++ .remove = ak_led_remove, ++ .driver = { ++ .name = "ak_led", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init ak_led_init(void) ++{ ++ return platform_driver_register(&ak_led_driver); ++} ++ ++static void __exit ak_led_exit(void) ++{ ++ platform_driver_unregister(&ak_led_driver); ++} ++ ++module_init(ak_led_init); ++module_exit(ak_led_exit); ++ ++MODULE_AUTHOR("Hongguang Du "); ++MODULE_DESCRIPTION("AK LED driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:ak_led"); +diff --git a/drivers/leds/ledtrig-sleep.c b/drivers/leds/ledtrig-sleep.c +new file mode 100644 +index 00000000..f1640421 +--- /dev/null ++++ b/drivers/leds/ledtrig-sleep.c +@@ -0,0 +1,80 @@ ++/* drivers/leds/ledtrig-sleep.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++ ++static int ledtrig_sleep_pm_callback(struct notifier_block *nfb, ++ unsigned long action, ++ void *ignored); ++ ++DEFINE_LED_TRIGGER(ledtrig_sleep) ++static struct notifier_block ledtrig_sleep_pm_notifier = { ++ .notifier_call = ledtrig_sleep_pm_callback, ++ .priority = 0, ++}; ++ ++static void ledtrig_sleep_early_suspend(struct early_suspend *h) ++{ ++ led_trigger_event(ledtrig_sleep, LED_FULL); ++} ++ ++static void ledtrig_sleep_early_resume(struct early_suspend *h) ++{ ++ led_trigger_event(ledtrig_sleep, LED_OFF); ++} ++ ++static struct early_suspend ledtrig_sleep_early_suspend_handler = { ++ .suspend = ledtrig_sleep_early_suspend, ++ .resume = ledtrig_sleep_early_resume, ++}; ++ ++static int ledtrig_sleep_pm_callback(struct notifier_block *nfb, ++ unsigned long action, ++ void *ignored) ++{ ++ switch (action) { ++ case PM_HIBERNATION_PREPARE: ++ case PM_SUSPEND_PREPARE: ++ led_trigger_event(ledtrig_sleep, LED_OFF); ++ return NOTIFY_OK; ++ case PM_POST_HIBERNATION: ++ case PM_POST_SUSPEND: ++ led_trigger_event(ledtrig_sleep, LED_FULL); ++ return NOTIFY_OK; ++ } ++ ++ return NOTIFY_DONE; ++} ++ ++static int __init ledtrig_sleep_init(void) ++{ ++ led_trigger_register_simple("sleep", &ledtrig_sleep); ++ register_pm_notifier(&ledtrig_sleep_pm_notifier); ++ register_early_suspend(&ledtrig_sleep_early_suspend_handler); ++ return 0; ++} ++ ++static void __exit ledtrig_sleep_exit(void) ++{ ++ unregister_early_suspend(&ledtrig_sleep_early_suspend_handler); ++ unregister_pm_notifier(&ledtrig_sleep_pm_notifier); ++ led_trigger_unregister_simple(ledtrig_sleep); ++} ++ ++module_init(ledtrig_sleep_init); ++module_exit(ledtrig_sleep_exit); ++ +diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig +index ce1e7ba9..3dee99a5 100644 +--- a/drivers/media/video/Kconfig ++++ b/drivers/media/video/Kconfig +@@ -1057,6 +1057,14 @@ config SOC_CAMERA_OV9740 + help + This is a ov9740 camera driver + ++menuconfig LINUX_AKSENSOR ++ tristate "aksensor support" ++ depends on SOC_CAMERA && I2C ++ help ++ This is a aksensor driver ++ ++source "drivers/media/video/plat-anyka/Kconfig" ++ + config MX1_VIDEO + bool + +@@ -1115,6 +1123,13 @@ config VIDEO_OMAP2 + ---help--- + This is a v4l2 driver for the TI OMAP2 camera capture interface + ++config VIDEO_AK ++ tristate "AK Camera Interface driver" ++ depends on VIDEO_DEV && SOC_CAMERA ++ select VIDEOBUF_DMA_CONTIG ++ ---help--- ++ This is a v4l2 driver for the AK Platform Camera Interface ++ + config VIDEO_MX2_HOSTSUPPORT + bool + +diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile +index a6282a3a..2010202e 100644 +--- a/drivers/media/video/Makefile ++++ b/drivers/media/video/Makefile +@@ -96,6 +96,7 @@ obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o + obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o + obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o + obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o ++obj-y += plat-anyka/ + + # And now the v4l2 drivers: + +@@ -182,6 +183,7 @@ obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o + obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o + obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o + obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o ++obj-$(CONFIG_VIDEO_AK) += plat-anyka/ak_camera.o plat-anyka/ak39_isp.o + + obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o + +diff --git a/drivers/media/video/plat-anyka/Kconfig b/drivers/media/video/plat-anyka/Kconfig +new file mode 100755 +index 00000000..b2edcdf9 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/Kconfig +@@ -0,0 +1,42 @@ ++config SENSOR_GC0308 ++ tristate "gc0308 sensor support" ++ depends on LINUX_AKSENSOR ++ help ++ This is a gc0308 sensor driver ++ ++config SENSOR_OV2643 ++ tristate "ov2643 sensor support" ++ depends on LINUX_AKSENSOR ++ help ++ This is a ov2643 sensor driver ++ ++config SENSOR_OV7725 ++ tristate "ov7725 sensor support" ++ depends on LINUX_AKSENSOR ++ help ++ This is a ov7725 sensor driver ++ ++config SENSOR_HM1375 ++ tristate "hm1375 sensor support" ++ depends on LINUX_AKSENSOR ++ help ++ This is a hm1375 sensor driver ++ ++config SENSOR_OV9712 ++ tristate "ov9712 sensor support" ++ depends on LINUX_AKSENSOR ++ help ++ This is a ov9712 sensor driver ++ ++config SENSOR_OV2710 ++ tristate "ov2710 sensor support" ++ depends on LINUX_AKSENSOR ++ help ++ This is a ov2710 sensor driver ++ ++config SENSOR_AR0130 ++ tristate "ar0130 sensor support" ++ depends on LINUX_AKSENSOR ++ help ++ This is a ar0130 sensor driver ++ +diff --git a/drivers/media/video/plat-anyka/Makefile b/drivers/media/video/plat-anyka/Makefile +new file mode 100755 +index 00000000..b8aeb4e6 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/Makefile +@@ -0,0 +1,11 @@ ++# sensor ++obj-$(CONFIG_SENSOR_GC0308) += camera_gc0308.o ++obj-$(CONFIG_SENSOR_OV2643) += camera_ov2643.o ++obj-$(CONFIG_SENSOR_OV7725) += camera_ov7725.o ++obj-$(CONFIG_SENSOR_HM1375) += camera_hm1375.o ++obj-$(CONFIG_SENSOR_OV9712) += camera_ov9712.o ++obj-$(CONFIG_SENSOR_OV2710) += camera_ov2710.o ++obj-$(CONFIG_SENSOR_AR0130) += camera_ar0130.o ++ ++# wrap sensor ++obj-$(CONFIG_LINUX_AKSENSOR) += wrap_sensor.o aksensor.o +diff --git a/drivers/media/video/plat-anyka/ak39_isp.c b/drivers/media/video/plat-anyka/ak39_isp.c +new file mode 100755 +index 00000000..23073a21 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/ak39_isp.c +@@ -0,0 +1,2166 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ak39_isp.h" ++ ++//#define ISP_DEBUG ++#ifdef ISP_DEBUG ++#define isp_dbg(stuff...) printk(KERN_INFO " ISP: " stuff) ++#else ++#define isp_dbg(fmt, args...) do{}while(0) ++#endif ++ ++#define isp_info(stuff...) printk(KERN_INFO " ISP: " stuff) ++ ++#define BRIGHTNESS_CHART_SIZE 64 ++#define HISTOGRAM_SIZE (1024*3) ++ ++static unsigned long histo_arr[256] = {0}; ++ ++//the gamma_table by called build_gamma_table() ++unsigned long gamma_table[7][BRIGHTNESS_CHART_SIZE] = { ++ { 0x15141312, 0x18171615, 0x1b1b1a19, 0x1f1e1d1c, 0x22212120, //disable gamma ++ 0x26252423, 0x29282727, 0x2d2c2b2a, 0x302f2e2d, 0x33333231, ++ 0x37363534, 0x3a393838, 0x3e3d3c3b, 0x41403f3e, 0x44444342, ++ 0x48474645, 0x4b4a4a49, 0x4f4e4d4c, 0x52515050, 0x56555453, ++ 0x59585756, 0x5c5c5b5a, 0x605f5e5d, 0x63626261, 0x67666564, ++ 0x6a696868, 0x6e6d6c6b, 0x71706f6e, 0x74747372, 0x78777675, ++ 0x7b7a7a79, 0x7f7e7d7c, 0x82818080, 0x85858483, 0x89888786, ++ 0x8c8b8b8a, 0x908f8e8d, 0x93929191, 0x97969594, 0x9a999897, ++ 0x9d9d9c9b, 0xa1a09f9e, 0xa4a3a3a2, 0xa8a7a6a5, 0xabaaa9a9, ++ 0xafaeadac, 0xb2b1b0af, 0xb5b5b4b3, 0xb9b8b7b6, 0xbcbbbbba, ++ 0xc0bfbebd, 0xc3c2c1c1, 0xc7c6c5c4, 0xcac9c8c7, 0xcdcccccb, ++ 0xd1d0cfce, 0xd4d3d2d2, 0xd8d7d6d5, 0xdbdad9d8, 0xdededddc, ++ 0xe2e1e0df, 0xe5e4e4e3, 0xe9e8e7e6, 0xecebeaea}, ++ { 0x0c080400, 0x201c1810, 0x26252422, 0x24252626, 0x27262525, //ycx ++ 0x29282827, 0x2a2a2929, 0x2d2c2c2b, 0x31302f2e, 0x35343332, ++ 0x39383736, 0x3d3c3b3a, 0x41403f3e, 0x46454342, 0x4a494847, ++ 0x4f4e4c4b, 0x53525150, 0x58575554, 0x5c5b5a59, 0x5f5e5e5d, ++ 0x62616160, 0x65646363, 0x69676766, 0x6d6c6b6a, 0x7371706f, ++ 0x78777574, 0x7c7b7a79, 0x807f7e7d, 0x85838281, 0x89888786, ++ 0x8d8c8b8a, 0x92918f8e, 0x97959493, 0x9b9a9998, 0x9f9e9d9c, ++ 0xa3a2a1a0, 0xa7a6a5a4, 0xaaa9a9a8, 0xaeadacab, 0xb1b0b0af, ++ 0xb5b4b3b2, 0xb8b7b6b5, 0xbcbbbab9, 0xc0bfbebd, 0xc3c2c1c1, ++ 0xc6c5c4c4, 0xc8c7c7c6, 0xcacac9c9, 0xcbcbcbca, 0xcecdcccc, ++ 0xd1d0cfce, 0xd5d4d3d2, 0xd9d8d7d6, 0xdddcdbda, 0xe1e0dfde, ++ 0xe4e4e3e2, 0xe8e7e6e5, 0xecebeae9, 0xf0efeeed, 0xf3f2f1f1, ++ 0xf6f5f5f4, 0xf9f8f8f7, 0xfcfbfaf9, 0xfefefdfc}, ++ { 0x10101010, 0x11111111, 0x12121212, 0x14131313, 0x15151514, 0x17171616, 0x19191818, 0x1b1b1a1a, ++ 0x1e1e1d1c, 0x20201f1f, 0x23222121, 0x25252423, 0x28272726, 0x2b2a2a29, 0x2e2d2c2c, 0x31302f2f, ++ 0x34333232, 0x37363635, 0x3a393938, 0x3d3c3c3b, 0x403f3f3e, 0x44434241, 0x47464544, 0x4a494948, ++ 0x4d4d4c4b, 0x51504f4e, 0x54535251, 0x57565655, 0x5b5a5958, 0x5f5e5d5c, 0x62616060, 0x65646362, ++ 0x68676665, 0x6b6a6969, 0x6e6e6d6c, 0x7271706f, 0x76757473, 0x7a797877, 0x7e7d7c7b, 0x8281807f, ++ 0x87868583, 0x8c8b8988, 0x91908e8d, 0x95949492, 0x99989796, 0x9d9c9b9a, 0xa09f9f9e, 0xa4a3a2a1, ++ 0xa8a7a6a5, 0xadabaaa9, 0xb1b0afae, 0xb5b4b3b2, 0xb9b8b7b6, 0xbebdbbba, 0xc2c1c0bf, 0xc7c5c4c3, ++ 0xcbcac9c8, 0xd0cfcdcc, 0xd4d3d2d1, 0xd9d8d7d5, 0xdedcdbda, 0xe2e1e0df, 0xe7e6e5e3, 0xebebe9e8}, ++ { 0x11111010, 0x14131212, 0x16151514, 0x18181717, 0x1b1a1a19, 0x1e1d1c1c, 0x20201f1e, 0x24232221, ++ 0x27262524, 0x2a292928, 0x2e2d2c2b, 0x3231302f, 0x37363533, 0x3b3b3938, 0x403f3e3d, 0x44434241, ++ 0x49474645, 0x4d4c4b4a, 0x5251504e, 0x57565553, 0x5c5b5a58, 0x61605f5d, 0x66656462, 0x6b6a6968, ++ 0x716f6e6d, 0x76747372, 0x7b797877, 0x807e7d7c, 0x84838281, 0x89888786, 0x8e8d8b8a, 0x9291908f, ++ 0x96959493, 0x9b9a9897, 0x9e9d9c9c, 0xa2a1a09f, 0xa6a5a4a3, 0xa9a8a8a7, 0xadacabaa, 0xb0afaead, ++ 0xb3b2b1b1, 0xb6b5b4b4, 0xb9b8b7b7, 0xbbbbbab9, 0xbebebdbc, 0xc1c0c0bf, 0xc4c3c2c2, 0xc6c5c5c4, ++ 0xc8c8c7c7, 0xcbcacac9, 0xcdcdcccc, 0xd0cfcfce, 0xd2d2d1d0, 0xd4d4d3d3, 0xd7d6d6d5, 0xd9d8d8d7, ++ 0xdbdbdada, 0xdedddcdc, 0xe0dfdfde, 0xe2e2e1e0, 0xe4e4e3e3, 0xe7e6e5e5, 0xe9e8e8e7, 0xebebeae9}, ++ { 0x10101010, 0x12121111, 0x13131312, 0x15151414, 0x17161615, 0x19181817, 0x1b1b1a19, 0x1e1d1d1c, ++ 0x21201f1f, 0x25242322, 0x29282726, 0x2e2c2b2a, 0x3332302f, 0x37363534, 0x3c3b3a38, 0x41403f3d, ++ 0x47464443, 0x4d4b4a48, 0x5351504e, 0x59575654, 0x5f5d5c5a, 0x65636260, 0x6b6a6867, 0x71706e6d, ++ 0x77767473, 0x7d7c7a79, 0x8382807f, 0x89878684, 0x8e8d8b8a, 0x9392908f, 0x98969594, 0x9c9b9a99, ++ 0xa09f9e9d, 0xa4a3a2a1, 0xa8a7a6a5, 0xacabaaa9, 0xafaeadad, 0xb2b1b1b0, 0xb5b5b4b3, 0xb8b8b7b6, ++ 0xbbbabab9, 0xbebdbdbc, 0xc1c0bfbf, 0xc3c3c2c1, 0xc6c5c5c4, 0xc8c7c7c6, 0xcac9c9c8, 0xcccbcbca, ++ 0xcecdcdcc, 0xd0cfcfce, 0xd2d1d1d0, 0xd4d3d3d2, 0xd6d5d5d4, 0xd8d7d7d6, 0xdad9d9d8, 0xdcdbdbda, ++ 0xdedddddc, 0xe0dfdfde, 0xe1e1e0e0, 0xe3e3e2e2, 0xe5e5e4e4, 0xe7e7e6e6, 0xe9e9e8e8, 0xebebeaea}, ++ { 0x10101010, 0x10101010, 0x11111111, 0x12121211, 0x13131312, 0x15151414, 0x17171616, 0x1a191918, ++ 0x1e1d1c1b, 0x2221201f, 0x27262523, 0x2e2c2a29, 0x32302f2e, 0x36353433, 0x3b393837, 0x3f3e3d3c, ++ 0x45434241, 0x4a494746, 0x504e4d4b, 0x55545251, 0x5b5a5857, 0x615f5e5c, 0x67656462, 0x6d6b6a68, ++ 0x7371706e, 0x79777674, 0x7f7d7c7a, 0x85838280, 0x8b898886, 0x908f8d8c, 0x96949392, 0x9b9a9897, ++ 0xa09f9e9c, 0xa5a4a2a1, 0xa9a8a7a6, 0xaeadacaa, 0xb2b1b0af, 0xb6b5b4b3, 0xb9b9b8b7, 0xbdbcbbba, ++ 0xc0c0bfbe, 0xc4c3c2c1, 0xc7c6c5c4, 0xc9c9c8c7, 0xcccbcbca, 0xcfcecdcd, 0xd1d0d0cf, 0xd3d3d2d1, ++ 0xd5d5d4d4, 0xd7d7d6d6, 0xd9d8d8d8, 0xdbdadad9, 0xdcdcdbdb, 0xdfdedddd, 0xe1e0e0df, 0xe3e2e2e1, ++ 0xe5e4e4e3, 0xe6e6e5e5, 0xe7e7e7e6, 0xe8e8e8e7, 0xe9e9e8e8, 0xe9e9e9e9, 0xeaeaeaea, 0xebebeaea}, ++ { 0x15131110, 0x1c1a1817, 0x23211f1e, 0x2a282725, 0x32302e2c, 0x39383634, 0x3d3c3b3a, 0x42413f3e, ++ 0x46454443, 0x4b4a4948, 0x504f4e4c, 0x55545251, 0x5a595756, 0x5f5e5c5b, 0x64636260, 0x68676666, ++ 0x6c6b6a69, 0x706f6e6d, 0x74737271, 0x78777675, 0x7c7b7a79, 0x807f7e7d, 0x84838281, 0x89888785, ++ 0x8d8c8b8a, 0x91908f8e, 0x95949392, 0x99989796, 0x9c9b9a99, 0x9f9e9d9c, 0xa2a1a0a0, 0xa5a4a4a3, ++ 0xa8a7a7a6, 0xabaaaaa9, 0xaeadadac, 0xb1b0b0af, 0xb4b3b3b2, 0xb7b6b5b5, 0xbab9b8b8, 0xbcbcbbba, ++ 0xbfbfbebd, 0xc2c1c1c0, 0xc5c4c3c3, 0xc7c7c6c5, 0xc9c9c8c8, 0xcbcacaca, 0xcdcccccb, 0xcfcececd, ++ 0xd1d0d0cf, 0xd3d2d2d1, 0xd4d4d3d3, 0xd6d6d5d5, 0xd8d8d7d7, 0xdad9d9d8, 0xdcdbdbda, 0xdddddcdc, ++ 0xdfdfdede, 0xe1e0e0df, 0xe2e2e2e1, 0xe4e4e3e3, 0xe6e5e5e5, 0xe8e7e7e6, 0xe9e9e8e8, 0xebebeaea}, ++}; ++ ++/* YUV 640x480, continuous mode, temporarily used */ ++void setup_yuv_video_out(struct isp_struct *pcdev) ++{ ++ pcdev->isp_ctrltbl[3] = 0x00000000; ++ pcdev->isp_ctrltbl[4] = 0x00000000; ++ pcdev->isp_ctrltbl[5] = 0x00000000; ++ pcdev->isp_ctrltbl[6] = 0x00000000; ++ pcdev->isp_ctrltbl[7] = 0x00000000; ++ pcdev->isp_ctrltbl[8] = 0x00000000; ++ pcdev->isp_ctrltbl[9] = 0x00000000; ++ pcdev->isp_ctrltbl[10] = 0x00000000; ++ pcdev->isp_ctrltbl[11] = 0x00000000; ++ pcdev->isp_ctrltbl[12] = 0x00000000; ++ pcdev->isp_ctrltbl[13] = 0x00001c68; ++ pcdev->isp_ctrltbl[14] = 0x00005538; ++ pcdev->isp_ctrltbl[15] = 0x00008e08; ++ pcdev->isp_ctrltbl[16] = 0x4000c6d8; ++ pcdev->isp_ctrltbl[23] = 0x00000000; ++ pcdev->isp_ctrltbl[24] = 0x00000000; ++ pcdev->isp_ctrltbl[27] = 0x00022bb0; ++ pcdev->isp_ctrltbl[28] = 0x0027bbb0; ++ pcdev->isp_ctrltbl[29] = 0x00000000; ++ pcdev->isp_ctrltbl[30] = 0x00000000; ++ pcdev->isp_ctrltbl[31] = 0x00000000; ++ ++ pcdev->img_ctrltbl1[15] = 0x00000000; ++ ++ pcdev->osd_chktbl[0] = 0x00008080; ++ pcdev->osd_chktbl[1] = 0x00ff8080; ++ pcdev->osd_chktbl[2] = 0x00c08080; ++ pcdev->osd_chktbl[3] = 0x00266ac0; ++ pcdev->osd_chktbl[4] = 0x0071408a; ++ pcdev->osd_chktbl[5] = 0x004b554a; ++ pcdev->osd_chktbl[6] = 0x00599540; ++ pcdev->osd_chktbl[7] = 0x000ec075; ++ pcdev->osd_chktbl[8] = 0x0034aab5; ++ pcdev->osd_chktbl[9] = 0x00786085; ++ pcdev->osd_chktbl[10] = 0x002c8aa0; ++ pcdev->osd_chktbl[11] = 0x0068d535; ++ pcdev->osd_chktbl[12] = 0x0034aa5a; ++ pcdev->osd_chktbl[13] = 0x0043e9ab; ++ pcdev->osd_chktbl[14] = 0x004b55a5; ++ pcdev->osd_chktbl[15] = 0x00008080; ++ pcdev->osd_chktbl[16] = 0x00000000; ++ pcdev->osd_chktbl[17] = 0x00000000; ++ pcdev->osd_chktbl[18] = 0x00000000; ++ pcdev->osd_chktbl[19] = 0x00000000; ++ pcdev->osd_chktbl[20] = 0x00000000; ++ pcdev->osd_chktbl[21] = 0x00000000; ++ pcdev->osd_chktbl[22] = 0x00000000; ++ pcdev->osd_chktbl[23] = 0x00000000; ++ pcdev->osd_chktbl[24] = 0x00000000; ++ pcdev->osd_chktbl[25] = 0x00000000; ++ pcdev->osd_chktbl[26] = 0x00000000; ++ pcdev->osd_chktbl[27] = 0x00000000; ++ pcdev->osd_chktbl[28] = 0x00000000; ++ pcdev->osd_chktbl[29] = 0x00000000; ++ pcdev->osd_chktbl[30] = 0x00000000; ++ pcdev->osd_chktbl[31] = 0x00000000; ++ ++ pcdev->img_ctrltbl2[25] = 0x00000000; ++ pcdev->img_ctrltbl2[26] = 0x00009000; ++} ++ ++ ++void setup_yuv_video_bypass(struct isp_struct *pcdev) ++{ ++ pcdev->isp_ctrltbl[3] = 0x00000000; ++ pcdev->isp_ctrltbl[4] = 0x00000000; ++ pcdev->isp_ctrltbl[5] = 0x00000000; ++ pcdev->isp_ctrltbl[6] = 0x00000000; ++ pcdev->isp_ctrltbl[7] = 0x00000000; ++ pcdev->isp_ctrltbl[8] = 0x00000000; ++ pcdev->isp_ctrltbl[9] = 0x00000000; ++ pcdev->isp_ctrltbl[10] = 0x00000000; ++ pcdev->isp_ctrltbl[11] = 0x00000000; ++ pcdev->isp_ctrltbl[12] = 0x00000000; ++ pcdev->isp_ctrltbl[13] = 0x00001c68; ++ pcdev->isp_ctrltbl[14] = 0x00005538; ++ pcdev->isp_ctrltbl[15] = 0x00008e08; ++ pcdev->isp_ctrltbl[16] = 0x4000c6d8; ++ pcdev->isp_ctrltbl[23] = 0x00000000; ++ pcdev->isp_ctrltbl[24] = 0x00000000; ++ pcdev->isp_ctrltbl[27] = 0x0001e0f8; ++ pcdev->isp_ctrltbl[28] = 0x002770f8; ++ pcdev->isp_ctrltbl[29] = 0x00000000; ++ pcdev->isp_ctrltbl[30] = 0x00000000; ++ pcdev->isp_ctrltbl[31] = 0x00000000; ++ ++ pcdev->img_ctrltbl1[15] = 0x00000000; ++ ++ //0x2000002c ++// memset(pcdev->osd_chktbl, 0 , sizeof(pcdev->osd_chktbl)); ++ pcdev->osd_chktbl[0] = 0x00008080; ++ pcdev->osd_chktbl[1] = 0x00ff8080; ++ pcdev->osd_chktbl[2] = 0x00c08080; ++ pcdev->osd_chktbl[3] = 0x00266ac0; ++ pcdev->osd_chktbl[4] = 0x0071408a; ++ pcdev->osd_chktbl[5] = 0x004b554a; ++ pcdev->osd_chktbl[6] = 0x00599540; ++ pcdev->osd_chktbl[7] = 0x000ec075; ++ pcdev->osd_chktbl[8] = 0x0034aab5; ++ pcdev->osd_chktbl[9] = 0x00786085; ++ pcdev->osd_chktbl[10] = 0x002c8aa0; ++ pcdev->osd_chktbl[11] = 0x0068d535; ++ pcdev->osd_chktbl[12] = 0x0034aa5a; ++ pcdev->osd_chktbl[13] = 0x0043e9ab; ++ pcdev->osd_chktbl[14] = 0x004b55a5; ++ pcdev->osd_chktbl[15] = 0x00008080; ++ pcdev->osd_chktbl[16] = 0x00000000; ++ pcdev->osd_chktbl[17] = 0x00000000; ++ pcdev->osd_chktbl[18] = 0x00000000; ++ pcdev->osd_chktbl[19] = 0x00000000; ++ pcdev->osd_chktbl[20] = 0x00000000; ++ pcdev->osd_chktbl[21] = 0x00000000; ++ pcdev->osd_chktbl[22] = 0x00000000; ++ pcdev->osd_chktbl[23] = 0x00000000; ++ pcdev->osd_chktbl[24] = 0x00000000; ++ pcdev->osd_chktbl[25] = 0x00000000; ++ pcdev->osd_chktbl[26] = 0x00000000; ++ pcdev->osd_chktbl[27] = 0x00000000; ++ pcdev->osd_chktbl[28] = 0x00000000; ++ pcdev->osd_chktbl[29] = 0x00000000; ++ pcdev->osd_chktbl[30] = 0x00000000; ++ pcdev->osd_chktbl[31] = 0x00000000; ++ ++ pcdev->img_ctrltbl2[25] = 0x00000000; ++ pcdev->img_ctrltbl2[26] = 0x00000000; ++} ++ ++void setup_rgb_video(struct isp_struct *pcdev) ++{ ++ pcdev->isp_ctrltbl[3] = 0x00000000; ++ pcdev->isp_ctrltbl[4] = 0x00000000; ++ pcdev->isp_ctrltbl[5] = 0x00000000; ++ pcdev->isp_ctrltbl[6] = 0x00000000; ++ pcdev->isp_ctrltbl[7] = 0x00000000; ++ pcdev->isp_ctrltbl[8] = 0x00000000; ++ pcdev->isp_ctrltbl[9] = 0x00000000; ++ pcdev->isp_ctrltbl[10] = 0x00000000; ++ pcdev->isp_ctrltbl[11] = 0x00000000; ++ pcdev->isp_ctrltbl[12] = 0x00000000; ++ pcdev->isp_ctrltbl[13] = 0x00005fbd; ++ pcdev->isp_ctrltbl[14] = 0x00011f37; ++ pcdev->isp_ctrltbl[15] = 0x0001deb1; ++ pcdev->isp_ctrltbl[16] = 0x40029e2b; ++ pcdev->isp_ctrltbl[23] = 0x00000000; ++ pcdev->isp_ctrltbl[24] = 0x00000000; ++ pcdev->isp_ctrltbl[27] = 0x0001ed68; ++ pcdev->isp_ctrltbl[28] = 0x01537168; ++ pcdev->isp_ctrltbl[29] = 0x00000000; ++ pcdev->isp_ctrltbl[30] = 0x00000000; ++ pcdev->isp_ctrltbl[31] = 0x00000000; ++ ++ pcdev->img_ctrltbl1[15] = 0x00000000; ++ ++ //0x2000002c ++ pcdev->osd_chktbl[0] = 0x00008080; ++ pcdev->osd_chktbl[1] = 0x00ff8080; ++ pcdev->osd_chktbl[2] = 0x00c08080; ++ pcdev->osd_chktbl[3] = 0x00266ac0; ++ pcdev->osd_chktbl[4] = 0x0071408a; ++ pcdev->osd_chktbl[5] = 0x004b554a; ++ pcdev->osd_chktbl[6] = 0x00599540; ++ pcdev->osd_chktbl[7] = 0x000ec075; ++ pcdev->osd_chktbl[8] = 0x0034aab5; ++ pcdev->osd_chktbl[9] = 0x00786085; ++ pcdev->osd_chktbl[10] = 0x002c8aa0; ++ pcdev->osd_chktbl[11] = 0x0068d535; ++ pcdev->osd_chktbl[12] = 0x0034aa5a; ++ pcdev->osd_chktbl[13] = 0x0043e9ab; ++ pcdev->osd_chktbl[14] = 0x004b55a5; ++ pcdev->osd_chktbl[15] = 0x00008080; ++ pcdev->osd_chktbl[16] = 0x00000000; ++ pcdev->osd_chktbl[17] = 0x00000000; ++ pcdev->osd_chktbl[18] = 0x00000000; ++ pcdev->osd_chktbl[19] = 0x00000000; ++ pcdev->osd_chktbl[20] = 0x00000000; ++ pcdev->osd_chktbl[21] = 0x00000000; ++ pcdev->osd_chktbl[22] = 0x00000000; ++ pcdev->osd_chktbl[23] = 0x00000000; ++ pcdev->osd_chktbl[24] = 0x00000000; ++ pcdev->osd_chktbl[25] = 0x00000000; ++ pcdev->osd_chktbl[26] = 0x00000000; ++ pcdev->osd_chktbl[27] = 0x00000000; ++ pcdev->osd_chktbl[28] = 0x00000000; ++ pcdev->osd_chktbl[29] = 0x00000000; ++ pcdev->osd_chktbl[30] = 0x00000000; ++ pcdev->osd_chktbl[31] = 0x00000000; ++ ++ pcdev->img_ctrltbl2[25] = 0x00000000; ++ pcdev->img_ctrltbl2[26] = 0x00008800; ++} ++ ++void isp_restart_update_param(struct isp_struct *isp) ++{ ++ // disable the status, then enable ++ REG32(isp->base + ISP_IRQ_STATUS) |= ++ (1 << (12+ISP_FRAME_UPDATE))|(1 << ISP_FRAME_UPDATE); ++} ++ ++void isp_update_regtable(struct isp_struct *isp, int force) ++{ ++ if (force) ++ REG32(isp->base + ISP_ORDER_CTRL) = ((3<<30) | (isp->addr & 0x3fffffff)); ++ else ++ REG32(isp->base + ISP_ORDER_CTRL) = ((1<<31) | (isp->addr & 0x3fffffff)); ++} ++ ++ ++int update_cur_mode_class(struct isp_struct *isp) ++{ ++ switch(isp->cur_mode) { ++ case ISP_JPEG_MODE: ++ case ISP_JPEG_VIDEO: ++ isp->cur_mode_class = ISP_JPEG_CLASS; ++ break; ++ case ISP_YUV_OUT: ++ case ISP_YUV_BYPASS: ++ case ISP_YUV_MERGER_OUT: ++ case ISP_YUV_BIG: ++ case ISP_YUV_VIDEO_OUT: ++ case ISP_YUV_VIDEO_BYPASS: ++ case ISP_YUV_VIDEO_MERGER_OUT: ++ isp->cur_mode_class = ISP_YUV_CLASS; ++ break; ++ case ISP_RGB_OUT: ++ case ISP_RGB_VIDEO_OUT: ++ case ISP_RGB_BIG: ++ isp->cur_mode_class = ISP_RGB_CLASS; ++ break; ++ } ++ return 0; ++} ++static void __isp_set_white_pram(struct isp_struct *isp, ++ unsigned int co_g, unsigned int co_r, unsigned int co_b) ++{ ++ isp->img_ctrltbl1[0] &= ~(0xffffff); ++ isp->img_ctrltbl1[1] &= ~(0xfff); ++ isp->img_ctrltbl1[0] |= ((co_g & 0xfff) << 12)|(co_r & 0xfff); ++ isp->img_ctrltbl1[1] |= (co_b & 0xfff); ++} ++ ++static int isp_set_auto_wb_support(struct isp_struct *isp, ++ struct isp_auto_white_balance *ctrl) ++{ ++ int i; ++ ++ for (i = 0; i < 6; i++) ++ isp->isp_ctrltbl[17+i] &= ~(0x3ff << 22); ++ isp->isp_ctrltbl[25] = 0x0; ++ isp->isp_ctrltbl[26] = 0x0; ++ ++ isp->isp_ctrltbl[17] |= ((ctrl->r_high & 0x3ff) << 22); ++ isp->isp_ctrltbl[18] |= ((ctrl->r_low & 0x3ff) << 22); ++ isp->isp_ctrltbl[19] |= ((ctrl->g_high & 0x3ff) << 22); ++ isp->isp_ctrltbl[20] |= ((ctrl->g_low & 0x3ff) << 22); ++ isp->isp_ctrltbl[21] |= ((ctrl->b_high & 0x3ff) << 22); ++ isp->isp_ctrltbl[22] |= ((ctrl->b_low & 0x3ff) << 22); ++ ++ // enable white balance calculate ++ isp->isp_ctrltbl[25] = (1 << 31)|((ctrl->grb_high & 0x3ff) << 20) ++ |((ctrl->grb_low & 0x3ff) << 10) ++ |(ctrl->gr_low & 0x3ff); ++ isp->isp_ctrltbl[26] = ((ctrl->gb_high & 0x3ff) << 20) ++ |((ctrl->gb_high & 0x3ff) << 10) ++ |(ctrl->gb_low & 0x3ff); ++ ++ ++ isp->wb_param.co_g = 1024; ++ isp->wb_param.co_r = 1024; ++ isp->wb_param.co_b = 1024; ++ ++ __isp_set_white_pram(isp, isp->wb_param.co_g, ++ isp->wb_param.co_r, isp->wb_param.co_b); ++ return 0; ++} ++ ++static void __isp_set_color_crr(struct isp_struct *isp, struct isp_color_correct *ctrl, ++ int row, int col) ++{ ++ int min[3][3]; ++ unsigned long cmd = 0; ++ int value, i, j; ++ ++ memset(min, 0, 9 * sizeof(int)); ++ min[0][0] = min[1][1] = min[2][2] = (1 << 10); ++ ++ if (ctrl->cc_thrs_high - ctrl->cc_thrs_low <= 1) { ++ ctrl->cc_thrs_high = ctrl->cc_thrs_low + 1; ++ } ++ ++ for (i = 0; i < row; i++) { ++ for (j= 0; jccMtrx[%d][%d] = %d\n", i, j, ctrl->ccMtrx[i][j]); ++ } ++ ++ for (i = 0; i < row; i++) { ++ ctrl->ccMtrx[i][0] = (ctrl->ccMtrx[i][0] > 2560)? 2560 ++ : ((ctrl->ccMtrx[i][0] < -2560) ? (-2560) : ctrl->ccMtrx[i][0]); ++ ctrl->ccMtrx[i][1] = (ctrl->ccMtrx[i][1] > 2560)? 2560 ++ : ((ctrl->ccMtrx[i][1] < -2560) ? (-2560) : ctrl->ccMtrx[i][1]); ++ ctrl->ccMtrx[i][2] = (ctrl->ccMtrx[i][2] > 2560)? 2560 ++ : ((ctrl->ccMtrx[i][2] < -2560) ? (-2560) : ctrl->ccMtrx[i][2]); ++ ++ value = (ctrl->ccMtrx[i][0] - min[i][0])/(ctrl->cc_thrs_high - ctrl->cc_thrs_low); ++ cmd = ((value & 0x1fff) << CC_CALVAL_BIT) | ((ctrl->ccMtrx[i][0] & 0x1fff) << CC_TMPVAL_BIT); ++ isp->img_ctrltbl1[2+3*i] = cmd; ++ ++ value = (ctrl->ccMtrx[i][1] - min[i][1])/(ctrl->cc_thrs_high - ctrl->cc_thrs_low); ++ cmd = ((value & 0x1fff) << CC_CALVAL_BIT) | ((ctrl->ccMtrx[i][1] & 0x1fff) << CC_TMPVAL_BIT); ++ isp->img_ctrltbl1[2+3*i+1] = cmd; ++ ++ value = (ctrl->ccMtrx[i][2] - min[i][2])/(ctrl->cc_thrs_high - ctrl->cc_thrs_low); ++ cmd = ((value & 0x1fff) << CC_CALVAL_BIT) | ((ctrl->ccMtrx[i][2] & 0x1fff) << CC_TMPVAL_BIT); ++ isp->img_ctrltbl1[2+3*i+2] = cmd; ++ } ++ ++ // enable color correction ++ isp->img_ctrltbl1[11] |= ((1 << 31)|((ctrl->cc_thrs_high & 0x7ff) << 11) ++ |(ctrl->cc_thrs_low & 0x7ff)); ++} ++ ++static void __isp_set_uv_saturation(struct isp_struct *isp, struct isp_saturation *ctrl) ++{ ++ ctrl->Chigh = (ctrl->Chigh > 255) ? 255 : (ctrl->Chigh < 0) ? 0 : ctrl->Chigh; ++ ctrl->Clow = (ctrl->Clow > 255) ? 255 : (ctrl->Clow < 0) ? 0 : ctrl->Clow; ++ ++ if (ctrl->Chigh <= ctrl->Clow) ++ ctrl->Chigh = ctrl->Clow + 1; ++ ++ if (ctrl->Khigh <= ctrl->Klow) ++ ctrl->Khigh = ctrl->Klow + 1; ++ ++ // enable saturation adjust ++ ctrl->Kslope = (ctrl->Khigh - ctrl->Klow) * 256 / (ctrl->Chigh - ctrl->Clow); ++ isp->img_ctrltbl1[13] |= (1 << 31)|(ctrl->Klow << 20)|(ctrl->Khigh << 10)|ctrl->Kslope; ++ isp->img_ctrltbl1[14] |= (ctrl->Chigh << 24)|(ctrl->Clow << 16); ++} ++ ++static void __isp_set_brigtness(struct isp_struct *isp, ++ struct isp_brightness_enhance *ctrl) ++{ ++ ctrl->y_thrs = (ctrl->y_thrs > 2048) ? 2048 : ctrl->y_thrs; ++ ctrl->y_edgek = (ctrl->y_edgek > 2048) ? 2048 : ctrl->y_edgek; ++ ctrl->ygain = (ctrl->ygain > 64) ? 63 : ++ ((ctrl->ygain < -64) ? -63 : ctrl->ygain); ++ ++ isp->img_ctrltbl1[12] = (ctrl->ygain << Y_GAIN_BIT) ++ |(((ctrl->y_thrs * 9) & 0x7ff) << Y_EDGE_THRS_BIT) ++ |((ctrl->y_edgek & 0x7ff) << Y_EDGE_K_BIT); ++ ++ // enable edge(brightness) adjust ++ isp->img_ctrltbl1[11] |= (1 << 29); ++ // enable iso filter ++ //isp->img_ctrltbl1[13] |= (1 << 30); ++} ++ ++static void __isp_set_gamma(struct isp_struct *isp, unsigned long *gamma) ++{ ++ int i; ++ ++ for(i = 0; i < BRIGHTNESS_CHART_SIZE; i++) { ++ isp->img_lumitbl[i] = gamma[i]; ++ } ++} ++ ++static void __isp_clear_lens_param(struct isp_struct *isp) ++{ ++ int i; ++ ++ for (i = 0; i < 10; i++) ++ isp->isp_ctrltbl[3+i] = 0x0; ++ ++ for (i = 0; i < 10; i++) ++ isp->isp_ctrltbl[13+i] &= ~(0x3fffff); ++ ++ isp->isp_ctrltbl[23] = 0x0; ++ isp->isp_ctrltbl[24] = 0x0; ++} ++ ++static void __isp_set_lens_correct(struct isp_struct *isp, struct isp_lens_correct *ctrl) ++{ ++ int width = isp->lens_use_width; ++ int height = isp->lens_use_height; ++ unsigned long cmd; ++ int value = 0, i; ++ ++ // lens coef setting ++ for (i = 0; i < 10; i++) { ++ ctrl->lens_coefa[i] = (ctrl->lens_coefa[i] > 1023)?1023 ++ : ((ctrl->lens_coefa[i] < -1023) ? (-1023): ctrl->lens_coefa[i]); ++ ctrl->lens_coefb[i] = (ctrl->lens_coefb[i] > 1023)?1023 ++ : ((ctrl->lens_coefb[i] < -1023) ? (-1023): ctrl->lens_coefb[i]); ++ ctrl->lens_coefc[i] = (ctrl->lens_coefc[i] > 511)?511 ++ : ((ctrl->lens_coefc[i] < -511) ? (-511): ctrl->lens_coefc[i]); ++ ++ isp->isp_ctrltbl[3+i] = ((ctrl->lens_coefa[i] & 0x7ff) << LENS_COEFA_BIT) ++ |((ctrl->lens_coefb[i] & 0x7ff) << LENS_COEFB_BIT) ++ |(ctrl->lens_coefc[i] & 0x3ff); ++ } ++ ++ if ((width <= 0) || (height <= 0)) { ++ width = isp->fmt_width; ++ height = isp->fmt_height; ++ } ++ ++ // lens range setting ++ value = (width/2) * (width/2) + (height/2) * (height/2); ++ cmd = value / 22; ++ for (i = 0; i < 10; i++) { ++ isp->isp_ctrltbl[13+i] |= (cmd * (2*i+1)) & 0x3fffff; ++ } ++ ++ isp->isp_ctrltbl[23] = ((ctrl->lens_yref & 0x1fff) << LENS_YREF_BIT) ++ |(ctrl->lens_xref & 0x1fff); ++ isp->isp_ctrltbl[24] = (1 << 31)|(((ctrl->lens_xref * ctrl->lens_xref) ++ + (ctrl->lens_yref * ctrl->lens_yref)) & 0x7ffffff); ++} ++ ++ ++int isp_check_irq(struct isp_struct *isp) ++{ ++ unsigned int irq_status; ++ int retval = 0; ++ //int ch_flag = 0; ++ ++ irq_status = REG32(isp->base + ISP_IRQ_STATUS); ++ //printk("isp irq: %#x, %d\n", irq_status, ((irq_status >> 27) & 0x1f)); ++ if (!(irq_status & (1 << ISP_FRAME_UPDATE))) { ++ isp_restart_update_param(isp); ++ isp_update_regtable(isp, 1); ++ retval = -EINTR; ++ } ++ ++ if (irq_status & (1 << ISP_FRAME_UPDATE)) { ++ if (isp->auto_wb_param_en) ++ isp_update_auto_wb_param(isp); ++ ++ //isp_set_histogram(isp, NULL); ++ //isp_get_histogram(isp); ++ } ++ ++#if 0 ++ if (irq_status & (1 << ISP_CH1_UPDMA_OVERFLOW)) { ++ printk("ch1 updma overflow\n"); ++ } ++ ++ if (!(irq_status & (1<base + ISP_IRQ_STATUS) |= ++ (1<<(12+ISP_CH1_ONE_FRAME_END))|(1<chl2_enable) { ++ if (!(irq_status & (1<base + ISP_IRQ_STATUS) |= ++ (1<<(12+ISP_CH2_ONE_FRAME_END))|(1<base + ISP_IRQ_STATUS) |= 0x7ff; ++ return 0; ++} ++ ++int isp_clear_irq(struct isp_struct *isp) ++{ ++ REG32(isp->base + ISP_IRQ_STATUS) = 0; ++ return 0; ++} ++ ++void isp_set_even_frame(struct isp_struct *isp, ++ unsigned long yaddr_chl1, unsigned long yaddr_chl2) ++{ ++ int size; ++ ++ size = isp->chl1_width * isp->chl1_height; ++ isp->img_ctrltbl2[12] = yaddr_chl1; ++ isp->img_ctrltbl2[14] = yaddr_chl1 + size; ++ isp->img_ctrltbl2[16] = yaddr_chl1 + size * 5 / 4; ++ ++ if (isp->chl2_enable) { ++ size = isp->chl2_width * isp->chl2_height; ++ //isp->img_ctrltbl2[18] &= (3 << 30); ++ isp->img_ctrltbl2[18] = yaddr_chl2 & ~(3 << 30); ++ isp->img_ctrltbl2[20] = yaddr_chl2 + size; ++ isp->img_ctrltbl2[22] = yaddr_chl2 + size * 5 /4; ++ } ++} ++ ++void isp_set_odd_frame(struct isp_struct *isp, ++ unsigned long yaddr_chl1, unsigned long yaddr_chl2) ++{ ++ int size; ++ ++ size = isp->chl1_width * isp->chl1_height; ++ isp->img_ctrltbl2[13] = yaddr_chl1; ++ isp->img_ctrltbl2[15] = yaddr_chl1 + size; ++ isp->img_ctrltbl2[17] = yaddr_chl1 + size * 5 / 4; ++ ++ if (isp->chl2_enable) { ++ size = isp->chl2_width * isp->chl2_height; ++ isp->img_ctrltbl2[19] = yaddr_chl2; ++ isp->img_ctrltbl2[21] = yaddr_chl2 + size; ++ isp->img_ctrltbl2[23] = yaddr_chl2 + size * 5 /4; ++ } ++} ++ ++static int update_image_size(struct isp_struct *isp) ++{ ++ int iwidth = isp->cut_width; ++ int iheight = isp->cut_height; ++ ++ // the follow setup parameter only ov9712 ++ if (isp->cur_mode_class == ISP_RGB_CLASS) { ++ iwidth -= 4; ++ iheight -= 4; ++ } ++ ++ iwidth = (isp->demo_sac_en)?(iwidth-8):iwidth; ++ iheight = (isp->demo_sac_en)?(iheight-6):iheight; ++ ++ if (isp->uv_isoflt_en || isp->yedge_en) { ++ iwidth = iwidth - 6; ++ iheight = iheight - 6; ++ } ++ ++ iwidth = (isp->rgb_filter_en)?(iwidth-4):iwidth; ++ iheight = (isp->rgb_filter_en)?(iheight-4):iheight; ++ ++ isp->img_ctrltbl2[24] = ((iheight & 0x1fff) << OSD_SIZE_V_BIT) ++ |(iwidth & 0x1fff); ++ ++ // save is here, used for lens_range ++ isp->lens_use_width = iwidth; ++ isp->lens_use_height = iheight; ++ ++ isp_dbg("%s: osd total size[w=%d h=%d]\n", __func__, iwidth, iheight); ++ return 0; ++} ++ ++int isp_set_osd(struct isp_struct *isp, struct isp_osd_info *osd_info) ++{ ++ int base = 1; ++ ++ if (osd_info->channel == 2) ++ base = 4; ++ ++ isp->img_ctrltbl2[base] = 0; ++ isp->img_ctrltbl2[base + 1] = 0; ++ isp->img_ctrltbl2[base + 2] = 0; ++ ++ isp->img_ctrltbl2[base + 1] = ((osd_info->color_transparency & 0xf) << 28) ++ |(osd_info->start_ypos << 13) | osd_info->start_xpos; ++ isp->img_ctrltbl2[base + 2] = (osd_info->end_ypos << 13) | osd_info->end_xpos; ++ ++ if (osd_info->enable) ++ isp->img_ctrltbl2[base] = (1 << 31) | (osd_info->phys_addr & 0x3fffffff); ++ else ++ isp->img_ctrltbl2[base] = (0 << 31) | (osd_info->phys_addr & 0x3fffffff); ++ ++ return 0; ++} ++ ++int isp_set_channel1_scale(struct isp_struct *isp, int width, int height) ++{ ++ int xrate, yrate; ++ ++ if (height < 0 || width < 0) { ++ printk(KERN_ERR "%s: channel1 scale remain the same\n", __func__); ++ return -EINVAL; ++ } ++ ++ xrate = (65536)/(width - 1); ++ yrate = (65536)/(height - 1); ++ ++ isp_dbg("%s: xrate=%d yrate=%d width=%d height=%d\n", __func__, ++ xrate, yrate, width, height); ++ ++ isp->img_ctrltbl2[8] &= ~((0x1fff << 13)|0x1fff); ++ ++ isp->img_ctrltbl2[7] = (yrate << 16)|xrate; ++ isp->img_ctrltbl2[8] |= (height << 13)|width; ++ isp->img_ctrltbl2[9] |= (0xff << UPSCALE_FRAME_CTRL); ++ ++ isp->chl1_width = width; ++ isp->chl1_height = height; ++ ++ return 0; ++} ++ ++ ++int isp_set_cutter_window(struct isp_struct *isp, int xpos, int ypos, int width, int height) ++{ ++ struct isp_demosaic demosac; ++ struct isp_rgb_filter rgb_filter; ++ int iwidth = width; ++ int iheight = height; ++ ++ isp_dbg("%s. entry. iwidth=%d iheight=%d\n", __func__, ++ iwidth, iheight); ++ ++ if (width > isp->fmt_width || height > isp->fmt_height) { ++ printk(KERN_ERR"%s: invalid cutter window size\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (xpos > isp->fmt_width || ypos > isp->fmt_height) { ++ printk(KERN_ERR"%s: invalid cutter window position\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (width < 0 || height < 0 || xpos < 0 || ypos < 0) { ++ printk(KERN_WARNING"%s: cutter window remain the same\n", __func__); ++ return -EINVAL; ++ } ++ ++ isp->sub_sample = 0; ++ xpos -= xpos % 4; ++ ++ // the follow setup parameter only ov9712 ++ if (isp->cur_mode_class == ISP_RGB_CLASS) { ++ xpos = 3; ++ ypos = 3; ++ iwidth -= 4; ++ iheight -= 4; ++ } ++ ++ isp->isp_ctrltbl[0] = (isp->sub_sample << DSMP_RTO_BIT) ++ |(ypos << CUT_STRY_BIT)|xpos; ++ isp->isp_ctrltbl[1] = (iheight << CUT_WINH_BIT) | iwidth; ++ ++ if (isp->cur_mode_class == ISP_RGB_CLASS) { ++ demosac.enable = 1; ++ rgb_filter.enable = 1; ++ } else { ++ demosac.enable = 0; ++ rgb_filter.enable = 0; ++ } ++ ++ // should enable demo saic in RGB module ++ if (isp->demo_sac_thres <= 0) { ++ demosac.threshold = 1300; ++ isp->demo_sac_thres = demosac.threshold; ++ } else ++ demosac.threshold = isp->demo_sac_thres; ++ isp_set_demosaic(isp, &demosac); ++ ++ // enable rgb filter (noise remove) ++ if (isp->rgb_filter_thres <= 0) { ++ rgb_filter.threshold = 142; ++ isp->rgb_filter_thres = rgb_filter.threshold; ++ } else ++ rgb_filter.threshold = isp->rgb_filter_thres; ++ isp_set_rgb_filter(isp, &rgb_filter); ++ ++ isp->cut_width = width; ++ isp->cut_height = height; ++ ++ update_image_size(isp); ++ return 0; ++} ++ ++int isp_set_channel2(struct isp_struct *isp, struct isp_channel2_info *chl2_info) ++{ ++ int width = chl2_info->width; ++ int height = chl2_info->height; ++ ++ isp_dbg("%s:\n\twidth=%d height=%d isp->chl1_width=%d isp->chl1_height=%d chl2_info->enable=%d\n", ++ __func__, width, height, isp->chl1_width, isp->chl1_height, chl2_info->enable); ++ ++ if ((isp->cur_mode_class == ISP_RGB_CLASS) ++ &&((isp->fmt_def_width == 640)||(isp->fmt_def_height == 480)) ++ &&((width == 640)||(height == 480))) { ++ printk(KERN_ERR "isp: chl2 isn't support VGA output as sensor VGA input\n"); ++ return -EINVAL; ++ } ++ ++ /* CH2 support output size up to as follow */ ++ if ((width > 720) || (height > 576)) { ++ printk(KERN_ERR "isp: size is higher, chl2 isn't support.\n"); ++ return -EINVAL; ++ } ++ ++ if ((isp->chl1_width < width)||(isp->chl1_height < height)) { ++ printk(KERN_ERR "%s: wrong channel2 width and height.\n", __func__); ++ return -EINVAL; ++ } ++ ++ isp->img_ctrltbl2[10] = (65536 / (height-1) << 16) | (65536 / (width -1)); ++ isp->img_ctrltbl2[11] = (height << 10) | width; ++ ++ /* Enable channel 2 */ ++ if (chl2_info->enable) { ++ isp->img_ctrltbl2[9] |= 0xff << 24; ++ } else { ++ isp->img_ctrltbl2[9] &= ~(0xff << 24); ++ } ++ ++ isp->chl2_width = width; ++ isp->chl2_height = height; ++ isp->chl2_enable = chl2_info->enable; ++ ++ return 0; ++} ++ ++int isp_set_occlusion_area(struct isp_struct *isp, struct isp_occlusion_info *oclu_info) ++{ ++ int target_area; ++ ++ if (oclu_info->number > 4 || oclu_info->number < 1) ++ return -EINVAL; ++ ++ target_area = (oclu_info->number - 1) * 2 + 16; ++ if (oclu_info->channel == 2) ++ target_area += 4 * 2; ++ ++ isp->img_ctrltbl1[target_area] = (oclu_info->start_ypos << 16) | oclu_info->start_xpos; ++ isp->img_ctrltbl1[target_area + 1] = (oclu_info->end_ypos << 16) | oclu_info->end_xpos; ++ ++ /* Enable the occlusion for target area */ ++ if (!oclu_info->enable) { ++ isp->img_ctrltbl1[target_area] &= ~(1 << 31); ++ } else { ++ isp->img_ctrltbl1[target_area] |= (1 << 31); ++ } ++ ++ isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++int isp_set_occlusion_color(struct isp_struct *isp, struct isp_occlusion_color *color_info) ++{ ++ isp->img_ctrltbl2[0] = 0x0; ++ ++ isp->img_ctrltbl2[0] = ((color_info->color_type & 3) << 30) ++ |((color_info->transparency & 0xf) << 24) ++ |((color_info->y_component & 0xff) << 16) ++ |((color_info->u_component & 0xff) << 8) ++ |(color_info->v_component & 0xff); ++ ++ isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++int isp_set_zoom(struct isp_struct *isp, struct isp_zoom_info *info) ++{ ++ struct isp_channel2_info chl2; ++ int offset_w = 0, offset_h = 0; ++ ++ isp_dbg("%s: entry. \ncut_xpos=%d cut_ypos=%d cut_width=%d cut_height=%d\n" ++ "out_width=%d out_height=%d channel=%d chl2_enable=%d\n", __func__, ++ info->cut_xpos, info->cut_ypos, info->cut_width, info->cut_height, ++ info->out_width, info->out_height, info->channel, isp->chl2_enable); ++ ++ if ((info->out_width > 1280) || (info->out_height > 720)) { ++ printk(KERN_ERR "isp: output scale is too big\n"); ++ return -EINVAL; ++ } ++ ++ if ((info->cut_width < 6 || info->cut_height < 6) ++ ||(info->out_width < 18 || info->out_height < 18)) { ++ printk(KERN_ERR "isp: the scale of ch1 remain the same\n"); ++ return -EINVAL; ++ } ++ ++ if (isp->cur_mode_class == ISP_RGB_CLASS) { ++ offset_w = 18; ++ offset_h = 16; ++ } else if (isp->cur_mode_class == ISP_YUV_CLASS) { ++ offset_w = 6; ++ offset_h = 6; ++ } ++ ++ if (isp->chl2_enable) { ++ // the second channel is not support up scale ++ if (info->channel == 2) { ++ if ((info->cut_width < info->out_width+offset_w)||(info->cut_height < info->out_height+offset_h)) { ++ printk(KERN_ERR "isp: ch2 isn't support up-scale.\n"); ++ return -EINVAL; ++ } ++ } else if ((info->channel == 1)||(info->channel == 0)) { ++ if ((info->cut_width < isp->chl2_width+offset_w)||(info->cut_height < isp->chl2_height+offset_h)) { ++ printk(KERN_ERR "isp: cut window should be larger than ch2 output.\n"); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ // enable master channel default, 0 also indicate ch1 ++ if ((info->channel == 1) || (info->channel == 0)) { ++ // the master channel support up-scale ratio up to is 3 ++ if (info->out_width > info->cut_width * 3 || info->out_height > info->cut_height * 3) { ++ printk(KERN_ERR "isp: ch1 up-scale ratio too big\n"); ++ return -EINVAL; ++ } ++ ++ // the master channel support down-scale up to is 1/2 ++ if ((info->out_width < (info->cut_width >> 1)) ++ || (info->out_height < (info->cut_height >> 1))) { ++ printk(KERN_ERR "isp: ch1 down-scale ratio too big\n"); ++ return -EINVAL; ++ } ++ ++ if (isp_set_channel1_scale(isp, info->out_width, info->out_height) < 0) ++ return -EINVAL; ++ ++ ++ } else if ((info->channel == 2) && (isp->chl2_enable)) { ++ // the second channel support down-scale up to is 1/8 ++ if ((info->out_width < (info->cut_width >> 3)) ++ || (info->out_height < (info->cut_height >> 3))) { ++ printk(KERN_ERR "isp: ch2 down-scale ratio too big\n"); ++ return -EINVAL; ++ } ++ ++ chl2.width = info->out_width; ++ chl2.height = info->out_height; ++ chl2.enable = 1; ++ if (isp_set_channel2(isp, &chl2) < 0) ++ return -EINVAL; ++ } ++ ++ if (isp_set_cutter_window(isp, info->cut_xpos, info->cut_ypos, info->cut_width, info->cut_height) < 0) ++ return -EINVAL; ++ return 0; ++} ++ ++int isp_set_crop(struct isp_struct *isp, struct v4l2_rect rect) ++{ ++ if (rect.width > isp->fmt_width || rect.height > isp->fmt_height) { ++ printk(KERN_ERR"%s: invalid cutter window size\n", __func__); ++ return -EINVAL; ++ } ++ ++ isp->isp_ctrltbl[0] &= (3 << DSMP_RTO_BIT); ++ if (1/*YUV_DATA*/) { //align four byte when YUV data input ++ rect.left = ALIGN(rect.left, 4); ++ } ++ isp->isp_ctrltbl[0] |= (rect.top << ISP_WIN_HEIGHT_OFFSET) | rect.left; ++ isp->isp_ctrltbl[1] = (rect.height << ISP_WIN_HEIGHT_OFFSET) | rect.width; ++ ++ //update_image_size(isp); ++ ++ return isp_set_channel1_scale(isp, rect.width, rect.height); ++} ++ ++/* *******this interface response to set image param ****** */ ++int isp_set_black_balance(struct isp_struct *isp, struct isp_black_balance *ctrl) ++{ ++ int i; ++ ++ isp_dbg("%s enter.\n", __func__); ++ ++ if (isp->cur_mode_class != ISP_RGB_CLASS) ++ return 0; ++ ++ BUG_ON(ctrl == NULL); ++ ++ for (i = 0; i < 3; i++) ++ isp->isp_ctrltbl[13+i] &= ~(0x3ff << BB_PARM_BIT); ++ ++ if (ctrl->enable == 0) { ++ isp->blkb_en = 0; ++ } else { ++ isp->isp_ctrltbl[13] |= (ctrl->r_offset << BB_PARM_BIT); ++ isp->isp_ctrltbl[14] |= (ctrl->g_offset << BB_PARM_BIT); ++ isp->isp_ctrltbl[15] |= (ctrl->b_offset << BB_PARM_BIT); ++ ++ isp->blkb_en = 1; ++ } ++ ++ //isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++int isp_set_lens_correct(struct isp_struct *isp, struct isp_lens_correct *ctrl) ++{ ++ isp_dbg("%s enter.\n", __func__); ++ ++ if (isp->cur_mode_class != ISP_RGB_CLASS) ++ return 0; ++ ++ BUG_ON(ctrl == NULL); ++ ++ __isp_clear_lens_param(isp); ++ ++ if (ctrl->enable == 0) { ++ isp->lens_crr_en = 0; ++ } else { ++ __isp_set_lens_correct(isp, ctrl); ++ isp->isp_ctrltbl[24] |= (1 << 31); ++ isp->lens_crr_en = 1; ++ } ++ ++ //isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++int isp_set_demosaic(struct isp_struct *isp, struct isp_demosaic *ctrl) ++{ ++ isp_dbg("%s enter.\n", __func__); ++ ++ BUG_ON(ctrl == NULL); ++ ++ if ((ctrl->threshold < 0)||(ctrl->threshold > 4095)) { ++ printk(KERN_ERR "invalid parameter. error\n"); ++ return -EINVAL; ++ } ++ ++ isp->isp_ctrltbl[2] = 0x0; ++ ++ if (isp->cur_mode_class != ISP_RGB_CLASS) { ++ isp->demo_sac_en = 0; ++ goto out; ++ } ++ ++ // always enable demo saic for rgb ++ if (ctrl->enable == 0) { ++ isp->isp_ctrltbl[2] = (1 << 12)|(isp->demo_sac_thres & 0xfff); ++ } else { ++ isp->isp_ctrltbl[2] = (1 << 12)|(ctrl->threshold & 0xfff); ++ isp->demo_sac_thres = ctrl->threshold; ++ } ++ isp->demo_sac_en = 1; ++ ++out: ++ update_image_size(isp); ++ isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++// noise remove ++int isp_set_rgb_filter(struct isp_struct *isp, struct isp_rgb_filter *ctrl) ++{ ++ isp_dbg("%s enter.\n", __func__); ++ ++ BUG_ON(ctrl == NULL); ++ ++ if ((ctrl->threshold < 0)||(ctrl->threshold > 1024)) { ++ printk("invalid parameter. error\n"); ++ return -EINVAL; ++ } ++ ++ isp->img_ctrltbl1[1] &= ~(0x3ff << 12); ++ ++ //disable rgb filter ++ if (isp->cur_mode_class != ISP_RGB_CLASS) { ++ isp->img_ctrltbl1[1] &= ~(1 << 31); ++ isp->rgb_filter_en = 0; ++ isp->rgb_filter_en_flag = 0; ++ goto out; ++ } ++ ++ if (ctrl->enable == 0) { ++ if (isp->defect_pixel_en == 0) { ++ isp->img_ctrltbl1[1] &= ~(1 << 31); ++ isp->rgb_filter_en = 0; ++ } ++ isp->rgb_filter_en_flag = 0; ++ } else { //enable rgb filter and set threshold ++ isp->img_ctrltbl1[1] |= (1 << 31)|((ctrl->threshold & 0x3ff) << 12); ++ isp->rgb_filter_thres = ctrl->threshold; ++ isp->rgb_filter_en = 1; ++ isp->rgb_filter_en_flag = 1; ++ } ++out: ++ update_image_size(isp); ++ isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++int isp_set_uv_iso_filter(struct isp_struct *isp, struct isp_uv_filter *ctrl) ++{ ++ isp_dbg("%s enter.\n", __func__); ++ ++ BUG_ON(ctrl == NULL); ++ ++ if (ctrl->enable == 0) { ++ //if (isp->yedge_en == 0) { ++ isp->img_ctrltbl1[13] &= ~(1 << 30); ++ isp->uv_isoflt_en = 0; ++ //} ++ //isp->uv_isoflt_en_flag = 0; ++ } else { ++ isp->img_ctrltbl1[13] |= (1 << 30); ++ isp->uv_isoflt_en = 1; ++ //isp->uv_isoflt_en_flag = 1; ++ } ++ ++ //isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++// bad pixel defect and fixed ++int isp_set_defect_pixel(struct isp_struct *isp, struct isp_defect_pixel *ctrl) ++{ ++ isp_dbg("%s enter.\n", __func__); ++ ++ if (isp->cur_mode_class != ISP_RGB_CLASS) ++ return 0; ++ ++ BUG_ON(ctrl == NULL); ++ ++ isp->img_ctrltbl1[1] &= ~(0x3 << 28); ++ ++ if (ctrl->enable == 0) { ++ //disable defect pixel remove ++ isp->img_ctrltbl1[1] &= ~(1 << 30); ++ isp->defect_pixel_en = 0; ++ ++ //disable rgb filter ++ if (isp->rgb_filter_en_flag == 0) { ++ isp->img_ctrltbl1[1] &= ~(1 << 31); ++ isp->rgb_filter_en = 0; ++ } ++ } else { ++ //enable defect pixel remove and set dfp_cf_sel ++ isp->img_ctrltbl1[1] |= (0x3 << 30)|((ctrl->threshold & 0x3) << 28); ++ isp->defect_pixel_en = 1; ++ isp->rgb_filter_en = 1; ++ } ++ ++ update_image_size(isp); ++ isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++int isp_set_manu_wb(struct isp_struct *isp, struct isp_white_balance *ctrl) ++{ ++ isp_dbg("%s enter.\n", __func__); ++ ++ if (isp->cur_mode_class != ISP_RGB_CLASS) ++ return 0; ++ ++ BUG_ON(ctrl == NULL); ++ ++ if (ctrl->enable == 0) { ++ // disable color correction ++ ctrl->co_r = 1024; ++ ctrl->co_g = 1024; ++ ctrl->co_b = 1024; ++ ++ isp->wb_param_en = 0; ++ } else { ++ //enable manu while balance, disable auto while balance ++ isp->wb_param_en = 1; ++ isp->auto_wb_param_en = 0; ++ } ++ ++ __isp_set_white_pram(isp, ctrl->co_g, ctrl->co_r, ctrl->co_b); ++ ++ //isp_update_regtable(isp, 0); ++ ++ //save the value ++ isp->wb_param.co_r = ctrl->co_r; ++ isp->wb_param.co_g = ctrl->co_g; ++ isp->wb_param.co_b = ctrl->co_b; ++ ++ return 0; ++} ++ ++int isp_set_auto_wb(struct isp_struct *isp, struct isp_auto_white_balance *ctrl) ++{ ++ isp_dbg("%s enter.\n", __func__); ++ ++ if (isp->cur_mode_class != ISP_RGB_CLASS) ++ return 0; ++ ++ BUG_ON(ctrl == NULL); ++ ++ // no use auto while balance ++ if (ctrl->enable == 0) { ++ isp->wb_param.co_r = 1024; ++ isp->wb_param.co_g = 1024; ++ isp->wb_param.co_b = 1024; ++ ++ __isp_set_white_pram(isp, isp->wb_param.co_g, ++ isp->wb_param.co_r, isp->wb_param.co_b); ++ ++ // disable white balance calculate ++ isp->isp_ctrltbl[25] &= ~(1 << 31); ++ isp->auto_wb_param_en = 0; ++ } else { ++ // enable white balance calculate ++ isp_set_auto_wb_support(isp, ctrl); ++ ++ isp->wb_param_en = 0; ++ isp->auto_wb_param_en = 1; ++ } ++ ++ //isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++int isp_set_color_correct(struct isp_struct *isp, struct isp_color_correct *ctrl) ++{ ++ int i; ++ ++ isp_dbg("%s enter.\n", __func__); ++ ++ if (isp->cur_mode_class != ISP_RGB_CLASS) ++ return 0; ++ ++ BUG_ON(ctrl == NULL); ++ ++ isp->img_ctrltbl1[11] &= ~(0x3fffff); ++ for (i = 0; i < 9; i++) ++ isp->img_ctrltbl1[2+i] = 0x0; ++ ++ if (ctrl->enable == 0) { ++ // disable color correction ++ isp->img_ctrltbl1[11] &= ~(1 << 31); ++ isp->color_crr_en = 0; ++ } else { ++ __isp_set_color_crr(isp, ctrl, 3, 3); ++ isp->color_crr_en = 1; ++ } ++ ++ //isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++int isp_set_gamma_calc(struct isp_struct *isp, struct isp_gamma_calculate *ctrl) ++{ ++ int i; ++ ++ isp_dbg("%s enter.\n", __func__); ++ ++ BUG_ON(ctrl == NULL); ++ ++ if (ctrl->enable == 0) { ++ memset(isp->img_lumitbl, 0, sizeof(unsigned long)*BRIGHTNESS_CHART_SIZE); ++ // disable brightness chart table ++ isp->img_ctrltbl1[11] &= ~(1 << 30); ++ isp->gamma_calc_en = 0; ++ ++ //isp_update_regtable(isp, 0); ++ } else { ++ if (ctrl->is_sync == 0) { ++ for(i = 0; i < 32/*BRIGHTNESS_CHART_SIZE >> 1*/; i++) ++ isp->img_lumitbl[i] = ctrl->gamma[i]; ++ } ++ ++ if (ctrl->is_sync == 1) { ++ for(i = 0; i < 32/*BRIGHTNESS_CHART_SIZE >> 1*/; i++) ++ isp->img_lumitbl[32+i] = ctrl->gamma[i]; ++ ++ // enable brightness adjust ++ isp->img_ctrltbl1[11] |= (1 << 30); ++ isp->gamma_calc_en = 1; ++ ++ //isp_update_regtable(isp, 0); ++ } ++ } ++ ++ return 0; ++} ++ ++int isp_set_brightness_enhance(struct isp_struct *isp, struct isp_brightness_enhance *ctrl) ++{ ++ isp_dbg("%s enter.\n", __func__); ++ ++ BUG_ON(ctrl == NULL); ++ ++ isp->img_ctrltbl1[12] = 0x0; ++ ++ if (ctrl->enable == 0) { ++ // disable edge(brightness) adjust ++ isp->img_ctrltbl1[11] &= ~(1 << 29); ++ isp->yedge_en = 0; ++ #if 0 ++ // disable isofilter ++ if (isp->uv_isoflt_en_flag == 0) { ++ isp->img_ctrltbl1[13] &= ~(1 << 30); ++ isp->uv_isoflt_en = 0; ++ } ++ #endif ++ } else { ++ __isp_set_brigtness(isp, ctrl); ++ ++ isp->yedge_en = 1; ++ //isp->uv_isoflt_en = 1; ++ } ++ ++ update_image_size(isp); ++ isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++int isp_set_uv_saturation(struct isp_struct *isp, struct isp_saturation *ctrl) ++{ ++ isp_dbg("%s enter.\n", __func__); ++ ++ BUG_ON(ctrl == NULL); ++ ++ isp->img_ctrltbl1[13] &= ~0x3fffffff; ++ isp->img_ctrltbl1[14] &= ~(0xffff << 16); ++ ++ if (ctrl->enable == 0) { ++ // disable saturation adjust ++ isp->img_ctrltbl1[13] &= ~(1 << 31); ++ isp->uv_saturate_en = 0; ++ } else { ++ __isp_set_uv_saturation(isp, ctrl); ++ ++ isp->uv_saturate_en = 1; ++ } ++ ++ //isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++int isp_set_histogram(struct isp_struct *isp, struct isp_histogram *ctrl) ++{ ++ unsigned long regval; ++ ++ isp_dbg("%s enter.\n", __func__); ++ ++ if (isp->cur_mode_class != ISP_RGB_CLASS) ++ return 0; ++ ++ //BUG_ON(ctrl == NULL); ++#if 0 ++ if (ctrl->enable == 0) { ++ ++ isp->histo_en = 0; ++ } else { ++#endif ++ do { ++ regval = REG32(isp->base + ISP_HISTO_CFG); ++ } while(regval >> 31); ++ ++ REG32(isp->base + ISP_HISTO_CFG) = (1 << 31) ++ |(isp->histo_phyaddr & 0x3fffffff); ++ isp->histo_en = 1; ++// } ++ ++ return 0; ++} ++ ++int isp_set_special_effect(struct isp_struct *isp, struct isp_special_effect *ctrl) ++{ ++ isp_dbg("%s enter.\n", __func__); ++ ++ BUG_ON(ctrl == NULL); ++ ++ isp->img_ctrltbl1[0] &= ~(0xff) << 24; ++ isp->img_ctrltbl1[14] &= ~(0xffff); ++ isp->img_ctrltbl1[15] = 0x0; ++ ++ if (ctrl->enable == 0) { ++ //disable yuv and yuv_solar ++ isp->img_ctrltbl1[11] &= ~(0x3 << 27); ++ ++ isp->yuv_effect_en = 0; ++ isp->yuv_solar_en = 0; ++ } else { ++ isp->img_ctrltbl1[0] |= (ctrl->solar_thrs & 0xff) << YUV_SOLAR_THRD_BIT; ++ isp->img_ctrltbl1[14] |= ((ctrl->y_eff_coefa < 0) << (SPEC_YCOEFA_BIT+7)) ++ |(((abs(ctrl->y_eff_coefa)) & 0x7f) << SPEC_YCOEFA_BIT) ++ |((ctrl->y_eff_coefb & 0xff) << SPEC_YCOEFB_BIT); ++ ++ isp->img_ctrltbl1[15] = ((ctrl->u_eff_coefa < 0) << (SPEC_UCOEFA_BIT+7)) ++ |(((abs(ctrl->u_eff_coefa)) & 0x7f) << SPEC_UCOEFA_BIT) ++ |(ctrl->u_eff_coefb << SPEC_UCOEFB_BIT) ++ |((ctrl->v_eff_coefa < 0) << (SPEC_VCOEFA_BIT+7)) ++ |(((abs(ctrl->v_eff_coefa)) & 0x7f) << SPEC_VCOEFA_BIT) ++ |(ctrl->v_eff_coefb << SPEC_VCOEFB_BIT); ++ ++ //enable yuv and yuv_solar ++ isp->img_ctrltbl1[11] |= (0x3 << 27); ++ isp->yuv_effect_en = 1; ++ isp->yuv_solar_en = 1; ++ } ++ ++ //isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++ ++/* ~~~~~~~~~~ ISP control ~~~~~~~~~ */ ++int isp_set_brightness(struct isp_struct *isp, struct v4l2_ctrl *ctrl) ++{ ++ struct isp_brightness_enhance bl_edge; ++ ++ isp_dbg("%s. enter. ctrl->val=%d\n", __func__, ctrl->val); ++ ++ memset(&bl_edge, 0, sizeof(struct isp_brightness_yedge)); ++ isp->img_ctrltbl1[12] = 0x0; ++ ++#if 0 ++ // ctrl->val is 0 indicate edge enhance closed ++ if (ctrl->val == ISP_BRIGHTNESS_0) { ++ // disable edge(brightness) adjust ++ isp->img_ctrltbl1[11] &= ~(1 << 29); ++ isp->yedge_en = 0; ++ ++ // disable isofilter ++ if (isp->uv_isoflt_en_flag == 0) { ++ isp->img_ctrltbl1[13] &= ~(1 << 30); ++ isp->uv_isoflt_en = 0; ++ } ++ } else { ++ ++#endif ++ bl_edge.y_edgek = 1000; ++ bl_edge.y_thrs = 2; ++ ++ switch(ctrl->val) { ++ case ISP_BRIGHTNESS_0: ++ bl_edge.ygain = -30; ++ break; ++ case ISP_BRIGHTNESS_1: ++ bl_edge.ygain = -15; ++ break; ++ case ISP_BRIGHTNESS_2: ++ bl_edge.ygain = 0; ++ break; ++ case ISP_BRIGHTNESS_3: ++ bl_edge.ygain = 10; ++ break; ++ case ISP_BRIGHTNESS_4: ++ bl_edge.ygain = 20; ++ break; ++ case ISP_BRIGHTNESS_5: ++ bl_edge.ygain = 30; ++ break; ++ case ISP_BRIGHTNESS_6: ++ bl_edge.ygain = 40; ++ break; ++ } ++ ++ __isp_set_brigtness(isp, &bl_edge); ++ ++ isp->yedge_en = 1; ++ //isp->uv_isoflt_en = 1; ++ //} ++ ++ update_image_size(isp); ++ isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++int isp_set_gamma(struct isp_struct *isp, struct v4l2_ctrl *ctrl) ++{ ++ isp_dbg("%s. enter. ctrl->val=%d\n", __func__, ctrl->val); ++ #if 0 ++ if (ctrl->val == ISP_GAMMA_0) { ++ memset(isp->img_lumitbl, 0, sizeof(unsigned long)*BRIGHTNESS_CHART_SIZE); ++ // disable brightness chart table ++ isp->img_ctrltbl1[11] &= ~(1 << 30); ++ isp->gamma_calc_en = 0; ++ } else { ++ #endif ++ switch(ctrl->val) { ++ case ISP_GAMMA_0: ++ __isp_set_gamma(isp, gamma_table[0]); ++ break; ++ case ISP_GAMMA_1: ++ __isp_set_gamma(isp, gamma_table[1]); ++ break; ++ case ISP_GAMMA_2: ++ __isp_set_gamma(isp, gamma_table[2]); ++ break; ++ case ISP_GAMMA_3: ++ __isp_set_gamma(isp, gamma_table[3]); ++ break; ++ case ISP_GAMMA_4: ++ __isp_set_gamma(isp, gamma_table[4]); ++ break; ++ case ISP_GAMMA_5: ++ __isp_set_gamma(isp, gamma_table[5]); ++ break; ++ case ISP_GAMMA_6: ++ __isp_set_gamma(isp, gamma_table[6]); ++ break; ++ } ++ ++ // enable brightness adjust ++ isp->img_ctrltbl1[11] |= (1 << 30); ++ isp->gamma_calc_en = 1; ++ //} ++ ++ //isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++int isp_set_saturation(struct isp_struct *isp, struct v4l2_ctrl *ctrl) ++{ ++ struct isp_saturation satu; ++ ++ isp_dbg("%s. enter. ctrl->val=%d\n", __func__, ctrl->val); ++ ++ memset(&satu, 0, sizeof(struct isp_saturation)); ++ isp->img_ctrltbl1[13] &= ~0x3fffffff; ++ isp->img_ctrltbl1[14] &= ~(0xffff << 16); ++ ++ if (ctrl->val == ISP_SATURATION_0) { ++ // disable saturation adjust ++ isp->img_ctrltbl1[13] &= ~(1 << 31); ++ isp->uv_saturate_en = 0; ++ } else { ++ switch(ctrl->val) { ++ case ISP_SATURATION_1: ++ satu.Chigh = 5; ++ satu.Clow = 0; ++ //tmp->Khigh should be 300-800 ++ satu.Khigh = 300; ++ satu.Klow = 249;//280; ++ break; ++ case ISP_SATURATION_2: ++ satu.Chigh = 5; ++ satu.Clow = 0; ++ satu.Khigh = 350; ++ satu.Klow = 249; ++ break; ++ case ISP_SATURATION_3: ++ satu.Chigh = 5; ++ satu.Clow = 0; ++ satu.Khigh = 400; ++ satu.Klow = 249; ++ break; ++ case ISP_SATURATION_4: ++ satu.Chigh = 5; ++ satu.Clow = 0; ++ satu.Khigh = 450; ++ satu.Klow = 249; ++ break; ++ case ISP_SATURATION_5: ++ satu.Chigh = 5; ++ satu.Clow = 0; ++ satu.Khigh = 500; ++ satu.Klow = 249; ++ break; ++ case ISP_SATURATION_6: ++ satu.Chigh = 5; ++ satu.Clow = 0; ++ satu.Khigh = 550; ++ satu.Klow = 249; ++ break; ++ } ++ ++ __isp_set_uv_saturation(isp, &satu); ++ ++ isp->uv_saturate_en = 1; ++ } ++ ++ //isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++//ɫУÿsensorһ ++// brief: return -1 is indicate the function is not support YUV data ++int isp_set_sharpness(struct isp_struct *isp, struct v4l2_ctrl *ctrl) ++{ ++ ++ struct isp_color_correct colcon; ++ int i; ++ ++ isp_dbg("%s enter. ctrl->val=%d\n", __func__, ctrl->val); ++ ++ if (isp->cur_mode_class != ISP_RGB_CLASS) { ++ return 0; ++ } ++ ++ memset(&colcon, 0, sizeof(struct isp_color_correct)); ++ isp->img_ctrltbl1[11] &= ~(0x3fffff); ++ for (i = 0; i < 9; i++) ++ isp->img_ctrltbl1[2+i] = 0x0; ++ ++ if (ctrl->val == ISP_SHARPNESS_0) { ++ // disable color correction ++ isp->img_ctrltbl1[11] &= ~(1 << 31); ++ isp->color_crr_en = 0; ++ } else { ++ switch(ctrl->val) { ++ case ISP_SHARPNESS_1: ++ colcon.cc_thrs_low = 40; ++ colcon.cc_thrs_high = 41; ++ colcon.ccMtrx[0][0] = 1024; ++ colcon.ccMtrx[0][1] = 150; ++ colcon.ccMtrx[0][2] = -150; ++ colcon.ccMtrx[1][0] = 150; ++ colcon.ccMtrx[1][1] = 1024; ++ colcon.ccMtrx[1][2] = -150; ++ colcon.ccMtrx[2][0] = 301; ++ colcon.ccMtrx[2][1] = -301; ++ colcon.ccMtrx[2][2] = 1024; ++ break; ++ case ISP_SHARPNESS_2: ++ case ISP_SHARPNESS_3: ++ case ISP_SHARPNESS_4: ++ case ISP_SHARPNESS_5: ++ case ISP_SHARPNESS_6: ++ break; ++ } ++ ++ __isp_set_color_crr(isp, &colcon, 3, 3); ++ ++ isp->color_crr_en = 1; ++ } ++ ++ //isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++#define ABS_DIFF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) ++ ++struct awb_rgb_param { ++ unsigned short r[3]; ++ unsigned short b[3]; ++}; ++ ++int isp_update_auto_wb_param(struct isp_struct *isp) ++{ ++ unsigned long co_g = 0, co_r = 0, co_b = 0; ++ unsigned short co_r_tmp, co_b_tmp; ++ unsigned short nr1, nr2, nr3; ++ unsigned short nb1, nb2, nb3; ++ static struct awb_rgb_param rb_arr; ++ static int count = 0; ++ ++ //first update register ++ REG32(isp->base + ISP_RGB_DATA) = 1; ++ ++ //then read value from register ++ co_r = REG32(isp->base + ISP_RGB_DATA); ++ co_g = REG32(isp->base + ISP_RGB_DATA); ++ co_b = REG32(isp->base + ISP_RGB_DATA); ++ //isp_dbg("[%ld %ld %ld]\n", co_r, co_g, co_b); ++ ++ if ((co_r > 0) && (co_g > 0) && (co_b > 0)) { ++ co_r >>= 10; ++ co_b >>= 10; ++ ++ if ((co_r > 0) && (co_b > 0)) { ++ co_r_tmp = co_g/co_r; ++ co_b_tmp = co_g/co_b; ++ ++ rb_arr.r[count] = co_r_tmp; ++ rb_arr.b[count] = co_b_tmp; ++ if (count >= 2) { ++ //isp_dbg("[r[0]:%d r[1]:%d r[2]:%d]\n", ++ // rb_arr.r[0],rb_arr.r[1],rb_arr.r[2]); ++ ++ nr1 = ABS_DIFF(rb_arr.r[0], rb_arr.r[1]); ++ nr2 = ABS_DIFF(rb_arr.r[0], rb_arr.r[2]); ++ nr3 = ABS_DIFF(rb_arr.r[1], rb_arr.r[2]); ++ ++ nb1 = ABS_DIFF(rb_arr.b[0], rb_arr.b[1]); ++ nb2 = ABS_DIFF(rb_arr.b[0], rb_arr.b[2]); ++ nb3 = ABS_DIFF(rb_arr.b[1], rb_arr.b[2]); ++ ++ if (((co_r_tmp > 700) && (co_r_tmp < 1700)) && ++ ((nr1+nr2)<30 || (nr1+nr3)<30 || (nr2+nr3)<30)) ++ isp->wb_param.co_r = co_r_tmp; ++ ++ if (((co_b_tmp > 700) && (co_b_tmp < 1700)) && ++ ((nb1+nb2)<30 || (nb1+nb3)<30 || (nb2+nb3)<30)) ++ isp->wb_param.co_b = co_b_tmp; ++ ++ rb_arr.r[0] = rb_arr.r[1]; ++ rb_arr.r[1] = rb_arr.r[2]; ++ ++ rb_arr.b[0] = rb_arr.b[1]; ++ rb_arr.b[1] = rb_arr.b[2]; ++ } else { ++ isp->wb_param.co_r = co_r_tmp; ++ isp->wb_param.co_b = co_b_tmp; ++ count = count+1; ++ } ++ ++ isp->wb_param.co_g = 1024; ++ } ++ } ++ ++ //isp_dbg("co_r %d, co_g %d, co_b %d\n", ++ // isp->wb_param.co_r, isp->wb_param.co_g, isp->wb_param.co_b); ++ ++ __isp_set_white_pram(isp, isp->wb_param.co_g, ++ isp->wb_param.co_r, isp->wb_param.co_b); ++ ++ isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++// brief: return -1 is indicate the function is not support YUV data ++int isp_auto_set_wb_param(struct isp_struct *isp, struct v4l2_ctrl *ctrl) ++{ ++ struct isp_auto_white_balance auto_wb_param; ++ ++ isp_dbg("%s enter.\n", __func__); ++ ++ if (isp->cur_mode_class != ISP_RGB_CLASS) { ++ return 0; ++ } ++ ++ memset(&auto_wb_param, 0, sizeof(struct isp_auto_white_balance)); ++ memset(&isp->wb_param, 0, sizeof(struct isp_wb_param)); ++ ++ // no use auto while balance ++ if (ctrl->val == 0) { ++ isp->wb_param.co_r = 1024; ++ isp->wb_param.co_g = 1024; ++ isp->wb_param.co_b = 1024; ++ ++ __isp_set_white_pram(isp, isp->wb_param.co_g, ++ isp->wb_param.co_r, isp->wb_param.co_b); ++ ++ // disable white balance calculate ++ isp->isp_ctrltbl[25] &= ~(1 << 31); ++ isp->auto_wb_param_en = 0; ++ } else { ++ auto_wb_param.r_high = 984; ++ auto_wb_param.r_low = 555; ++ auto_wb_param.g_high = 988; ++ auto_wb_param.g_low = 718; ++ auto_wb_param.b_high = 979; ++ auto_wb_param.b_low = 542; ++ ++ auto_wb_param.grb_high = 826; ++ auto_wb_param.grb_low = 437; ++ auto_wb_param.gr_high = 822; ++ auto_wb_param.gr_low = 433; ++ auto_wb_param.gb_high = 818; ++ auto_wb_param.gb_low = 420; ++ ++ isp_set_auto_wb_support(isp, &auto_wb_param); ++ ++ //disable manu while balance, enable auto while balance ++ isp->wb_param_en = 0; ++ isp->auto_wb_param_en = 1; ++ } ++ ++ //isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++// brief: return -1 is indicate the function is not support YUV data ++int isp_manu_set_wb_param(struct isp_struct *isp, struct v4l2_ctrl *ctrl) ++{ ++ isp_dbg("%s enter.\n", __func__); ++ ++ if (isp->cur_mode_class != ISP_RGB_CLASS) { ++ return 0; ++ } ++ ++ memset(&isp->wb_param, 0, sizeof(struct isp_wb_param)); ++ ++ if (ctrl->val == ISP_MANU_WB_0) { ++ // disable color correction ++ isp->wb_param.co_r = 1024; ++ isp->wb_param.co_g = 1024; ++ isp->wb_param.co_b = 1024; ++ ++ isp->wb_param_en = 0; ++ } else { ++ switch(ctrl->val) { ++ case ISP_MANU_WB_1: ++ isp->wb_param.co_r = 1137; ++ isp->wb_param.co_g = 1024; ++ isp->wb_param.co_b = 1108; ++ break; ++ case ISP_MANU_WB_2: ++ isp->wb_param.co_r = 1235; ++ isp->wb_param.co_g = 1024; ++ isp->wb_param.co_b = 1345; ++ break; ++ case ISP_MANU_WB_3: ++ isp->wb_param.co_r = 1489; ++ isp->wb_param.co_g = 1024; ++ isp->wb_param.co_b = 1427; ++ break; ++ case ISP_MANU_WB_4: ++ case ISP_MANU_WB_5: ++ case ISP_MANU_WB_6: ++ break; ++ } ++ ++ //enable manu while balance, disable auto while balance ++ isp->wb_param_en = 1; ++ isp->auto_wb_param_en = 0; ++ } ++ ++ __isp_set_white_pram(isp, isp->wb_param.co_g, ++ isp->wb_param.co_r, isp->wb_param.co_b); ++ ++ //isp_update_regtable(isp, 0); ++ return 0; ++} ++ ++static unsigned int brightness_average(struct isp_struct *isp) ++{ ++ unsigned int sum = 0, tmp = 0; ++ unsigned int average; ++ int i; ++ ++ for (i = 0; i < 256; i++) { ++ sum += i *(histo_arr[i]); ++ tmp += (histo_arr[i]); ++ } ++ ++ if (tmp == 0) ++ tmp = 1; ++ ++ average = sum/tmp; ++ //isp_dbg("[%d] %d %d\n", average, sum, tmp); ++ return average; ++} ++ ++void update_exposure_value(struct isp_struct *isp) ++{ ++ unsigned int value; ++ static int flags_max = 0, flags_min = 0; ++ ++ value = brightness_average(isp); ++ ++ if ((value > 0xB4) && (flags_max < 2)) { ++ //isp_dbg(" >0xB4 -> 0x48\n"); ++ flags_max++; ++ flags_min = 0; ++ aksensor_set_param(0x14, 0x48); ++ } else if ((value < 0x60) && (flags_min < 2)) { ++ //isp_dbg(" <0x60 -> 0x40\n"); ++ flags_max = 0; ++ flags_min++; ++ aksensor_set_param(0x14, 0x40); ++ } ++} ++ ++/* compress histogram */ ++int isp_get_histogram(struct isp_struct *isp) ++{ ++ int i, j; ++ ++ if (isp->cur_mode_class != ISP_RGB_CLASS) ++ return 0; ++ ++ for (i = 0, j = 0; i < HISTOGRAM_SIZE; i = i+12, j++) { ++ histo_arr[j] = ((isp->histo_base[i+11]<<16)|(isp->histo_base[i+10]<<8)|(isp->histo_base[i+9])) ++ +((isp->histo_base[i+8]<<16)|(isp->histo_base[i+7]<<8)|(isp->histo_base[i+6])) ++ +((isp->histo_base[i+5]<<16)|(isp->histo_base[i+4]<<8)|(isp->histo_base[i+3])) ++ +((isp->histo_base[i+2]<<16)|(isp->histo_base[i+1]<<8)|(isp->histo_base[i])); ++ } ++ ++ schedule_delayed_work(&isp->work, msecs_to_jiffies(20)); ++ return 0; ++} ++ ++int isp_apply_mode(struct isp_struct *isp) ++{ ++ int width = isp->fmt_width; ++ int height = isp->fmt_height; ++ int ret = 0; ++ ++ isp_dbg("%s: width=%d, height=%d\n", __func__, width, height); ++ switch(isp->cur_mode_class) { ++ case ISP_YUV_CLASS: ++ switch(isp->cur_mode) { ++ ++ // ISP_YUV_OUT mode is direct output, and is not support minor channel output ++ case ISP_YUV_OUT: ++ REG32(isp->base + ISP_FMT_CHK) = (width * 2 | (height << 14)); ++ REG32(isp->base + ISP_IMG_PARA) = 0x80001000; ++ REG32(isp->base + ISP_IRQ_STATUS) = 0x040017ff; ++ REG32(isp->base + ISP_PERI_PARA) = 0x200340c0; ++ break; ++ ++ // ISP_YUV_BYPASS mode is support minor channel output ++ case ISP_YUV_BYPASS: ++ setup_yuv_video_bypass(isp); ++ ++ REG32(isp->base + ISP_FMT_CHK) = (width * 2 | (height << 14)); ++ REG32(isp->base + ISP_IMG_PARA) = 0x80001000; ++ REG32(isp->base + ISP_IRQ_STATUS) = 0x040017ff; ++ REG32(isp->base + ISP_PERI_PARA) = 0x000104c0; ++ break; ++ ++ case ISP_YUV_VIDEO_OUT: ++ setup_yuv_video_out(isp); ++ ++ REG32(isp->base + ISP_FMT_CHK) = (width * 2 | (height << 14)); ++ REG32(isp->base + ISP_IMG_PARA) = 0x80001000; ++ REG32(isp->base + ISP_IRQ_STATUS) = 0x044007ff; ++ REG32(isp->base + ISP_PERI_PARA) = 0x000340c5; ++ break; ++ ++ case ISP_YUV_VIDEO_BYPASS: ++ setup_yuv_video_bypass(isp); ++ ++ REG32(isp->base + ISP_FMT_CHK) = (width * 2 | (height << 14)); ++ REG32(isp->base + ISP_IMG_PARA) = 0x80001000; ++ REG32(isp->base + ISP_IRQ_STATUS) = 0x000017ff; ++ REG32(isp->base + ISP_PERI_PARA) = 0x000104c5; ++ break; ++ ++ case ISP_YUV_MERGER_OUT: ++ case ISP_YUV_VIDEO_MERGER_OUT: ++ case ISP_YUV_BIG: ++ break; ++ ++ default: ++ break; ++ } ++ ++ isp_update_regtable(isp, 1); ++ break; ++ ++ case ISP_RGB_CLASS: ++ setup_rgb_video(isp); ++ ++ REG32(isp->base + ISP_FMT_CHK) = (width | (height<<14)); ++ REG32(isp->base + ISP_IMG_PARA) = 0x80000800; ++ //REG32(isp->base + ISP_HISTO_CFG) = 0x00000000; ++ REG32(isp->base + ISP_IRQ_STATUS) = 0x000017ff; ++ ++ switch(isp->cur_mode) { ++ case ISP_RGB_OUT: ++ REG32(isp->base + ISP_PERI_PARA) = 0x000120c0; ++ break; ++ ++ case ISP_RGB_VIDEO_OUT: ++ REG32(isp->base + ISP_PERI_PARA) = 0x000120c5; ++ break; ++ ++ case ISP_RGB_BIG: ++ break; ++ ++ default: ++ break; ++ } ++ ++ isp_update_regtable(isp, 1); ++ break; ++ ++ case ISP_JPEG_CLASS: ++ switch(isp->cur_mode) { ++ case ISP_JPEG_MODE: ++ case ISP_JPEG_VIDEO: ++ break; ++ ++ default: ++ break; ++ } ++ break; ++ ++ default: ++ printk("Unrecognized mode %d\n", isp->cur_mode); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Note: To enable raw upload, the 14th bit of peripheral parameter register must be ++ * set, otherwise the data cannot be captured ++ */ ++void isp_start_capturing(struct isp_struct *isp) ++{ ++ unsigned long peri_status; ++ ++ //REG32(isp->base + ISP_PERI_PARA) &= ~(1 << 28); ++ ++ switch(isp->cur_mode) { ++ case ISP_YUV_OUT: ++ case ISP_YUV_VIDEO_OUT: ++ case ISP_YUV_BIG: ++ case ISP_JPEG_MODE: ++ case ISP_JPEG_VIDEO: ++ case ISP_RGB_BIG: ++ peri_status = REG32(isp->base + ISP_PERI_PARA); ++ peri_status |= (3 << 30) | (1 << 14); ++ REG32(isp->base + ISP_PERI_PARA) = peri_status; ++ break; ++ default: ++ REG32(isp->base + ISP_PERI_PARA) |= (3 << 30); ++ } ++} ++ ++void isp_stop_capturing(struct isp_struct *isp) ++{ ++ unsigned long peri_status; ++ ++ peri_status = REG32(isp->base + ISP_PERI_PARA); ++ peri_status |= (1 << 30); ++ peri_status &= ~(1 << 31); ++ REG32(isp->base + ISP_PERI_PARA) = peri_status; ++} ++ ++ ++void isp_dump_register(struct isp_struct *isp) ++{ ++ int i; ++ ++ printk("0x20000000=0x%08lx\n", REG32(isp->base + ISP_PERI_PARA)); ++ printk("0x20000004=0x%08lx\n", REG32(isp->base + ISP_FMT_CHK)); ++ printk("0x20000008=0x%08lx\n", REG32(isp->base + ISP_IMG_PARA)); ++ printk("0x200000014=0x%08lx\n", REG32(isp->base + ISP_IRQ_STATUS)); ++ printk("0x20000001c=0x%08lx\n", REG32(isp->base + ISP_ORDER_CTRL)); ++ ++ printk("0x200000020:\n"); ++ REG32(isp->base + 0x20) = 0x00000001; ++ for (i = 0; i <28; i++) { ++ printk("%02d: 0x%08lx\t", i, REG32(isp->base + 0x20)); ++ if (i % 5 == 0) ++ printk("\n"); ++ } ++ printk("\n"); ++ ++ printk("0x200000024:\n"); ++ REG32(isp->base + 0x24) = 0x00000001; ++ for (i = 0; i <28; i++) { ++ printk("%02d: 0x%08lx\t", i, REG32(isp->base + 0x24)); ++ if (i % 5 == 0) ++ printk("\n"); ++ } ++ printk("\n"); ++ ++ printk("0x200000028:\n"); ++ REG32(isp->base + 0x28) = 0x00000001; ++ for (i = 0; i <64; i++) { ++ printk("%02d: 0x%08lx\t", i, REG32(isp->base + 0x28)); ++ if (i % 5 == 0) ++ printk("\n"); ++ } ++ printk("\n"); ++ ++ printk("0x20000002c:\n"); ++ REG32(isp->base + 0x2c) = 0x00000001; ++ for (i = 0; i <28; i++) { ++ printk("%02d: 0x%08lx\t", i, REG32(isp->base + 0x2c)); ++ if (i % 5 == 0) ++ printk("\n"); ++ } ++ printk("\n"); ++ ++ printk("0x200000030:\n"); ++ REG32(isp->base + 0x30) = 0x00000001; ++ for (i = 0; i <27; i++) { ++ printk("%02d: 0x%08lx\t", i, REG32(isp->base + 0x30)); ++ if (i % 5 == 0) ++ printk("\n"); ++ } ++ printk("\n"); ++} ++ ++int isp_module_init(struct isp_struct *isp) ++{ ++ //register table total size is (32 * 4 * 6) = 768byte. here is more than real size. ++ isp->bytes = 1024; ++ isp->area = dma_alloc_coherent(NULL, isp->bytes, &isp->addr, GFP_KERNEL); ++ if (!isp->area) { ++ printk(KERN_ERR"Failed to allocate memory for register table\n"); ++ return -1; ++ } ++ ++ memset(isp->area, 0, isp->bytes); ++ isp_dbg("isp device init: isp->area=0x%p, isp->addr=0x%p, isp->bytes=%d\n", ++ isp->area, (void *)isp->addr, isp->bytes); ++ ++ isp->isp_ctrltbl = (unsigned long *)isp->area; //0x20, register table ++ isp->img_ctrltbl1 = isp->isp_ctrltbl + 32; //0x24 ++ isp->img_lumitbl = isp->img_ctrltbl1 + 32; //0x28 ++ isp->osd_chktbl = isp->img_lumitbl + 64; //0x2c ++ isp->img_ctrltbl2 = isp->osd_chktbl + 32; //0x30 ++ ++ isp->histo_base = dma_alloc_coherent(NULL, HISTOGRAM_SIZE, &isp->histo_phyaddr, GFP_KERNEL); ++ if (isp->histo_base == NULL) { ++ printk("no memory for histopram %d\n", -ENOMEM); ++ } ++ memset(isp->histo_base, 0, HISTOGRAM_SIZE); ++ ++ isp_dbg("Allocate %d bytes for register table\n", isp->bytes); ++ ++ return 0; ++} ++ ++ ++void isp_module_fini(struct isp_struct *isp) ++{ ++ dma_free_coherent(NULL, HISTOGRAM_SIZE, isp->histo_base, isp->histo_phyaddr); ++ dma_free_coherent(NULL, isp->bytes, isp->area, isp->addr); ++} ++ ++ ++#if 0 ++void build_gamma_table(struct isp_struct *isp) ++{ ++ int i; ++ unsigned char lum_table[256] = {0}; ++ ++ // generate lum correction parameter ++ for (i=0; i<256; i++) { ++ if(i<=13) ++ lum_table[i]=(unsigned char)(0.46154*i +0); ++ if (13255?254:(1.4286*i -109.29)); ++ } ++ } ++ ++ for (i=5; i<247; i++) { ++ lum_table[i]=(unsigned char)((lum_table[i-5]+lum_table[i-4] ++ +lum_table[i-3]+lum_table[i-2] ++ +lum_table[i-1]+lum_table[i] ++ +lum_table[i+1]+lum_table[i+2] ++ +lum_table[i+3]+lum_table[i+4] ++ +lum_table[i+5]+lum_table[i+6] ++ +lum_table[i+7])/13); ++ } ++ ++ //copy to isp->img_lumitbl ++ for(i = 0; i < 256; i+=4) { ++ isp->img_lumitbl[i>>2] = lum_table[i]|(lum_table[i+1] << 8) ++ |(lum_table[i+2] << 16)|(lum_table[i+3] << 24); ++ } ++ ++ printk("\n"); ++ for(i = 0; i < 64; i++) { ++ printk("%08x ", isp->img_lumitbl[i]); ++ if ((i != 0) && (i % 5 == 0)) ++ printk("\n"); ++ } ++} ++#endif ++ +diff --git a/drivers/media/video/plat-anyka/ak39_isp.h b/drivers/media/video/plat-anyka/ak39_isp.h +new file mode 100755 +index 00000000..7140ae41 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/ak39_isp.h +@@ -0,0 +1,333 @@ ++#ifndef _AK39_ISP_H ++#define _AK39_ISP_H ++ ++#include ++#include ++ ++#include ++ ++#define ISP_PERI_PARA 0x00 ++#define ISP_FMT_CHK 0x04 ++#define ISP_IMG_PARA 0x08 ++#define ISP_HISTO_CFG 0x10 ++#define ISP_IRQ_STATUS 0x14 ++#define ISP_RGB_DATA 0x18 ++#define ISP_ORDER_CTRL 0x1c ++ ++#define ISP_FRAME_UPDATE (0) ++#define ISP_RAW_UPDMA_OVERFLOW (1) ++#define ISP_BIG_PIC_MODE_DONE (2) ++#define ISP_CH1_UPDMA_OVERFLOW (3) ++#define ISP_CH1_ONE_FRAME_END (4) ++#define ISP_PCLK_VER_ERR (5) ++#define ISP_PCLK_HOR_ERR (6) ++#define ISP_CH2_ONE_FRAME_END (7) ++#define ISP_HIST_END (8) ++#define ISP_RAW_UPLOAD (9) ++#define ISP_ALL_FARME_END (10) ++ ++ ++//dsamp rto + cut pos ++#define DSMP_RTO_BIT 30 ++#define CUT_STRY_BIT 13 ++#define CUT_STRX_BIT 0 ++ ++//cut winsize ++#define CUT_WINH_BIT 13 ++#define CUT_WINW_BIT 0 ++ ++//demosaic ++#define DEMO_ENABLE_BIT 12 // demosaic enable ++#define DEMO_THRD_BIT 0 // [11:0] demosaic threshold, asic presice+2 ++ ++//lens coef, 0~9, 10times aa[10:0],bb[10:0],cc[9:0] ++#define LENS_COEFA_BIT 21 ++#define LENS_COEFB_BIT 10 ++#define LENS_COEFC_BIT 0 // c=[9:0]+1'b0 ++ ++//lens range 0~9 10 times + black level parm / white balance parm ++#define LENS_RANGE_BIT 0 // [21:0] ++#define BB_PARM_BIT 22 // [31:22] black level parm, R,G,B ++#define WB_SETPARM_BIT 22 // [31:22] white balance set parm, r/g/b_high/low, ++#define RAW_DMA_CYL_BIT 28 // [31:28] raw DMA download min cycle, min val = 2 ++ ++//lens refpos center point{3b0, yref[12:0], 3b0, xref[12:0]} ++#define LENS_YREF_BIT 16 // [28:16] ++#define LENS_XREF_BIT 0 // [12:0] ++ ++//lens istsqr ++#define LENS_ENABLE_BIT 31 // lens correction enable ++#define LENS_1STSQR_BIT 0 // [26:0](xref)^2 + (yref) ^2 ++ ++//WB calculate parm ++#define WB_PARMCAL_EN_BIT 31 //white balance parameter calculate enable ++#define WB_GRB_HIGH_BIT 20 // [29:20] grb_high ++#define WB_GRR_LOW_BIT 10 // [19:10] grb_low ++#define WB_GR_LOW_BIT 0 // [9:0] gr_low ++#define WB_GR_HIGH_BIT 20 // [29:20] gr_high ++#define WB_GB_HIGH_BIT 10 // [19:10] gb_high ++#define WB_GB_LOW_BIT 0 // [9:0] gb_low ++ ++ ++#define ISP_WIN_HEIGHT_OFFSET (13) ++#define ISP_WIN_WIDTH_OFFSET (0) ++ ++#define UPSCALE_FRAME_CTRL (16) ++#define DOWNSCALE_FRAME_CTRL (24) ++ ++#define CC_CALVAL_BIT 16 // [28:16] (tmp(I, j) Cmin(I, j) ) /( high-low) ++#define CC_TMPVAL_BIT 0 // [12:0] tmp(i, j) ++ ++// Y gain edge enhance ++#define Y_GAIN_BIT 24 // [31:24] ++#define Y_EDGE_THRS_BIT 12 // [22:12] ythrs * 9 ++#define Y_EDGE_K_BIT 0 // [10:0] k/9*2048 ++ ++//uv saturation1 ++#define UVSATUR_EN_BIT 31 ++#define ISOFLT_EN_BIT 30 ++#define UVSATUR_K_LOW_BIT 20 // [29:20] 10bit, xx.xxxx xxxx klow*256, 0.1~3 ++#define UVSATUR_K_HIGH_BIT 10 // [19:10] 10bit, khigh*256, 0.1~3 ++#define UVSATUR_K_SLOPE_BIT 0 // [9:0] (iHigh - iLow) * 256 / (CHigh - CLow) ++ ++// uv saturation 2 ++#define UVSATUR_CHIGH_BIT 24 // [31:24] 0~255 ++#define UVSATUR_CLOW_BIT 16 // [23:16] 0~255 ++#define SPEC_YCOEFA_BIT 8 // [15:8] Y effect A -64~64 ++#define SPEC_YCOEFB_BIT 0 // [7:0] B 0~255 ++ ++// uv special effect ++#define YUV_SOLAR_THRD_BIT 24 // [31:24] yuv_eff_solar_thd ++#define SPEC_UCOEFA_BIT 24 // [31:24] U effect -64~64 ++#define SPEC_UCOEFB_BIT 16 // [23:16] B 0~255 ++#define SPEC_VCOEFA_BIT 8 // [15:8] V effect -64~64 ++#define SPEC_VCOEFB_BIT 0 // [7:0] B 0~255 ++ ++ ++#define OSD_SIZE_H_BIT 0 ++#define OSD_SIZE_V_BIT 16 ++ ++enum { ++ ISP_BRIGHTNESS_0 = 0, ++ ISP_BRIGHTNESS_1, ++ ISP_BRIGHTNESS_2, ++ ISP_BRIGHTNESS_3, ++ ISP_BRIGHTNESS_4, ++ ISP_BRIGHTNESS_5, ++ ISP_BRIGHTNESS_6 ++}; ++ ++enum { ++ ISP_GAMMA_0 = 0, ++ ISP_GAMMA_1, ++ ISP_GAMMA_2, ++ ISP_GAMMA_3, ++ ISP_GAMMA_4, ++ ISP_GAMMA_5, ++ ISP_GAMMA_6 ++}; ++ ++// isp saturation enum defined ++enum { ++ ISP_SATURATION_0 = 0, ++ ISP_SATURATION_1, ++ ISP_SATURATION_2, ++ ISP_SATURATION_3, ++ ISP_SATURATION_4, ++ ISP_SATURATION_5, ++ ISP_SATURATION_6 ++}; ++ ++enum { ++ ISP_SHARPNESS_0 = 0, ++ ISP_SHARPNESS_1, ++ ISP_SHARPNESS_2, ++ ISP_SHARPNESS_3, ++ ISP_SHARPNESS_4, ++ ISP_SHARPNESS_5, ++ ISP_SHARPNESS_6 ++}; ++ ++enum { ++ ISP_MANU_WB_0 = 0, ++ ISP_MANU_WB_1, ++ ISP_MANU_WB_2, ++ ISP_MANU_WB_3, ++ ISP_MANU_WB_4, ++ ISP_MANU_WB_5, ++ ISP_MANU_WB_6 ++}; ++ ++//enum for sub sample ++typedef enum ++{ ++ SUBSMP_1X, /*no sub sample*/ ++ SUBSMP_2X, /*sub sample 1/2 * 1/2 */ ++ SUBSMP_4X, /*sub sample 1/4 * 1/4 */ ++ SUBSMP_8X /*sub sample 1/8 * 1/8 */ ++} T_SUBSMP_RTO; ++ ++//define the image sensor controller register address ++#undef REG32 ++#define REG32(_reg_) (*(volatile unsigned long *)(_reg_)) ++ ++struct isp_brightness_yedge { ++ unsigned short yedgeK; ++ unsigned short ythresold; ++ signed char ygain; ++}; ++ ++struct isp_wb_param { ++ unsigned short co_r; ++ unsigned short co_g; ++ unsigned short co_b; ++}; ++ ++struct isp_struct { ++ void __iomem *base; ++ enum isp_working_mode cur_mode; ++ enum isp_mode_class cur_mode_class; ++ ++ /*the input size from sensor*/ ++ int fmt_width; ++ int fmt_height; ++ ++ /* indicate VGA size */ ++ int fmt_def_width; ++ int fmt_def_height; ++ ++ /*the input size from sensor*/ ++ int cut_width; ++ int cut_height; ++ ++ /* master channel output size*/ ++ int chl1_width; ++ int chl1_height; ++ ++ /* minor channel */ ++ int chl2_width; ++ int chl2_height; ++ int chl2_enable; ++ ++ int lens_use_width; // used for lens correct, width ++ int lens_use_height; // used for lens correct, height ++ ++ int demo_sac_en; ++ unsigned int demo_sac_thres; ++ int rgb_filter_en; ++ int rgb_filter_en_flag; ++ unsigned int rgb_filter_thres; ++ ++ int yedge_en; ++ int uv_isoflt_en; ++ int uv_isoflt_en_flag; ++ int gamma_calc_en; ++ int uv_saturate_en; ++ int auto_wb_param_en; ++ int wb_param_en; ++ int color_crr_en; ++ int yuv_effect_en; ++ int yuv_solar_en; ++ int defect_pixel_en; ++ int blkb_en; // black balance enable flag ++ int lens_crr_en; ++ int histo_en; ++ ++ T_SUBSMP_RTO sub_sample; //0:1x,1:2x,2:4x,3:8x ++ struct isp_wb_param wb_param; ++ unsigned char *area; /* virtual pointer */ ++ dma_addr_t addr; /* physical address */ ++ size_t bytes; /* buffer size in bytes */ ++ unsigned long *isp_ctrltbl; /* ISP control Information */ ++ unsigned long *img_ctrltbl1; /* Image backend process control information 1 */ ++ unsigned long *img_lumitbl; /* Image luminosity transformation table */ ++ unsigned long *osd_chktbl; /* OSD color check table */ ++ unsigned long *img_ctrltbl2; /* Image backend process control information 2 */ ++ dma_addr_t histo_phyaddr; ++ unsigned char *histo_base; ++ ++ struct delayed_work work; ++}; ++ ++void isp_start_capturing(struct isp_struct *isp); ++ ++void isp_stop_capturing(struct isp_struct *isp); ++ ++static inline void isp_clear_irq_counter(struct isp_struct *isp) ++{ ++ REG32(isp->base + ISP_IRQ_STATUS) |= 1 << 26; ++} ++ ++static inline bool isp_capture_running(struct isp_struct *isp) ++{ ++ return REG32(isp->base + ISP_PERI_PARA) & (1 << 31); ++} ++ ++static inline bool isp_is_continuous(struct isp_struct *isp) ++{ ++ return REG32(isp->base + ISP_PERI_PARA) & (1 << 2); ++} ++ ++static inline bool isp_is_capturing_odd(struct isp_struct *isp) ++{ ++ return REG32(isp->base + ISP_IRQ_STATUS) & (1 << 27); ++} ++ ++ ++void isp_update_regtable(struct isp_struct *isp, int force); ++int update_cur_mode_class(struct isp_struct *isp); ++ ++void isp_set_even_frame(struct isp_struct *isp, ++ unsigned long yaddr_chl1, unsigned long yaddr_chl2); ++void isp_set_odd_frame(struct isp_struct *isp, ++ unsigned long yaddr_chl1, unsigned long yaddr_chl2); ++ ++int isp_check_irq(struct isp_struct *isp); ++int isp_clear_irq(struct isp_struct *isp); ++int isp_clear_irq_status(struct isp_struct *isp); ++ ++int isp_set_osd(struct isp_struct *isp, struct isp_osd_info *); ++int isp_set_channel2(struct isp_struct *isp, struct isp_channel2_info *); ++int isp_set_occlusion_area(struct isp_struct *isp, struct isp_occlusion_info *); ++int isp_set_occlusion_color(struct isp_struct *isp, struct isp_occlusion_color *); ++int isp_set_channel1_scale(struct isp_struct *isp, int width, int height); ++int isp_set_cutter_window(struct isp_struct *isp, int xpos, int ypos, int width, int height); ++int isp_set_zoom(struct isp_struct *isp, struct isp_zoom_info *info); ++int isp_set_crop(struct isp_struct *isp, struct v4l2_rect rect); ++int isp_set_brightness(struct isp_struct *isp, struct v4l2_ctrl *ctrl); ++int isp_set_gamma(struct isp_struct *isp, struct v4l2_ctrl *ctrl); ++int isp_set_saturation(struct isp_struct *isp, struct v4l2_ctrl *ctrl); ++int isp_set_sharpness(struct isp_struct *isp, struct v4l2_ctrl *ctrl); ++int isp_update_auto_wb_param(struct isp_struct *isp); ++int isp_auto_set_wb_param(struct isp_struct *isp, struct v4l2_ctrl *ctrl); ++int isp_manu_set_wb_param(struct isp_struct *isp, struct v4l2_ctrl *ctrl); ++ ++ ++/* *******this interface response to set image param ****** */ ++int isp_set_black_balance(struct isp_struct *isp, struct isp_black_balance *ctrl); ++int isp_set_lens_correct(struct isp_struct *isp, struct isp_lens_correct *ctrl); ++int isp_set_demosaic(struct isp_struct *isp, struct isp_demosaic *ctrl); ++int isp_set_rgb_filter(struct isp_struct *isp, struct isp_rgb_filter *ctrl); ++int isp_set_uv_iso_filter(struct isp_struct *isp, struct isp_uv_filter *ctrl); ++int isp_set_defect_pixel(struct isp_struct *isp, struct isp_defect_pixel *ctrl); ++int isp_set_manu_wb(struct isp_struct *isp, struct isp_white_balance *ctrl); ++int isp_set_auto_wb(struct isp_struct *isp, struct isp_auto_white_balance *ctrl); ++int isp_set_color_correct(struct isp_struct *isp, struct isp_color_correct *ctrl); ++int isp_set_gamma_calc(struct isp_struct *isp, struct isp_gamma_calculate *ctrl); ++int isp_set_brightness_enhance(struct isp_struct *isp, struct isp_brightness_enhance *ctrl); ++int isp_set_uv_saturation(struct isp_struct *isp, struct isp_saturation *ctrl); ++int isp_set_histogram(struct isp_struct *isp, struct isp_histogram *ctrl); ++int isp_get_histogram(struct isp_struct *isp); ++int isp_set_special_effect(struct isp_struct *isp, struct isp_special_effect *ctrl); ++ ++int isp_apply_mode(struct isp_struct *isp); ++void isp_start_capturing(struct isp_struct *isp); ++void isp_dump_register(struct isp_struct *isp); ++ ++int isp_module_init(struct isp_struct *isp); ++void isp_module_fini(struct isp_struct *isp); ++ ++int isp_set_histogram(struct isp_struct *isp, struct isp_histogram *ctrl); ++void update_exposure_value(struct isp_struct *isp); ++ ++#endif +diff --git a/drivers/media/video/plat-anyka/ak_camera.c b/drivers/media/video/plat-anyka/ak_camera.c +new file mode 100755 +index 00000000..bc2c281f +--- /dev/null ++++ b/drivers/media/video/plat-anyka/ak_camera.c +@@ -0,0 +1,1545 @@ ++/* ++ * @file ak camera.c ++ * @camera host driver for ak ++ * @Copyright (C) 2010 Anyka (Guangzhou) Microelectronics Technology Co ++ * @author wu_daochao ++ * @date 2011-04 ++ * @version ++ * @for more information , please refer to AK980x Programmer's Guide Mannul ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ak39_isp.h" ++ ++ ++//#define CAMIF_DEBUG ++#ifdef CAMIF_DEBUG ++#define isp_dbg(fmt...) printk(KERN_INFO " ISP: " fmt) ++#else ++#define isp_dbg(fmt, args...) do{}while(0) ++#endif ++ ++#define CAMDBG(fmt, args...) do{}while(0) ++ ++struct ak_buffer { ++ struct videobuf_buffer vb; ++ enum v4l2_mbus_pixelcode code; ++ int inwork; ++}; ++ ++struct ak_camera_dev { ++ struct soc_camera_host soc_host; ++ struct soc_camera_device *icd; ++ struct ak_camera_pdata *pdata; ++ ++ void __iomem *base; // mapped baseaddress for CI register(0x2000c000) ++ struct resource *res; ++ struct clk *clk; // camera controller clk. it's parent is vclk defined in clock.c ++ struct clk *cis_sclk; // cis_sclk clock for sensor ++ unsigned int irq; ++ unsigned int dma_running; ++ /* members to manage the dma and buffer*/ ++ struct list_head capture; ++ struct ak_buffer *active; ++ spinlock_t lock; /* for videobuf_queue , passed in init_videobuf */ ++ ++ /* personal members for platform relative */ ++ unsigned long mclk; ++ ++ struct isp_struct isp; ++ enum isp_working_mode def_mode; ++ unsigned char *osd_swbuff; ++ unsigned char *osd_buff; ++}; ++ ++struct ak_camera_cam { ++ /* Client output, as seen by the CEU */ ++ unsigned int width; ++ unsigned int height; ++}; ++ ++#define ISP_TIMEOUT (1) //unit: s ++#define EMPTY_FRAME_NUM (2) ++ ++static const char *ak_cam_driver_description = "AK_Camera"; ++static int irq_buf_empty_flag = 0; ++static int irq_need_baffle = 0; ++ ++extern void *getRecordSyncSamples(void); ++ ++/* for ak_videobuf_release */ ++static void free_buffer(struct videobuf_queue *vq, struct ak_buffer *buf) ++{ ++ struct soc_camera_device *icd = vq->priv_data; ++ struct videobuf_buffer *vb = &buf->vb; ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ struct ak_camera_dev *pcdev = ici->priv; ++ ++ isp_dbg("%s (vb=0x%p) buf[%d] 0x%08lx %d\n", ++ __func__, vb, vb->i, vb->baddr, vb->bsize); ++ ++ BUG_ON(in_interrupt()); ++ ++ /* This waits until this buffer is out of danger, i.e., until it is no ++ * longer in STATE_QUEUED or STATE_ACTIVE */ ++ if (vb->state == VIDEOBUF_ACTIVE && !pcdev->dma_running) { ++ printk("free_buffer: dma_running=%d, doesn't neee to wait\n", pcdev->dma_running); ++ vb->state = VIDEOBUF_ERROR; ++ list_del(&vb->queue); ++ } else { ++ videobuf_waiton(vq, vb, 0, 0); ++ } ++ videobuf_dma_contig_free(vq, vb); ++ ++ vb->state = VIDEOBUF_NEEDS_INIT; ++} ++ ++/* Called when application apply buffers */ ++static int ak_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, ++ unsigned int *size) ++{ ++ struct soc_camera_device *icd = vq->priv_data; ++ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, ++ icd->current_fmt->host_fmt); ++ ++ bytes_per_line = icd->user_width * 3 /2; ++ if (bytes_per_line < 0) ++ return bytes_per_line; ++ ++ *size = bytes_per_line * icd->user_height; ++ ++ if (*count < 4) { ++ printk("if use video mode, vbuf num isn't less than 4\n"); ++ *count = 4; ++ } ++ ++ if (*size * *count > CONFIG_VIDEO_RESERVED_MEM_SIZE) ++ *count = (CONFIG_VIDEO_RESERVED_MEM_SIZE) / *size; ++ ++ isp_dbg("%s count=%d, size=%d, bytes_per_line=%d\n", ++ __func__, *count, *size, bytes_per_line); ++ ++ return 0; ++} ++ ++/* platform independent */ ++static int ak_videobuf_prepare(struct videobuf_queue *vq, ++ struct videobuf_buffer *vb, enum v4l2_field field) ++{ ++ struct soc_camera_device *icd = vq->priv_data; ++ struct ak_buffer *buf = container_of(vb, struct ak_buffer, vb); ++ int ret; ++ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, ++ icd->current_fmt->host_fmt); ++ ++ isp_dbg("%s (vb=0x%p) buf[%d] vb->baddr=0x%08lx vb->bsize=%d bytes_per_line=%d\n", ++ __func__, vb, vb->i, vb->baddr, vb->bsize, bytes_per_line); ++ ++ bytes_per_line = icd->user_width * 3 /2; ++ ++ if (bytes_per_line < 0) ++ return bytes_per_line; ++ ++ /* Added list head initialization on alloc */ ++ WARN_ON(!list_empty(&vb->queue)); ++ ++#if 0 ++//#ifdef ISP_DEBUG ++ /* ++ * This can be useful if you want to see if we actually fill ++ * the buffer with something ++ */ ++ memset((void *)vb->baddr, 0xaa, vb->bsize); ++#endif ++ ++ BUG_ON(NULL == icd->current_fmt); ++ ++ /* I think, in buf_prepare you only have to protect global data, ++ * the actual buffer is yours */ ++ buf->inwork = 1; ++ ++ if (buf->code != icd->current_fmt->code || ++ vb->width != icd->user_width || ++ vb->height != icd->user_height || ++ vb->field != field) { ++ buf->code = icd->current_fmt->code; ++ vb->width = icd->user_width; ++ vb->height = icd->user_height; ++ vb->field = field; ++ vb->state = VIDEOBUF_NEEDS_INIT; ++ } ++ ++ vb->size = bytes_per_line * vb->height; ++ if (0 != vb->baddr && vb->bsize < vb->size) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (vb->state == VIDEOBUF_NEEDS_INIT) { ++ ret = videobuf_iolock(vq, vb, NULL); ++ if (ret) ++ goto fail; ++ ++ vb->state = VIDEOBUF_PREPARED; ++ } ++ ++ buf->inwork = 0; ++ ++ return 0; ++ ++fail: ++ free_buffer(vq, buf); ++out: ++ buf->inwork = 0; ++ return ret; ++} ++ ++static void ak_videobuf_queue(struct videobuf_queue *vq, ++ struct videobuf_buffer *vb) ++{ ++ struct soc_camera_device *icd = vq->priv_data; ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ struct ak_camera_dev *pcdev = ici->priv; ++ struct ak_buffer *buf = container_of(vb, struct ak_buffer, vb); ++ u32 yaddr_chl1, yaddr_chl2, size; ++ static int ch2_sync = 0; ++ ++ isp_dbg("%s (vb=0x%p) buf[%d] baddr = 0x%08lx, bsize = %d\n", ++ __func__, vb, vb->i, vb->baddr, vb->bsize); ++ ++ list_add_tail(&vb->queue, &pcdev->capture); ++ ++ vb->state = VIDEOBUF_ACTIVE; ++ size = vb->width * vb->height; ++ yaddr_chl1 = videobuf_to_dma_contig(vb); /* for mater channel */ ++ yaddr_chl2 = yaddr_chl1 + size * 3 / 2; /* for secondary channel */ ++ ++ switch(pcdev->isp.cur_mode) { ++ case ISP_YUV_OUT: ++ case ISP_YUV_BYPASS: ++ case ISP_RGB_OUT: ++ /* for single mode */ ++ if (!pcdev->active) { ++ pcdev->active = buf; ++ pcdev->dma_running = 1; ++ ++ isp_set_even_frame(&pcdev->isp, yaddr_chl1, yaddr_chl2); ++ isp_apply_mode(&pcdev->isp); ++ isp_start_capturing(&pcdev->isp); ++ ++ isp_dbg("queue[single]: vbuf[%d] start run.\n", vb->i); ++ } ++ break; ++ ++ case ISP_YUV_VIDEO_OUT: ++ case ISP_YUV_VIDEO_BYPASS: ++ case ISP_RGB_VIDEO_OUT: ++ /* for continous mode */ ++ if (!pcdev->active) { ++ pcdev->active = buf; ++ pcdev->dma_running = 0; ++ ch2_sync = 1; ++ ++ isp_set_even_frame(&pcdev->isp, yaddr_chl1, yaddr_chl2); ++ isp_dbg("queue[continue]: vbuf1[%d]\n", vb->i); ++ return; ++ } ++ ++ if (!pcdev->dma_running) { ++ pcdev->dma_running = 1; ++ ++ if (ch2_sync) { ++ ch2_sync = 0; ++ irq_buf_empty_flag = 0; ++ ++ isp_set_odd_frame(&pcdev->isp, yaddr_chl1, yaddr_chl2); ++ isp_apply_mode(&pcdev->isp); ++ isp_start_capturing(&pcdev->isp); ++ ++ isp_dbg("queue[continue]: vbuf2[%d] start.\n", vb->i); ++ return; ++ } ++ ++ // ensure that can update yaddr immediately ++ if (isp_is_capturing_odd(&pcdev->isp)) ++ isp_set_even_frame(&pcdev->isp, yaddr_chl1, yaddr_chl2); ++ else ++ isp_set_odd_frame(&pcdev->isp, yaddr_chl1, yaddr_chl2); ++ ++ } ++ break; ++ default: ++ printk("The working mode of ISP hasn't been initialized.\n"); ++ } ++} ++ ++static void ak_videobuf_release(struct videobuf_queue *vq, ++ struct videobuf_buffer *vb) ++{ ++ struct ak_buffer *buf = container_of(vb, struct ak_buffer, vb); ++// struct soc_camera_device *icd = vq->priv_data; ++// struct device *dev = icd->parent; ++ ++ isp_dbg("%s (vb=0x%p) buf[%d] 0x%08lx %d\n", ++ __func__, vb, vb->i, vb->baddr, vb->bsize); ++ ++ switch (vb->state) { ++ case VIDEOBUF_ACTIVE: ++ CAMDBG("vb status: ACTIVE\n"); ++ break; ++ case VIDEOBUF_QUEUED: ++ CAMDBG("vb status: QUEUED\n"); ++ break; ++ case VIDEOBUF_PREPARED: ++ CAMDBG("vb status: PREPARED\n"); ++ break; ++ default: ++ CAMDBG("vb status: unknown\n"); ++ break; ++ } ++ ++ free_buffer(vq, buf); ++} ++ ++static struct videobuf_queue_ops ak_videobuf_ops = { ++ .buf_setup = ak_videobuf_setup, ++ .buf_prepare = ak_videobuf_prepare, ++ .buf_queue = ak_videobuf_queue, ++ .buf_release = ak_videobuf_release, ++}; ++ ++/* platform code*/ ++static int ak_camera_setup_dma(struct ak_camera_dev *pcdev) ++{ ++ struct videobuf_buffer *vb_active = &pcdev->active->vb; ++ struct videobuf_buffer *vb; ++ struct list_head *next; ++ unsigned long yaddr_chl1_active, yaddr_chl2_active; ++ unsigned long yaddr_chl1_next, yaddr_chl2_next; ++ int size; ++ ++ size = vb_active->width * vb_active->height; ++ yaddr_chl1_active = videobuf_to_dma_contig(vb_active); ++ yaddr_chl2_active = yaddr_chl1_active + size * 3 / 2; ++ ++ /* for single mode */ ++ if (!isp_is_continuous(&pcdev->isp)) { ++ isp_set_even_frame(&pcdev->isp, yaddr_chl1_active, yaddr_chl2_active); ++ isp_update_regtable(&pcdev->isp, 1); ++ isp_start_capturing(&pcdev->isp); ++ return 0; ++ } ++ ++ /* ISP is in the continuous mode */ ++ next = pcdev->capture.next; ++ next = next->next; ++ if (next == &pcdev->capture) { ++ isp_dbg("irq: the next vbuf is empty.\n"); ++ //isp_stop_capturing(&pcdev->isp); ++ irq_buf_empty_flag = 1; ++ irq_need_baffle = 1; ++ pcdev->dma_running = 0; ++ goto out; ++ } else ++ irq_buf_empty_flag = 0; ++ ++ vb = list_entry(next, struct videobuf_buffer, queue); ++ ++ /* setup the DMA address for transferring */ ++ yaddr_chl1_next = videobuf_to_dma_contig(vb); ++ yaddr_chl2_next = yaddr_chl1_next + size * 3 / 2; ++ if (isp_is_capturing_odd(&pcdev->isp)) ++ isp_set_even_frame(&pcdev->isp, yaddr_chl1_next, yaddr_chl2_next); ++ else ++ isp_set_odd_frame(&pcdev->isp, yaddr_chl1_next, yaddr_chl2_next); ++out: ++ isp_update_regtable(&pcdev->isp, 0); ++ return 0; ++} ++ ++/* platform code please fix me */ ++static void ak_camera_wakeup(struct ak_camera_dev *pcdev, ++ struct videobuf_buffer *vb, ++ struct ak_buffer *buf) ++{ ++ struct captureSync *adctime; ++ struct timeval cam_tv; ++ unsigned long adc_stamp; ++ unsigned long useconds; ++ unsigned long long actuallyBytes = 0; ++ ++ isp_dbg("%s (vb=0x%p) buf[%d], baddr = 0x%08lx, bsize = %d\n", ++ __func__, vb, vb->i, vb->baddr, vb->bsize); ++ ++ do_gettimeofday(&cam_tv); ++ ++ adctime = getRecordSyncSamples(); ++ ++ /* figure out the timestamp of frame */ ++ //adc_stamp = (unsigned long)(( adctime->adcCapture_bytes * 1000) / ( adctime->rate * ( adctime->frame_bits / 8 ) ) ); ++ actuallyBytes = adctime->adcCapture_bytes * (unsigned long long)1000; ++ if ( actuallyBytes != 0 ) { ++ do_div( actuallyBytes, ( adctime->rate * ( adctime->frame_bits / 8 ) ) ); ++ adc_stamp = actuallyBytes; ++ }else { //if current no audio ++ adc_stamp = 1000; //any value. ++ } ++ ++ if (cam_tv.tv_sec > adctime->tv.tv_sec) { ++ useconds = cam_tv.tv_usec + 1000000 - adctime->tv.tv_usec; ++ } else { ++ useconds = cam_tv.tv_usec - adctime->tv.tv_usec; ++ } ++ ++ vb->ts.tv_usec = (adc_stamp % 1000) * 1000 + useconds; ++ ++ if(vb->ts.tv_usec >= 1000000) { ++ vb->ts.tv_sec = adc_stamp / 1000 + 1; ++ vb->ts.tv_usec = vb->ts.tv_usec % 1000000; ++ } else { ++ vb->ts.tv_sec = adc_stamp / 1000; ++ } ++ ++ /* We don't have much to do if the capturing list is empty */ ++ if (list_empty(&pcdev->capture)) { ++ pcdev->active = NULL; ++ pcdev->dma_running = 0; ++ ++ //REG32(&pcdev->isp.base + ISP_PERI_PARA) &= ~(1 << 29); ++ //REG32(&pcdev->isp.base + ISP_PERI_PARA) |= (1 << 28); ++ ++ isp_stop_capturing(&pcdev->isp); ++ printk("isp-irq: vbuf queue is empty.\n"); ++ return; ++ } ++ ++ if (!irq_buf_empty_flag) { ++ list_del_init(&vb->queue); ++ vb->state = VIDEOBUF_DONE; ++ vb->field_count++; ++ // here, current frame commit to video_buffer layer ++ wake_up(&vb->done); ++ ++ isp_dbg("wakeup (vb=0x%p) buf[%d], baddr = 0x%08lx, bsize = %d\n", ++ vb, vb->i, vb->baddr, vb->bsize); ++ } ++ ++ pcdev->active = list_entry(pcdev->capture.next, ++ struct ak_buffer, vb.queue); ++ ++ ak_camera_setup_dma(pcdev); ++} ++ ++/* fix me */ ++static irqreturn_t ak_camera_dma_irq(int channel, void *data) ++{ ++ struct ak_camera_dev *pcdev = data; ++ struct ak_buffer *buf; ++ struct videobuf_buffer *vb; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&pcdev->lock, flags); ++ ++ /*Fixme: isp_check_irq must be improved */ ++ if (isp_check_irq(&pcdev->isp) < 0) ++ goto out; ++ ++ // throw the last irq when video mode working stopped. ++ if (irq_need_baffle) { ++ irq_need_baffle = 0; ++ goto out; ++ } ++ ++ vb = &pcdev->active->vb; ++ buf = container_of(vb, struct ak_buffer, vb); ++ //buf = pcdev->active; ++ WARN_ON(buf->inwork || list_empty(&vb->queue)); ++ ++ ak_camera_wakeup(pcdev, vb, buf); ++ ++out: ++ isp_clear_irq_status(&pcdev->isp); ++ spin_unlock_irqrestore(&pcdev->lock, flags); ++ return IRQ_HANDLED; ++} ++ ++static void isp_work(struct work_struct *work) ++{ ++ struct isp_struct *isp = container_of(work, struct isp_struct, work.work); ++ ++ update_exposure_value(isp); ++} ++ ++ ++#if 0 ++/* for debugging osd function of ISP */ ++static int create_osd_picture(struct ak_camera_dev *pcdev) ++{ ++ int startX, startY, endX, endY; ++ int picsize; ++ int osd_width, osd_height; ++ int i; ++ ++ startX = startY = 0; ++ endX = endY = 100; ++ osd_width = endX - startX + 1; ++ osd_height = endY - startY + 1; ++ picsize = osd_width * osd_height; ++ ++ if (!pcdev->osd_swbuff) { ++ pcdev->osd_swbuff = kmalloc(picsize, GFP_KERNEL); ++ if (!pcdev->osd_swbuff) ++ return -1; ++ } ++ ++ if (!pcdev->osd_buff) { ++ pcdev->osd_buff = kmalloc(picsize / 2 + picsize %2, GFP_KERNEL); ++ if (!pcdev->osd_buff) ++ return -1; ++ } ++ memset(pcdev->osd_buff, 0, (picsize / 2 + picsize % 2)); ++ ++ for (i = 0; i osd_swbuff[i] = 3; ++ ++ for (i = picsize/2; i osd_swbuff[i+1] = 4; ++ ++ for(i = 0; iosd_buff[i] = (pcdev->osd_swbuff[2*i] &0xf) | ((pcdev->osd_swbuff[2*i+1]&0xf)<<4); ++ ++ if(picsize%2) ++ pcdev->osd_buff[i] = pcdev->osd_swbuff[2*i]; ++ ++ return 0; ++} ++#endif ++ ++static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl) ++{ ++ return container_of(ctrl->handler, struct soc_camera_device, ++ ctrl_handler); ++} ++ ++static int ak_camera_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ++{ ++// struct soc_camera_device *icd = ctrl_to_icd(ctrl); ++// struct v4l2_subdev *sd = soc_camera_to_subdev(icd); ++// struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++// struct ak_camera_dev *pcdev = ici->priv; ++ ++ isp_dbg("entry %s, ctrl->id=%x\n", __func__, ctrl->id); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ isp_dbg("%s(): V4L2_CID_BRIGHTNESS\n", __func__); ++ break; ++ case V4L2_CID_GAMMA: ++ isp_dbg("%s(): V4L2_CID_GAMMA\n", __func__); ++ break; ++ case V4L2_CID_SATURATION: ++ isp_dbg("%s(): V4L2_CID_SATURATION\n", __func__); ++ break; ++ case V4L2_CID_SHARPNESS: ++ isp_dbg("%s(): V4L2_CID_SHARPNESS\n", __func__); ++ break; ++ case V4L2_CID_DO_WHITE_BALANCE: ++ isp_dbg("%s(): V4L2_CID_DO_WHITE_BALANCE\n", __func__); ++ break; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ isp_dbg("%s(): V4L2_CID_AUTO_WHITE_BALANCE\n", __func__); ++ break; ++ } ++ ++ return 0; ++} ++ ++/* ++ * @the isp standard control should be implemented here. ++ */ ++static int ak_camera_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_control control; ++ struct soc_camera_device *icd = ctrl_to_icd(ctrl); ++ struct v4l2_subdev *sd = soc_camera_to_subdev(icd); ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ struct ak_camera_dev *pcdev = ici->priv; ++ ++ isp_dbg("entry %s\n", __func__); ++ ++ if (pcdev->isp.cur_mode_class == ISP_RGB_CLASS) { ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ if (isp_set_brightness(&pcdev->isp, ctrl) == 0) ++ return 0; ++ break; ++ case V4L2_CID_GAMMA: ++ if (isp_set_gamma(&pcdev->isp, ctrl) == 0) ++ return 0; ++ break; ++ case V4L2_CID_SATURATION: ++ if (isp_set_saturation(&pcdev->isp, ctrl) == 0) ++ return 0; ++ break; ++ case V4L2_CID_SHARPNESS: ++ if (isp_set_sharpness(&pcdev->isp, ctrl) == 0) ++ return 0; ++ break; ++ case V4L2_CID_DO_WHITE_BALANCE: ++ if (isp_manu_set_wb_param(&pcdev->isp, ctrl) == 0) ++ return 0; ++ break; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ if (isp_auto_set_wb_param(&pcdev->isp, ctrl) == 0) ++ return 0; ++ break; ++ } ++ } ++ ++ control.id = ctrl->id; ++ control.value = ctrl->val; ++ v4l2_subdev_call(sd, core, s_ctrl, &control); ++ ++ return 0; ++} ++ ++ ++static const struct v4l2_ctrl_ops ak_camera_ctrl_ops = { ++ .g_volatile_ctrl = ak_camera_g_volatile_ctrl, ++ .s_ctrl = ak_camera_s_ctrl, ++}; ++ ++static void set_sensor_cis_sclk(unsigned int cis_sclk) ++{ ++ unsigned long regval; ++ unsigned int cis_sclk_div; ++ ++ unsigned int peri_pll = ak_get_peri_pll_clk()/1000000; ++ ++ cis_sclk_div = peri_pll/cis_sclk - 1; ++ ++ regval = REG32(CLOCK_PERI_PLL_CTRL2); ++ regval &= ~(0x3f << 10); ++ regval |= (cis_sclk_div << 10); ++ REG32(CLOCK_PERI_PLL_CTRL2) = (1 << 19)|regval; ++ ++ isp_dbg("%s() cis_sclk=%dMHz peri_pll=%dMHz cis_sclk_div=%d\n", ++ __func__, cis_sclk, peri_pll, cis_sclk_div); ++} ++ ++ ++/* ++ * @Called when the /dev/videox is opened. ++ */ ++static int ak_camera_add_device(struct soc_camera_device *icd) ++{ ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ struct ak_camera_dev *pcdev = ici->priv; ++ struct v4l2_subdev *sd = soc_camera_to_subdev(icd); ++ struct ak_camera_cam *cam; ++ int cis_sclk; ++ ++ CAMDBG("entry %s\n", __func__); ++ ++ /* The ak camera host driver only support one image sensor */ ++ if (pcdev->icd) ++ return -EBUSY; ++ ++ dev_info(icd->parent, "AK Camera driver attached to camera %d\n", ++ icd->devnum); ++ ++ /* for debugging. Capture list should be empty when the video opened. */ ++ if (!list_empty(&pcdev->capture)) { ++ printk("Bug: pcdev->capture is not empty\n"); ++ list_del_init(&pcdev->capture); ++ } ++ ++ /********** config sensor module **********/ ++ //get sensor clk and power up the sensor ++ cis_sclk = v4l2_subdev_call(sd, core, init, 0); ++ if (cis_sclk <= 0) { ++ cis_sclk = 24; ++ } ++ ++ //set cis_sclk, the sensor present working 24MHz ++ clk_enable(pcdev->cis_sclk); ++ set_sensor_cis_sclk(cis_sclk); ++ ++ // load the default setting for sensor ++ v4l2_subdev_call(sd, core, load_fw); ++ ++ /********** config isp module **********/ ++ ak_soft_reset(AK_SRESET_CAMERA); ++ //set shared GPIO to CAMERA function ++ ak_group_config(ePIN_AS_CAMERA); ++ // enable isp clock ++ clk_enable(pcdev->clk); ++ ++ pcdev->icd = icd; ++ ++ /* FIXME Here, add out control */ ++ if (!icd->host_priv) { ++ v4l2_ctrl_new_std(&icd->ctrl_handler, &ak_camera_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, ISP_BRIGHTNESS_0, ++ ISP_BRIGHTNESS_6, 1, ISP_BRIGHTNESS_2); ++ v4l2_ctrl_new_std(&icd->ctrl_handler, &ak_camera_ctrl_ops, ++ V4L2_CID_GAMMA, ISP_GAMMA_0, ++ ISP_GAMMA_6, 1, ISP_GAMMA_0); ++ v4l2_ctrl_new_std(&icd->ctrl_handler, &ak_camera_ctrl_ops, ++ V4L2_CID_SATURATION, ISP_SATURATION_0, ++ ISP_SATURATION_6, 1, ISP_SATURATION_1); ++ v4l2_ctrl_new_std(&icd->ctrl_handler, &ak_camera_ctrl_ops, ++ V4L2_CID_SHARPNESS, ISP_SHARPNESS_0, ++ ISP_SHARPNESS_6, 1, ISP_SHARPNESS_1); ++ v4l2_ctrl_new_std(&icd->ctrl_handler, &ak_camera_ctrl_ops, ++ V4L2_CID_DO_WHITE_BALANCE, ISP_MANU_WB_0, ++ ISP_MANU_WB_6, 1, ISP_MANU_WB_0); ++ v4l2_ctrl_new_std(&icd->ctrl_handler, &ak_camera_ctrl_ops, ++ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); ++ ++ /* FIXME: subwindow is lost between close / open */ ++ cam = kzalloc(sizeof(*cam), GFP_KERNEL); ++ if (!cam) ++ return -ENOMEM; ++ ++ /* We are called with current camera crop, initialise subrect with it */ ++ icd->host_priv = cam; ++ } else { ++ cam = icd->host_priv; ++ } ++ ++ return 0; ++} ++ ++/* ++ * @Called when the /dev/videox is closed. ++ */ ++static void ak_camera_remove_device(struct soc_camera_device *icd) ++{ ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ struct ak_camera_dev *pcdev = ici->priv; ++ struct v4l2_subdev *sd = soc_camera_to_subdev(icd); ++ ++ CAMDBG("entry %s\n", __func__); ++ ++ BUG_ON(icd != pcdev->icd); ++ ++ v4l2_subdev_call(sd, core, reset, 0); ++ ++ isp_clear_irq(&pcdev->isp); ++ isp_stop_capturing(&pcdev->isp); ++ ++ /* disable sensor clk */ ++ clk_disable(pcdev->cis_sclk); ++ ++ /* disable the clock of isp module */ ++ clk_disable(pcdev->clk); ++ ++ //ak_soft_reset(AK_SRESET_CAMERA); ++ ++ dev_info(icd->parent, "AK Camera driver detached from camera %d\n", ++ icd->devnum); ++ ++ pcdev->active = NULL; ++ pcdev->icd = NULL; ++ ++ CAMDBG("Leave %s\n", __func__); ++} ++ ++/* platform independent finished */ ++static int ak_camera_querycap(struct soc_camera_host *ici, ++ struct v4l2_capability *cap) ++{ ++ isp_dbg("entry %s\n", __func__); ++ ++ /* cap->name is set by the friendly caller:-> */ ++ strlcpy(cap->card, ak_cam_driver_description, sizeof(cap->card)); ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ ++ return 0; ++} ++ ++static int ak_camera_cropcap(struct soc_camera_device *icd, ++ struct v4l2_cropcap *crop) ++{ ++ struct v4l2_subdev *sd = soc_camera_to_subdev(icd); ++ ++ isp_dbg("enter %s\n", __func__); ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ // isp support crop, need complete. ++ return v4l2_subdev_call(sd, video, cropcap, crop); ++} ++ ++static int ak_camera_get_crop(struct soc_camera_device *icd, ++ struct v4l2_crop *crop) ++{ ++ struct v4l2_subdev *sd = soc_camera_to_subdev(icd); ++ //struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ //struct ak_camera_dev *pcdev = ici->priv; ++ ++ isp_dbg("entry %s\n", __func__); ++ ++ return v4l2_subdev_call(sd, video, g_crop, crop); ++} ++ ++static int ak_camera_set_crop(struct soc_camera_device *icd, ++ struct v4l2_crop *crop) ++{ ++ struct v4l2_subdev *sd = soc_camera_to_subdev(icd); ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ struct ak_camera_dev *pcdev = ici->priv; ++ int ret, width, height; ++ ++ isp_dbg("entry %s\n", __func__); ++ ++ if (pcdev->dma_running) { ++ /* make sure streaming is not started */ ++ v4l2_err(&ici->v4l2_dev, ++ "Cannot change crop when streaming is ON\n"); ++ return -EBUSY; ++ } ++ ++ width = crop->c.width - crop->c.left; ++ height = crop->c.height - crop->c.top; ++ if ((crop->c.top < 0 || crop->c.left < 0) ++ ||(((width * 3) < 18) || (height * 3) < 18) ++ ||((width > 1280) || (height > 720))) { ++ v4l2_err(&ici->v4l2_dev, ++ "doesn't support negative values for top & left\n"); ++ return -EINVAL; ++ } ++ ++ if ((ret = isp_set_crop(&pcdev->isp, crop->c)) < 0) ++ ret = v4l2_subdev_call(sd, video, s_crop, crop); ++ ++ return ret; ++} ++ ++ ++/* ++ * @Called before ak_camera_set_fmt. ++ */ ++static int ak_camera_try_fmt(struct soc_camera_device *icd, ++ struct v4l2_format *f) ++{ ++ struct v4l2_subdev *sd = soc_camera_to_subdev(icd); ++ const struct soc_camera_format_xlate *xlate; ++ struct v4l2_pix_format *pix = &f->fmt.pix; ++ struct v4l2_mbus_framefmt mf; ++ int ret; ++ /* TODO: limit to ak hardware capabilities */ ++ CAMDBG("entry %s\n", __func__); ++ ++ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); ++ if (!xlate) { ++ dev_warn(icd->parent, "Format %x not found\n", ++ pix->pixelformat); ++ return -EINVAL; ++ } ++ ++ mf.width = pix->width; ++ mf.height = pix->height; ++ mf.field = pix->field; ++ mf.colorspace = pix->colorspace; ++ mf.code = xlate->code; ++ ++ /* limit to sensor capabilities */ ++ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ pix->width = mf.width; ++ pix->height = mf.height; ++ pix->field = mf.field; ++ pix->colorspace = mf.colorspace; ++ ++ return 0; ++} ++ ++/* platform independent finished */ ++static int ak_camera_set_fmt(struct soc_camera_device *icd, ++ struct v4l2_format *f) ++{ ++ struct v4l2_subdev *sd = soc_camera_to_subdev(icd); ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ struct ak_camera_dev *pcdev = ici->priv; ++ const struct soc_camera_format_xlate *xlate; ++ struct v4l2_pix_format *pix = &f->fmt.pix; ++ struct v4l2_mbus_framefmt mf; ++ struct v4l2_cropcap cropcap; ++ int ret, buswidth; ++ ++ isp_dbg("entry %s\n", __func__); ++ ++ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); ++ if (!xlate) { ++ dev_warn(icd->parent, "Format %x not found\n", ++ pix->pixelformat); ++ return -EINVAL; ++ } ++ ++ //if (YUV_OUT) ++ ++ buswidth = xlate->host_fmt->bits_per_sample; ++ if (buswidth > 10) { ++ dev_warn(icd->parent, ++ "bits-per-sample %d for format %x unsupported\n", ++ buswidth, pix->pixelformat); ++ return -EINVAL; ++ } ++ ++ mf.width = pix->width; ++ mf.height = pix->height; ++ mf.field = pix->field; ++ mf.colorspace = pix->colorspace; ++ mf.code = xlate->code; ++ icd->current_fmt = xlate; ++ ++ v4l2_subdev_call(sd, video, cropcap, &cropcap); ++ if (mf.width != cropcap.bounds.width ++ || mf.height != cropcap.bounds.height) { ++ mf.width = cropcap.defrect.width; ++ mf.height = cropcap.defrect.height; ++ } ++ ++ isp_dbg("%s. mf.width = %d, mf.height = %d\n", ++ __func__, mf.width, mf.height); ++ ++ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); ++ if (ret < 0) ++ return ret; ++ ++ if (mf.code != xlate->code) ++ return -EINVAL; ++ ++ /* recored the VGA size used for check to enable isp ch2 when RGB input*/ ++ pcdev->isp.fmt_def_width = mf.width; ++ pcdev->isp.fmt_def_height = mf.height; ++ ++ /* ++ * @fmt_width and fmt_height is the input image size of sensor. ++ * @chl1_width and chl1_height is the output image size for user. ++ */ ++ pcdev->isp.fmt_width = pix->width; ++ pcdev->isp.fmt_height = pix->height; ++ pcdev->isp.chl1_width = pix->width; ++ pcdev->isp.chl1_height= pix->height; ++ ++ isp_set_cutter_window(&pcdev->isp, 0, 0, mf.width, mf.height); ++ isp_set_channel1_scale(&pcdev->isp, pix->width, pix->height); ++ ++ isp_dbg("%s: chl1_width=%d, chl1_height=%d\n", __func__, ++ pcdev->isp.chl1_width, pcdev->isp.chl1_height); ++ ++ return ret; ++} ++ ++static int ak_camera_get_formats(struct soc_camera_device *icd, unsigned int idx, ++ struct soc_camera_format_xlate *xlate) ++{ ++ struct v4l2_subdev *sd = soc_camera_to_subdev(icd); ++ struct device *dev = icd->parent; ++ struct soc_camera_host *ici = to_soc_camera_host(dev); ++ struct ak_camera_dev *pcdev = ici->priv; ++ int ret, formats = 0; ++ enum v4l2_mbus_pixelcode code; ++ const struct soc_mbus_pixelfmt *fmt; ++ ++ CAMDBG("entry %s\n", __func__); ++ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); ++ if (ret < 0) ++ /* No more formats */ ++ return 0; ++ ++ /* ++ * @Note: ISP only support yuv420 output and jpeg out. ++ * FIXME1: We miss jpeg here. ++ * FIXME2: the output squence of YUV is actually UYVY. ++ */ ++ fmt = soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_2X8); ++ if (!fmt) { ++ dev_warn(dev, "unsupported format code #%u: %d\n", idx, code); ++ return 0; ++ } ++ CAMDBG("get format %s code=%d from sensor\n", fmt->name, code); ++ ++ /* Generic pass-through */ ++ formats++; ++ if (xlate) { ++ xlate->host_fmt = fmt; ++ xlate->code = code; ++ xlate++; ++ ++ /* ++ * @decide the default working mode of isp ++ * @prefer RGB mode ++ */ ++ if (code < V4L2_MBUS_FMT_Y8_1X8) { ++ pcdev->def_mode = ISP_RGB_VIDEO_OUT; ++ //pcdev->def_mode = ISP_RGB_OUT; ++ } ++ ++ if ((pcdev->def_mode != ISP_RGB_VIDEO_OUT) ++ && (pcdev->def_mode != ISP_RGB_OUT)) { ++ pcdev->def_mode = ISP_YUV_VIDEO_BYPASS; ++ //pcdev->def_mode = ISP_YUV_BYPASS; ++ } ++ pcdev->isp.cur_mode = pcdev->def_mode; ++ update_cur_mode_class(&pcdev->isp); ++ ++ dev_dbg(dev, "Providing format %s in pass-through mode\n", ++ fmt->name); ++ } ++ ++ return formats; ++} ++ ++static void ak_camera_put_formats(struct soc_camera_device *icd) ++{ ++ CAMDBG("entry %s\n", __func__); ++ kfree(icd->host_priv); ++ icd->host_priv = NULL; ++ CAMDBG("leave %s\n", __func__); ++} ++ ++static int isp_set_sensor_param(struct isp_struct *isp, struct isp_config_sensor_reg *ctrl) ++{ ++ if (ctrl->enable) ++ aksensor_set_param(ctrl->cmd, ctrl->data); ++ return 0; ++} ++ ++/* ++ * The private interface of ISP for application user. ++ */ ++ static int ak_camera_set_parm(struct soc_camera_device *icd, ++ struct v4l2_streamparm *a) ++{ ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ struct ak_camera_dev *pcdev = ici->priv; ++ struct isp_mode_info *mode_info; ++ int retval = 0, *parm_type; ++ ++ CAMDBG("entry %s\n", __func__); ++ ++ parm_type = (int *)a->parm.raw_data; ++ switch(*parm_type) { ++ case ISP_PARM_MODE: ++ mode_info = (struct isp_mode_info *)parm_type; ++ if (!pcdev->dma_running) { ++ pcdev->isp.cur_mode = mode_info->mode; ++ ++ update_cur_mode_class(&pcdev->isp); ++ isp_apply_mode(&pcdev->isp); ++ ++ printk("%s: change working mode to %d\n", __func__, mode_info->mode); ++ } else { ++ printk("%s: working mode can not be changed when dma is running\n", __func__); ++ retval = -EBUSY; ++ } ++ break; ++ case ISP_PARM_CHANNEL2: ++ retval = isp_set_channel2(&pcdev->isp, (struct isp_channel2_info *)parm_type); ++ break; ++ case ISP_PARM_OSD: ++ retval = isp_set_osd(&pcdev->isp, (struct isp_osd_info *)parm_type); ++ break; ++ case ISP_PARM_OCCLUSION: ++ retval = isp_set_occlusion_area(&pcdev->isp, (struct isp_occlusion_info *)parm_type); ++ break; ++ case ISP_PARM_OCCLUSION_COLOR: ++ retval = isp_set_occlusion_color(&pcdev->isp, (struct isp_occlusion_color *)parm_type); ++ break; ++ case ISP_PARM_ZOOM: ++ retval = isp_set_zoom(&pcdev->isp, (struct isp_zoom_info *)parm_type); ++ break; ++ case ISP_CID_BLACK_BALANCE: ++ retval = isp_set_black_balance(&pcdev->isp, (struct isp_black_balance *)parm_type); ++ break; ++ case ISP_CID_LENS: ++ retval = isp_set_lens_correct(&pcdev->isp, (struct isp_lens_correct *)parm_type); ++ break; ++ case ISP_CID_DEMOSAIC: ++ retval = isp_set_demosaic(&pcdev->isp, (struct isp_demosaic *)parm_type); ++ break; ++ case ISP_CID_RGB_FILTER: ++ retval = isp_set_rgb_filter(&pcdev->isp, (struct isp_rgb_filter *)parm_type); ++ break; ++ case ISP_CID_UV_FILTER: ++ retval = isp_set_uv_iso_filter(&pcdev->isp, (struct isp_uv_filter *)parm_type); ++ break; ++ case ISP_CID_DEFECT_PIXEL: ++ retval = isp_set_defect_pixel(&pcdev->isp, (struct isp_defect_pixel *)parm_type); ++ break; ++ case ISP_CID_WHITE_BALANCE: ++ retval = isp_set_manu_wb(&pcdev->isp, (struct isp_white_balance *)parm_type); ++ break; ++ case ISP_CID_AUTO_WHITE_BALANCE: ++ retval = isp_set_auto_wb(&pcdev->isp, (struct isp_auto_white_balance *)parm_type); ++ break; ++ case ISP_CID_COLOR: ++ retval = isp_set_color_correct(&pcdev->isp, (struct isp_color_correct *)parm_type); ++ break; ++ case ISP_CID_GAMMA: ++ retval = isp_set_gamma_calc(&pcdev->isp, (struct isp_gamma_calculate *)parm_type); ++ break; ++ case ISP_CID_BRIGHTNESS_ENHANCE: ++ retval = isp_set_brightness_enhance(&pcdev->isp, (struct isp_brightness_enhance *)parm_type); ++ break; ++ case ISP_CID_SATURATION: ++ retval = isp_set_uv_saturation(&pcdev->isp, (struct isp_saturation *)parm_type); ++ break; ++ case ISP_CID_HISTOGRAM: ++ retval = isp_set_histogram(&pcdev->isp, (struct isp_histogram *)parm_type); ++ break; ++ case ISP_CID_SPECIAL_EFFECT: ++ retval = isp_set_special_effect(&pcdev->isp, (struct isp_special_effect *)parm_type); ++ break; ++ case ISP_CID_SET_SENSOR_PARAM: ++ retval = isp_set_sensor_param(&pcdev->isp, (struct isp_config_sensor_reg *)parm_type); ++ break; ++ default: ++ retval = -EINVAL; ++ printk("%s: private control encounter unknown type\n", __func__); ++ } ++ ++ CAMDBG("leave %s\n", __func__); ++ ++ return retval; ++} ++ ++ ++ static int ak_camera_get_parm(struct soc_camera_device *icd, ++ struct v4l2_streamparm *a) ++{ ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ struct ak_camera_dev *pcdev = ici->priv; ++ struct isp_channel2_info *chl2_info; ++ struct isp_white_balance *co_rgb_info; ++ struct isp_mode_info *mode_info; ++ int retval = 0, *parm_type; ++ ++ parm_type = (int *)a->parm.raw_data; ++ ++ switch(*parm_type) { ++ case ISP_PARM_MODE: ++ mode_info = (struct isp_mode_info *)parm_type; ++ mode_info->mode = pcdev->isp.cur_mode; ++ break; ++ case ISP_PARM_CHANNEL2: ++ chl2_info = (struct isp_channel2_info *)parm_type; ++ chl2_info->width = pcdev->isp.chl2_width; ++ chl2_info->height = pcdev->isp.chl2_height; ++ chl2_info->enable = pcdev->isp.chl2_enable; ++ break; ++ case ISP_CID_WHITE_BALANCE: ++ case ISP_CID_AUTO_WHITE_BALANCE: ++ co_rgb_info = (struct isp_white_balance *)parm_type; ++ co_rgb_info->co_r = pcdev->isp.wb_param.co_r; ++ co_rgb_info->co_g = pcdev->isp.wb_param.co_g; ++ co_rgb_info->co_b = pcdev->isp.wb_param.co_b; ++ break; ++ default: ++ retval = -EINVAL; ++ printk("%s: private control encounter unknown type\n", __func__); ++ break; ++ } ++ ++ CAMDBG("leave %s\n", __func__); ++ ++ return retval; ++} ++ ++ ++/* Maybe belong platform code fix me */ ++static int ak_camera_set_bus_param(struct soc_camera_device *icd) ++{ ++ struct v4l2_subdev *sd = soc_camera_to_subdev(icd); ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ struct ak_camera_dev *pcdev = ici->priv; ++ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; ++ unsigned long common_flags; ++ int ret; ++ ++ CAMDBG("entry %s\n", __func__); ++ ++ /* AK39 supports 8bit and 10bit buswidth */ ++ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); ++ if (!ret) { ++ common_flags = soc_mbus_config_compatible(&cfg, CSI_BUS_FLAGS); ++ if (!common_flags) { ++ dev_warn(icd->parent, ++ "Flags incompatible: camera 0x%x, host 0x%x\n", ++ cfg.flags, CSI_BUS_FLAGS); ++ return -EINVAL; ++ } ++ } else if (ret != -ENOIOCTLCMD) { ++ return ret; ++ } else { ++ common_flags = CSI_BUS_FLAGS; ++ } ++ ++ /* Make choises, based on platform choice */ ++ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && ++ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { ++ if (!pcdev->pdata || ++ pcdev->pdata->flags & AK_CAMERA_VSYNC_HIGH) ++ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; ++ else ++ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; ++ } ++ ++ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && ++ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { ++ if (!pcdev->pdata || ++ pcdev->pdata->flags & AK_CAMERA_PCLK_RISING) ++ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; ++ else ++ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; ++ } ++ ++ if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) && ++ (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) { ++ if (!pcdev->pdata || ++ pcdev->pdata->flags & AK_CAMERA_DATA_HIGH) ++ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW; ++ else ++ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH; ++ } ++ ++ cfg.flags = common_flags; ++ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); ++ if (ret < 0 && ret != -ENOIOCTLCMD) { ++ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", ++ common_flags, ret); ++ return ret; ++ } ++ ++ CAMDBG("leave %s\n", __func__); ++ ++ return 0; ++} ++ ++/* platform independent finished*/ ++static void ak_camera_init_videobuf(struct videobuf_queue *q, ++ struct soc_camera_device *icd) ++{ ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ struct ak_camera_dev *pcdev = ici->priv; ++ ++ CAMDBG("entry %s\n", __func__); ++ ++ videobuf_queue_dma_contig_init(q, &ak_videobuf_ops, icd->parent, ++ &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_NONE, ++ sizeof(struct ak_buffer), icd, &icd->video_lock); ++ ++ CAMDBG("leave %s\n", __func__); ++} ++ ++/* platform independent finished*/ ++static int ak_camera_reqbufs(struct soc_camera_device *icd, ++ struct v4l2_requestbuffers *p) ++{ ++ int i; ++ ++ CAMDBG("entry %s\n", __func__); ++ ++ /* This is for locking debugging only. I removed spinlocks and now I ++ * check whether .prepare is ever called on a linked buffer, or whether ++ * a dma IRQ can occur for an in-work or unlinked buffer. Until now ++ * it hadn't triggered */ ++ for (i = 0; i < p->count; i++) { ++ struct ak_buffer *buf = container_of(icd->vb_vidq.bufs[i], ++ struct ak_buffer, vb); ++ buf->inwork = 0; ++ INIT_LIST_HEAD(&buf->vb.queue); ++ } ++ ++ CAMDBG("leave %s\n", __func__); ++ ++ return 0; ++} ++ ++/* platform independent */ ++static unsigned int ak_camera_poll(struct file *file, poll_table *pt) ++{ ++ struct soc_camera_device *icd = file->private_data; ++ struct ak_buffer *buf; ++ ++ buf = list_entry(icd->vb_vidq.stream.next, struct ak_buffer, ++ vb.stream); ++ ++ poll_wait(file, &buf->vb.done, pt); ++ ++ if (buf->vb.state == VIDEOBUF_DONE || ++ buf->vb.state == VIDEOBUF_ERROR) { ++ return POLLIN | POLLRDNORM; ++ } ++ ++ return 0; ++} ++ ++ ++static struct soc_camera_host_ops ak_soc_camera_host_ops = { ++ .owner = THIS_MODULE, ++ .add = ak_camera_add_device, ++ .remove = ak_camera_remove_device, ++ .get_formats = ak_camera_get_formats, ++ .put_formats = ak_camera_put_formats, ++ .set_bus_param = ak_camera_set_bus_param, ++ .cropcap = ak_camera_cropcap, ++ .get_crop = ak_camera_get_crop, ++ .set_crop = ak_camera_set_crop, ++ .set_fmt = ak_camera_set_fmt, ++ .try_fmt = ak_camera_try_fmt, ++ .init_videobuf = ak_camera_init_videobuf, ++ .reqbufs = ak_camera_reqbufs, ++ .poll = ak_camera_poll, ++ .querycap = ak_camera_querycap, ++ .set_parm = ak_camera_set_parm, ++ .get_parm = ak_camera_get_parm, ++}; ++ ++static int ak_camera_probe(struct platform_device *pdev) ++{ ++ struct ak_camera_dev *pcdev; ++ struct resource *res; ++ struct clk *clk, *cis_sclk; ++ void __iomem *base; ++ unsigned int irq; ++ int err = 0; ++ ++ CAMDBG("entry %s\n", __func__); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ irq = platform_get_irq(pdev, 0); ++ if (!res || irq < 0) { ++ printk("platform_get_irq | platform_get_resource\n"); ++ err = -ENODEV; ++ goto exit; ++ } ++ ++ /* ++ * @get isp working clock ++ */ ++ clk = clk_get(&pdev->dev, "camera"); ++ if (IS_ERR(clk)) { ++ err = PTR_ERR(clk); ++ goto exit; ++ } ++ ++ /* ++ * @get cis_sclk for sensor ++ */ ++ cis_sclk = clk_get(&pdev->dev, "sensor"); ++ if (IS_ERR(cis_sclk)) { ++ err = PTR_ERR(cis_sclk); ++ goto exit_put_clk; ++ } ++ ++ /* ++ ** @allocate memory to struct ak_camera, including struct soc_camera_host ++ ** @and struct v4l2_device ++ */ ++ pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); ++ if (!pcdev) { ++ err = -ENOMEM; ++ goto exit_put_cisclk; ++ } ++ ++ /* @initailization for struct pcdev */ ++ pcdev->res = res; ++ pcdev->clk = clk; ++ pcdev->cis_sclk = cis_sclk; ++ pcdev->dma_running = 0; ++ ++ pcdev->pdata = pdev->dev.platform_data; ++ if (!pcdev->pdata) { ++ err = -ENODEV; ++ goto exit_put_cisclk; ++ } ++ ++ if (pcdev->mclk > 0) ++ pcdev->mclk = pcdev->pdata->mclk; ++ else { ++ dev_warn(&pdev->dev, ++ "Platform mclk == 0! Please, fix your platform data. " ++ "Using default 24MHz\n"); ++ pcdev->mclk = 24; ++ } ++ ++ INIT_LIST_HEAD(&pcdev->capture); ++ spin_lock_init(&pcdev->lock); ++ ++ /* ++ * Request the regions. ++ */ ++ if (!request_mem_region(res->start, resource_size(res), AK_CAM_DRV_NAME)) { ++ err = -EBUSY; ++ goto exit_kfree; ++ } ++ ++ base = ioremap_nocache(res->start, resource_size(res)); ++ if (!base) { ++ err = -ENOMEM; ++ goto exit_release; ++ } ++ pcdev->irq = irq; ++ pcdev->base = base; ++ ++ /* ++ * @initialize pcdev->isp_struct ++ */ ++ pcdev->isp.base = base; ++ if (isp_module_init(&pcdev->isp) < 0) { ++ err = -ENOMEM; ++ goto exit_iounmap; ++ } ++ ++ /* ++ * request irq ++ */ ++ err = request_irq(irq, ak_camera_dma_irq, IRQF_DISABLED, "ak_camera", pcdev); ++ if (err) { ++ err = -EBUSY; ++ goto exit_freeisp; ++ } ++ ++ INIT_DELAYED_WORK(&pcdev->isp.work, isp_work); ++ ++ /* ++ ** @register soc_camera_host ++ */ ++ pcdev->soc_host.drv_name = AK_CAM_DRV_NAME; ++ pcdev->soc_host.ops = &ak_soc_camera_host_ops; ++ pcdev->soc_host.priv = pcdev; ++ pcdev->soc_host.v4l2_dev.dev = &pdev->dev; ++ pcdev->soc_host.nr = pdev->id; ++ ++ err = soc_camera_host_register(&pcdev->soc_host); ++ if (err) { ++ goto exit_freeirq; ++ } ++ ++ dev_info(&pdev->dev, "AK Camera driver loaded\n"); ++ ++ return 0; ++ ++exit_freeirq: ++ free_irq(irq, pcdev); ++exit_freeisp: ++ isp_module_fini(&pcdev->isp); ++exit_iounmap: ++ iounmap(base); ++exit_release: ++ release_mem_region(res->start, resource_size(res)); ++exit_kfree: ++ kfree(pcdev); ++exit_put_cisclk: ++ clk_put(cis_sclk); ++exit_put_clk: ++ clk_put(clk); ++exit: ++ return err; ++} ++ ++static int ak_camera_remove(struct platform_device *pdev) ++{ ++ ++ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); ++ struct ak_camera_dev *pcdev = container_of(soc_host, ++ struct ak_camera_dev, soc_host); ++ struct resource *res; ++ ++ CAMDBG("entry %s\n", __func__); ++ ++ /* free irq */ ++ free_irq(pcdev->irq, pcdev); ++ ++ /* free clk */ ++ clk_put(pcdev->clk); ++ clk_put(pcdev->cis_sclk); ++ ++ soc_camera_host_unregister(soc_host); ++ ++ iounmap(pcdev->base); ++ ++ res = pcdev->res; ++ release_mem_region(res->start, resource_size(res)); ++ ++ /* ++ * @deconstruct the isp object. ++ */ ++ isp_module_fini(&pcdev->isp); ++ ++ kfree(pcdev); ++ ++ dev_info(&pdev->dev, "AK Camera driver unloaded\n"); ++ ++ return 0; ++} ++ ++static struct platform_driver ak_camera_driver = { ++ .probe = ak_camera_probe, ++ .remove = ak_camera_remove, ++ .driver = { ++ .name = AK_CAM_DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init ak_camera_init(void) ++{ ++ CAMDBG("entry %s\n", __func__); ++ ++ return platform_driver_register(&ak_camera_driver); ++} ++ ++static void __exit ak_camera_exit(void) ++{ ++ CAMDBG("entry %s\n", __func__); ++ ++ platform_driver_unregister(&ak_camera_driver); ++} ++ ++module_init(ak_camera_init); ++module_exit(ak_camera_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("wu_daochao "); ++MODULE_DESCRIPTION("Driver for ak Camera Interface"); ++ +diff --git a/drivers/media/video/plat-anyka/aksensor.c b/drivers/media/video/plat-anyka/aksensor.c +new file mode 100755 +index 00000000..0da78b9a +--- /dev/null ++++ b/drivers/media/video/plat-anyka/aksensor.c +@@ -0,0 +1,750 @@ ++/* ++ * ak sensor Driver ++ * ++ * Copyright (C) 2012 Anyka ++ * ++ * Based on anykaplatform driver, ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++//#define SENSOR_DEBUG ++#ifdef SENSOR_DEBUG ++#define sensor_dbg(fmt...) printk(KERN_INFO "Sensor: " fmt) ++#else ++#define sensor_dbg(fmt, args...) do{}while(0) ++#endif ++ ++#define SENDBG(fmt, args...) do{}while(0) ++ ++static struct sensor_info *cur_sensor_info; ++static const struct aksensor_color_format *cur_sensor_cfmts; ++ ++T_CAMERA_WORKMODE g_mode = CAMERA_WMODE_REC; ++static struct i2c_client *g_client; ++ ++struct aksensor_priv { ++ struct v4l2_subdev subdev; ++ struct v4l2_ctrl_handler hdl; ++ struct aksensor_camera_info *info; ++ const struct aksensor_color_format *cfmt; ++ struct aksensor_win_size win; ++ int model; ++}; ++ ++static struct sensor_info *sensor_info_array[SENSOR_MAX_SUPPORT]; ++ ++int register_sensor(struct sensor_info *si) ++{ ++ int i, ret; ++ ++ if (!si) ++ return -1; ++ ++ if ((si->sensor_id <=0) || (si->handler == NULL)) ++ return -1; ++ ++ ret = -1; ++ for (i = 0; i < SENSOR_MAX_SUPPORT; i++) { ++ if (sensor_info_array[i] != NULL) ++ continue; ++ ++ sensor_info_array[i] = si; ++ ret = 0; ++ SENDBG("register sensor(id=0x%x) successfully\n", si->sensor_id); ++ break; ++ } ++ ++ if (i == SENSOR_MAX_SUPPORT) ++ SENDBG("register sensor(id=0x%x) failed!(no enough space)\n", si->sensor_id); ++ ++ return ret; ++} ++EXPORT_SYMBOL(register_sensor); ++ ++/** ++ * @brief camera probe pointer ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param ++ * @return sensor_info * camera device pointer ++ * @retval ++ */ ++static struct sensor_info *probe_sensors(struct i2c_client *client) ++{ ++ int i, read_id; ++ ++ for (i = 0; i < SENSOR_MAX_SUPPORT; i++) ++ { ++ if (sensor_info_array[i]) ++ { ++ sensor_info_array[i]->handler->cam_open_func(); ++ read_id = sensor_info_array[i]->handler->cam_read_id_func(); ++ ++ if (!(strcmp(sensor_info_array[i]->sensor_name, "hm1375"))) ++ read_id = read_id & 0x0fff; ++ ++ if ((read_id == sensor_info_array[i]->sensor_id) ++ && ((read_id != 0xffff) || (read_id != 0xff))) { ++ ++ dev_info(&client->dev, "Probing %s Sensor ID: 0x%x\n", ++ sensor_info_array[i]->sensor_name, read_id); ++ ++ return sensor_info_array[i]; ++ } else { ++ sensor_info_array[i]->handler->cam_close_func(); ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++ ++s32 aksensor_i2c_write_byte_short(u8 daddr, u16 raddr, u8 *data, u32 size) ++{ ++ unsigned char msg[3]; ++ msg[0] = raddr >> 8; ++ msg[1] = raddr & 0xff; ++ msg[2] = *data; ++ ++// printk("msg=0x%02x%02x, 0x%02x(write)\n", msg[0], msg[1], msg[2]); ++ return i2c_master_send(g_client, msg, 3); ++} ++ ++s32 aksensor_i2c_read_byte_short(u8 daddr, u16 raddr) ++{ ++ unsigned char msg[2]; ++ unsigned char data; ++ ++ g_client->addr = daddr/2; ++ msg[0] = raddr >> 8; ++ msg[1] = raddr & 0xff; ++ ++ i2c_master_send(g_client, msg, 2); ++ ++ i2c_master_recv(g_client, &data, 1); ++ ++ // printk("msg=0x%02x%02x, 0x%02x(read)\n", msg[0], msg[1], data); ++ ++ return data; ++} ++ ++s32 aksensor_i2c_write_word_data(u8 daddr, u16 raddr, u16 *data, u32 size) ++{ ++ unsigned char msg[4]; ++ ++ msg[0] = raddr >> 8; //high 8bit first send ++ msg[1] = raddr & 0xff; //low 8bit second send ++ msg[2] = *data & 0xff; //low 8bit first send ++ msg[3] = *data >> 8; //high 8bit second send ++ ++ g_client->addr = daddr >> 1; ++ ++ //printk("(cmd): 0x%02x %02x, (data): %02x %02x\n", msg[0], msg[1], msg[2], msg[3]); ++ return i2c_master_send(g_client, msg, 4); ++} ++ ++s32 aksensor_i2c_read_word_data(u8 daddr, u16 raddr) ++{ ++ unsigned char msg[4]; ++ unsigned char buf[2]; ++ ++ msg[0] = raddr >> 8; //high 8bit first send ++ msg[1] = raddr & 0xff; //low 8bit second send ++ ++ g_client->addr = daddr >> 1; ++ ++ i2c_master_send(g_client, msg, 2); ++ ++ i2c_master_recv(g_client, buf, 2); ++ ++ return (buf[1] << 8)|buf[0]; ++} ++ ++s32 aksensor_i2c_write_byte_data(u8 daddr, u8 raddr, u8 *data, u32 size) ++{ ++ return i2c_smbus_write_byte_data(g_client, raddr, *data); ++} ++ ++s32 aksensor_i2c_read_byte_data(u8 daddr, u8 raddr) ++{ ++ g_client->addr = daddr/2; ++ return i2c_smbus_read_byte_data(g_client,raddr); ++} ++ ++/*****************************************/ ++static struct aksensor_priv *to_aksensor(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct aksensor_priv, subdev); ++} ++ ++static int aksensor_init(struct v4l2_subdev *sd, u32 val) ++{ ++ int ret; ++ SENDBG("entry %s\n", __func__); ++ if (cur_sensor_info->handler != NULL) ++ { ++ cur_sensor_info->handler->cam_open_func(); ++ ret = cur_sensor_info->handler->cam_mclk; ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int aksensor_loadfw(struct v4l2_subdev *sd) ++{ ++ SENDBG("entry %s\n", __func__); ++ if ((cur_sensor_info->handler != NULL) ++ && (cur_sensor_info->handler->cam_init_func != NULL)) ++ { ++ if (cur_sensor_info->handler->cam_init_func()) ++ return AK_TRUE; ++ } ++ return AK_FALSE; ++} ++ ++static int aksensor_reset( struct v4l2_subdev *sd, u32 val ) ++{ ++// struct i2c_client *client = v4l2_get_subdevdata(sd); ++// struct aksensor_priv *priv = i2c_get_clientdata(client); ++ ++ SENDBG("entry %s\n", __func__); ++ ++ //priv->win.width = VGA_WIDTH; ++ //priv->win.height = VGA_HEIGHT; ++ ++ if (NULL != cur_sensor_info->handler) { ++ cur_sensor_info->handler->cam_close_func(); ++ } ++ ++ return 0; ++} ++ ++static int aksensor_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct aksensor_priv *priv = to_aksensor(client); ++ ++ id->ident = priv->model; ++ id->revision = 0; ++ ++ return 0; ++} ++ ++static int aksensor_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) ++{ ++ int ret = 0; ++ sensor_dbg("entry %s\n", __func__); ++ //if (cur_sensor_info->ctrls) ++ // ret = cur_sensor_info->ctrls->ops->queryctrl(ctrl); ++ return ret; ++} ++static int aksensor_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++// struct v4l2_ctrl v4l2ctrl; ++ ++ sensor_dbg("entry %s\n", __func__); ++#if 0 ++ if (cur_sensor_info->ctrls) { ++ cur_sensor_info->ctrls->ops->g_volatile_ctrl(&v4l2ctrl); ++ ctrl->id = v4l2ctrl.id; ++ ctrl->val = v4l2ctrl.val; ++ } ++#endif ++ return 0; ++} ++static int aksensor_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ int ret = 0; ++ struct v4l2_ctrl v4l2ctrl; ++ ++ sensor_dbg("entry %s\n", __func__); ++ ++ if (cur_sensor_info->ctrls) { ++ v4l2ctrl.id = ctrl->id; ++ v4l2ctrl.val = ctrl->value; ++ ret = cur_sensor_info->ctrls->ops->s_ctrl(&v4l2ctrl); ++ } ++ return ret; ++} ++ ++static struct v4l2_subdev_core_ops aksensor_subdev_core_ops = { ++ .init = aksensor_init, ++ .load_fw = aksensor_loadfw, ++ .reset = aksensor_reset, ++ .g_chip_ident = aksensor_g_chip_ident, ++ .queryctrl = aksensor_queryctrl, ++ .g_ctrl = aksensor_g_ctrl, ++ .s_ctrl = aksensor_s_ctrl, ++}; ++ ++ ++/* ++ * soc_camera_ops function ++ */ ++static int aksensor_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ SENDBG("entry %s\n", __func__); ++ SENDBG("%s==>enable=%d\n", __func__, enable); ++ SENDBG("leave %s\n", __func__); ++ return 0; ++} ++ ++static int aksensor_get_params(struct i2c_client *client, ++ enum v4l2_mbus_pixelcode code) ++{ ++ struct aksensor_priv *priv = to_aksensor(client); ++ int ret = -EINVAL; ++ int i; ++ ++ SENDBG("entry %s\n", __func__); ++ ++ /* ++ * select format ++ */ ++ priv->cfmt = NULL; ++ for (i = 0; i < cur_sensor_info->num_formats; i++) { ++ if (code == cur_sensor_cfmts[i].code) { ++ priv->cfmt = cur_sensor_cfmts + i; ++ break; ++ } ++ } ++ if (!priv->cfmt) ++ goto aksensor_set_fmt_error; ++ ++ return 0; ++ ++aksensor_set_fmt_error: ++ priv->cfmt = NULL; ++ return ret; ++} ++ ++/* first called by soc_camera_prove to initialize icd->user_width... */ ++static int aksensor_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct aksensor_priv *priv = container_of(sd, struct aksensor_priv, subdev); ++ int ret; ++ ++ SENDBG("entry %s\n", __func__); ++ ++ if (!priv->cfmt) { ++ SENDBG("select VGA for first time\n"); ++ ret = aksensor_get_params(client, V4L2_MBUS_FMT_YUYV8_2X8); ++ if (ret < 0) ++ return ret; ++ } ++ ++ mf->width = priv->win.width; ++ mf->height = priv->win.height; ++ mf->code = priv->cfmt->code; ++ mf->colorspace = priv->cfmt->colorspace; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++ ++static int aksensor_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ SENDBG("entry %s\n", __func__); ++ SENDBG("leave %s\n", __func__); ++ ++ return 0; ++} ++ ++static int aksensor_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct aksensor_priv *priv = container_of(sd, struct aksensor_priv, subdev); ++// struct v4l2_pix_format *pix = &f->fmt.pix; ++ ++ T_BOOL bRet = AK_FALSE; ++ int ret = -EINVAL; ++ ++ sensor_dbg("entry %s\n", __func__); ++ ++ //лģʽǰһmode ++ if ( V4L2_BUF_TYPE_PRIVATE == mf->reserved[0]) ++ { ++ g_mode = mf->reserved[1]; ++ } ++ priv->win.width = mf->width; ++ priv->win.height =mf->height; ++ ++ sensor_dbg("---%s. g_mode=%d mf->width=%d mf->height=%d\n", ++ __func__, g_mode, mf->width, mf->height); ++ ++ switch (g_mode) ++ { ++ case CAMERA_WMODE_PREV: ++ if (cur_sensor_info->handler->cam_set_to_prev_func != NULL) ++ bRet = cur_sensor_info->handler->cam_set_to_prev_func(mf->width, mf->height); ++ break; ++ case CAMERA_WMODE_CAP: ++ if (cur_sensor_info->handler->cam_set_to_cap_func != NULL) ++ bRet = cur_sensor_info->handler->cam_set_to_cap_func(mf->width, mf->height); ++ break; ++ case CAMERA_WMODE_REC: ++ if (cur_sensor_info->handler->cam_set_to_record_func != NULL) ++ bRet = cur_sensor_info->handler->cam_set_to_record_func(mf->width, mf->height); ++ break; ++ default : ++ if (cur_sensor_info->handler->cam_set_to_record_func != NULL) ++ bRet = cur_sensor_info->handler->cam_set_to_record_func(mf->width, mf->height); ++ break; ++ } ++ ++ if ( bRet ) ++ ret = 0; ++ ++ SENDBG("leave %s\n", __func__); ++ return ret; ++} ++ ++ ++ ++static int aksensor_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct aksensor_priv *priv = to_aksensor(client); ++ int i; ++ ++ sensor_dbg("entry %s. priv=%p\n", __func__, priv); ++ ++ for (i = 0; i < cur_sensor_info->num_resolution; ++i) { ++ if (!strcmp(cur_sensor_info->resolution[i].name, "720P")) ++ break; ++ } ++ // the resolution is 720P or larger ++ if (i == cur_sensor_info->num_resolution) ++ --i; ++ a->bounds.width = cur_sensor_info->resolution[i].width; ++ a->bounds.height = cur_sensor_info->resolution[i].height; ++ a->bounds.left = 0; ++ a->bounds.top = 0; ++ ++ a->defrect.width = priv->win.width; ++ a->defrect.height = priv->win.height; ++ a->defrect.left = 0; ++ a->defrect.top = 0; ++ ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ sensor_dbg("%s.\n" ++ " a->bounds.width=%d, a->bounds.height=%d\n" ++ " a->defrect.width=%d, a->defrect.height=%d\n", ++ __func__, ++ a->bounds.width, a->bounds.height, ++ a->defrect.width, a->defrect.height); ++ return 0; ++} ++ ++static int aksensor_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct aksensor_priv *priv = to_aksensor(client); ++ ++ sensor_dbg("entry %s\n", __func__); ++ ++ a->c.left = 0; ++ a->c.top = 0; ++ a->c.width = priv->win.width; ++ a->c.height = priv->win.height; ++ ++ return 0; ++} ++ ++static int aksensor_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct aksensor_priv *priv = to_aksensor(client); ++ int ret = -EINVAL; ++ T_BOOL bRet = AK_FALSE; ++ ++ sensor_dbg("entry %s\n", __func__); ++ ++ priv->win.width = a->c.width; ++ priv->win.height =a->c.height; ++ ++ switch (g_mode) ++ { ++ case CAMERA_WMODE_PREV: ++ if (cur_sensor_info->handler->cam_set_to_prev_func != NULL) ++ bRet = cur_sensor_info->handler->cam_set_to_prev_func(a->c.width, a->c.height); ++ break; ++ case CAMERA_WMODE_CAP: ++ if (cur_sensor_info->handler->cam_set_to_cap_func != NULL) ++ bRet = cur_sensor_info->handler->cam_set_to_cap_func(a->c.width, a->c.height); ++ break; ++ case CAMERA_WMODE_REC: ++ if (cur_sensor_info->handler->cam_set_to_record_func != NULL) ++ bRet = cur_sensor_info->handler->cam_set_to_record_func(a->c.width, a->c.height); ++ break; ++ default : ++ if (cur_sensor_info->handler->cam_set_to_record_func != NULL) ++ bRet = cur_sensor_info->handler->cam_set_to_record_func(a->c.width, a->c.height); ++ break; ++ } ++ ++ if ( bRet ) ++ ret = 0; ++ ++ return ret; ++} ++ ++static int aksensor_video_probe(struct i2c_client *client) ++{ ++ struct aksensor_priv *priv = to_aksensor(client); ++ const char *devname; ++ ++ SENDBG("entry %s\n", __func__); ++ ++ /* ++ * check and show product ID and manufacturer ID ++ */ ++ g_client = client; ++ if (cur_sensor_info != NULL) { ++ dev_info(&client->dev, "Probing %s Sensor ID 0x%x\n", ++ cur_sensor_info->sensor_name, ++ cur_sensor_info->sensor_id); ++ } else { ++ cur_sensor_info = probe_sensors(client); ++ if (cur_sensor_info == NULL) { ++ dev_err(&client->dev, "Sensor ID error\n"); ++ return -ENODEV; ++ } ++ } ++ cur_sensor_cfmts = cur_sensor_info->formats; ++ ++ devname = "aksensor"; ++ priv->model = cur_sensor_info->sensor_id; ++ ++ return 0; ++} ++ ++static int aksensor_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= cur_sensor_info->num_formats) ++ return -EINVAL; ++ ++ *code = cur_sensor_cfmts[index].code; ++ return 0; ++} ++ ++static int aksensor_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_link *icl = soc_camera_i2c_to_link(client); ++ ++ SENDBG("entry %s\n", __func__); ++ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | ++ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | ++ V4L2_MBUS_DATA_ACTIVE_HIGH; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(icl, cfg); ++ SENDBG("leave %s\n", __func__); ++ ++ return 0; ++} ++ ++static struct v4l2_subdev_video_ops aksensor_subdev_video_ops = { ++ .s_stream = aksensor_s_stream, ++ .g_mbus_fmt = aksensor_g_fmt, ++ .s_mbus_fmt = aksensor_s_fmt, ++ .try_mbus_fmt = aksensor_try_fmt, ++ .cropcap = aksensor_cropcap, ++ .g_crop = aksensor_g_crop, ++ .s_crop = aksensor_s_crop, ++ .enum_mbus_fmt = aksensor_enum_fmt, ++ .g_mbus_config = aksensor_g_mbus_config, ++}; ++ ++static struct v4l2_subdev_ops aksensor_subdev_ops = { ++ .core = &aksensor_subdev_core_ops, ++ .video = &aksensor_subdev_video_ops, ++}; ++ ++void aksensor_set_param(unsigned int cmd, unsigned int data) ++{ ++ cur_sensor_info->handler->cam_set_sensor_param_func(cmd, data); ++} ++EXPORT_SYMBOL(aksensor_set_param); ++ ++/* ++ * i2c_driver function ++ */ ++static int aksensor_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct aksensor_priv *priv; ++ struct soc_camera_link *icl = soc_camera_i2c_to_link(client); ++ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); ++ int i, ret; ++ ++ SENDBG("entry %s\n", __func__); ++ ++ if (!icl || !icl->priv) { ++ dev_err(&client->dev, "AKSENSOR: missing platform data!\n"); ++ return -EINVAL; ++ } ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ dev_err(&adapter->dev, ++ "I2C-Adapter doesn't support " ++ "I2C_FUNC_SMBUS_BYTE_DATA\n"); ++ return -EIO; ++ } ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ return -ENOMEM; ++ } ++ ++ priv->info = icl->priv; ++ v4l2_i2c_subdev_init(&priv->subdev, client, &aksensor_subdev_ops); ++ ++ ret = aksensor_video_probe(client); ++ ++ if (ret) { ++ kfree(priv); ++ return ret; ++ } ++ ++ v4l2_ctrl_handler_init(&priv->hdl, cur_sensor_info->nr_ctrls); ++ for (i = 0; i < cur_sensor_info->nr_ctrls; i++) ++ v4l2_ctrl_new_custom(&priv->hdl, &cur_sensor_info->ctrls[i], NULL); ++ priv->subdev.ctrl_handler = &priv->hdl; ++ if (priv->hdl.error) { ++ int err = priv->hdl.error; ++ v4l2_ctrl_handler_free(&priv->hdl); ++ kfree(priv); ++ return err; ++ } ++ ++ // init sensor resolution, default VGA ++ for (i = 0; i < cur_sensor_info->num_resolution; i++) ++ if (!strcmp(cur_sensor_info->resolution[i].name, "VGA")) { ++ priv->win.width = cur_sensor_info->resolution[i].width; ++ priv->win.height = cur_sensor_info->resolution[i].height; ++ } ++ sensor_dbg("%s: priv->win.width=%d priv->win.height=%d\n", ++ __func__, priv->win.width, priv->win.height); ++ return ret; ++} ++ ++static int aksensor_remove(struct i2c_client *client) ++{ ++ struct aksensor_priv *priv = to_aksensor(client); ++ ++ if (NULL != cur_sensor_info->handler) { ++ cur_sensor_info->handler->cam_close_func(); ++ cur_sensor_info->handler = NULL; ++ } ++ ++ v4l2_device_unregister_subdev(&priv->subdev); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ kfree(priv); ++ return 0; ++} ++ ++static const struct i2c_device_id aksensor_id[] = { ++ { "aksensor", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, aksensor); ++ ++static struct i2c_driver aksensor_i2c_driver = { ++ .driver = { ++ .name = "aksensor", ++ }, ++ .probe = aksensor_probe, ++ .remove = aksensor_remove, ++ .id_table = aksensor_id, ++}; ++ ++/** ++ * @brief get GPIO pin value ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param GPIO pin type ++ * @return GPIO pin value ++ * @retval ++ */ ++T_U32 cam_getpin(T_CAMERA_PINTYPE pin_type) ++{ ++ T_U32 pin = INVALID_GPIO; ++ ++// SENDBG("entry %s\n", __func__); ++ ++ if (AK_NULL != g_client) ++ { ++ struct aksensor_priv *priv = to_aksensor(g_client); ++ ++ switch (pin_type) ++ { ++ case PIN_AVDD: ++ pin = priv->info->pin_avdd; ++ break; ++ case PIN_POWER: ++ pin = priv->info->pin_power; ++ break; ++ case PIN_RESET: ++ pin = priv->info->pin_reset; ++ break; ++ default : ++ break; ++ } ++ } ++ ++ return pin; ++} ++ ++/* ++ * module function ++ */ ++ ++static int __init aksensor_module_init(void) ++{ ++ SENDBG("entry %s\n", __func__); ++ ++ return i2c_add_driver(&aksensor_i2c_driver); ++} ++ ++static void __exit aksensor_module_exit(void) ++{ ++ SENDBG("entry %s\n", __func__); ++ ++ i2c_del_driver(&aksensor_i2c_driver); ++} ++ ++module_init(aksensor_module_init); ++module_exit(aksensor_module_exit); ++ ++MODULE_DESCRIPTION("SoC Camera driver for aksensor"); ++MODULE_AUTHOR("dengzhou"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/video/plat-anyka/camera_ar0130.c b/drivers/media/video/plat-anyka/camera_ar0130.c +new file mode 100755 +index 00000000..5a00810d +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_ar0130.c +@@ -0,0 +1,1068 @@ ++/** ++ * @file camera_ar0130.c ++ * @brief camera driver file ++ * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author caolianming ++ * @date 2013-07-31 ++ * @version 1.0 ++ * @ref ++ */ ++#ifdef CONFIG_LINUX_AKSENSOR ++#include ++#include ++#include ++#include "camera_ar0130.h" ++#else ++#include "akdefine.h" ++#include "cam_com_sensor.h" ++#include "camera_ar0130.h" ++#include "Gpio_config.h" ++#endif ++ ++#if defined (USE_CAMERA_AR0130) || defined (CONFIG_SENSOR_AR0130) ++ ++#define CAM_EN_LEVEL 0 ++#define CAM_RESET_LEVEL 0 ++ ++#define CAMERA_SCCB_ADDR 0x20 ++#define CAMERA_AR0130_ID 0x0130 ++ ++#define AR0130_CAMERA_MCLK 27 ++ ++static T_CAMERA_TYPE camera_ar0130_type = CAMERA_2M; ++static T_NIGHT_MODE night_mode = CAMERA_DAY_MODE; ++static T_CAMERA_MODE s_ar0130_CurMode = CAMERA_MODE_VGA; ++ ++#if 0 ++static T_VOID camera_setbit(T_U16 reg, T_U8 bit, T_U8 value) ++{ ++ T_U8 tmp; ++ ++ tmp = sccb_read_short(CAMERA_SCCB_ADDR, reg); ++ if (value == 1) ++ { ++ tmp |= 0x1 << bit; ++ } ++ else ++ { ++ tmp &= ~(0x1 << bit); ++ } ++ ++ sccb_write_word(CAMERA_SCCB_ADDR, reg, &tmp, 1); ++} ++#endif ++static T_U32 cam_ar0130_read_id(T_VOID); ++ ++static T_BOOL camera_set_param(const T_U16 tabParameter[]) ++{ ++ int i = 0; ++ T_U8 temp_value; ++ T_U16 data; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ data = tabParameter[i + 1]; ++ sccb_write_word(CAMERA_SCCB_ADDR, tabParameter[i], &data, 1); ++ ++ if ((tabParameter[i] != 0x0000) || (tabParameter[i] != 0x0022) ++ || (tabParameter[i] != 0x0100) || (tabParameter[i] != 0x0101)) ++ { ++ temp_value = sccb_read_short(CAMERA_SCCB_ADDR, tabParameter[i]); ++ if (temp_value != tabParameter[i + 1]) ++ { ++ akprintf(C1, M_DRVSYS, "set parameter error!\n"); ++ akprintf(C1, M_DRVSYS, "reg 0x%x write data is 0x%x, read data is 0x%x!\n", tabParameter[i], tabParameter[i + 1], temp_value); ++ ++ return AK_FALSE; ++ } ++ } ++ } ++ ++ i += 2; ++ } ++ ++ return AK_TRUE; ++} ++ ++static T_VOID read_camera_reg(const T_U16 tabParameter[]) ++{ ++ unsigned short data; ++ int i = 0; ++ ++ while (1) { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) { ++ break; ++ } else if (DELAY_FLAG != tabParameter[i]) { ++ data = sccb_read_word(CAMERA_SCCB_ADDR, tabParameter[i]); ++ //printk("read: [0x%04x 0x%04x]\n", tabParameter[i], data); ++ } ++ i += 2; ++ } ++} ++ ++static T_VOID camera_setup(const T_U16 tabParameter[]) ++{ ++ int i = 0; ++ T_U16 data; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ data = tabParameter[i + 1]; ++ sccb_write_word(CAMERA_SCCB_ADDR, tabParameter[i], &data, 2); ++ } ++ i += 2; ++ } ++} ++ ++static T_VOID cam_ar0130_open(T_VOID) ++{ ++ gpio_set_pin_dir(GPIO_CAMERA_AVDD, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_CHIP_ENABLE); ++ gpio_set_pin_dir(GPIO_CAMERA_CHIP_ENABLE, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, CAM_EN_LEVEL); ++ mini_delay(10); ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_RESET); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, CAM_RESET_LEVEL); ++ mini_delay(10); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, !CAM_RESET_LEVEL); ++ ++ mini_delay(20); ++} ++ ++static T_BOOL cam_ar0130_close(T_VOID) ++{ ++ //sccb software standby mode ++// T_U8 Reg0x3d = 0x48; ++// T_U8 Reg0xc3 = 0x00; ++ ++// sccb_write_word(CAMERA_SCCB_ADDR, 0x3d, &Reg0x3d, 1); ++// sccb_write_word(CAMERA_SCCB_ADDR, 0xc3, &Reg0xc3, 1); ++ ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, !CAM_EN_LEVEL); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, !gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_INPUT); ++ ++ gpio_set_pin_dir(GPIO_I2C_SCL, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SCL, GPIO_LEVEL_LOW); ++ gpio_set_pin_dir(GPIO_I2C_SDA, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SDA, GPIO_LEVEL_LOW); ++ ++ return AK_TRUE; ++} ++ ++static T_U32 cam_ar0130_read_id(T_VOID) ++{ ++#if 0 ++ T_U8 value = 0x00; ++ T_U32 id = 0; ++ ++ sccb_init(GPIO_I2C_SCL, GPIO_I2C_SDA); //init sccb first here!! ++ ++ value = sccb_read_short(CAMERA_SCCB_ADDR, 0x0001); ++ id = value << 8; ++ value = sccb_read_short(CAMERA_SCCB_ADDR, 0x0002); ++ id |= value; ++ ++ return id; ++#else ++ return CAMERA_AR0130_ID; ++#endif ++} ++ ++/** ++ * @brief initialize the parameters of camera, should be done after reset and open camera to initialize ++ * @author cao_lianming ++ * @date 2013-07-31 ++ * @return T_BOOL ++ * @retval AK_TRUE if success, else AK_FALSE ++ */ ++static T_BOOL cam_ar0130_init(void) ++{ ++ if (!camera_set_param(INIT_TAB)) ++ { ++ return AK_FALSE; ++ } ++ else ++ { ++ night_mode = CAMERA_DAY_MODE; ++ return AK_TRUE; ++ } ++} ++ ++/** ++ * @brief Set camera mode to specify image quality, SXGA/VGA/CIF etc ++ * @author cao_lianming ++ * @date 2013-07-31 ++ * @param[in] mode mode value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ar0130_set_mode(T_CAMERA_MODE mode) ++{ ++ s_ar0130_CurMode = mode; ++ switch(mode) { ++ case CAMERA_MODE_UXGA: ++ camera_setup(UXGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_SXGA: ++ camera_setup(SXGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_VGA: ++ camera_setup(VGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_CIF: ++ camera_setup(CIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QVGA: ++ camera_setup(QVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_QCIF: ++ camera_setup(QCIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QQVGA: ++ camera_setup(QQVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_PREV: ++ camera_setup(PREV_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_REC: ++ camera_setup(RECORD_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_720P: ++ camera_setup(RECORD_720P_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ default: ++ s_ar0130_CurMode = CAMERA_MODE_VGA; ++ akprintf(C1, M_DRVSYS, "set camera mode parameter error!\n"); ++ break; ++ } ++#if 0 ++ if (mode == CAMERA_MODE_720P) ++ read_camera_reg(RECORD_720P_TAB); ++#endif ++} ++ ++/** ++ * @brief Set camera exposure mode ++ * @author cao_lianming ++ * @date 2013-07-31 ++ * @param[in] exposure exposure mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ar0130_set_exposure(T_CAMERA_EXPOSURE exposure) ++{ ++ switch(exposure) ++ { ++ case EXPOSURE_WHOLE: ++ camera_setup(EXPOSURE_WHOLE_TAB); ++ break; ++ case EXPOSURE_CENTER: ++ camera_setup(EXPOSURE_CENTER_TAB); ++ break; ++ case EXPOSURE_MIDDLE: ++ camera_setup(EXPOSURE_MIDDLE_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set exposure parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera brightness level ++ * @author cao_lianming ++ * @date 2013-07-31 ++ * @param[in] brightness brightness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ar0130_set_brightness(T_CAMERA_BRIGHTNESS brightness) ++{ ++ switch(brightness) ++ { ++ case CAMERA_BRIGHTNESS_0: ++ camera_setup(BRIGHTNESS_0_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_1: ++ camera_setup(BRIGHTNESS_1_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_2: ++ camera_setup(BRIGHTNESS_2_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_3: ++ camera_setup(BRIGHTNESS_3_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_4: ++ camera_setup(BRIGHTNESS_4_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_5: ++ camera_setup(BRIGHTNESS_5_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_6: ++ camera_setup(BRIGHTNESS_6_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set brightness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera contrast level ++ * @author cao_lianming ++ * @date 2013-07-31 ++ * @param[in] contrast contrast value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ar0130_set_contrast(T_CAMERA_CONTRAST contrast) ++{ ++ switch(contrast) ++ { ++ case CAMERA_CONTRAST_1: ++ camera_setup(CONTRAST_1_TAB); ++ break; ++ case CAMERA_CONTRAST_2: ++ camera_setup(CONTRAST_2_TAB); ++ break; ++ case CAMERA_CONTRAST_3: ++ camera_setup(CONTRAST_3_TAB); ++ break; ++ case CAMERA_CONTRAST_4: ++ camera_setup(CONTRAST_4_TAB); ++ break; ++ case CAMERA_CONTRAST_5: ++ camera_setup(CONTRAST_5_TAB); ++ break; ++ case CAMERA_CONTRAST_6: ++ camera_setup(CONTRAST_6_TAB); ++ break; ++ case CAMERA_CONTRAST_7: ++ camera_setup(CONTRAST_7_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set contrast parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera saturation level ++ * @author cao_lianming ++ * @date 2013-07-31 ++ * @param[in] saturation saturation value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ar0130_set_saturation(T_CAMERA_SATURATION saturation) ++{ ++ switch(saturation) ++ { ++ case CAMERA_SATURATION_1: ++ camera_setup(SATURATION_1_TAB); ++ break; ++ case CAMERA_SATURATION_2: ++ camera_setup(SATURATION_2_TAB); ++ break; ++ case CAMERA_SATURATION_3: ++ camera_setup(SATURATION_3_TAB); ++ break; ++ case CAMERA_SATURATION_4: ++ camera_setup(SATURATION_4_TAB); ++ break; ++ case CAMERA_SATURATION_5: ++ camera_setup(SATURATION_5_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set saturation parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera sharpness level ++ * @author cao_lianming ++ * @date 2013-07-31 ++ * @param[in] sharpness sharpness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ar0130_set_sharpness(T_CAMERA_SHARPNESS sharpness) ++{ ++ switch(sharpness) ++ { ++ case CAMERA_SHARPNESS_0: ++ camera_setup(SHARPNESS_0_TAB); ++ break; ++ case CAMERA_SHARPNESS_1: ++ camera_setup(SHARPNESS_1_TAB); ++ break; ++ case CAMERA_SHARPNESS_2: ++ camera_setup(SHARPNESS_2_TAB); ++ break; ++ case CAMERA_SHARPNESS_3: ++ camera_setup(SHARPNESS_3_TAB); ++ break; ++ case CAMERA_SHARPNESS_4: ++ camera_setup(SHARPNESS_4_TAB); ++ break; ++ case CAMERA_SHARPNESS_5: ++ camera_setup(SHARPNESS_5_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set sharpness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera AWB mode ++ * @author cao_lianming ++ * @date 2013-07-31 ++ * @param[in] awb AWB mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ar0130_set_AWB(T_CAMERA_AWB awb) ++{ ++ switch(awb) ++ { ++ case AWB_AUTO: ++ camera_setup(AWB_AUTO_TAB); ++ break; ++ case AWB_SUNNY: ++ camera_setup(AWB_SUNNY_TAB); ++ break; ++ case AWB_CLOUDY: ++ camera_setup(AWB_CLOUDY_TAB); ++ break; ++ case AWB_OFFICE: ++ camera_setup(AWB_OFFICE_TAB); ++ break; ++ case AWB_HOME: ++ camera_setup(AWB_HOME_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set AWB mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera mirror mode ++ * @author cao_lianming ++ * @date 2013-07-31 ++ * @param[in] mirror mirror mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ar0130_set_mirror(T_CAMERA_MIRROR mirror) ++{ ++ switch(mirror) ++ { ++ case CAMERA_MIRROR_V: ++ break; ++ case CAMERA_MIRROR_H: ++ break; ++ case CAMERA_MIRROR_NORMAL: ++ break; ++ case CAMERA_MIRROR_FLIP: ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set mirror parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera effect mode ++ * @author cao_lianming ++ * @date 2013-07-31 ++ * @param[in] effect effect mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ar0130_set_effect(T_CAMERA_EFFECT effect) ++{ ++ switch(effect) ++ { ++ case CAMERA_EFFECT_NORMAL: ++ camera_setup(EFFECT_NORMAL_TAB); ++ break; ++ case CAMERA_EFFECT_SEPIA: ++ camera_setup(EFFECT_SEPIA_TAB); ++ break; ++ case CAMERA_EFFECT_ANTIQUE: ++ camera_setup(EFFECT_ANTIQUE_TAB); ++ break; ++ case CAMERA_EFFECT_BLUE: ++ camera_setup(EFFECT_BLUISH_TAB); ++ break; ++ case CAMERA_EFFECT_GREEN: ++ camera_setup(EFFECT_GREENISH_TAB); ++ break; ++ case CAMERA_EFFECT_RED: ++ camera_setup(EFFECT_REDDISH_TAB); ++ break; ++ case CAMERA_EFFECT_NEGATIVE: ++ camera_setup(EFFECT_NEGATIVE_TAB); ++ break; ++ case CAMERA_EFFECT_BW: ++ camera_setup(EFFECT_BW_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set camer effect parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief set camera window ++ * @author cao_lianming ++ * @date 2013-07-31 ++ * @param[in] srcWidth window width ++ * @param[in] srcHeight window height ++ * @return T_S32 ++ * @retval 0 if error mode ++ * @retval 1 if success ++ * @retval -1 if failed ++ */ ++static T_S32 cam_ar0130_set_digital_zoom(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ return 1; ++} ++ ++static T_VOID cam_ar0130_set_night_mode(T_NIGHT_MODE mode) ++{ ++ switch(mode) ++ { ++ case CAMERA_DAY_MODE: ++ camera_setup(DAY_MODE_TAB); ++ night_mode = CAMERA_DAY_MODE; ++ break; ++ case CAMERA_NIGHT_MODE: ++ camera_setup(NIGHT_MODE_TAB); ++ night_mode = CAMERA_NIGHT_MODE; ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set night mode parameter error!\n"); ++ break; ++ } ++} ++ ++static T_VOID cam_ar0130_set_anti_flicker(T_U32 value) ++{ ++ switch(value) { ++ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: ++ //camera_setup(ANTI_FLICKER_DISABLE_TAB); ++ akprintf(C1, M_DRVSYS, "Anti-flicker not support 'Disable', Error." ++ " please select other frequency!\n"); ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: ++ camera_setup(ANTI_FLICKER_50HZ_TAB); ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: ++ camera_setup(ANTI_FLICKER_60HZ_TAB); ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: ++ //camera_setup(ANTI_FLICKER_AUTO_TAB); ++ akprintf(C1, M_DRVSYS, "Anti-flicker not support 'Auto', Error." ++ " please select other frequency!\n"); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set Anti-flicker parameter error!\n"); ++ break; ++ } ++} ++ ++static T_BOOL cam_ar0130_set_to_cap(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ ++ if ((srcWidth <= 160) && (srcHeight <= 120)) ++ { ++ Cammode = CAMERA_MODE_QQVGA; ++ } ++ else if ((srcWidth <= 176) && (srcHeight <= 144)) ++ { ++ Cammode = CAMERA_MODE_QCIF; ++ } ++ else if ((srcWidth <= 320) && (srcHeight <= 240)) ++ { ++ Cammode = CAMERA_MODE_QVGA; ++ } ++ else if ((srcWidth <= 352) && (srcHeight <= 288)) ++ { ++ Cammode = CAMERA_MODE_CIF; ++ } ++ else if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_VGA; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 720)) ++ { ++ Cammode = CAMERA_MODE_720P; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 1024)) ++ { ++ Cammode = CAMERA_MODE_SXGA; ++ } ++ else if ((srcWidth <= 1600) && (srcHeight <= 1200)) ++ { ++ Cammode = CAMERA_MODE_UXGA; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "ar0130 unsupport %d & %d mode!\n", srcWidth, srcHeight); ++ return AK_FALSE; ++ } ++ ++ cam_ar0130_set_mode(Cammode); ++ cam_ar0130_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_ar0130_set_to_prev(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ cam_ar0130_set_mode(CAMERA_MODE_PREV); ++ cam_ar0130_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_ar0130_set_to_record(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_REC; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 720)) ++ { ++ Cammode = CAMERA_MODE_720P; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "200W camera dose not support such mode"); ++ return AK_FALSE; ++ } ++ ++ cam_ar0130_set_mode(Cammode); ++ cam_ar0130_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_CAMERA_TYPE cam_ar0130_get_type(T_VOID) ++{ ++ return camera_ar0130_type; ++} ++ ++static T_VOID cam_ar0130_set_sensor_param(T_U32 cmd, T_U32 data) ++{ ++ T_U16 value; ++ ++ value = (T_U16)data; ++ sccb_write_word(CAMERA_SCCB_ADDR, (T_U16)cmd, &value, 1); ++} ++ ++static T_CAMERA_FUNCTION_HANDLER ar0130_function_handler = ++{ ++ AR0130_CAMERA_MCLK, ++ cam_ar0130_open, ++ cam_ar0130_close, ++ cam_ar0130_read_id, ++ cam_ar0130_init, ++ cam_ar0130_set_mode, ++ cam_ar0130_set_exposure, ++ cam_ar0130_set_brightness, ++ cam_ar0130_set_contrast, ++ cam_ar0130_set_saturation, ++ cam_ar0130_set_sharpness, ++ cam_ar0130_set_AWB, ++ cam_ar0130_set_mirror, ++ cam_ar0130_set_effect, ++ cam_ar0130_set_digital_zoom, ++ cam_ar0130_set_night_mode, ++ AK_NULL, ++ cam_ar0130_set_anti_flicker, ++ cam_ar0130_set_to_cap, ++ cam_ar0130_set_to_prev, ++ cam_ar0130_set_to_record, ++ cam_ar0130_get_type, ++ cam_ar0130_set_sensor_param ++}; ++ ++#ifndef CONFIG_LINUX_AKSENSOR ++static int camera_ar0130_reg(void) ++{ ++ camera_reg_dev(CAMERA_AR0130_ID, &ar0130_function_handler); ++ return 0; ++} ++ ++#ifdef __CC_ARM ++#pragma arm section rwdata = "__initcall_", zidata = "__initcall_" ++#endif ++module_init(camera_ar0130_reg) ++#ifdef __CC_ARM ++#pragma arm section ++#endif ++ ++#else ++static const char * awb_menu[] = { ++ [AWB_AUTO] = "auto", ++ [AWB_SUNNY] = "sunny", ++ [AWB_CLOUDY] = "cloudy", ++ [AWB_OFFICE] = "office", ++ [AWB_HOME] = "home", ++ [AWB_NIGHT] = "night", ++}; ++ ++static const char * effect_menu[] = { ++ [CAMERA_EFFECT_NORMAL] = "normal", ++ [CAMERA_EFFECT_SEPIA] = "sepia", ++ [CAMERA_EFFECT_ANTIQUE] = "antique", ++ [CAMERA_EFFECT_BLUE] = "blue", ++ [CAMERA_EFFECT_GREEN] = "green", ++ [CAMERA_EFFECT_RED] = "red", ++ [CAMERA_EFFECT_NEGATIVE] = "negative", ++ [CAMERA_EFFECT_BW] = "bw", ++ [CAMERA_EFFECT_BWN] = "bwn", ++ [CAMERA_EFFECT_AQUA] = "aqua", ++ [CAMERA_EFFECT_COOL] = "cool", ++ [CAMERA_EFFECT_WARM] = "warm", ++}; ++ ++static const char * resolution_menu[] = { ++ [0] = "1280x960", ++ [1] = "1280x720", ++ [2] = "640x480", ++}; ++ ++static const char * hflip_menu[] = { ++ [0] = "normal", ++ [1] = "horizontal flip", ++}; ++ ++static const char * vflip_menu[] = { ++ [0] = "normal", ++ [1] = "vertical flip", ++}; ++ ++static const char * night_menu[] = { ++ [CAMERA_DAY_MODE] = "daylight", ++ [CAMERA_NIGHT_MODE] = "night", ++}; ++ ++static const char * anti_flicker_menu[] = { ++ [V4L2_CID_POWER_LINE_FREQUENCY_DISABLED] = "Disable", ++ [V4L2_CID_POWER_LINE_FREQUENCY_50HZ] = "50Hz", ++ [V4L2_CID_POWER_LINE_FREQUENCY_60HZ] = "60Hz", ++ [V4L2_CID_POWER_LINE_FREQUENCY_AUTO] = "Auto", ++}; ++ ++static int ar0130_s_ctl(struct v4l2_ctrl *ctrl) ++{ ++ int ret = -EINVAL; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ if (ar0130_function_handler.cam_set_AWB_func) { ++ ar0130_function_handler.cam_set_AWB_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_COLORFX: ++ if (ar0130_function_handler.cam_set_effect_func) { ++ ar0130_function_handler.cam_set_effect_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ ++ case V4L2_CID_BRIGHTNESS: ++ if (ar0130_function_handler.cam_set_brightness_func) { ++ ar0130_function_handler.cam_set_brightness_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_CONTRAST: ++ if (ar0130_function_handler.cam_set_contrast_func) { ++ ar0130_function_handler.cam_set_contrast_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SATURATION: ++ if (ar0130_function_handler.cam_set_saturation_func) { ++ ar0130_function_handler.cam_set_saturation_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SHARPNESS: ++ if (ar0130_function_handler.cam_set_sharpness_func) { ++ ar0130_function_handler.cam_set_sharpness_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_HFLIP: ++ if (ar0130_function_handler.cam_set_mirror_func) { ++ ar0130_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_H : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_VFLIP: ++ if (ar0130_function_handler.cam_set_mirror_func) { ++ ar0130_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_V : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_NIGHTMODE: ++ if (ar0130_function_handler.cam_set_night_mode_func) { ++ ar0130_function_handler.cam_set_night_mode_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY: ++ if (ar0130_function_handler.cam_set_anti_flicker_func) { ++ ar0130_function_handler.cam_set_anti_flicker_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++}; ++ ++static struct v4l2_ctrl_ops ar0130_ctrl_ops = { ++ .s_ctrl = ar0130_s_ctl, ++}; ++ ++static const struct v4l2_ctrl_config ar0130_ctrls[] = { ++ { ++ .ops = &ar0130_ctrl_ops, ++ .id = V4L2_CID_AUTO_WHITE_BALANCE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "AWB", ++ .min = 0, ++ .max = ARRAY_SIZE(awb_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = awb_menu, ++ }, ++ { ++ .ops = &ar0130_ctrl_ops, ++ .id = V4L2_CID_COLORFX, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Effect", ++ .min = 0, ++ .max = ARRAY_SIZE(effect_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = effect_menu, ++ }, ++ { ++ .ops = &ar0130_ctrl_ops, ++ .id = V4L2_CID_HFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Horizontal Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(hflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = hflip_menu, ++ }, ++ { ++ .ops = &ar0130_ctrl_ops, ++ .id = V4L2_CID_VFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Vetical Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(vflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = vflip_menu, ++ }, ++ { ++ .ops = &ar0130_ctrl_ops, ++ .id = V4L2_CID_PICTURE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Resolution", ++ .min = 0, ++ .max = ARRAY_SIZE(resolution_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = resolution_menu, ++ }, ++ { ++ .ops = &ar0130_ctrl_ops, ++ .id = V4L2_CID_NIGHTMODE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Night mode", ++ .min = 0, ++ .max = ARRAY_SIZE(night_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = night_menu, ++ }, ++ { ++ .ops = &ar0130_ctrl_ops, ++ .id = V4L2_CID_BRIGHTNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Brightness", ++ .min = 0, ++ .max = CAMERA_BRIGHTNESS_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ar0130_ctrl_ops, ++ .id = V4L2_CID_CONTRAST, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Contrast", ++ .min = 0, ++ .max = CAMERA_CONTRAST_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ar0130_ctrl_ops, ++ .id = V4L2_CID_SATURATION, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Saturation", ++ .min = 0, ++ .max = CAMERA_SATURATION_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ar0130_ctrl_ops, ++ .id = V4L2_CID_SHARPNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Sharpness", ++ .min = 0, ++ .max = CAMERA_SHARPNESS_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ar0130_ctrl_ops, ++ .id = V4L2_CID_POWER_LINE_FREQUENCY, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "anti flicker", ++ .min = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, ++ .max = V4L2_CID_POWER_LINE_FREQUENCY_AUTO, ++ .step = 0, ++ .def = V4L2_CID_POWER_LINE_FREQUENCY_50HZ, ++ .flags = 0, ++ .menu_skip_mask = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, ++ .qmenu = anti_flicker_menu, ++ } ++}; ++ ++ ++/* ++ * supported format list ++ */ ++static const struct aksensor_color_format ar0130_formats[] = { ++ { ++ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB565_2X8_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB565_2X8_BE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++}; ++ ++static const struct aksensor_win_size ar0130_win[] = { ++ {.name = "VGA", .width = 640, .height = 480}, ++ {.name = "720P", .width = 1280, .height = 720}, ++ {.name = "960P", .width = 1280, .height = 960}, ++}; ++ ++static struct sensor_info ar0130_sensor_info = { ++ .sensor_name = "ar0130", ++ .sensor_id = CAMERA_AR0130_ID, ++ .ctrls = ar0130_ctrls, ++ .nr_ctrls = ARRAY_SIZE(ar0130_ctrls), ++ .formats = ar0130_formats, ++ .num_formats = ARRAY_SIZE(ar0130_formats), ++ .resolution = ar0130_win, ++ .num_resolution = ARRAY_SIZE(ar0130_win), ++ .handler = &ar0130_function_handler, ++}; ++ ++static int ar0130_module_init(void) ++{ ++ return register_sensor(&ar0130_sensor_info); ++} ++module_init(ar0130_module_init) ++#endif ++ ++#endif ++ ++ +diff --git a/drivers/media/video/plat-anyka/camera_ar0130.h b/drivers/media/video/plat-anyka/camera_ar0130.h +new file mode 100755 +index 00000000..219a18fe +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_ar0130.h +@@ -0,0 +1,467 @@ ++/** ++ * @file camera_ar0130.h ++ * @brief camera driver file ++ * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author caolianming ++ * @date 2013-07-31 ++ * @version 1.0 ++ * @ref ++ */ ++#ifndef __CAMERA_AR0130_H__ ++#define __CAMERA_AR0130_H__ ++ ++ ++#if defined (USE_CAMERA_AR0130) || defined (CONFIG_SENSOR_AR0130) ++ ++#undef DELAY_FLAG ++#undef END_FLAG ++#define DELAY_FLAG 0xfd // first parameter is 0xfe, then 2nd parameter is delay time count ++#define END_FLAG 0xfe // first parameter is 0xff, then parameter table is over ++ ++static const T_U16 INIT_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 UXGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SXGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 VGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CIF_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 QVGA_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 QCIF_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 QQVGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 PREV_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 RECORD_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++//[720p--30fps] ++static const T_U16 RECORD_720P_TAB[] = ++{ ++ 0x301A, 0x0001, // RESET_REGISTER ++ 0x301A, 0x10D8, // RESET_REGISTER ++ ++ DELAY_FLAG, 200, //DELAY= 200 ++ ++ 0x3088, 0x8000, // SEQ_CTRL_PORT ++ 0x3086, 0x0225, // SEQ_DATA_PORT ++ 0x3086, 0x5050, // SEQ_DATA_PORT ++ 0x3086, 0x2D26, // SEQ_DATA_PORT ++ 0x3086, 0x0828, // SEQ_DATA_PORT ++ 0x3086, 0x0D17, // SEQ_DATA_PORT ++ 0x3086, 0x0926, // SEQ_DATA_PORT ++ 0x3086, 0x0028, // SEQ_DATA_PORT ++ 0x3086, 0x0526, // SEQ_DATA_PORT ++ 0x3086, 0xA728, // SEQ_DATA_PORT ++ 0x3086, 0x0725, // SEQ_DATA_PORT ++ 0x3086, 0x8080, // SEQ_DATA_PORT ++ 0x3086, 0x2917, // SEQ_DATA_PORT ++ 0x3086, 0x0525, // SEQ_DATA_PORT ++ 0x3086, 0x0040, // SEQ_DATA_PORT ++ 0x3086, 0x2702, // SEQ_DATA_PORT ++ 0x3086, 0x1616, // SEQ_DATA_PORT ++ 0x3086, 0x2706, // SEQ_DATA_PORT ++ 0x3086, 0x1736, // SEQ_DATA_PORT ++ 0x3086, 0x26A6, // SEQ_DATA_PORT ++ 0x3086, 0x1703, // SEQ_DATA_PORT ++ 0x3086, 0x26A4, // SEQ_DATA_PORT ++ 0x3086, 0x171F, // SEQ_DATA_PORT ++ 0x3086, 0x2805, // SEQ_DATA_PORT ++ 0x3086, 0x2620, // SEQ_DATA_PORT ++ 0x3086, 0x2804, // SEQ_DATA_PORT ++ 0x3086, 0x2520, // SEQ_DATA_PORT ++ 0x3086, 0x2027, // SEQ_DATA_PORT ++ 0x3086, 0x0017, // SEQ_DATA_PORT ++ 0x3086, 0x1E25, // SEQ_DATA_PORT ++ 0x3086, 0x0020, // SEQ_DATA_PORT ++ 0x3086, 0x2117, // SEQ_DATA_PORT ++ 0x3086, 0x1028, // SEQ_DATA_PORT ++ 0x3086, 0x051B, // SEQ_DATA_PORT ++ 0x3086, 0x1703, // SEQ_DATA_PORT ++ 0x3086, 0x2706, // SEQ_DATA_PORT ++ 0x3086, 0x1703, // SEQ_DATA_PORT ++ 0x3086, 0x1741, // SEQ_DATA_PORT ++ 0x3086, 0x2660, // SEQ_DATA_PORT ++ 0x3086, 0x17AE, // SEQ_DATA_PORT ++ 0x3086, 0x2500, // SEQ_DATA_PORT ++ 0x3086, 0x9027, // SEQ_DATA_PORT ++ 0x3086, 0x0026, // SEQ_DATA_PORT ++ 0x3086, 0x1828, // SEQ_DATA_PORT ++ 0x3086, 0x002E, // SEQ_DATA_PORT ++ 0x3086, 0x2A28, // SEQ_DATA_PORT ++ 0x3086, 0x081E, // SEQ_DATA_PORT ++ 0x3086, 0x0831, // SEQ_DATA_PORT ++ 0x3086, 0x1440, // SEQ_DATA_PORT ++ 0x3086, 0x4014, // SEQ_DATA_PORT ++ 0x3086, 0x2020, // SEQ_DATA_PORT ++ 0x3086, 0x1410, // SEQ_DATA_PORT ++ 0x3086, 0x1034, // SEQ_DATA_PORT ++ 0x3086, 0x1400, // SEQ_DATA_PORT ++ 0x3086, 0x1014, // SEQ_DATA_PORT ++ 0x3086, 0x0020, // SEQ_DATA_PORT ++ 0x3086, 0x1400, // SEQ_DATA_PORT ++ 0x3086, 0x4013, // SEQ_DATA_PORT ++ 0x3086, 0x1802, // SEQ_DATA_PORT ++ 0x3086, 0x1470, // SEQ_DATA_PORT ++ 0x3086, 0x7004, // SEQ_DATA_PORT ++ 0x3086, 0x1470, // SEQ_DATA_PORT ++ 0x3086, 0x7003, // SEQ_DATA_PORT ++ 0x3086, 0x1470, // SEQ_DATA_PORT ++ 0x3086, 0x7017, // SEQ_DATA_PORT ++ 0x3086, 0x2002, // SEQ_DATA_PORT ++ 0x3086, 0x1400, // SEQ_DATA_PORT ++ 0x3086, 0x2002, // SEQ_DATA_PORT ++ 0x3086, 0x1400, // SEQ_DATA_PORT ++ 0x3086, 0x5004, // SEQ_DATA_PORT ++ 0x3086, 0x1400, // SEQ_DATA_PORT ++ 0x3086, 0x2004, // SEQ_DATA_PORT ++ 0x3086, 0x1400, // SEQ_DATA_PORT ++ 0x3086, 0x5022, // SEQ_DATA_PORT ++ 0x3086, 0x0314, // SEQ_DATA_PORT ++ 0x3086, 0x0020, // SEQ_DATA_PORT ++ 0x3086, 0x0314, // SEQ_DATA_PORT ++ 0x3086, 0x0050, // SEQ_DATA_PORT ++ 0x3086, 0x2C2C, // SEQ_DATA_PORT ++ 0x3086, 0x2C2C, // SEQ_DATA_PORT ++ 0x309E, 0x0000, // ERS_PROG_START_ADDR ++ ++ DELAY_FLAG, 200, //DELAY= 200 ++ ++ 0x30E4, 0x6372, // ADC_BITS_6_7 ++ 0x30E2, 0x7253, // ADC_BITS_4_5 ++ 0x30E0, 0x5470, // ADC_BITS_2_3 ++ 0x30E6, 0xC4CC, // ADC_CONFIG1 ++ 0x30E8, 0x8050, // ADC_CONFIG2 ++ 0x3082, 0x0029, // OPERATION_MODE_CTRL ++ 0x30B0, 0x1300, // DIGITAL_TEST ++ 0x30D4, 0xE007, // COLUMN_CORRECTION ++ 0x301A, 0x10DC, // RESET_REGISTER ++ 0x301A, 0x10D8, // RESET_REGISTER ++ 0x3044, 0x0400, // DARK_CONTROL ++ 0x3EDA, 0x0F03, // DAC_LD_14_15 ++ 0x3ED8, 0x01EF, // DAC_LD_12_13 ++ 0x3012, 0x02A0, // COARSE_INTEGRATION_TIME ++ 0x3032, 0x0000, // DIGITAL_BINNING ++ 0x3002, 0x003e, // Y_ADDR_START ++ 0x3004, 0x0004, // X_ADDR_START ++ 0x3006, 0x030d, // Y_ADDR_END ++ 0x3008, 0x0503, // X_ADDR_END ++ 0x300A, 0x02EE, // FRAME_LENGTH_LINES ++ 0x300C, 0x0CE4, // LINE_LENGTH_PCK, 30fps ++ 0x301A, 0x10D8, // RESET_REGISTER ++ 0x31D0, 0x0001, // HDR_COMP ++ ++ //Load = PLL Enabled 27Mhz to 74.25Mhz ++ 0x302C, 0x0002, // VT_SYS_CLK_DIV ++ 0x302A, 0x0004, // VT_PIX_CLK_DIV ++ 0x302E, 0x0002, // PRE_PLL_CLK_DIV ++ 0x3030, 0x002C, // PLL_MULTIPLIER ++ 0x30B0, 0x0000, // DIGITAL_TEST ++ DELAY_FLAG, 100, //DELAY= 100 ++ ++ //LOAD= Disable Embedded Data and Stats ++ 0x3064, 0x1802, // SMIA_TEST, EMBEDDED_STATS_EN, 0x0000 ++ 0x3064, 0x1802, // SMIA_TEST, EMBEDDED_DATA, 0x0000 ++ ++ 0x30BA, 0x0008, //20120502 ++ ++ 0x301A, 0x10DC, // RESET_REGISTER ++ ++ DELAY_FLAG, 200, //DELAY= 200 ++ ++ END_FLAG, END_FLAG ++}; ++ ++ ++/**************** Camera Exposure Table ****************/ ++static const T_U16 EXPOSURE_WHOLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EXPOSURE_CENTER_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EXPOSURE_MIDDLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Brightness Table ****************/ ++static const T_U16 BRIGHTNESS_0_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Contrast Table ****************/ ++static const T_U16 CONTRAST_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_7_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Saturation Table ****************/ ++static const T_U16 SATURATION_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Sharpness Table ****************/ ++static const T_U16 SHARPNESS_0_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera AWB Table ****************/ ++static const T_U16 AWB_AUTO_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 AWB_SUNNY_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 AWB_CLOUDY_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 AWB_OFFICE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 AWB_HOME_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Effect Table ****************/ ++static const T_U16 EFFECT_NORMAL_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_SEPIA_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_ANTIQUE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_BLUISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_GREENISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_REDDISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_NEGATIVE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_BW_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera night/day mode ****************/ ++static const T_U16 DAY_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 NIGHT_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera anti-flicker mode ****************/ ++static const T_U16 ANTI_FLICKER_DISABLE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 ANTI_FLICKER_50HZ_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 ANTI_FLICKER_60HZ_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 ANTI_FLICKER_AUTO_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++#endif ++#endif +diff --git a/drivers/media/video/plat-anyka/camera_gc0308.c b/drivers/media/video/plat-anyka/camera_gc0308.c +new file mode 100755 +index 00000000..13bb841e +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_gc0308.c +@@ -0,0 +1,1141 @@ ++/** ++ * @file camera_gc0308.c ++ * @brief camera driver file ++ * Copyright (C) 2010 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author xia_wenting ++ * @date 2011-09-21 ++ * @version 1.0 ++ * @ref ++ */ ++#ifdef CONFIG_LINUX_AKSENSOR ++#include ++#include ++#include ++#include "camera_gc0308.h" ++#else ++#include "akdefine.h" ++#include "cam_com_sensor.h" ++#include "camera_gc0308.h" ++#include "Gpio_config.h" ++#endif ++ ++#if defined (USE_CAMERA_GC0308) || defined (CONFIG_SENSOR_GC0308) ++ ++#define CAM_EN_LEVEL 0 ++#define CAM_RESET_LEVEL 0 ++ ++#define CAMERA_SCCB_ADDR 0x42 ++#define CAMERA_GC0308_ID 0x9b ++ ++#define GC0308_CAMERA_MCLK 24 //28 ++ ++static T_CAMERA_TYPE camera_gc0308_type = CAMERA_P3M; ++static T_NIGHT_MODE night_mode = CAMERA_DAY_MODE; ++static T_CAMERA_MODE s_gc0308_CurMode = CAMERA_MODE_VGA; ++ ++ ++/* ++static T_VOID camera_setbit(T_U8 reg, T_U8 bit, T_U8 value) ++{ ++ T_U8 tmp; ++ ++ tmp = sccb_read_data(CAMERA_SCCB_ADDR, reg); ++ if (value == 1) ++ tmp |= 0x1 << bit; ++ else ++ tmp &= ~(0x1 << bit); ++ sccb_write_data(CAMERA_SCCB_ADDR, reg, &tmp, 1); ++} ++*/ ++ ++static T_BOOL camera_set_param(const T_U8 tabParameter[]) ++{ ++ int i = 0; ++ //T_U8 temp_value; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ sccb_write_data(CAMERA_SCCB_ADDR, tabParameter[i], (T_U8 *)(&tabParameter[i + 1]), 1); ++ /*if (!((tabParameter[i] == 0x0e) && (tabParameter[i + 1] & 0x02)) ++ && !((tabParameter[i] == 0x10) && (tabParameter[i + 1] & 0x26)) ++ && !((tabParameter[i] == 0x14) && (tabParameter[i + 1] & 0x10)) ++ && !((tabParameter[i] == 0x17) && (tabParameter[i + 1] & 0x01)) ++ && !((tabParameter[i] == 0x66) && (tabParameter[i + 1] & 0xe8)) ++ && !((tabParameter[i] == 0x68) && (tabParameter[i + 1] & 0xa2))) ++ { ++ temp_value = sccb_read_data(CAMERA_SCCB_ADDR, tabParameter[i]); ++ if (temp_value != tabParameter[i + 1]) ++ { ++ akprintf(C1, M_DRVSYS, "set parameter error!\n"); ++ akprintf(C1, M_DRVSYS, "reg 0x%02x write data is 0x%02x, read data is 0x%02x!\n", tabParameter[i], tabParameter[i + 1], temp_value); ++ ++ return AK_FALSE; ++ } ++ }*/ ++ } ++ ++ i += 2; ++ } ++ ++ return AK_TRUE; ++} ++ ++static T_VOID camera_setup(const T_U8 tabParameter[]) ++{ ++ int i = 0; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ sccb_write_data(CAMERA_SCCB_ADDR, tabParameter[i], (T_U8 *)&tabParameter[i + 1], 1); ++ } ++ i += 2; ++ } ++} ++ ++static T_VOID cam_gc0308_open(T_VOID) ++{ ++ gpio_set_pin_dir(GPIO_CAMERA_AVDD, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_CHIP_ENABLE); ++ gpio_set_pin_dir(GPIO_CAMERA_CHIP_ENABLE, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, CAM_EN_LEVEL); ++ mini_delay(10); ++ ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_RESET); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, CAM_RESET_LEVEL); ++ mini_delay(10); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, !CAM_RESET_LEVEL); ++ ++ mini_delay(20); ++ ++} ++ ++static T_BOOL cam_gc0308_close(T_VOID) ++{ ++ //sccb software standby mode ++ T_U8 Reg0x1a = 0x2b; ++ T_U8 Reg0x25 = 0x00; ++ sccb_write_data(CAMERA_SCCB_ADDR, 0x1a, &Reg0x1a, 1); ++ sccb_write_data(CAMERA_SCCB_ADDR, 0x25, &Reg0x25, 1); ++ ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, !CAM_EN_LEVEL); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, !gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_INPUT); ++ ++ gpio_set_pin_dir(GPIO_I2C_SCL, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SCL, GPIO_LEVEL_LOW); ++ gpio_set_pin_dir(GPIO_I2C_SDA, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SDA, GPIO_LEVEL_LOW); ++ ++ return AK_TRUE; ++} ++ ++static T_U32 cam_gc0308_read_id(T_VOID) ++{ ++ T_U8 value = 0x00; ++ T_U32 id = 0; ++ ++ sccb_init(GPIO_I2C_SCL, GPIO_I2C_SDA); ++ ++ value = sccb_read_data(CAMERA_SCCB_ADDR, 0x00); ++ id |= value; ++ ++ //akprintf(C1, M_DRVSYS, "i2c addr 0x%x, cam_gc0308_read_id = 0x%x\r\n", CAMERA_SCCB_ADDR, id); ++ return id; ++} ++ ++/** ++ * @brief initialize the parameters of camera, should be done after reset and open camera to initialize ++ * @author xia_wenting ++ * @date 2011-01-11 ++ * @return T_BOOL ++ * @retval AK_TRUE if success, else AK_FALSE ++ */ ++static T_BOOL cam_gc0308_init(void) ++{ ++ if (!camera_set_param(INIT_TAB)) ++ { ++ return AK_FALSE; ++ } ++ else ++ { ++ night_mode = CAMERA_DAY_MODE; ++ return AK_TRUE; ++ } ++} ++ ++/** ++ * @brief Set camera mode to specify image quality, SXGA/VGA/CIF/ etc ++ * @author xia_wenting ++ * @date 2011-01-11 ++ * @param[in] mode mode value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_gc0308_set_mode(T_CAMERA_MODE mode) ++{ ++ s_gc0308_CurMode = mode; ++ switch(mode) ++ { ++ case CAMERA_MODE_VGA: ++ camera_setup(VGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_CIF: ++ camera_setup(CIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QVGA: ++ camera_setup(QVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_QCIF: ++ camera_setup(QCIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QQVGA: ++ camera_setup(QQVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_PREV: //preview mode ++ camera_setup(PREV_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_REC: //record mode ++ camera_setup(RECORD_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ default: ++ s_gc0308_CurMode = CAMERA_MODE_VGA; ++ akprintf(C1, M_DRVSYS, "set camera mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera exposure mode ++ * @author xia_wenting ++ * @date 2011-01-11 ++ * @param[in] exposure exposure mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_gc0308_set_exposure(T_CAMERA_EXPOSURE exposure) ++{ ++ switch(exposure) ++ { ++ case EXPOSURE_WHOLE: ++ camera_setup(EXPOSURE_WHOLE_TAB); ++ break; ++ case EXPOSURE_CENTER: ++ camera_setup(EXPOSURE_CENTER_TAB); ++ break; ++ case EXPOSURE_MIDDLE: ++ camera_setup(EXPOSURE_MIDDLE_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set exposure parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera brightness level ++ * @author xia_wenting ++ * @date 2011-01-11 ++ * @param[in] brightness brightness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_gc0308_set_brightness(T_CAMERA_BRIGHTNESS brightness) ++{ ++ switch(brightness) ++ { ++ case CAMERA_BRIGHTNESS_0: ++ camera_setup(BRIGHTNESS_0_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_1: ++ camera_setup(BRIGHTNESS_1_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_2: ++ camera_setup(BRIGHTNESS_2_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_3: ++ camera_setup(BRIGHTNESS_3_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_4: ++ camera_setup(BRIGHTNESS_4_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_5: ++ camera_setup(BRIGHTNESS_5_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_6: ++ camera_setup(BRIGHTNESS_6_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set brightness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera contrast level ++ * @author xia_wenting ++ * @date 2011-01-11 ++ * @param[in] contrast contrast value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_gc0308_set_contrast(T_CAMERA_CONTRAST contrast) ++{ ++ switch(contrast) ++ { ++ case CAMERA_CONTRAST_1: ++ camera_setup(CONTRAST_1_TAB); ++ break; ++ case CAMERA_CONTRAST_2: ++ camera_setup(CONTRAST_2_TAB); ++ break; ++ case CAMERA_CONTRAST_3: ++ camera_setup(CONTRAST_3_TAB); ++ break; ++ case CAMERA_CONTRAST_4: ++ camera_setup(CONTRAST_4_TAB); ++ break; ++ case CAMERA_CONTRAST_5: ++ camera_setup(CONTRAST_5_TAB); ++ break; ++ case CAMERA_CONTRAST_6: ++ camera_setup(CONTRAST_6_TAB); ++ break; ++ case CAMERA_CONTRAST_7: ++ camera_setup(CONTRAST_7_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set contrast parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera saturation level ++ * @author xia_wenting ++ * @date 2011-01-11 ++ * @param[in] saturation saturation value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_gc0308_set_saturation(T_CAMERA_SATURATION saturation) ++{ ++ switch(saturation) ++ { ++ case CAMERA_SATURATION_1: ++ camera_setup(SATURATION_1_TAB); ++ break; ++ case CAMERA_SATURATION_2: ++ camera_setup(SATURATION_2_TAB); ++ break; ++ case CAMERA_SATURATION_3: ++ camera_setup(SATURATION_3_TAB); ++ break; ++ case CAMERA_SATURATION_4: ++ camera_setup(SATURATION_4_TAB); ++ break; ++ case CAMERA_SATURATION_5: ++ camera_setup(SATURATION_5_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set saturation parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera sharpness level ++ * @author xia_wenting ++ * @date 2011-01-11 ++ * @param[in] sharpness sharpness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_gc0308_set_sharpness(T_CAMERA_SHARPNESS sharpness) ++{ ++ switch(sharpness) ++ { ++ case CAMERA_SHARPNESS_0: ++ camera_setup(SHARPNESS_0_TAB); ++ break; ++ case CAMERA_SHARPNESS_1: ++ camera_setup(SHARPNESS_1_TAB); ++ break; ++ case CAMERA_SHARPNESS_2: ++ camera_setup(SHARPNESS_2_TAB); ++ break; ++ case CAMERA_SHARPNESS_3: ++ camera_setup(SHARPNESS_3_TAB); ++ break; ++ case CAMERA_SHARPNESS_4: ++ camera_setup(SHARPNESS_4_TAB); ++ break; ++ case CAMERA_SHARPNESS_5: ++ camera_setup(SHARPNESS_5_TAB); ++ break; ++ case CAMERA_SHARPNESS_6: ++ camera_setup(SHARPNESS_6_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set sharpness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera AWB mode ++ * @author xia_wenting ++ * @date 2011-01-11 ++ * @param[in] awb AWB mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_gc0308_set_AWB(T_CAMERA_AWB awb) ++{ ++ switch(awb) ++ { ++ case AWB_AUTO: ++ camera_setup(AWB_AUTO_TAB); ++ break; ++ case AWB_SUNNY: ++ camera_setup(AWB_SUNNY_TAB); ++ break; ++ case AWB_CLOUDY: ++ camera_setup(AWB_CLOUDY_TAB); ++ break; ++ case AWB_OFFICE: ++ camera_setup(AWB_OFFICE_TAB); ++ break; ++ case AWB_HOME: ++ camera_setup(AWB_HOME_TAB); ++ break; ++ case AWB_NIGHT: ++ camera_setup(AWB_NIGHT_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set AWB mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera mirror mode ++ * @author xia_wenting ++ * @date 2011-03-21 ++ * @param[in] mirror mirror mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_gc0308_set_mirror(T_CAMERA_MIRROR mirror) ++{ ++ switch(mirror) ++ { ++ case CAMERA_MIRROR_V: ++ camera_setup(MIRROR_V_TAB); ++ break; ++ case CAMERA_MIRROR_H: ++ camera_setup(MIRROR_H_TAB); ++ break; ++ case CAMERA_MIRROR_NORMAL: ++ camera_setup(MIRROR_NORMAL_TAB); ++ break; ++ case CAMERA_MIRROR_FLIP: ++ camera_setup(MIRROR_FLIP_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "camera gc0308 set mirror parameter error!\n"); ++ break; ++ } ++} ++ ++ ++/** ++ * @brief Set camera effect mode ++ * @author xia_wenting ++ * @date 2011-03-21 ++ * @param[in] effect effect mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_gc0308_set_effect(T_CAMERA_EFFECT effect) ++{ ++ switch(effect) ++ { ++ case CAMERA_EFFECT_NORMAL: ++ camera_setup(EFFECT_NORMAL_TAB); ++ break; ++ case CAMERA_EFFECT_SEPIA: ++ camera_setup(EFFECT_SEPIA_TAB); ++ break; ++ case CAMERA_EFFECT_ANTIQUE: ++ camera_setup(EFFECT_ANTIQUE_TAB); ++ break; ++ case CAMERA_EFFECT_BLUE: ++ camera_setup(EFFECT_BLUISH_TAB); ++ break; ++ case CAMERA_EFFECT_GREEN: ++ camera_setup(EFFECT_GREENISH_TAB); ++ break; ++ case CAMERA_EFFECT_NEGATIVE: ++ camera_setup(EFFECT_NEGATIVE_TAB); ++ break; ++ case CAMERA_EFFECT_BW: ++ camera_setup(EFFECT_BW_TAB); ++ break; ++ case CAMERA_EFFECT_BWN: ++ camera_setup(EFFECT_BWN_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set camer effect parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief set camera window ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] srcWidth window width ++ * @param[in] srcHeight window height ++ * @return T_S32 ++ * @retval 0 if error mode ++ * @retval 1 if success ++ * @retval -1 if failed ++ */ ++static T_S32 cam_gc0308_set_digital_zoom(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_U16 hrefstart = 0, vrefstart = 0; ++ T_U8 high_bit = 0, low_bit = 0; ++ T_CAMERA_MODE Cammode = s_gc0308_CurMode; ++ T_U8 Camera_window_table[] = ++ { ++ 0x46, 0, ++ 0x47, 0, ++ 0x48, 0, ++ 0x49, 0, ++ 0x4a, 0, ++ 0x4b, 0, ++ 0x4c, 0, ++ END_FLAG, END_FLAG ++ }; ++ ++ akprintf(C1, M_DRVSYS, "set window size %d, %d, %d\r\n", Cammode, srcWidth, srcHeight); ++ ++ if (((srcWidth == 640) && (srcHeight == 480)) ++ || ((srcWidth == 352) && (srcHeight == 288)) ++ || ((srcWidth == 320) && (srcHeight == 240)) ++ || ((srcWidth == 176) && (srcHeight == 144))) ++ { ++ return 1; ++ } ++ ++ switch (s_gc0308_CurMode) ++ { ++ case CAMERA_MODE_VGA: ++ hrefstart = (640 - srcWidth) / 2; ++ vrefstart = (480 - srcHeight) / 2; ++ break; ++ ++ case CAMERA_MODE_CIF: ++ hrefstart = (352 - srcWidth) / 2; ++ vrefstart = (288 - srcHeight) / 2; ++ break; ++ ++ case CAMERA_MODE_QVGA: ++ hrefstart = (320 - srcWidth) / 2; ++ vrefstart = (240 - srcHeight) / 2; ++ break; ++ ++ case CAMERA_MODE_QCIF: ++ hrefstart = (176 - srcWidth) / 2; ++ vrefstart = (144 - srcHeight) / 2; ++ break; ++ ++ case CAMERA_MODE_QQVGA: ++ hrefstart = (160 - srcWidth) / 2; ++ vrefstart = (120 - srcHeight) / 2; ++ break; ++ ++ case CAMERA_MODE_PREV: ++ hrefstart = (640 - srcWidth) / 2; ++ vrefstart = (480 - srcHeight) / 2; ++ break; ++ ++ case CAMERA_MODE_REC: ++ hrefstart = (640 - srcWidth) / 2; ++ vrefstart = (480 - srcHeight) / 2; ++ break; ++ ++ default: ++ akprintf(C1, M_DRVSYS, "unsupported WINDOWING in mode %d!!\n", s_gc0308_CurMode); ++ return 0; ++ } ++ ++ high_bit = hrefstart >> 8; //horizontal frame start high 3-bit ++ low_bit = hrefstart & 0xff; //horizontal frame start low 8-bit ++ Camera_window_table[1] = 0x80 | (high_bit & 0x07); ++ Camera_window_table[5] = low_bit; ++ Camera_window_table[11] = srcWidth >> 8; ++ Camera_window_table[13] = srcWidth & 0xff; ++ ++ high_bit = vrefstart >> 8; //vertical frame start high 2-bit ++ low_bit = vrefstart & 0xff; //vertical frame start low 8-bit ++ Camera_window_table[1] = 0x80 | (high_bit & 0x30); ++ Camera_window_table[3] = low_bit; ++ Camera_window_table[7] = srcHeight >> 8; ++ Camera_window_table[9] = srcHeight & 0xff; ++ ++ if (camera_set_param(Camera_window_table) == AK_TRUE) ++ { ++ return 1; ++ } ++ else ++ { ++ return -1; ++ } ++} ++ ++static T_VOID cam_gc0308_set_night_mode(T_NIGHT_MODE mode) ++{ ++ switch(mode) ++ { ++ case CAMERA_DAY_MODE: ++ camera_setup(DAY_MODE_TAB); ++ night_mode = CAMERA_DAY_MODE; ++ break; ++ case CAMERA_NIGHT_MODE: ++ camera_setup(NIGHT_MODE_TAB); ++ night_mode = CAMERA_NIGHT_MODE; ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set night mode parameter error!\n"); ++ break; ++ } ++} ++ ++static T_VOID cam_gc0308_set_anti_flicker(T_U32 value) ++{ ++ switch(value) { ++ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: ++ camera_setup(ANTI_FLICKER_DISABLE_TAB); ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: ++ camera_setup(ANTI_FLICKER_50HZ_TAB); ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: ++ camera_setup(ANTI_FLICKER_60HZ_TAB); ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: ++ camera_setup(ANTI_FLICKER_AUTO_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set Anti-flicker parameter error!\n"); ++ break; ++ } ++} ++ ++static T_BOOL cam_gc0308_set_to_cap(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ ++ if ((srcWidth <= 160) && (srcHeight <= 120)) ++ { ++ Cammode = CAMERA_MODE_QQVGA; ++ } ++ else if ((srcWidth <= 176) && (srcHeight <= 144)) ++ { ++ Cammode = CAMERA_MODE_QCIF; ++ } ++ else if ((srcWidth <= 320) && (srcHeight <= 240)) ++ { ++ Cammode = CAMERA_MODE_QVGA; ++ } ++ else if ((srcWidth <= 352) && (srcHeight <= 288)) ++ { ++ Cammode = CAMERA_MODE_CIF; ++ } ++ else if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_VGA; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "30W camera dose not support such mode"); ++ return AK_FALSE; ++ } ++ ++ cam_gc0308_set_mode(Cammode); ++ cam_gc0308_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(200); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_gc0308_set_to_prev(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ cam_gc0308_set_mode(CAMERA_MODE_PREV); ++ cam_gc0308_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(200); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_gc0308_set_to_record(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ ++ if ((srcWidth <= 160) && (srcHeight <= 120)) ++ { ++ Cammode = CAMERA_MODE_QQVGA; ++ } ++ else if ((srcWidth <= 176) && (srcHeight <= 144)) ++ { ++ Cammode = CAMERA_MODE_QCIF; ++ } ++ else if ((srcWidth <= 320) && (srcHeight <= 240)) ++ { ++ Cammode = CAMERA_MODE_QVGA; ++ } ++ else if ((srcWidth <= 352) && (srcHeight <= 288)) ++ { ++ Cammode = CAMERA_MODE_CIF; ++ } ++ else if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_REC; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "30W camera dose not support such mode"); ++ return AK_FALSE; ++ } ++ ++ cam_gc0308_set_mode(Cammode); ++ cam_gc0308_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(200); ++ return AK_TRUE; ++} ++ ++static T_CAMERA_TYPE cam_gc0308_get_type(T_VOID) ++{ ++ return camera_gc0308_type; ++} ++ ++static T_VOID cam_gc0308_set_sensor_param(T_U32 cmd, T_U32 data) ++{ ++ T_U8 value; ++ ++ value = (T_U8)data; ++ sccb_write_data(CAMERA_SCCB_ADDR, (T_U8)cmd, &value, 1); ++} ++ ++static T_CAMERA_FUNCTION_HANDLER gc0308_function_handler = ++{ ++ GC0308_CAMERA_MCLK, ++ cam_gc0308_open, ++ cam_gc0308_close, ++ cam_gc0308_read_id, ++ cam_gc0308_init, ++ cam_gc0308_set_mode, ++ cam_gc0308_set_exposure, ++ cam_gc0308_set_brightness, ++ cam_gc0308_set_contrast, ++ cam_gc0308_set_saturation, ++ cam_gc0308_set_sharpness, ++ cam_gc0308_set_AWB, ++ cam_gc0308_set_mirror, ++ cam_gc0308_set_effect, ++ cam_gc0308_set_digital_zoom, ++ cam_gc0308_set_night_mode, ++ AK_NULL, ++ cam_gc0308_set_anti_flicker, ++ cam_gc0308_set_to_cap, ++ cam_gc0308_set_to_prev, ++ cam_gc0308_set_to_record, ++ cam_gc0308_get_type, ++ cam_gc0308_set_sensor_param ++}; ++ ++#ifndef CONFIG_LINUX_AKSENSOR ++static int camera_gc0308_reg(void) ++{ ++ camera_reg_dev(CAMERA_GC0308_ID, &gc0308_function_handler); ++ return 0; ++} ++ ++#ifdef __CC_ARM ++#pragma arm section rwdata = "__initcall_", zidata = "__initcall_" ++#endif ++module_init(camera_gc0308_reg) ++#ifdef __CC_ARM ++#pragma arm section ++#endif ++ ++ ++#else ++ ++static const char * exposure_menu[] = { ++ [EXPOSURE_WHOLE] = "whole", ++ [EXPOSURE_CENTER] = "center", ++ [EXPOSURE_MIDDLE] = "middle", ++}; ++static const char * awb_menu[] = { ++ [AWB_AUTO] = "auto", ++ [AWB_SUNNY] = "sunny", ++ [AWB_CLOUDY] = "cloudy", ++ [AWB_OFFICE] = "office", ++ [AWB_HOME] = "home", ++ [AWB_NIGHT] = "night", ++}; ++static const char * effect_menu[] = { ++ [CAMERA_EFFECT_NORMAL] = "normal", ++ [CAMERA_EFFECT_SEPIA] = "sepia", ++ [CAMERA_EFFECT_ANTIQUE] = "antique", ++ [CAMERA_EFFECT_BLUE] = "blue", ++ [CAMERA_EFFECT_GREEN] = "green", ++ [CAMERA_EFFECT_RED] = "red", ++ [CAMERA_EFFECT_NEGATIVE] = "negative", ++ [CAMERA_EFFECT_BW] = "bw", ++ [CAMERA_EFFECT_BWN] = "bwn", ++ [CAMERA_EFFECT_AQUA] = "aqua", ++ [CAMERA_EFFECT_COOL] = "cool", ++ [CAMERA_EFFECT_WARM] = "warm", ++}; ++ ++static const char * resolution_menu[] = { ++ [0] = "640x480", ++ [1] = "352x288", ++ [2] = "320x240", ++ [3] = "176x144", ++ [4] = "160x120", ++}; ++ ++static const char * hflip_menu[] = { ++ [0] = "normal", ++ [1] = "horizontal flip", ++}; ++ ++static const char * vflip_menu[] = { ++ [0] = "normal", ++ [1] = "vertical flip", ++}; ++ ++static const char * night_menu[] = { ++ [CAMERA_DAY_MODE] = "daylight", ++ [CAMERA_NIGHT_MODE] = "night", ++}; ++ ++static const char * anti_flicker_menu[] = { ++ [V4L2_CID_POWER_LINE_FREQUENCY_DISABLED] = "Disable", ++ [V4L2_CID_POWER_LINE_FREQUENCY_50HZ] = "50Hz", ++ [V4L2_CID_POWER_LINE_FREQUENCY_60HZ] = "60Hz", ++ [V4L2_CID_POWER_LINE_FREQUENCY_AUTO] = "Auto", ++}; ++ ++static int gc0308_s_ctl(struct v4l2_ctrl *ctrl) ++{ ++ int ret = -EINVAL; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_EXPOSURE: ++ if (gc0308_function_handler.cam_set_exposure_func) { ++ gc0308_function_handler.cam_set_exposure_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ if (gc0308_function_handler.cam_set_AWB_func) { ++ gc0308_function_handler.cam_set_AWB_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_COLORFX: ++ if (gc0308_function_handler.cam_set_effect_func) { ++ gc0308_function_handler.cam_set_effect_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_BRIGHTNESS: ++ if (gc0308_function_handler.cam_set_brightness_func) { ++ gc0308_function_handler.cam_set_brightness_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_CONTRAST: ++ if (gc0308_function_handler.cam_set_contrast_func) { ++ gc0308_function_handler.cam_set_contrast_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SATURATION: ++ if (gc0308_function_handler.cam_set_saturation_func) { ++ gc0308_function_handler.cam_set_saturation_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SHARPNESS: ++ if (gc0308_function_handler.cam_set_sharpness_func) { ++ gc0308_function_handler.cam_set_sharpness_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_HFLIP: ++ if (gc0308_function_handler.cam_set_mirror_func) { ++ gc0308_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_H : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_VFLIP: ++ if (gc0308_function_handler.cam_set_mirror_func) { ++ gc0308_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_V : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_NIGHTMODE: ++ if (gc0308_function_handler.cam_set_night_mode_func) { ++ gc0308_function_handler.cam_set_night_mode_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY: ++ if (gc0308_function_handler.cam_set_anti_flicker_func) { ++ gc0308_function_handler.cam_set_anti_flicker_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++}; ++ ++static struct v4l2_ctrl_ops gc0308_ctrl_ops = { ++ .s_ctrl = gc0308_s_ctl, ++}; ++ ++static const struct v4l2_ctrl_config gc0308_ctrls[] = { ++ { ++ .ops = &gc0308_ctrl_ops, ++ .id = V4L2_CID_EXPOSURE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Exposure", ++ .min = 0, ++ .max = ARRAY_SIZE(exposure_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = exposure_menu, ++ }, ++ { ++ .ops = &gc0308_ctrl_ops, ++ .id = V4L2_CID_AUTO_WHITE_BALANCE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "AWB", ++ .min = 0, ++ .max = ARRAY_SIZE(awb_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = awb_menu, ++ }, ++ { ++ .ops = &gc0308_ctrl_ops, ++ .id = V4L2_CID_COLORFX, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Effect", ++ .min = 0, ++ .max = ARRAY_SIZE(effect_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = effect_menu, ++ }, ++ { ++ .ops = &gc0308_ctrl_ops, ++ .id = V4L2_CID_HFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Horizontal Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(hflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = hflip_menu, ++ }, ++ { ++ .ops = &gc0308_ctrl_ops, ++ .id = V4L2_CID_VFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Vetical Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(vflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = vflip_menu, ++ }, ++ { ++ .ops = &gc0308_ctrl_ops, ++ .id = V4L2_CID_PICTURE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Resolution", ++ .min = 0, ++ .max = ARRAY_SIZE(resolution_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = resolution_menu, ++ }, ++ { ++ .ops = &gc0308_ctrl_ops, ++ .id = V4L2_CID_NIGHTMODE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Night mode", ++ .min = 0, ++ .max = ARRAY_SIZE(night_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = night_menu, ++ }, ++ { ++ .ops = &gc0308_ctrl_ops, ++ .id = V4L2_CID_BRIGHTNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Brightness", ++ .min = 0, ++ .max = CAMERA_BRIGHTNESS_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &gc0308_ctrl_ops, ++ .id = V4L2_CID_CONTRAST, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Contrast", ++ .min = 0, ++ .max = CAMERA_CONTRAST_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &gc0308_ctrl_ops, ++ .id = V4L2_CID_SATURATION, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Saturation", ++ .min = 0, ++ .max = CAMERA_SATURATION_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &gc0308_ctrl_ops, ++ .id = V4L2_CID_SHARPNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Sharpness", ++ .min = 0, ++ .max = CAMERA_SHARPNESS_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &gc0308_ctrl_ops, ++ .id = V4L2_CID_POWER_LINE_FREQUENCY, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "anti flicker", ++ .min = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, ++ .max = V4L2_CID_POWER_LINE_FREQUENCY_AUTO, ++ .step = 0, ++ .def = V4L2_CID_POWER_LINE_FREQUENCY_50HZ, ++ .flags = 0, ++ .menu_skip_mask = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, ++ .qmenu = anti_flicker_menu, ++ } ++}; ++ ++/* ++ * supported format list ++ */ ++static const struct aksensor_color_format gc0308_formats[] = { ++ { ++ .code = V4L2_MBUS_FMT_YUYV8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_YVYU8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_UYVY8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++}; ++ ++static const struct aksensor_win_size gc0308_win[] = { ++ {.name = "VGA", .width = 640, .height = 480}, ++}; ++ ++ ++static struct sensor_info gc0308_sensor_info = { ++ .sensor_name = "gc0308", ++ .sensor_id = CAMERA_GC0308_ID, ++ .ctrls = gc0308_ctrls, ++ .nr_ctrls = ARRAY_SIZE(gc0308_ctrls), ++ .formats = gc0308_formats, ++ .num_formats = ARRAY_SIZE(gc0308_formats), ++ .resolution = gc0308_win, ++ .num_resolution = ARRAY_SIZE(gc0308_win), ++ .handler = &gc0308_function_handler, ++}; ++ ++static int gc0308_module_init(void) ++{ ++ return register_sensor(&gc0308_sensor_info); ++} ++module_init(gc0308_module_init) ++#endif ++ ++#endif ++ +diff --git a/drivers/media/video/plat-anyka/camera_gc0308.h b/drivers/media/video/plat-anyka/camera_gc0308.h +new file mode 100755 +index 00000000..895219c4 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_gc0308.h +@@ -0,0 +1,1410 @@ ++/** ++ * @file camera_gc0308.h ++ * @brief camera driver file ++ * Copyright (C) 2010 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author xia_wenting ++ * @date 2011-09-21 ++ * @version 1.0 ++ * @ref ++ */ ++#ifndef __CAMERA_GC0308_H__ ++#define __CAMERA_GC0308_H__ ++ ++#if defined (USE_CAMERA_GC0308) || defined (CONFIG_SENSOR_GC0308) ++ ++#undef DELAY_FLAG ++#undef END_FLAG ++#define DELAY_FLAG 0xfb // first parameter is 0xfb, then 2nd parameter is delay time count ++#define END_FLAG 0xfc // first parameter is 0xfc, then parameter table is over ++ ++ ++static const T_U8 INIT_TAB[] = ++{ ++#if 1 ++ /* ++ 0xfe, 0x00, ++ 0x01, 0xb5, //28M guding 25fps ++ 0x02, 0x98, ++ 0x0f, 0x00, ++ 0xe2, 0x00, ++ 0xe3, 0xa0, ++ 0xe4, 0x02, ++ 0xe5, 0x80, ++ 0xe6, 0x02, ++ 0xe7, 0x80, ++ 0xe8, 0x02, ++ 0xe9, 0x80, ++ 0xea, 0x02, ++ 0xeb, 0x80,*/ ++ ++ 0xfe, 0x00, ++ 0x01, 0x6a, //24M guding 25fps ++ 0x02, 0x70, ++ 0x0f, 0x00, ++ 0xe2, 0x00, ++ 0xe3, 0x96, ++ 0xe4, 0x02, ++ 0xe5, 0x58, ++ 0xe6, 0x02, ++ 0xe7, 0x58, ++ 0xe8, 0x02, ++ 0xe9, 0x58, ++ 0xea, 0x0e, ++ 0xeb, 0xa6, ++ ++ 0xec, 0x20, //[5:4]exp_level [3:0]minimum exposure high 4 bits ++ 0x05, 0x00, // row_start_high ++ 0x06, 0x00, // row_start_low ++ 0x07, 0x00, // col_start_high ++ 0x08, 0x00, // col_start_low ++ 0x09, 0x01, //[8]cis_win_height 488 ++ 0x0a, 0xe8, //[7:0]cis_win_height ++ 0x0b, 0x02, //[9:8]cis_win_width 648 ++ 0x0c, 0x88, //[7:0]cis_win_width ++ 0x0d, 0x02, //vs_st ++ 0x0e, 0x02, //vs_et ++ 0x10, 0x26, //[7:4]restg_width, [3:0]sh_width ++ 0x11, 0x0d, //fd//[7:4]tx_width, [3:0]space width,*2 ++ 0x12, 0x2a, //sh_delay ++ 0x13, 0x00, //[3:0] row_tail_width ++ 0x14, 0x10, //[7]hsync_always ,[6] NA, [5:4] CFA sequence ++ // [3:2]NA, [1]upside_down, [0] mirror ++ 0x15, 0x0a, //[7:6]output_mode,,[5:4]restg_mode,[3:2]sdark_mode, [1]new exposure,[0]badframe_en ++ 0x16, 0x05, //[7:5]NA, [4]capture_ad_data_edge, [3:0]Number of A/D pipe stages ++ 0x17, 0x01, //[7:6]analog_opa_r,[5]coltest_en, [4]ad_test_enable, ++ //[3]tx_allow,[2]black sun correction,[1:0]black sun control reg ++ 0x18, 0x44, //[7]NA, [6:4]column gain ee, [3]NA, [2:0]column gain eo ++ 0x19, 0x44, //[7]NA, [6:4]column gain oe, [3]NA, [2:0]column gain oo ++ 0x1a, 0x2a, //1e//[7]rsv1,[6]rsv0, [5:4]coln_r, ++ //[3:2]colg_r column gain opa bias current, [1]clk_delay, [0] apwd ++ 0x1b, 0x00, //[7:2]NA, [1:0]BIN4 AND BIN2 ++ 0x1c, 0x49, //c1//[7]hrst_enbale, [6:4]da_rsg, [3]tx high enable, [2]NA, [1:0]da18_r ++ 0x1d, 0x9a, //08//[7]vref_en, [6:4]da_vef, [3]da25_en, [2]NA, [1:0]da25_r,set da25 voltage ++ 0x1e, 0x61, //60//[7]LP_MTD,[6:5]opa_r,ADC's operating current, [4:2]NA, [1:0]sref ++ 0x1f, 0x12, //[7:6]NA, [5:4]sync_drv, [3:2]data_drv, [1:0]pclk_drv ++ ++ 0x20, 0xff, //[7]bks[6]gamma[5]cc[4]ee[3]intp[2]dn[1]dd[0]lsc ++ 0x21, 0xf8, //[7]na[6]na[5]skin_ee[4]cbcr_hue_en[3]y_as_en[2]auto_gray_en[1]y_gamma_en[0]na ++ 0x22, 0x57, //[7]na [6]auto_dndd [5]auto_ee [4]auto_sa [3]na [2]abs [1]awb [0]na ++ 0x24, 0xa0, //a2 ++ 0x25, 0x0f, ++ //output sync_mode ++ 0x26, 0x02, ////02 ++ 0x2f, 0x01, //debug mode3 ++ ++ //grab ++ 0x30, 0xf7, //blk mode [7]dark current mode:1 use exp rated dark ,0 use ndark row calculated ++ //[1]dark_current_en//[0]offset_en ++ 0x31, 0x50, //blk_value limit.64 low align to 11bits;8 for 256 range ++ 0x32, 0x00, //global offset ++ 0x39, 0x04, // exp_ate_darkc ++ 0x3a, 0x20, //{7:6}offset submode {5:0}offset ratio ++ 0x3b, 0x20, //{7:6}darkc submode {5:0}dark current ratio ++ 0x3c, 0x00, //manual g1 offset ++ 0x3d, 0x00, //manual r offset ++ 0x3e, 0x00, //manual b offset ++ 0x3f, 0x00, //manual g2 offset ++ //gain ++ 0x50, 0x14, //10 //global gain ++ ++ 0x53, 0x80, //G ++ 0x54, 0x80, //R channel gain ++ 0x55, 0x80, //B channel gain ++ 0x56, 0x80, ++ ++ //LSC_t ++ 0x8b, 0x20, //r2 ++ 0x8c, 0x20, //g2 ++ 0x8d, 0x20, //b2 ++ 0x8e, 0x14, //r4 ++ 0x8f, 0x10, //g4 ++ 0x90, 0x14, //b4 ++ 0x91, 0x3c, //[7]singed4 [6:0]row_cneter ++ 0x92, 0x50, //col_center ++ 0x5d, 0x12, //decrease 1 ++ 0x5e, 0x1a, //decrease 2 ++ 0x5f, 0x24, //decrease 3 ++ ++ //DNDD_t ++ 0x60, 0x07, //[4]zero weight mode ++ //[3]share mode ++ //[,]c weight mode ++ //[,]lsc decrease mode ++ //[,]b mode ++ 0x61, 0x15, //[7:6]na ++ //[5:4]c weight adap ratio ++ //[,:2]dn lsc ratio ++ //[,:0]b ratio ++ 0x62, 0x08, //b base ++ //0x63,0x02,//b increase RO ++ 0x64, 0x02, //03//[7:4]n base [3:0]c weight ++ //0x65, , //[7:4]n increase [3:0]c coeff ++ 0x66, 0xe8, //dark_th ,bright_th ++ 0x67, 0x86, //flat high, flat low ++ 0x68, 0xa2, //[7:4]dd limit [1:0]dd ratio ++ ++ //asde_t ++ 0x69, 0x18, //gain high th ++ 0x6a, 0x0f, //[7:4]dn_c slop //[3]use post_gain [2]use pre_gain [1]use global gain [0]use col gain ++ 0x6b, 0x00, //[7:4]dn_b slop [3:0]dn_n slop ++ 0x6c, 0x5f, //[7:4]bright_th start [3:0]bright_th slop ++ 0x6d, 0x8f, //[7:4]dd_limit_start[3:0]dd_limit slop ++ 0x6e, 0x55, //[7:4]ee1 effect start [3:0]slope broad ++ 0x6f, 0x38, //[7:4]ee2 effect start [3:0]slope narrow ++ 0x70, 0x15, //saturation dec slope ++ 0x71, 0x33, //33//[7:4]low limit,[3:0]saturation slope ++ ++ //eeintp_t ++ 0x72, 0xdc, //[7]edge_add_mode [6]new edge mode [5]edge2_mode [4]HP_mode ++ //[3]lp intp en [2]lp edge en [1:0]lp edge mode ++ 0x73, 0x80, //[7]edge_add_mode2 [6]NA [5]only 2direction [4]fixed direction th ++ //[3]only defect map [2]intp_map dir [1]HP_acc [0]only edge map ++ ++ //for high resolution in light scene ++ 0x74, 0x02, //direction th1 ++ 0x75, 0x3f, //direction th2 ++ 0x76, 0x02, //direction diff th h>v+diff ; h>th1 ; v 1/2 subsample 176x144 ++ 0x26, 0x02, ++ 0x10, 0x26, ++ ++ 0x05, 0x00, //row_start ++ 0x06, 0x60, ++ 0x07, 0x00, //col start ++ 0x08, 0x90, ++ 0x09, 0x01, //win height 296 ++ 0x0A, 0x28, ++ 0x0B, 0x01, //win width 360 ++ 0x0c, 0x68, ++ ++ 0x91, 0x25, //LSC row center ++ 0x92, 0x31, //LSC colum center ++ ++ 0xf7, 0x01, //big_win_x0,x4 ++ 0xf8, 0x01, //big_win_y0 ++ 0xf9, 0x58, //big_win_x1 ++ 0xfa, 0x48, //big_win_y1 ++ ++ 0xfe, 0x01, ++ //GC0309_Set_SubsampleRatio(GC_1_2_SUBSAMPLE_RATIO); // 1/2 subsample ++ 0x54, 0x22, ++ 0x55, 0x03, ++ 0x56, 0x00, ++ 0x57, 0x00, ++ 0x58, 0x00, ++ 0x59, 0x00, ++ ++ 0x0e, 0x2b, ++ 0x0f, 0x23, ++ ++ 0xfe, 0x00, ++ 0x01, 0x22, ++ 0x02, 0xCC, ++ 0x0f, 0x02, ++ 0xe2, 0x00, ++ 0xe3, 0x7D, ++ 0xe4, 0x01, ++ 0xe5, 0xF4, ++ 0xe6, 0x02, ++ 0xe7, 0xEE, ++ 0xe8, 0x05, ++ 0xe9, 0xDC, ++ 0xea, 0x0C, ++ 0xeb, 0x35, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 QQVGA_MODE_TAB[] = ++{ ++ //1/4 subsample 160x120 ++ 0x26, 0x02, ++ 0x10, 0x26, ++ ++ 0x05, 0x00, //row_start ++ 0x06, 0x00, ++ 0x07, 0x00, //col start ++ 0x08, 0x00, ++ ++ 0x09, 0x01, //win height 488 ++ 0x0A, 0xe8, ++ 0x0B, 0x02, //win width 648 ++ 0x0c, 0x88, ++ ++ 0x91, 0x3c, //LSC row center ++ 0x92, 0x50, //LSC colum center ++ ++ 0xf7, 0x12, //big_win_x0,x4 ++ 0xf8, 0x0a, //big_win_y0 ++ 0xf9, 0x9f, //big_win_x1 ++ 0xfa, 0x78, //big_win_y1 ++ ++ 0xfe, 0x01, ++ //GC0309_Set_SubsampleRatio(GC_1_4_SUBSAMPLE_RATIO); // 1/4 subsample ++ 0x54, 0x44, ++ 0x55, 0x03, ++ 0x56, 0x00, ++ 0x57, 0x00, ++ 0x58, 0x00, ++ 0x59, 0x00, ++ ++ 0x0e, 0x44, ++ 0x0f, 0x32, ++ ++ 0xfe, 0x00, ++ 0x01, 0x6a, ++ 0x02, 0x70, ++ 0x0f, 0x00, ++ 0xe2, 0x00, ++ 0xe3, 0x96, ++ 0xe4, 0x02, ++ 0xe5, 0x58, ++ 0xe6, 0x03, ++ 0xe7, 0x84, ++ 0xe8, 0x07, ++ 0xe9, 0x08, ++ 0xea, 0x0d, ++ 0xeb, 0x7a, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 PREV_MODE_TAB[] = ++{ ++ 0x26, 0x02, ++ 0x10, 0x26, ++ ++ 0x05, 0x00, // row_start_high ++ 0x06, 0x00, // row_start_low ++ 0x07, 0x00, // col_start_high ++ 0x08, 0x00, // col_start_low ++ 0x09, 0x01, //[8]cis_win_height 488 ++ 0x0a, 0xe8, //[7:0]cis_win_height ++ 0x0b, 0x02, //[9:8]cis_win_width 648 ++ 0x0c, 0x88, //[7:0]cis_win_width ++ ++ 0x91, 0x3c, //[7]singed4 [6:0]row_cneter ++ 0x92, 0x50, //col_center ++ ++ 0xf7, 0x12, //big_win_x0,x4 ++ 0xf8, 0x0a, //big_win_y0 ++ 0xf9, 0x9f, //big_win_x1 ++ 0xfa, 0x78, //big_win_y1 ++ ++ 0xfe, 0x01, ++ //GC0309_Set_SubsampleRatio(GC_1_2_SUBSAMPLE_RATIO); // 1/1 subsample ++ 0x54, 0x11, ++ 0x55, 0x03, ++ 0x56, 0x00, ++ 0x57, 0x00, ++ 0x58, 0x00, ++ 0x59, 0x00, ++ ++ 0x0e, 0x44, ++ 0x0f, 0x32, ++ ++ 0xfe, 0x00, ++ 0x01, 0x6a, //24M guding 25fps ++ 0x02, 0x70, ++ 0x0f, 0x00, ++ 0xe2, 0x00, ++ 0xe3, 0x96, ++ 0xe4, 0x02, ++ 0xe5, 0x58, ++ 0xe6, 0x02, ++ 0xe7, 0x58, ++ 0xe8, 0x02, ++ 0xe9, 0x58, ++ 0xea, 0x0e, ++ 0xeb, 0xa6, ++ END_FLAG, END_FLAG ++}; ++ ++ ++static const T_U8 RECORD_MODE_TAB[] = ++{ ++ 0x26, 0x02, ++ 0x10, 0x26, ++ ++ 0x05, 0x00, // row_start_high ++ 0x06, 0x00, // row_start_low ++ 0x07, 0x00, // col_start_high ++ 0x08, 0x00, // col_start_low ++ 0x09, 0x01, //[8]cis_win_height 488 ++ 0x0a, 0xe8, //[7:0]cis_win_height ++ 0x0b, 0x02, //[9:8]cis_win_width 648 ++ 0x0c, 0x88, //[7:0]cis_win_width ++ ++ 0x91, 0x3c, //[7]singed4 [6:0]row_cneter ++ 0x92, 0x50, //col_center ++ ++ 0xf7, 0x12, //big_win_x0,x4 ++ 0xf8, 0x0a, //big_win_y0 ++ 0xf9, 0x9f, //big_win_x1 ++ 0xfa, 0x78, //big_win_y1 ++ ++ 0xfe, 0x01, ++ //GC0309_Set_SubsampleRatio(GC_1_2_SUBSAMPLE_RATIO); // 1/1 subsample ++ 0x54, 0x11, ++ 0x55, 0x03, ++ 0x56, 0x00, ++ 0x57, 0x00, ++ 0x58, 0x00, ++ 0x59, 0x00, ++ ++ 0x0e, 0x44, ++ 0x0f, 0x32, ++ ++ 0xfe, 0x00, ++ 0x01, 0x6a, //24M guding 25fps ++ 0x02, 0x70, ++ 0x0f, 0x00, ++ 0xe2, 0x00, ++ 0xe3, 0x96, ++ 0xe4, 0x02, ++ 0xe5, 0x58, ++ 0xe6, 0x02, ++ 0xe7, 0x58, ++ 0xe8, 0x02, ++ 0xe9, 0x58, ++ 0xea, 0x0e, ++ 0xeb, 0xa6, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Exposure Table ****************/ ++static const T_U8 EXPOSURE_WHOLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EXPOSURE_CENTER_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EXPOSURE_MIDDLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Brightness Table ****************/ ++static const T_U8 BRIGHTNESS_0_TAB[] = ++{ ++ //0xb5, 0xd0, ++ //0xd3, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_1_TAB[] = ++{ ++ //0xb5, 0xe0, ++ //0xd3, 0x48, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_2_TAB[] = ++{ ++ //0xb5, 0xf0, ++ //0xd3, 0x50, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_3_TAB[] = ++{ ++ //0xb5, 0x00, ++ //0xd3, 0x58, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_4_TAB[] = ++{ ++ //0xb5, 0x10, ++ //0xd3, 0x60, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_5_TAB[] = ++{ ++ //0xb5, 0x20, ++ //0xd3, 0x68, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_6_TAB[] = ++{ ++ //0xb5, 0x30, ++ //0xd3, 0x70, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Contrast Table ****************/ ++static const T_U8 CONTRAST_1_TAB[] = ++{ ++ //0xb3, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_2_TAB[] = ++{ ++ //0xb3, 0x50, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_3_TAB[] = ++{ ++ //0xb3, 0x60, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_4_TAB[] = ++{ ++ //0xb3, 0x70, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_5_TAB[] = ++{ ++ //0xb3, 0x80, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_6_TAB[] = ++{ ++ //0xb3, 0x90, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_7_TAB[] = ++{ ++ //0xb3, 0xa0, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Saturation Table ****************/ ++static const T_U8 SATURATION_1_TAB[] = ++{ ++ //0xb1, 0x40, ++ //0xb2, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_2_TAB[] = ++{ ++ //0xb1, 0x48, ++ //0xb2, 0x48, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_3_TAB[] = ++{ ++ //0xb1, 0x50, ++ //0xb2, 0x50, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_4_TAB[] = ++{ ++ //0xb1, 0x58, ++ //0xb2, 0x58, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_5_TAB[] = ++{ ++ //0xb1, 0x60, ++ //0xb2, 0x60, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Sharpness Table ****************/ ++static const T_U8 SHARPNESS_0_TAB[] = ++{ ++ 0x77, 0x11, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_1_TAB[] = ++{ ++ 0x77, 0x33, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_2_TAB[] = ++{ ++ 0x77, 0x55, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_3_TAB[] = ++{ ++ 0x77, 0x77, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_4_TAB[] = ++{ ++ 0x77, 0x99, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_5_TAB[] = ++{ ++ 0x77, 0xbb, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_6_TAB[] = ++{ ++ 0x77, 0xdd, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera AWB Table ****************/ ++static const T_U8 AWB_AUTO_TAB[] = ++{ ++ 0x5a, 0x56, ++ 0x5b, 0x40, ++ 0x5c, 0x4a, ++ 0x22, 0x57, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_SUNNY_TAB[] = ++{ ++ 0x22, 0x55, ++ 0x5a, 0x74, ++ 0x5b, 0x52, ++ 0x5c, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_CLOUDY_TAB[] = ++{ ++ 0x22, 0x55, ++ 0x5a, 0x8c, ++ 0x5b, 0x50, ++ 0x5c, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_OFFICE_TAB[] = ++{ ++ 0x22, 0x55, ++ 0x5a, 0x40, ++ 0x5b, 0x42, ++ 0x5c, 0x50, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_HOME_TAB[] = ++{ ++ 0x22, 0x55, ++ 0x5a, 0x48, ++ 0x5b, 0x40, ++ 0x5c, 0x5c, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_NIGHT_TAB[] = ++{ ++ 0x22, 0x55, ++ 0x5a, 0x40, ++ 0x5b, 0x54, ++ 0x5c, 0x70, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Effect Table ****************/ ++static const T_U8 EFFECT_NORMAL_TAB[] = ++{ ++ 0x23, 0x00, ++ 0x2d, 0x0a, ++ 0x20, 0xff, ++ 0xd2, 0x90, ++ 0x73, 0x00, ++ 0x77, 0x54, ++ ++ 0xb3, 0x40, ++ 0xb4, 0x80, ++ 0xba, 0x00, ++ 0xbb, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_SEPIA_TAB[] = ++{ ++ 0x23, 0x02, ++ 0x2d, 0x0a, ++ 0x20, 0xff, ++ 0xd2, 0x90, ++ 0x73, 0x00, ++ ++ 0xb3, 0x40, ++ 0xb4, 0x80, ++ 0xba, 0xd0, ++ 0xbb, 0x28, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_ANTIQUE_TAB[] = ++{ ++ 0x23, 0x01, ++ 0x2d, 0x0a, ++ 0x20, 0xff, ++ 0xd2, 0x90, ++ 0x73, 0x00, ++ ++ 0xb3, 0x40, ++ 0xb4, 0x80, ++ 0xba, 0x00, ++ 0xbb, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_BLUISH_TAB[] = ++{ ++ 0x23, 0x02, ++ 0x2d, 0x0a, ++ 0x20, 0xff, ++ 0xd2, 0x90, ++ 0x73, 0x00, ++ ++ 0xb3, 0x40, ++ 0xb4, 0x80, ++ 0xba, 0x50, ++ 0xbb, 0xe0, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_GREENISH_TAB[] = ++{ ++ 0x23, 0x02, ++ 0x2d, 0x0a, ++ 0x20, 0xff, ++ 0xd2, 0x90, ++ 0x77, 0x88, ++ ++ 0xb3, 0x40, ++ 0xb4, 0x80, ++ 0xba, 0xc0, ++ 0xbb, 0xc0, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_NEGATIVE_TAB[] = ++{ ++ 0x23, 0x03, ++ 0x2d, 0x0a, ++ 0x20, 0xff, ++ 0xd2, 0x90, ++ 0x73, 0x00, ++ ++ 0xb3, 0x40, ++ 0xb4, 0x80, ++ 0xba, 0x00, ++ 0xbb, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_BW_TAB[] = ++{ ++ 0x23, 0x02, ++ 0x2d, 0x0a, ++ 0x20, 0xbf, ++ 0xd2, 0x10, ++ 0x73, 0x01, ++ ++ 0x51, 0x40, ++ 0x52, 0x40, ++ 0xb3, 0x60, ++ 0xb4, 0x40, ++ 0xba, 0x00, ++ 0xbb, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_BWN_TAB[] = ++{ ++ 0x23, 0x02, ++ 0x2d, 0x0a, ++ 0x20, 0xbf, ++ 0xd2, 0x10, ++ 0x73, 0x01, ++ ++ 0x51, 0x40, ++ 0x52, 0x40, ++ 0xb3, 0x98, ++ 0xb4, 0xb0, ++ 0xba, 0x00, ++ 0xbb, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera night/day mode ****************/ ++static const T_U8 DAY_MODE_TAB[] = ++{ ++ 0xec, 0x20, ++ 0x20, 0xff, ++ 0x3c, 0x02, ++ 0x3d, 0x02, ++ 0x3e, 0x02, ++ 0x3f, 0x02, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 NIGHT_MODE_TAB[] = ++{ ++ 0xec, 0x30, ++ 0x20, 0x5f, ++ 0x3c, 0x08, ++ 0x3d, 0x08, ++ 0x3e, 0x08, ++ 0x3f, 0x08, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Mirror Table ****************/ ++static const T_U8 MIRROR_V_TAB[] = ++{ ++ //flip ++ 0xfe, 0x00,//page0 ++ 0x14, 0x12, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 MIRROR_H_TAB[] = ++{ ++ //mirror ++ 0xfe, 0x00,//page0 ++ 0x14, 0x11, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 MIRROR_NORMAL_TAB[] = ++{ ++ //no mirror/flip ++ 0xfe, 0x00,//page0 ++ 0x14, 0x10,//0x13 180㷭ת ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 MIRROR_FLIP_TAB[] = ++{ ++ //flip/mirror ++ 0xfe, 0x00,//page0 ++ 0x14, 0x13, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera anti-flicker mode ****************/ ++static const T_U8 ANTI_FLICKER_DISABLE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 ANTI_FLICKER_50HZ_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 ANTI_FLICKER_60HZ_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 ANTI_FLICKER_AUTO_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++#endif ++#endif ++ +diff --git a/drivers/media/video/plat-anyka/camera_hm1375.c b/drivers/media/video/plat-anyka/camera_hm1375.c +new file mode 100755 +index 00000000..72a9e2a7 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_hm1375.c +@@ -0,0 +1,1051 @@ ++/** ++ * @file camera_hm1375.c ++ * @brief camera driver file ++ * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author xia_wenting ++ * @date 2011-09-21 ++ * @version 1.0 ++ * @ref ++ */ ++#ifdef CONFIG_LINUX_AKSENSOR ++#include ++#include ++#include ++#include "camera_hm1375.h" ++#else ++#include "akdefine.h" ++#include "cam_com_sensor.h" ++#include "camera_hm1375.h" ++#include "Gpio_config.h" ++#endif ++ ++#if defined (USE_CAMERA_HM1375) || defined (CONFIG_SENSOR_HM1375) ++ ++#define CAM_EN_LEVEL 0 ++#define CAM_RESET_LEVEL 0 ++ ++#define CAMERA_SCCB_ADDR 0x48 ++ ++#define CAMERA_HM1375_ID 0x0375 ++ ++#define HM1375_CAMERA_MCLK 24 ++ ++static T_CAMERA_TYPE camera_hm1375_type = CAMERA_2M; ++static T_NIGHT_MODE night_mode = CAMERA_DAY_MODE; ++static T_CAMERA_MODE s_hm1375_CurMode = CAMERA_MODE_VGA; ++ ++#if 0 ++static T_VOID camera_setbit(T_U16 reg, T_U8 bit, T_U8 value) ++{ ++ T_U8 tmp; ++ ++ tmp = sccb_read_short(CAMERA_SCCB_ADDR, reg); ++ if (value == 1) ++ { ++ tmp |= 0x1 << bit; ++ } ++ else ++ { ++ tmp &= ~(0x1 << bit); ++ } ++ ++ sccb_write_short(CAMERA_SCCB_ADDR, reg, &tmp, 1); ++} ++#endif ++static T_U32 cam_hm1375_read_id(T_VOID); ++ ++static T_BOOL camera_set_param(const T_U16 tabParameter[]) ++{ ++ int i = 0; ++ T_U8 temp_value; ++ T_U8 data; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ data = tabParameter[i + 1]; ++ sccb_write_short(CAMERA_SCCB_ADDR, tabParameter[i], &data, 1); ++ ++ if ((tabParameter[i] != 0x0000) || (tabParameter[i] != 0x0022) ++ || (tabParameter[i] != 0x0100) || (tabParameter[i] != 0x0101)) ++ { ++ temp_value = sccb_read_short(CAMERA_SCCB_ADDR, tabParameter[i]); ++ if (temp_value != tabParameter[i + 1]) ++ { ++ akprintf(C1, M_DRVSYS, "set parameter error!\n"); ++ akprintf(C1, M_DRVSYS, "reg 0x%x write data is 0x%x, read data is 0x%x!\n", tabParameter[i], tabParameter[i + 1], temp_value); ++ ++ return AK_FALSE; ++ } ++ } ++ } ++ ++ i += 2; ++ } ++ ++ return AK_TRUE; ++} ++ ++static T_VOID camera_setup(const T_U16 tabParameter[]) ++{ ++ int i = 0; ++ T_U8 data; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ data = tabParameter[i + 1]; ++ sccb_write_short(CAMERA_SCCB_ADDR, tabParameter[i], &data, 1); ++ } ++ i += 2; ++ } ++ ++} ++ ++static T_VOID cam_hm1375_open(T_VOID) ++{ ++ gpio_set_pin_dir(GPIO_CAMERA_AVDD, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_CHIP_ENABLE); ++ gpio_set_pin_dir(GPIO_CAMERA_CHIP_ENABLE, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, CAM_EN_LEVEL); ++ mini_delay(10); ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_RESET); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, CAM_RESET_LEVEL); ++ mini_delay(10); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, !CAM_RESET_LEVEL); ++ ++ mini_delay(20); ++} ++ ++static T_BOOL cam_hm1375_close(T_VOID) ++{ ++ //sccb software standby mode ++// T_U8 Reg0x3d = 0x48; ++// T_U8 Reg0xc3 = 0x00; ++ ++// sccb_write_short(CAMERA_SCCB_ADDR, 0x3d, &Reg0x3d, 1); ++// sccb_write_short(CAMERA_SCCB_ADDR, 0xc3, &Reg0xc3, 1); ++ ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, !CAM_EN_LEVEL); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, !gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_INPUT); ++ ++ gpio_set_pin_dir(GPIO_I2C_SCL, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SCL, GPIO_LEVEL_LOW); ++ gpio_set_pin_dir(GPIO_I2C_SDA, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SDA, GPIO_LEVEL_LOW); ++ ++ return AK_TRUE; ++} ++ ++static T_U32 cam_hm1375_read_id(T_VOID) ++{ ++ T_U8 value = 0x00; ++ T_U32 id = 0; ++ ++ sccb_init(GPIO_I2C_SCL, GPIO_I2C_SDA); //init sccb first here!! ++ ++ value = sccb_read_short(CAMERA_SCCB_ADDR, 0x0001); ++ id = value << 8; ++ value = sccb_read_short(CAMERA_SCCB_ADDR, 0x0002); ++ id |= value; ++ ++ return id; ++} ++ ++/** ++ * @brief initialize the parameters of camera, should be done after reset and open camera to initialize ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @return T_BOOL ++ * @retval AK_TRUE if success, else AK_FALSE ++ */ ++static T_BOOL cam_hm1375_init(void) ++{ ++ if (!camera_set_param(INIT_TAB)) ++ { ++ return AK_FALSE; ++ } ++ else ++ { ++ night_mode = CAMERA_DAY_MODE; ++ return AK_TRUE; ++ } ++} ++ ++/** ++ * @brief Set camera mode to specify image quality, SXGA/VGA/CIF etc ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] mode mode value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_hm1375_set_mode(T_CAMERA_MODE mode) ++{ ++ s_hm1375_CurMode = mode; ++ switch(mode) ++ { ++ case CAMERA_MODE_UXGA: ++ camera_setup(UXGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_SXGA: ++ camera_setup(SXGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_VGA: ++ camera_setup(VGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_CIF: ++ camera_setup(CIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QVGA: ++ camera_setup(QVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_QCIF: ++ camera_setup(QCIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QQVGA: ++ camera_setup(QQVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_PREV: ++ camera_setup(PREV_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_REC: ++ camera_setup(RECORD_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_720P: ++ camera_setup(RECORD_720P_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ default: ++ s_hm1375_CurMode = CAMERA_MODE_VGA; ++ akprintf(C1, M_DRVSYS, "set camera mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera exposure mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] exposure exposure mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_hm1375_set_exposure(T_CAMERA_EXPOSURE exposure) ++{ ++ switch(exposure) ++ { ++ case EXPOSURE_WHOLE: ++ camera_setup(EXPOSURE_WHOLE_TAB); ++ break; ++ case EXPOSURE_CENTER: ++ camera_setup(EXPOSURE_CENTER_TAB); ++ break; ++ case EXPOSURE_MIDDLE: ++ camera_setup(EXPOSURE_MIDDLE_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set exposure parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera brightness level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] brightness brightness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_hm1375_set_brightness(T_CAMERA_BRIGHTNESS brightness) ++{ ++ switch(brightness) ++ { ++ case CAMERA_BRIGHTNESS_0: ++ camera_setup(BRIGHTNESS_0_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_1: ++ camera_setup(BRIGHTNESS_1_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_2: ++ camera_setup(BRIGHTNESS_2_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_3: ++ camera_setup(BRIGHTNESS_3_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_4: ++ camera_setup(BRIGHTNESS_4_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_5: ++ camera_setup(BRIGHTNESS_5_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_6: ++ camera_setup(BRIGHTNESS_6_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set brightness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera contrast level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] contrast contrast value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_hm1375_set_contrast(T_CAMERA_CONTRAST contrast) ++{ ++ switch(contrast) ++ { ++ case CAMERA_CONTRAST_1: ++ camera_setup(CONTRAST_1_TAB); ++ break; ++ case CAMERA_CONTRAST_2: ++ camera_setup(CONTRAST_2_TAB); ++ break; ++ case CAMERA_CONTRAST_3: ++ camera_setup(CONTRAST_3_TAB); ++ break; ++ case CAMERA_CONTRAST_4: ++ camera_setup(CONTRAST_4_TAB); ++ break; ++ case CAMERA_CONTRAST_5: ++ camera_setup(CONTRAST_5_TAB); ++ break; ++ case CAMERA_CONTRAST_6: ++ camera_setup(CONTRAST_6_TAB); ++ break; ++ case CAMERA_CONTRAST_7: ++ camera_setup(CONTRAST_7_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set contrast parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera saturation level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] saturation saturation value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_hm1375_set_saturation(T_CAMERA_SATURATION saturation) ++{ ++ switch(saturation) ++ { ++ case CAMERA_SATURATION_0: ++ camera_setup(SATURATION_0_TAB); ++ break; ++ case CAMERA_SATURATION_1: ++ camera_setup(SATURATION_1_TAB); ++ break; ++ case CAMERA_SATURATION_2: ++ camera_setup(SATURATION_2_TAB); ++ break; ++ case CAMERA_SATURATION_3: ++ camera_setup(SATURATION_3_TAB); ++ break; ++ case CAMERA_SATURATION_4: ++ camera_setup(SATURATION_4_TAB); ++ break; ++ case CAMERA_SATURATION_5: ++ camera_setup(SATURATION_5_TAB); ++ break; ++ case CAMERA_SATURATION_6: ++ camera_setup(SATURATION_6_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set saturation parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera sharpness level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] sharpness sharpness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_hm1375_set_sharpness(T_CAMERA_SHARPNESS sharpness) ++{ ++ switch(sharpness) ++ { ++ case CAMERA_SHARPNESS_0: ++ camera_setup(SHARPNESS_0_TAB); ++ break; ++ case CAMERA_SHARPNESS_1: ++ camera_setup(SHARPNESS_1_TAB); ++ break; ++ case CAMERA_SHARPNESS_2: ++ camera_setup(SHARPNESS_2_TAB); ++ break; ++ case CAMERA_SHARPNESS_3: ++ camera_setup(SHARPNESS_3_TAB); ++ break; ++ case CAMERA_SHARPNESS_4: ++ camera_setup(SHARPNESS_4_TAB); ++ break; ++ case CAMERA_SHARPNESS_5: ++ camera_setup(SHARPNESS_5_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set sharpness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera AWB mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] awb AWB mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_hm1375_set_AWB(T_CAMERA_AWB awb) ++{ ++ switch(awb) ++ { ++ case AWB_AUTO: ++ camera_setup(AWB_AUTO_TAB); ++ break; ++ case AWB_SUNNY: ++ camera_setup(AWB_SUNNY_TAB); ++ break; ++ case AWB_CLOUDY: ++ camera_setup(AWB_CLOUDY_TAB); ++ break; ++ case AWB_OFFICE: ++ camera_setup(AWB_OFFICE_TAB); ++ break; ++ case AWB_HOME: ++ camera_setup(AWB_HOME_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set AWB mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera mirror mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] mirror mirror mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_hm1375_set_mirror(T_CAMERA_MIRROR mirror) ++{ ++ switch(mirror) ++ { ++ case CAMERA_MIRROR_V: ++ break; ++ case CAMERA_MIRROR_H: ++ break; ++ case CAMERA_MIRROR_NORMAL: ++ break; ++ case CAMERA_MIRROR_FLIP: ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set mirror parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera effect mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] effect effect mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_hm1375_set_effect(T_CAMERA_EFFECT effect) ++{ ++ switch(effect) ++ { ++ case CAMERA_EFFECT_NORMAL: ++ camera_setup(EFFECT_NORMAL_TAB); ++ break; ++ case CAMERA_EFFECT_SEPIA: ++ camera_setup(EFFECT_SEPIA_TAB); ++ break; ++ case CAMERA_EFFECT_ANTIQUE: ++ camera_setup(EFFECT_ANTIQUE_TAB); ++ break; ++ case CAMERA_EFFECT_BLUE: ++ camera_setup(EFFECT_BLUISH_TAB); ++ break; ++ case CAMERA_EFFECT_GREEN: ++ camera_setup(EFFECT_GREENISH_TAB); ++ break; ++ case CAMERA_EFFECT_RED: ++ camera_setup(EFFECT_REDDISH_TAB); ++ break; ++ case CAMERA_EFFECT_NEGATIVE: ++ camera_setup(EFFECT_NEGATIVE_TAB); ++ break; ++ case CAMERA_EFFECT_BW: ++ camera_setup(EFFECT_BW_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set camer effect parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief set camera window ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] srcWidth window width ++ * @param[in] srcHeight window height ++ * @return T_S32 ++ * @retval 0 if error mode ++ * @retval 1 if success ++ * @retval -1 if failed ++ */ ++static T_S32 cam_hm1375_set_digital_zoom(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ return 1; ++} ++ ++static T_VOID cam_hm1375_set_night_mode(T_NIGHT_MODE mode) ++{ ++ switch(mode) ++ { ++ case CAMERA_DAY_MODE: ++ camera_setup(DAY_MODE_TAB); ++ night_mode = CAMERA_DAY_MODE; ++ break; ++ case CAMERA_NIGHT_MODE: ++ camera_setup(NIGHT_MODE_TAB); ++ night_mode = CAMERA_NIGHT_MODE; ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set night mode parameter error!\n"); ++ break; ++ } ++} ++ ++static T_VOID cam_hm1375_set_anti_flicker(T_U32 value) ++{ ++ switch(value) { ++ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: ++ //camera_setup(ANTI_FLICKER_DISABLE_TAB); ++ akprintf(C1, M_DRVSYS, "Anti-flicker not support 'Disable', Error." ++ " please select other frequency!\n"); ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: ++ camera_setup(ANTI_FLICKER_50HZ_TAB); ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: ++ camera_setup(ANTI_FLICKER_60HZ_TAB); ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: ++ //camera_setup(ANTI_FLICKER_AUTO_TAB); ++ akprintf(C1, M_DRVSYS, "Anti-flicker not support 'Auto', Error." ++ " please select other frequency!\n"); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set Anti-flicker parameter error!\n"); ++ break; ++ } ++} ++ ++static T_BOOL cam_hm1375_set_to_cap(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ ++ if ((srcWidth <= 160) && (srcHeight <= 120)) ++ { ++ Cammode = CAMERA_MODE_QQVGA; ++ } ++ else if ((srcWidth <= 176) && (srcHeight <= 144)) ++ { ++ Cammode = CAMERA_MODE_QCIF; ++ } ++ else if ((srcWidth <= 320) && (srcHeight <= 240)) ++ { ++ Cammode = CAMERA_MODE_QVGA; ++ } ++ else if ((srcWidth <= 352) && (srcHeight <= 288)) ++ { ++ Cammode = CAMERA_MODE_CIF; ++ } ++ else if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_VGA; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 720)) ++ { ++ Cammode = CAMERA_MODE_720P; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 1024)) ++ { ++ Cammode = CAMERA_MODE_SXGA; ++ } ++ else if ((srcWidth <= 1600) && (srcHeight <= 1200)) ++ { ++ Cammode = CAMERA_MODE_UXGA; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "hm1375 unsupport %d & %d mode!\n", srcWidth, srcHeight); ++ return AK_FALSE; ++ } ++ ++ cam_hm1375_set_mode(Cammode); ++ cam_hm1375_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_hm1375_set_to_prev(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ cam_hm1375_set_mode(CAMERA_MODE_PREV); ++ cam_hm1375_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_hm1375_set_to_record(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_REC; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 720)) ++ { ++ Cammode = CAMERA_MODE_720P; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "200W camera dose not support such mode"); ++ return AK_FALSE; ++ } ++ ++ cam_hm1375_set_mode(Cammode); ++ cam_hm1375_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_CAMERA_TYPE cam_hm1375_get_type(T_VOID) ++{ ++ return camera_hm1375_type; ++} ++ ++static T_VOID cam_hm1375_set_sensor_param(T_U32 cmd, T_U32 data) ++{ ++ T_U8 value; ++ ++ value = (T_U8)data; ++ sccb_write_short(CAMERA_SCCB_ADDR, (T_U16)cmd, &value, 1); ++} ++ ++static T_CAMERA_FUNCTION_HANDLER hm1375_function_handler = ++{ ++ HM1375_CAMERA_MCLK, ++ cam_hm1375_open, ++ cam_hm1375_close, ++ cam_hm1375_read_id, ++ cam_hm1375_init, ++ cam_hm1375_set_mode, ++ cam_hm1375_set_exposure, ++ cam_hm1375_set_brightness, ++ cam_hm1375_set_contrast, ++ cam_hm1375_set_saturation, ++ cam_hm1375_set_sharpness, ++ cam_hm1375_set_AWB, ++ cam_hm1375_set_mirror, ++ cam_hm1375_set_effect, ++ cam_hm1375_set_digital_zoom, ++ cam_hm1375_set_night_mode, ++ AK_NULL, ++ cam_hm1375_set_anti_flicker, ++ cam_hm1375_set_to_cap, ++ cam_hm1375_set_to_prev, ++ cam_hm1375_set_to_record, ++ cam_hm1375_get_type, ++ cam_hm1375_set_sensor_param ++}; ++ ++#ifndef CONFIG_LINUX_AKSENSOR ++static int camera_hm1375_reg(void) ++{ ++ camera_reg_dev(CAMERA_HM1375_ID, &hm1375_function_handler); ++ return 0; ++} ++ ++#ifdef __CC_ARM ++#pragma arm section rwdata = "__initcall_", zidata = "__initcall_" ++#endif ++module_init(camera_hm1375_reg) ++#ifdef __CC_ARM ++#pragma arm section ++#endif ++ ++#else ++static const char * awb_menu[] = { ++ [AWB_AUTO] = "auto", ++ [AWB_SUNNY] = "sunny", ++ [AWB_CLOUDY] = "cloudy", ++ [AWB_OFFICE] = "office", ++ [AWB_HOME] = "home", ++ [AWB_NIGHT] = "night", ++}; ++ ++static const char * effect_menu[] = { ++ [CAMERA_EFFECT_NORMAL] = "normal", ++ [CAMERA_EFFECT_SEPIA] = "sepia", ++ [CAMERA_EFFECT_ANTIQUE] = "antique", ++ [CAMERA_EFFECT_BLUE] = "blue", ++ [CAMERA_EFFECT_GREEN] = "green", ++ [CAMERA_EFFECT_RED] = "red", ++ [CAMERA_EFFECT_NEGATIVE] = "negative", ++ [CAMERA_EFFECT_BW] = "bw", ++ [CAMERA_EFFECT_BWN] = "bwn", ++ [CAMERA_EFFECT_AQUA] = "aqua", ++ [CAMERA_EFFECT_COOL] = "cool", ++ [CAMERA_EFFECT_WARM] = "warm", ++}; ++ ++static const char * resolution_menu[] = { ++ [0] = "1280x960", ++ [1] = "1280x800", ++ [2] = "1280x720", ++ [3] = "640x480", ++}; ++ ++static const char * hflip_menu[] = { ++ [0] = "normal", ++ [1] = "horizontal flip", ++}; ++ ++static const char * vflip_menu[] = { ++ [0] = "normal", ++ [1] = "vertical flip", ++}; ++ ++static const char * night_menu[] = { ++ [CAMERA_DAY_MODE] = "daylight", ++ [CAMERA_NIGHT_MODE] = "night", ++}; ++ ++static const char * anti_flicker_menu[] = { ++ [V4L2_CID_POWER_LINE_FREQUENCY_DISABLED] = "Disable", ++ [V4L2_CID_POWER_LINE_FREQUENCY_50HZ] = "50Hz", ++ [V4L2_CID_POWER_LINE_FREQUENCY_60HZ] = "60Hz", ++ [V4L2_CID_POWER_LINE_FREQUENCY_AUTO] = "Auto", ++}; ++ ++static int hm1375_s_ctl(struct v4l2_ctrl *ctrl) ++{ ++ int ret = -EINVAL; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ if (hm1375_function_handler.cam_set_AWB_func) { ++ hm1375_function_handler.cam_set_AWB_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_COLORFX: ++ if (hm1375_function_handler.cam_set_effect_func) { ++ hm1375_function_handler.cam_set_effect_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ ++ case V4L2_CID_BRIGHTNESS: ++ if (hm1375_function_handler.cam_set_brightness_func) { ++ hm1375_function_handler.cam_set_brightness_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_CONTRAST: ++ if (hm1375_function_handler.cam_set_contrast_func) { ++ hm1375_function_handler.cam_set_contrast_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SATURATION: ++ if (hm1375_function_handler.cam_set_saturation_func) { ++ hm1375_function_handler.cam_set_saturation_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SHARPNESS: ++ if (hm1375_function_handler.cam_set_sharpness_func) { ++ hm1375_function_handler.cam_set_sharpness_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_HFLIP: ++ if (hm1375_function_handler.cam_set_mirror_func) { ++ hm1375_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_H : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_VFLIP: ++ if (hm1375_function_handler.cam_set_mirror_func) { ++ hm1375_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_V : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_NIGHTMODE: ++ if (hm1375_function_handler.cam_set_night_mode_func) { ++ hm1375_function_handler.cam_set_night_mode_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY: ++ if (hm1375_function_handler.cam_set_anti_flicker_func) { ++ hm1375_function_handler.cam_set_anti_flicker_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++}; ++ ++static struct v4l2_ctrl_ops hm1375_ctrl_ops = { ++ .s_ctrl = hm1375_s_ctl, ++}; ++ ++static const struct v4l2_ctrl_config hm1375_ctrls[] = { ++ { ++ .ops = &hm1375_ctrl_ops, ++ .id = V4L2_CID_AUTO_WHITE_BALANCE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "AWB", ++ .min = 0, ++ .max = ARRAY_SIZE(awb_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = awb_menu, ++ }, ++ { ++ .ops = &hm1375_ctrl_ops, ++ .id = V4L2_CID_COLORFX, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Effect", ++ .min = 0, ++ .max = ARRAY_SIZE(effect_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = effect_menu, ++ }, ++ { ++ .ops = &hm1375_ctrl_ops, ++ .id = V4L2_CID_HFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Horizontal Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(hflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = hflip_menu, ++ }, ++ { ++ .ops = &hm1375_ctrl_ops, ++ .id = V4L2_CID_VFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Vetical Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(vflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = vflip_menu, ++ }, ++ { ++ .ops = &hm1375_ctrl_ops, ++ .id = V4L2_CID_PICTURE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Resolution", ++ .min = 0, ++ .max = ARRAY_SIZE(resolution_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = resolution_menu, ++ }, ++ { ++ .ops = &hm1375_ctrl_ops, ++ .id = V4L2_CID_NIGHTMODE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Night mode", ++ .min = 0, ++ .max = ARRAY_SIZE(night_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = night_menu, ++ }, ++ { ++ .ops = &hm1375_ctrl_ops, ++ .id = V4L2_CID_BRIGHTNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Brightness", ++ .min = 0, ++ .max = CAMERA_BRIGHTNESS_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &hm1375_ctrl_ops, ++ .id = V4L2_CID_CONTRAST, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Contrast", ++ .min = 0, ++ .max = CAMERA_CONTRAST_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &hm1375_ctrl_ops, ++ .id = V4L2_CID_SATURATION, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Saturation", ++ .min = 0, ++ .max = CAMERA_SATURATION_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &hm1375_ctrl_ops, ++ .id = V4L2_CID_SHARPNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Sharpness", ++ .min = 0, ++ .max = CAMERA_SHARPNESS_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &hm1375_ctrl_ops, ++ .id = V4L2_CID_POWER_LINE_FREQUENCY, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "anti flicker", ++ .min = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, ++ .max = V4L2_CID_POWER_LINE_FREQUENCY_AUTO, ++ .step = 0, ++ .def = V4L2_CID_POWER_LINE_FREQUENCY_50HZ, ++ .flags = 0, ++ .menu_skip_mask = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, ++ .qmenu = anti_flicker_menu, ++ } ++}; ++ ++ ++/* ++ * supported format list ++ */ ++static const struct aksensor_color_format hm1375_formats[] = { ++ { ++ .code = V4L2_MBUS_FMT_YUYV8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_YVYU8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_UYVY8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++}; ++ ++static const struct aksensor_win_size hm1375_win[] = { ++ {.name = "VGA", .width = 640, .height = 480}, ++ {.name = "720P", .width = 1280, .height = 720}, ++ {.name = "800P", .width = 1280, .height = 800}, ++ {.name = "960P", .width = 1280, .height = 960}, ++}; ++ ++static struct sensor_info hm1375_sensor_info = { ++ .sensor_name = "hm1375", ++ .sensor_id = CAMERA_HM1375_ID, ++ .ctrls = hm1375_ctrls, ++ .nr_ctrls = ARRAY_SIZE(hm1375_ctrls), ++ .formats = hm1375_formats, ++ .num_formats = ARRAY_SIZE(hm1375_formats), ++ .resolution = hm1375_win, ++ .num_resolution = ARRAY_SIZE(hm1375_win), ++ .handler = &hm1375_function_handler, ++}; ++ ++static int hm1375_module_init(void) ++{ ++ return register_sensor(&hm1375_sensor_info); ++} ++module_init(hm1375_module_init) ++#endif ++ ++#endif ++ ++ +diff --git a/drivers/media/video/plat-anyka/camera_hm1375.h b/drivers/media/video/plat-anyka/camera_hm1375.h +new file mode 100755 +index 00000000..10e47fc0 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_hm1375.h +@@ -0,0 +1,1537 @@ ++/** ++ * @file camera_hm1375.h ++ * @brief camera driver file ++ * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author wudaochao ++ * @date 2013-04-26 ++ * @version 1.0 ++ * @ref ++ */ ++#ifndef __CAMERA_HM1375_H__ ++#define __CAMERA_HM1375_H__ ++ ++ ++#if defined (USE_CAMERA_HM1375) || defined (CONFIG_SENSOR_HM1375) ++ ++#undef DELAY_FLAG ++#undef END_FLAG ++#define DELAY_FLAG 0xfd // first parameter is 0xfe, then 2nd parameter is delay time count ++#define END_FLAG 0xfe // first parameter is 0xff, then parameter table is over ++ ++static const T_U16 INIT_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 UXGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SXGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 VGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CIF_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 QVGA_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 QCIF_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 QQVGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 PREV_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 RECORD_MODE_TAB[] = ++{ ++ 0x0022, 0x00, ++ 0x000C, 0x04, ++ 0x0006, 0x0F,//װ˵ģҪVFLIP on,Mirror on ++ 0x000A, 0x20, ++ 0x000F, 0x10, ++ 0x0012, 0x01, ++ 0x0013, 0x02, ++ 0x0015, 0x01, ++ 0x0016, 0x00, ++ 0x0018, 0x00, ++ 0x001D, 0x40, ++ //0x0020, 0x10, ++ 0x0020, 0x50,//VSYNC: active low ++ 0x0023, 0x43, ++ 0x0024, 0x20, ++ 0x0025, 0x00, ++ 0x0026, 0x6F, ++ //0x0027, 0x30,//YUYV(YUV422) ++ 0x0027, 0x10, ++ 0x0028, 0x01, ++ 0x002E, 0x0E, ++ 0x0030, 0x00, ++ 0x0034, 0x0E, ++ 0x0035, 0x01, ++ 0x0036, 0x00, ++ 0x0038, 0x02, ++ 0x0039, 0x01, ++ 0x003A, 0x01, ++ 0x003B, 0xFF, ++ 0x003C, 0xFF, ++ 0x003D, 0x40, ++ 0x003F, 0x14, ++ 0x0040, 0x10, ++ 0x0044, 0x07, ++ 0x0045, 0x25, ++ 0x0048, 0x7F, ++ 0x004E, 0xFF, ++ 0x0070, 0x00, ++ 0x0071, 0x4F, ++ 0x0072, 0x00, ++ 0x0073, 0x30, ++ 0x0074, 0x13, ++ 0x0075, 0x40, ++ 0x0076, 0x24, ++ 0x0078, 0x0F, ++ 0x007A, 0x05, ++ 0x007B, 0xF2, ++ 0x007C, 0x10, ++ 0x0080, 0xC9, ++ 0x0081, 0x00, ++ 0x0082, 0x28, ++ 0x0083, 0xB0, ++ 0x0084, 0x70, ++ 0x0086, 0x3E, ++ 0x0087, 0x70, ++ 0x0088, 0x11, ++ 0x0089, 0x3C, ++ 0x008A, 0x87, ++ 0x008D, 0x64, ++ 0x0090, 0x07, ++ 0x0091, 0x09, ++ 0x0092, 0x0C, ++ 0x0093, 0x0C, ++ 0x0094, 0x0C, ++ 0x0095, 0x0C, ++ 0x0096, 0x01, ++ 0x0097, 0x00, ++ 0x0098, 0x04, ++ 0x0099, 0x08, ++ 0x009A, 0x0C, ++ 0x0120, 0x36, ++ 0x0121, 0x81, ++ 0x0122, 0xEB, ++ 0x0123, 0x29, ++ 0x0124, 0x50, ++ 0x0125, 0xDE, ++ 0x0126, 0xB1, ++ 0x013D, 0x0F, ++ 0x013E, 0x0F, ++ 0x013F, 0x0F, ++ 0x0140, 0x14, ++ 0x0141, 0x0A, ++ 0x0142, 0x14, ++ 0x0143, 0x0A, ++ 0x0144, 0x08, ++ 0x0145, 0x04, ++ 0x0146, 0x28, ++ 0x0147, 0x3C, ++ 0x0148, 0x28, ++ 0x0149, 0x3C, ++ 0x014A, 0x96, ++ 0x014B, 0xC8, ++ 0x0150, 0x14, ++ 0x0151, 0x30, ++ 0x0152, 0x54, ++ 0x0153, 0x70, ++ 0x0154, 0x14, ++ 0x0155, 0x30, ++ 0x0156, 0x54, ++ 0x0157, 0x70, ++ 0x0158, 0x14, ++ 0x0159, 0x30, ++ 0x015A, 0x54, ++ 0x015B, 0x70, ++ 0x015C, 0x30, ++ 0x015D, 0x00, ++ 0x01D8, 0x20, ++ 0x01D9, 0x08, ++ 0x01DA, 0x20, ++ 0x01DB, 0x08, ++ 0x01DC, 0x20, ++ 0x01DD, 0x08, ++ 0x01DE, 0x20, ++ 0x01DF, 0x08, ++ 0x01E0, 0x20, ++ 0x01E1, 0x08, ++ 0x01E2, 0xFF, ++ 0x01E3, 0x00, ++ 0x01E4, 0x10, ++ 0x01E5, 0x10, ++ 0x01E6, 0x02, ++ 0x01E7, 0x10, ++ 0x01E8, 0x10, ++ 0x01E9, 0x10, ++ 0x01EA, 0x10, ++ 0x01EC, 0xFA, ++ 0x01EB, 0x10, ++ 0x0220, 0xF0, ++ 0x0221, 0xA0, ++ 0x0222, 0x00, ++ 0x0223, 0x80, ++ 0x0224, 0x80, ++ 0x0225, 0x00, ++ 0x0226, 0x80, ++ 0x0227, 0x80, ++ 0x0228, 0x00, ++ 0x0229, 0x80, ++ 0x022A, 0x80, ++ 0x022B, 0x00, ++ 0x022C, 0x80, ++ 0x022D, 0x12, ++ 0x022E, 0x10, ++ 0x022F, 0x12, ++ 0x0230, 0x10, ++ 0x0231, 0x12, ++ 0x0232, 0x10, ++ 0x0233, 0x12, ++ 0x0234, 0x10, ++ 0x0235, 0x80, ++ 0x0236, 0x02, ++ 0x0237, 0x80, ++ 0x0238, 0x02, ++ 0x0239, 0x80, ++ 0x023A, 0x02, ++ 0x023B, 0x80, ++ 0x023C, 0x02, ++ 0x023D, 0x00, ++ 0x023E, 0x02, ++ 0x023F, 0x00, ++ 0x0240, 0x02, ++ 0x0241, 0x00, ++ 0x0242, 0x02, ++ 0x0243, 0x00, ++ 0x0244, 0x02, ++ 0x0251, 0x10, ++ 0x0280, 0x00, ++ 0x0281, 0x41, ++ 0x0282, 0x00, ++ 0x0283, 0x6D, ++ 0x0284, 0x00, ++ 0x0285, 0xBC, ++ 0x0286, 0x01, ++ 0x0287, 0x45, ++ 0x0288, 0x01, ++ 0x0289, 0x7B, ++ 0x028A, 0x01, ++ 0x028B, 0xAC, ++ 0x028C, 0x01, ++ 0x028D, 0xD2, ++ 0x028E, 0x01, ++ 0x028F, 0xF6, ++ 0x0290, 0x02, ++ 0x0291, 0x16, ++ 0x0292, 0x02, ++ 0x0293, 0x35, ++ 0x0294, 0x02, ++ 0x0295, 0x6E, ++ 0x0296, 0x02, ++ 0x0297, 0xA2, ++ 0x0298, 0x02, ++ 0x0299, 0xFF, ++ 0x029A, 0x03, ++ 0x029B, 0x51, ++ 0x029C, 0x03, ++ 0x029D, 0x9B, ++ 0x029E, 0x00, ++ 0x029F, 0x85, ++ 0x02A0, 0x04, ++ 0x02C0, 0x80, ++ 0x02C1, 0x01, ++ 0x02C2, 0x71, ++ 0x02C3, 0x04, ++ 0x02C4, 0x0F, ++ 0x02C5, 0x04, ++ 0x02C6, 0x3D, ++ 0x02C7, 0x04, ++ 0x02C8, 0x94, ++ 0x02C9, 0x01, ++ 0x02CA, 0x57, ++ 0x02CB, 0x04, ++ 0x02CC, 0x0F, ++ 0x02CD, 0x04, ++ 0x02CE, 0x8F, ++ 0x02CF, 0x04, ++ 0x02D0, 0x9E, ++ 0x02D1, 0x01, ++ 0x02E0, 0x06, ++ 0x02E1, 0xC0, ++ 0x02E2, 0xE0, ++ 0x02F0, 0x48, ++ 0x02F1, 0x01, ++ 0x02F2, 0x32, ++ 0x02F3, 0x04, ++ 0x02F4, 0x16, ++ 0x02F5, 0x04, ++ 0x02F6, 0x52, ++ 0x02F7, 0x04, ++ 0x02F8, 0xAA, ++ 0x02F9, 0x01, ++ 0x02FA, 0x58, ++ 0x02FB, 0x04, ++ 0x02FC, 0x56, ++ 0x02FD, 0x04, ++ 0x02FE, 0xDD, ++ 0x02FF, 0x04, ++ 0x0300, 0x33, ++ 0x0301, 0x02, ++ 0x0324, 0x00, ++ 0x0325, 0x01, ++ 0x0333, 0x86, ++ 0x0334, 0x00, ++ 0x0335, 0x86, ++ 0x0340, 0x40, ++ 0x0341, 0x44, ++ 0x0342, 0x4A, ++ 0x0343, 0x2B, ++ 0x0344, 0x94, ++ 0x0345, 0x3F, ++ 0x0346, 0x8E, ++ 0x0347, 0x51, ++ 0x0348, 0x75, ++ 0x0349, 0x5C, ++ 0x034A, 0x6A, ++ 0x034B, 0x68, ++ 0x034C, 0x5E, ++ 0x0350, 0x7C, ++ 0x0351, 0x78, ++ 0x0352, 0x08, ++ 0x0353, 0x04, ++ 0x0354, 0x80, ++ 0x0355, 0x9A, ++ 0x0356, 0xCC, ++ 0x0357, 0xFF, ++ 0x0358, 0xFF, ++ 0x035A, 0xFF, ++ 0x035B, 0x00, ++ 0x035C, 0x70, ++ 0x035D, 0x80, ++ 0x035F, 0xA0, ++ 0x0488, 0x30, ++ 0x0360, 0xDF, ++ 0x0361, 0x00, ++ 0x0362, 0xFF, ++ 0x0363, 0x03, ++ 0x0364, 0xFF, ++ 0x037B, 0x11, ++ 0x037C, 0x1E, ++ 0x0380, 0xFF, ++ 0x0383, 0x50, ++ 0x038A, 0x64, ++ 0x038B, 0x64, ++ 0x038E, 0x3C, ++ 0x0391, 0x2A, ++ 0x0393, 0x1E, ++ 0x0394, 0x64, ++ 0x0395, 0x23, ++ 0x0398, 0x03, ++ 0x0399, 0x45, ++ 0x039A, 0x06, ++ 0x039B, 0x8B, ++ 0x039C, 0x0D, ++ 0x039D, 0x16, ++ 0x039E, 0x0A, ++ 0x039F, 0x10, ++ 0x03A0, 0x10, ++ 0x03A1, 0xE5, ++ 0x03A2, 0x06, ++ 0x03A4, 0x18, ++ 0x03A5, 0x48, ++ 0x03A6, 0x2D, ++ 0x03A7, 0x78, ++ 0x03AC, 0x5A, ++ 0x03AD, 0x0F, ++ 0x03AE, 0x7F, ++ 0x03AF, 0x04, ++ 0x03B0, 0x35, ++ 0x03B1, 0x14, ++ 0x036F, 0x04, ++ 0x0370, 0x0A, ++ 0x0371, 0x04, ++ 0x0372, 0x10, ++ 0x0373, 0x40, ++ 0x0374, 0x20, ++ 0x0375, 0x04, ++ 0x0376, 0x00, ++ 0x0377, 0x08, ++ 0x0378, 0x08, ++ 0x0379, 0x04, ++ 0x037A, 0x08, ++ 0x0420, 0x00, ++ 0x0421, 0x00, ++ 0x0422, 0x00, ++ 0x0423, 0x84, ++ 0x0430, 0x10, ++ 0x0431, 0x60, ++ 0x0432, 0x10, ++ 0x0433, 0x20, ++ 0x0434, 0x00, ++ 0x0435, 0x30, ++ 0x0436, 0x00, ++ 0x0450, 0xFD, ++ 0x0451, 0xD8, ++ 0x0452, 0xA0, ++ 0x0453, 0x50, ++ 0x0454, 0x00, ++ 0x0459, 0x04, ++ 0x045A, 0x00, ++ 0x045B, 0x30, ++ 0x045C, 0x01, ++ 0x045D, 0x70, ++ 0x0460, 0x00, ++ 0x0461, 0x00, ++ 0x0462, 0x00, ++ 0x0465, 0x16, ++ 0x0466, 0x14, ++ 0x0478, 0x00, ++ 0x0480, 0x60, ++ 0x0481, 0x06, ++ 0x0482, 0x0C, ++ 0x04B0, 0x4C, ++ 0x04B1, 0x86, ++ 0x04B2, 0x00, ++ 0x04B3, 0x18, ++ 0x04B4, 0x00, ++ 0x04B5, 0x00, ++ 0x04B6, 0x30, ++ 0x04B7, 0x00, ++ 0x04B8, 0x00, ++ 0x04B9, 0x10, ++ 0x04BA, 0x00, ++ 0x04BB, 0x00, ++ 0x04BD, 0x00, ++ 0x04D0, 0x56, ++ 0x04D6, 0x30, ++ 0x04DD, 0x10, ++ 0x04D9, 0x16, ++ 0x04D3, 0x18, ++ 0x0540, 0x01, ++ 0x0541, 0x06, ++ 0x0542, 0x01, ++ 0x0543, 0x3B, ++ 0x0580, 0x50, ++ 0x0581, 0x30, ++ 0x0582, 0x2D, ++ 0x0583, 0x16, ++ 0x0584, 0x1E, ++ 0x0585, 0x0F, ++ 0x0586, 0x08, ++ 0x0587, 0x10, ++ 0x0590, 0x10, ++ 0x0591, 0x10, ++ 0x0592, 0x05, ++ 0x0593, 0x05, ++ 0x0594, 0x04, ++ 0x0595, 0x06, ++ 0x05B0, 0x04, ++ 0x05B1, 0x00, ++ 0x05E4, 0x02, ++ 0x05E5, 0x00, ++ 0x05E6, 0x81, ++ 0x05E7, 0x02, ++ 0x05E8, 0x09, ++ 0x05E9, 0x00, ++ 0x05EA, 0xE8, ++ 0x05EB, 0x01, ++ 0x0666, 0x02, ++ 0x0667, 0xE0, ++ 0x067F, 0x19, ++ 0x067C, 0x00, ++ 0x067D, 0x00, ++ 0x0682, 0x00, ++ 0x0683, 0x00, ++ 0x0688, 0x00, ++ 0x0689, 0x00, ++ 0x068E, 0x00, ++ 0x068F, 0x00, ++ 0x0695, 0x00, ++ 0x0694, 0x00, ++ 0x0697, 0x19, ++ 0x069B, 0x00, ++ 0x069C, 0x20, ++ 0x0720, 0x00, ++ 0x0725, 0x6A, ++ 0x0726, 0x03, ++ 0x072B, 0x64, ++ 0x072C, 0x64, ++ 0x072D, 0x20, ++ 0x072E, 0x82, ++ 0x072F, 0x08, ++ 0x0800, 0x16, ++ 0x0801, 0x4F, ++ 0x0802, 0x00, ++ 0x0803, 0x68, ++ 0x0804, 0x01, ++ 0x0805, 0x28, ++ 0x0806, 0x10, ++ 0x0808, 0x1D, ++ 0x0809, 0x18, ++ 0x080A, 0x10, ++ 0x080B, 0x07, ++ 0x080D, 0x0F, ++ 0x080E, 0x0F, ++ 0x0810, 0x00, ++ 0x0811, 0x08, ++ 0x0812, 0x20, ++ 0x0857, 0x0A, ++ 0x0858, 0x04, ++ 0x0859, 0x01, ++ 0x085A, 0x04, ++ 0x085B, 0x18, ++ 0x085C, 0x03, ++ 0x085D, 0x7F, ++ 0x085E, 0x02, ++ 0x085F, 0xD0, ++ 0x0860, 0x03, ++ 0x0861, 0x7F, ++ 0x0862, 0x02, ++ 0x0863, 0xD0, ++ 0x0864, 0x02, ++ 0x0865, 0x7F, ++ 0x0866, 0x01, ++ 0x0867, 0x00, ++ 0x0868, 0x40, ++ 0x0869, 0x01, ++ 0x086A, 0x00, ++ 0x086B, 0x40, ++ 0x086C, 0x01, ++ 0x086D, 0x00, ++ 0x086E, 0x40, ++ 0x0870, 0x00, ++ 0x0871, 0x14, ++ 0x0872, 0x01, ++ 0x0873, 0x20, ++ 0x0874, 0x00, ++ 0x0875, 0x14, ++ 0x0876, 0x00, ++ 0x0877, 0xEC, ++ 0x0815, 0x00, ++ 0x0816, 0x4C, ++ 0x0817, 0x00, ++ 0x0818, 0x7B, ++ 0x0819, 0x00, ++ 0x081A, 0xCA, ++ 0x081B, 0x01, ++ 0x081C, 0x3E, ++ 0x081D, 0x01, ++ 0x081E, 0x77, ++ 0x081F, 0x01, ++ 0x0820, 0xAA, ++ 0x0821, 0x01, ++ 0x0822, 0xCE, ++ 0x0823, 0x01, ++ 0x0824, 0xEE, ++ 0x0825, 0x02, ++ 0x0826, 0x16, ++ 0x0827, 0x02, ++ 0x0828, 0x33, ++ 0x0829, 0x02, ++ 0x082A, 0x65, ++ 0x082B, 0x02, ++ 0x082C, 0x91, ++ 0x082D, 0x02, ++ 0x082E, 0xDC, ++ 0x082F, 0x03, ++ 0x0830, 0x28, ++ 0x0831, 0x03, ++ 0x0832, 0x74, ++ 0x0833, 0x03, ++ 0x0834, 0xFF, ++ 0x0882, 0x00, ++ 0x0883, 0x3E, ++ 0x0884, 0x00, ++ 0x0885, 0x70, ++ 0x0886, 0x00, ++ 0x0887, 0xB8, ++ 0x0888, 0x01, ++ 0x0889, 0x28, ++ 0x088A, 0x01, ++ 0x088B, 0x5B, ++ 0x088C, 0x01, ++ 0x088D, 0x8A, ++ 0x088E, 0x01, ++ 0x088F, 0xB1, ++ 0x0890, 0x01, ++ 0x0891, 0xD9, ++ 0x0892, 0x01, ++ 0x0893, 0xEE, ++ 0x0894, 0x02, ++ 0x0895, 0x0F, ++ 0x0896, 0x02, ++ 0x0897, 0x4C, ++ 0x0898, 0x02, ++ 0x0899, 0x74, ++ 0x089A, 0x02, ++ 0x089B, 0xC3, ++ 0x089C, 0x03, ++ 0x089D, 0x0F, ++ 0x089E, 0x03, ++ 0x089F, 0x57, ++ 0x08A0, 0x03, ++ 0x08A1, 0xFF, ++ 0x0100, 0x01, ++ 0x0101, 0x01, ++ 0x0000, 0x01, ++ 0x002C, 0x00, ++ 0x0005, 0x01, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 RECORD_720P_TAB[] = ++{ ++ 0x0022, 0x00, // SFTSRT _Soft Reset ++ 0x000C, 0x04, ++ 0x0006, 0x0B, //װ˵ģҪVFLIP on,Mirror on ++ 0x000A, 0x00, ++ 0x000F, 0x10, ++ 0x0012, 0x01, ++ 0x0013, 0x02, ++ 0x0015, 0x01, ++ 0x0016, 0x00, ++ 0x0018, 0x00, ++ 0x001D, 0x40, ++// 0x0020, 0x10, //VSYNC: active high ++ 0x0020, 0x50, //VSYNC: active low ++ 0x0023, 0x43, ++ 0x0024, 0x20, ++ 0x0025, 0x00, //PCLK=78MHz ++// 0x0025, 0x01, //PCLK=39MHz ++ 0x0026, 0x6C, ++// 0x0027, 0x30, //YUYV(YUV422) ++ 0x0027, 0x10, //UYVY(YUV422) ++ 0x0028, 0x01, ++ 0x0030, 0x00, ++ 0x0034, 0x0E, ++ 0x0035, 0x01, ++ 0x0036, 0x00, ++ 0x0038, 0x02, ++ 0x0039, 0x01, ++ 0x003A, 0x01, ++ 0x003B, 0xFF, ++ 0x003C, 0xFF, ++ 0x003D, 0x40, ++ 0x003F, 0x14, ++ 0x0040, 0x10, ++ 0x0044, 0x07, ++ 0x0045, 0x35, ++ 0x0048, 0x7F, ++ 0x004E, 0xFF, ++ 0x0070, 0x22, ++ 0x0071, 0x3F, ++ 0x0072, 0x22, ++ 0x0073, 0x30, ++ 0x0074, 0x13, ++ 0x0075, 0x40, ++ 0x0076, 0x24, ++ 0x0078, 0x0F, ++ 0x007A, 0x06, ++ 0x007B, 0x14, ++ 0x007C, 0x10, ++ 0x0080, 0xC9, ++ 0x0081, 0x00, ++ 0x0082, 0x28, ++ 0x0083, 0xB0, ++ 0x0084, 0x60, ++ 0x0086, 0x3E, ++ 0x0087, 0x70, ++ 0x0088, 0x11, ++ 0x0089, 0x3C, ++ 0x008A, 0x87, ++ 0x008D, 0x64, ++ 0x0090, 0x07, ++ 0x0091, 0x09, ++ 0x0092, 0x0C, ++ 0x0093, 0x0C, ++ 0x0094, 0x0C, ++ 0x0095, 0x0C, ++ 0x0096, 0x01, ++ 0x0097, 0x00, ++ 0x0098, 0x04, ++ 0x0099, 0x08, ++ 0x009A, 0x0C, ++ 0x0120, 0x36, ++ 0x0121, 0x81, ++ 0x0122, 0xEB, ++ 0x0123, 0x29, ++ 0x0124, 0x50, ++ 0x0125, 0xDE, ++ 0x0126, 0xB1, ++ 0x013D, 0x0F, ++ 0x013E, 0x0F, ++ 0x013F, 0x0F, ++ 0x0140, 0x14, ++ 0x0141, 0x0A, ++ 0x0142, 0x14, ++ 0x0143, 0x0A, ++ 0x0144, 0x08, ++ 0x0145, 0x04, ++ 0x0146, 0x28, ++ 0x0147, 0x3C, ++ 0x0148, 0x28, ++ 0x0149, 0x3C, ++ 0x014A, 0x96, ++ 0x014B, 0xC8, ++ 0x0150, 0x14, ++ 0x0151, 0x30, ++ 0x0152, 0x54, ++ 0x0153, 0x70, ++ 0x0154, 0x14, ++ 0x0155, 0x30, ++ 0x0156, 0x54, ++ 0x0157, 0x70, ++ 0x0158, 0x14, ++ 0x0159, 0x30, ++ 0x015A, 0x54, ++ 0x015B, 0x70, ++ 0x015C, 0x30, ++ 0x015D, 0x00, ++ 0x01D8, 0x20, ++ 0x01D9, 0x08, ++ 0x01DA, 0x20, ++ 0x01DB, 0x08, ++ 0x01DC, 0x20, ++ 0x01DD, 0x08, ++ 0x01DE, 0x50, ++ 0x01E0, 0x50, ++ 0x01E2, 0x50, ++ 0x01E4, 0x10, ++ 0x01E5, 0x10, ++ 0x01E6, 0x02, ++ 0x01E7, 0x10, ++ 0x01E8, 0x10, ++ 0x01E9, 0x10, ++ 0x01EC, 0x28, ++ 0x0220, 0x00, ++ 0x0221, 0xA0, ++ 0x0222, 0x00, ++ 0x0223, 0x80, ++ 0x0224, 0x80, ++ 0x0225, 0x00, ++ 0x0226, 0x80, ++ 0x0227, 0x80, ++ 0x0228, 0x00, ++ 0x0229, 0x80, ++ 0x022A, 0x80, ++ 0x022B, 0x00, ++ 0x022C, 0x80, ++ 0x022D, 0x12, ++ 0x022E, 0x10, ++ 0x022F, 0x12, ++ 0x0230, 0x10, ++ 0x0231, 0x12, ++ 0x0232, 0x10, ++ 0x0233, 0x12, ++ 0x0234, 0x10, ++ 0x0235, 0x88, ++ 0x0236, 0x02, ++ 0x0237, 0x88, ++ 0x0238, 0x02, ++ 0x0239, 0x88, ++ 0x023A, 0x02, ++ 0x023B, 0x88, ++ 0x023C, 0x02, ++ 0x023D, 0x04, ++ 0x023E, 0x02, ++ 0x023F, 0x04, ++ 0x0240, 0x02, ++ 0x0241, 0x04, ++ 0x0242, 0x02, ++ 0x0243, 0x04, ++ 0x0244, 0x02, ++ 0x0251, 0x10, ++ 0x0280, 0x00, ++ 0x0281, 0x41, ++ 0x0282, 0x00, ++ 0x0283, 0x6D, ++ 0x0284, 0x00, ++ 0x0285, 0xBC, ++ 0x0286, 0x01, ++ 0x0287, 0x45, ++ 0x0288, 0x01, ++ 0x0289, 0x7B, ++ 0x028A, 0x01, ++ 0x028B, 0xAC, ++ 0x028C, 0x01, ++ 0x028D, 0xD2, ++ 0x028E, 0x01, ++ 0x028F, 0xF6, ++ 0x0290, 0x02, ++ 0x0291, 0x16, ++ 0x0292, 0x02, ++ 0x0293, 0x35, ++ 0x0294, 0x02, ++ 0x0295, 0x6E, ++ 0x0296, 0x02, ++ 0x0297, 0xA2, ++ 0x0298, 0x02, ++ 0x0299, 0xFF, ++ 0x029A, 0x03, ++ 0x029B, 0x51, ++ 0x029C, 0x03, ++ 0x029D, 0x9B, ++ 0x029E, 0x00, ++ 0x029F, 0x85, ++ 0x02A0, 0x04, ++ 0x02C0, 0x80, ++ 0x02C1, 0x01, ++ 0x02C2, 0x71, ++ 0x02C3, 0x04, ++ 0x02C4, 0x0F, ++ 0x02C5, 0x04, ++ 0x02C6, 0x3D, ++ 0x02C7, 0x04, ++ 0x02C8, 0x94, ++ 0x02C9, 0x01, ++ 0x02CA, 0x57, ++ 0x02CB, 0x04, ++ 0x02CC, 0x0F, ++ 0x02CD, 0x04, ++ 0x02CE, 0x8F, ++ 0x02CF, 0x04, ++ 0x02D0, 0x9E, ++ 0x02D1, 0x01, ++ 0x02E0, 0x06, ++ 0x02E1, 0xC0, ++ 0x02E2, 0xE0, ++ 0x02F0, 0x48, ++ 0x02F1, 0x01, ++ 0x02F2, 0x32, ++ 0x02F3, 0x04, ++ 0x02F4, 0x16, ++ 0x02F5, 0x04, ++ 0x02F6, 0x52, ++ 0x02F7, 0x04, ++ 0x02F8, 0xAA, ++ 0x02F9, 0x01, ++ 0x02FA, 0x58, ++ 0x02FB, 0x04, ++ 0x02FC, 0x56, ++ 0x02FD, 0x04, ++ 0x02FE, 0xDD, ++ 0x02FF, 0x04, ++ 0x0300, 0x33, ++ 0x0301, 0x02, ++ 0x0324, 0x00, ++ 0x0325, 0x01, ++ 0x0333, 0x86, ++ 0x0334, 0x00, ++ 0x0335, 0x86, ++ 0x0340, 0x40, ++ 0x0341, 0x44, ++ 0x0342, 0x4A, ++ 0x0343, 0x2B, ++ 0x0344, 0x94, ++ 0x0345, 0x3F, ++ 0x0346, 0x8E, ++ 0x0347, 0x51, ++ 0x0348, 0x75, ++ 0x0349, 0x5C, ++ 0x034A, 0x6A, ++ 0x034B, 0x68, ++ 0x034C, 0x5E, ++ 0x0350, 0x7C, ++ 0x0351, 0x78, ++ 0x0352, 0x08, ++ 0x0353, 0x04, ++ 0x0354, 0x80, ++ 0x0355, 0x9A, ++ 0x0356, 0xCC, ++ 0x0357, 0xFF, ++ 0x0358, 0xFF, ++ 0x035A, 0xFF, ++ 0x035B, 0x00, ++ 0x035C, 0x70, ++ 0x035D, 0x80, ++ 0x035F, 0xA0, ++ 0x0488, 0x30, ++ 0x0360, 0xDF, ++ 0x0361, 0x00, ++ 0x0362, 0xFF, ++ 0x0363, 0x03, ++ 0x0364, 0xFF, ++ 0x037B, 0x11, ++ 0x037C, 0x1E, ++ 0x0380, 0xFF, ++ 0x0383, 0x50, ++ 0x038A, 0x64, ++ 0x038B, 0x64, ++ 0x038E, 0x3C, ++ 0x0391, 0x2A, ++ 0x0393, 0x1E, ++ 0x0394, 0x64, ++ 0x0395, 0x23, ++ 0x0398, 0x03, ++ 0x0399, 0x45, ++ 0x039A, 0x06, ++ 0x039B, 0x8B, ++ 0x039C, 0x0D, ++ 0x039D, 0x16, ++ 0x039E, 0x0A, ++ 0x039F, 0x10, ++ 0x03A0, 0x10, ++ 0x03A1, 0xE5, ++ 0x03A2, 0x06, ++ 0x03A4, 0x18, ++ 0x03A5, 0x48, ++ 0x03A6, 0x2D, ++ 0x03A7, 0x78, ++ 0x03AC, 0x5A, ++ 0x03AD, 0x0F, ++ 0x03AE, 0x7F, ++ 0x03AF, 0x04, ++ 0x03B0, 0x35, ++ 0x03B1, 0x14, ++ 0x036F, 0x04, ++ 0x0370, 0x0A, ++ 0x0371, 0x04, ++ 0x0372, 0x00, ++ 0x0373, 0x40, ++ 0x0374, 0x20, ++ 0x0375, 0x04, ++ 0x0376, 0x00, ++ 0x0377, 0x08, ++ 0x0378, 0x08, ++ 0x0379, 0x04, ++ 0x037A, 0x08, ++ 0x0420, 0x00, ++ 0x0421, 0x00, ++ 0x0422, 0x00, ++ 0x0423, 0x84, ++ 0x0430, 0x10, ++ 0x0431, 0x60, ++ 0x0432, 0x10, ++ 0x0433, 0x20, ++ 0x0434, 0x00, ++ 0x0435, 0x30, ++ 0x0436, 0x00, ++ 0x0450, 0xFD, ++ 0x0451, 0xD8, ++ 0x0452, 0xA0, ++ 0x0453, 0x50, ++ 0x0454, 0x00, ++ 0x0459, 0x04, ++ 0x045A, 0x00, ++ 0x045B, 0x30, ++ 0x045C, 0x01, ++ 0x045D, 0x70, ++ 0x0460, 0x00, ++ 0x0461, 0x00, ++ 0x0462, 0x00, ++ 0x0465, 0x16, ++ 0x0466, 0x14, ++ 0x0478, 0x00, ++ 0x0480, 0x60, ++ 0x0481, 0x06, ++ 0x0482, 0x0C, ++ 0x04B0, 0x4C, ++ 0x04B1, 0x86, ++ 0x04B2, 0x00, ++ 0x04B3, 0x18, ++ 0x04B4, 0x00, ++ 0x04B5, 0x00, ++ 0x04B6, 0x30, ++ 0x04B7, 0x00, ++ 0x04B8, 0x00, ++ 0x04B9, 0x10, ++ 0x04BA, 0x00, ++ 0x04BB, 0x00, ++ 0x04BD, 0x00, ++ 0x04D0, 0x56, ++ 0x04D6, 0x30, ++ 0x04DD, 0x10, ++ 0x04D9, 0x16, ++ 0x04D3, 0x18, ++ 0x0540, 0x00, ++ 0x0541, 0xD0, ++ 0x0542, 0x00, ++ 0x0543, 0xFA, ++ 0x0580, 0x50, ++ 0x0581, 0x30, ++ 0x0582, 0x2D, ++ 0x0583, 0x16, ++ 0x0584, 0x1E, ++ 0x0585, 0x0F, ++ 0x0586, 0x08, ++ 0x0587, 0x10, ++ 0x0590, 0x10, ++ 0x0591, 0x10, ++ 0x0592, 0x05, ++ 0x0593, 0x05, ++ 0x0594, 0x04, ++ 0x0595, 0x06, ++ 0x05B0, 0x04, ++ 0x05B1, 0x00, ++ 0x05E4, 0x08, ++ 0x05E5, 0x00, ++ 0x05E6, 0x07, ++ 0x05E7, 0x05, ++ 0x05E8, 0x0A, ++ 0x05E9, 0x00, ++ 0x05EA, 0xD9, ++ 0x05EB, 0x02, ++ 0x0666, 0x02, ++ 0x0667, 0xE0, ++ 0x067F, 0x19, ++ 0x067C, 0x00, ++ 0x067D, 0x00, ++ 0x0682, 0x00, ++ 0x0683, 0x00, ++ 0x0688, 0x00, ++ 0x0689, 0x00, ++ 0x068E, 0x00, ++ 0x068F, 0x00, ++ 0x0695, 0x00, ++ 0x0694, 0x00, ++ 0x0697, 0x19, ++ 0x069B, 0x00, ++ 0x069C, 0x30, ++ 0x0720, 0x00, ++ 0x0725, 0x6A, ++ 0x0726, 0x03, ++ 0x072B, 0x64, ++ 0x072C, 0x64, ++ 0x072D, 0x20, ++ 0x072E, 0x82, ++ 0x072F, 0x08, ++ 0x0800, 0x16, ++ 0x0801, 0x30, ++ 0x0802, 0x00, ++ 0x0803, 0x68, ++ 0x0804, 0x01, ++ 0x0805, 0x28, ++ 0x0806, 0x10, ++ 0x0808, 0x1D, ++ 0x0809, 0x18, ++ 0x080A, 0x10, ++ 0x080B, 0x07, ++ 0x080D, 0x0F, ++ 0x080E, 0x0F, ++ 0x0810, 0x00, ++ 0x0811, 0x08, ++ 0x0812, 0x20, ++ 0x0857, 0x0A, ++ 0x0858, 0x30, ++ 0x0859, 0x01, ++ 0x085A, 0x03, ++ 0x085B, 0x40, ++ 0x085C, 0x03, ++ 0x085D, 0x7F, ++ 0x085E, 0x02, ++ 0x085F, 0xD0, ++ 0x0860, 0x03, ++ 0x0861, 0x7F, ++ 0x0862, 0x02, ++ 0x0863, 0xD0, ++ 0x0864, 0x00, ++ 0x0865, 0x7F, ++ 0x0866, 0x01, ++ 0x0867, 0x00, ++ 0x0868, 0x40, ++ 0x0869, 0x01, ++ 0x086A, 0x00, ++ 0x086B, 0x40, ++ 0x086C, 0x01, ++ 0x086D, 0x00, ++ 0x086E, 0x40, ++ 0x0870, 0x00, ++ 0x0871, 0x14, ++ 0x0872, 0x01, ++ 0x0873, 0x20, ++ 0x0874, 0x00, ++ 0x0875, 0x14, ++ 0x0876, 0x00, ++ 0x0877, 0xEC, ++ 0x0815, 0x00, ++ 0x0816, 0x4C, ++ 0x0817, 0x00, ++ 0x0818, 0x7B, ++ 0x0819, 0x00, ++ 0x081A, 0xCA, ++ 0x081B, 0x01, ++ 0x081C, 0x3E, ++ 0x081D, 0x01, ++ 0x081E, 0x77, ++ 0x081F, 0x01, ++ 0x0820, 0xAA, ++ 0x0821, 0x01, ++ 0x0822, 0xCE, ++ 0x0823, 0x01, ++ 0x0824, 0xEE, ++ 0x0825, 0x02, ++ 0x0826, 0x16, ++ 0x0827, 0x02, ++ 0x0828, 0x33, ++ 0x0829, 0x02, ++ 0x082A, 0x65, ++ 0x082B, 0x02, ++ 0x082C, 0x91, ++ 0x082D, 0x02, ++ 0x082E, 0xDC, ++ 0x082F, 0x03, ++ 0x0830, 0x28, ++ 0x0831, 0x03, ++ 0x0832, 0x74, ++ 0x0833, 0x03, ++ 0x0834, 0xFF, ++ 0x0882, 0x00, ++ 0x0883, 0x3E, ++ 0x0884, 0x00, ++ 0x0885, 0x70, ++ 0x0886, 0x00, ++ 0x0887, 0xB8, ++ 0x0888, 0x01, ++ 0x0889, 0x28, ++ 0x088A, 0x01, ++ 0x088B, 0x5B, ++ 0x088C, 0x01, ++ 0x088D, 0x8A, ++ 0x088E, 0x01, ++ 0x088F, 0xB1, ++ 0x0890, 0x01, ++ 0x0891, 0xD9, ++ 0x0892, 0x01, ++ 0x0893, 0xEE, ++ 0x0894, 0x02, ++ 0x0895, 0x0F, ++ 0x0896, 0x02, ++ 0x0897, 0x4C, ++ 0x0898, 0x02, ++ 0x0899, 0x74, ++ 0x089A, 0x02, ++ 0x089B, 0xC3, ++ 0x089C, 0x03, ++ 0x089D, 0x0F, ++ 0x089E, 0x03, ++ 0x089F, 0x57, ++ 0x08A0, 0x03, ++ 0x08A1, 0xFF, ++ 0x0100, 0x01, ++ 0x0101, 0x01, ++ 0x0000, 0x01, ++ 0x002C, 0x00, ++ 0x0005, 0x01, ++ END_FLAG, END_FLAG ++}; ++ ++ ++/**************** Camera Exposure Table ****************/ ++static const T_U16 EXPOSURE_WHOLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EXPOSURE_CENTER_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EXPOSURE_MIDDLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Brightness Table ****************/ ++static const T_U16 BRIGHTNESS_0_TAB[] = ++{ ++ 0x04B0, 0x46, ++ 0x04B1, 0x84, ++ 0x04B2, 0x00, ++ 0x04B3, 0x18, ++ 0x04B4, 0x00, ++ 0x04B5, 0x00, ++ 0x04B6, 0x30, ++ 0x04B7, 0x00, ++ 0x04B8, 0x00, ++ 0x04B9, 0x10, ++ 0x04BA, 0x00, ++ 0x04BB, 0x00, ++ 0x04BD, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_1_TAB[] = ++{ ++ 0x04B0, 0x4C, ++ 0x04B1, 0x86, ++ 0x04B2, 0x00, ++ 0x04B3, 0x18, ++ 0x04B4, 0x00, ++ 0x04B5, 0x00, ++ 0x04B6, 0x30, ++ 0x04B7, 0x00, ++ 0x04B8, 0x00, ++ 0x04B9, 0x10, ++ 0x04BA, 0x00, ++ 0x04BB, 0x00, ++ 0x04BD, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_2_TAB[] = ++{ ++ 0x04B0, 0x5c, ++ 0x04B1, 0x86, ++ 0x04B2, 0x00, ++ 0x04B3, 0x18, ++ 0x04B4, 0x00, ++ 0x04B5, 0x00, ++ 0x04B6, 0x30, ++ 0x04B7, 0x00, ++ 0x04B8, 0x00, ++ 0x04B9, 0x10, ++ 0x04BA, 0x00, ++ 0x04BB, 0x00, ++ 0x04BD, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_3_TAB[] = ++{ ++ 0x04B0, 0x7C, ++ 0x04B1, 0x86, ++ 0x04B2, 0x00, ++ 0x04B3, 0x18, ++ 0x04B4, 0x00, ++ 0x04B5, 0x00, ++ 0x04B6, 0x30, ++ 0x04B7, 0x00, ++ 0x04B8, 0x00, ++ 0x04B9, 0x10, ++ 0x04BA, 0x00, ++ 0x04BB, 0x00, ++ 0x04BD, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_4_TAB[] = ++{ ++ 0x04B0, 0x9C, ++ 0x04B1, 0x86, ++ 0x04B2, 0x00, ++ 0x04B3, 0x18, ++ 0x04B4, 0x00, ++ 0x04B5, 0x00, ++ 0x04B6, 0x30, ++ 0x04B7, 0x00, ++ 0x04B8, 0x00, ++ 0x04B9, 0x10, ++ 0x04BA, 0x00, ++ 0x04BB, 0x00, ++ 0x04BD, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_5_TAB[] = ++{ ++ 0x04B0, 0xAC, ++ 0x04B1, 0x86, ++ 0x04B2, 0x00, ++ 0x04B3, 0x18, ++ 0x04B4, 0x00, ++ 0x04B5, 0x00, ++ 0x04B6, 0x30, ++ 0x04B7, 0x00, ++ 0x04B8, 0x00, ++ 0x04B9, 0x10, ++ 0x04BA, 0x00, ++ 0x04BB, 0x00, ++ 0x04BD, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_6_TAB[] = ++{ ++ 0x04B0, 0xCC, ++ 0x04B1, 0x86, ++ 0x04B2, 0x00, ++ 0x04B3, 0x18, ++ 0x04B4, 0x00, ++ 0x04B5, 0x00, ++ 0x04B6, 0x30, ++ 0x04B7, 0x00, ++ 0x04B8, 0x00, ++ 0x04B9, 0x10, ++ 0x04BA, 0x00, ++ 0x04BB, 0x00, ++ 0x04BD, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Contrast Table ****************/ ++static const T_U16 CONTRAST_1_TAB[] = ++{ ++ 0x04D0, 0x56, ++ 0x04D6, 0x30, ++ 0x04DD, 0x10, ++ 0x04D9, 0x16, ++ 0x04D3, 0x18, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_7_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Saturation Table ****************/ ++static const T_U16 SATURATION_0_TAB[] = ++{ ++ 0x0480, 0x30, ++ 0x0481, 0x06, ++ 0x0482, 0x0C, ++ 0x0483, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_1_TAB[] = ++{ ++ 0x0480, 0x40, ++ 0x0481, 0x06, ++ 0x0482, 0x0C, ++ 0x0483, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_2_TAB[] = ++{ ++ 0x0480, 0x50, ++ 0x0481, 0x06, ++ 0x0482, 0x0C, ++ 0x0483, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_3_TAB[] = ++{ ++ 0x0480, 0x60, ++ 0x0481, 0x06, ++ 0x0482, 0x0C, ++ 0x0483, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_4_TAB[] = ++{ ++ 0x0480, 0x70, ++ 0x0481, 0x06, ++ 0x0482, 0x0C, ++ 0x0483, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_5_TAB[] = ++{ ++ 0x0480, 0x80, ++ 0x0481, 0x06, ++ 0x0482, 0x0C, ++ 0x0483, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_6_TAB[] = ++{ ++ 0x0480, 0x90, ++ 0x0481, 0x06, ++ 0x0482, 0x0C, ++ 0x0483, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Sharpness Table ****************/ ++static const T_U16 SHARPNESS_0_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera AWB Table ****************/ ++static const T_U16 AWB_AUTO_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 AWB_SUNNY_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 AWB_CLOUDY_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 AWB_OFFICE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 AWB_HOME_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Effect Table ****************/ ++static const T_U16 EFFECT_NORMAL_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_SEPIA_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_ANTIQUE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_BLUISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_GREENISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_REDDISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_NEGATIVE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_BW_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera night/day mode ****************/ ++static const T_U16 DAY_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 NIGHT_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera anti-flicker mode ****************/ ++static const T_U16 ANTI_FLICKER_DISABLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 ANTI_FLICKER_50HZ_TAB[] = ++{ ++ 0x0120, 0x36, ++ 0x0542, 0x00, ++ 0x0543, 0xFA, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 ANTI_FLICKER_60HZ_TAB[] = ++{ ++ 0x0120, 0x37, ++ 0x0540, 0x00, ++ 0x0541, 0xD0, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 ANTI_FLICKER_AUTO_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++#endif ++#endif +diff --git a/drivers/media/video/plat-anyka/camera_ov2643.c b/drivers/media/video/plat-anyka/camera_ov2643.c +new file mode 100755 +index 00000000..4938db2f +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_ov2643.c +@@ -0,0 +1,994 @@ ++/** ++ * @file camera_ov2643.c ++ * @brief camera driver file ++ * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author xia_wenting ++ * @date 2011-09-21 ++ * @version 1.0 ++ * @ref ++ */ ++#ifdef CONFIG_LINUX_AKSENSOR ++#include ++#include ++#include ++#include "camera_ov2643.h" ++#else ++#include "akdefine.h" ++#include "cam_com_sensor.h" ++#include "camera_ov2643.h" ++#include "Gpio_config.h" ++#endif ++ ++#if defined (USE_CAMERA_OV2643) || defined (CONFIG_SENSOR_OV2643) ++ ++#define CAM_EN_LEVEL 0 ++#define CAM_RESET_LEVEL 0 ++ ++#define CAMERA_SCCB_ADDR 0x60 ++#define CAMERA_OV2643_ID 0x2643 ++ ++#define OV2643_CAMERA_MCLK 24 ++ ++static T_CAMERA_TYPE camera_ov2643_type = CAMERA_2M; ++static T_NIGHT_MODE night_mode = CAMERA_DAY_MODE; ++static T_CAMERA_MODE s_ov2643_CurMode = CAMERA_MODE_VGA; ++ ++static T_VOID camera_setbit(T_U8 reg, T_U8 bit, T_U8 value) ++{ ++ T_U8 tmp; ++ ++ tmp = sccb_read_data(CAMERA_SCCB_ADDR, reg); ++ if (value == 1) ++ { ++ tmp |= 0x1 << bit; ++ } ++ else ++ { ++ tmp &= ~(0x1 << bit); ++ } ++ ++ sccb_write_data(CAMERA_SCCB_ADDR, reg, &tmp, 1); ++} ++ ++static T_BOOL camera_set_param(const T_U8 tabParameter[]) ++{ ++ int i = 0; ++ T_U8 temp_value; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ sccb_write_data(CAMERA_SCCB_ADDR, tabParameter[i], (T_U8 *)(&tabParameter[i + 1]), 1); ++ ++ if (!((tabParameter[i] == 0x12) && (tabParameter[i + 1] & 0x80))) ++ { ++ temp_value = sccb_read_data(CAMERA_SCCB_ADDR, tabParameter[i]); ++ if (temp_value != tabParameter[i + 1]) ++ { ++ akprintf(C1, M_DRVSYS, "set parameter error!\n"); ++ akprintf(C1, M_DRVSYS, "reg 0x%x write data is 0x%x, read data is 0x%x!\n", tabParameter[i], tabParameter[i + 1], temp_value); ++ ++ return AK_FALSE; ++ } ++ } ++ } ++ ++ i += 2; ++ } ++ ++ return AK_TRUE; ++} ++ ++static T_VOID camera_setup(const T_U8 tabParameter[]) ++{ ++ int i = 0; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ sccb_write_data(CAMERA_SCCB_ADDR, tabParameter[i], (T_U8 *)&tabParameter[i + 1], 1); ++ } ++ ++ i += 2; ++ } ++} ++ ++static T_VOID cam_ov2643_open(T_VOID) ++{ ++ gpio_set_pin_dir(GPIO_CAMERA_AVDD, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_CHIP_ENABLE); ++ gpio_set_pin_dir(GPIO_CAMERA_CHIP_ENABLE, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, CAM_EN_LEVEL); ++ mini_delay(10); ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_RESET); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, CAM_RESET_LEVEL); ++ mini_delay(10); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, !CAM_RESET_LEVEL); ++ ++ mini_delay(20); ++} ++ ++static T_BOOL cam_ov2643_close(T_VOID) ++{ ++ //sccb software standby mode ++ T_U8 Reg0x3d = 0x48; ++ T_U8 Reg0xc3 = 0x00; ++ ++ sccb_write_data(CAMERA_SCCB_ADDR, 0x3d, &Reg0x3d, 1); ++ sccb_write_data(CAMERA_SCCB_ADDR, 0xc3, &Reg0xc3, 1); ++ ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, !CAM_EN_LEVEL); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, !gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_INPUT); ++ ++ gpio_set_pin_dir(GPIO_I2C_SCL, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SCL, GPIO_LEVEL_LOW); ++ gpio_set_pin_dir(GPIO_I2C_SDA, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SDA, GPIO_LEVEL_LOW); ++ ++ return AK_TRUE; ++} ++ ++static T_U32 cam_ov2643_read_id(T_VOID) ++{ ++ T_U8 value = 0x00; ++ T_U32 id = 0; ++ ++ sccb_init(GPIO_I2C_SCL, GPIO_I2C_SDA); //init sccb first here!! ++ ++ value = sccb_read_data(CAMERA_SCCB_ADDR, 0x0a); ++ id = value << 8; ++ value = sccb_read_data(CAMERA_SCCB_ADDR, 0x0b); ++ id |= value; ++ ++ return id; ++} ++ ++/** ++ * @brief initialize the parameters of camera, should be done after reset and open camera to initialize ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @return T_BOOL ++ * @retval AK_TRUE if success, else AK_FALSE ++ */ ++static T_BOOL cam_ov2643_init(void) ++{ ++ if (!camera_set_param(INIT_TAB)) ++ { ++ return AK_FALSE; ++ } ++ else ++ { ++ night_mode = CAMERA_DAY_MODE; ++ return AK_TRUE; ++ } ++} ++ ++/** ++ * @brief Set camera mode to specify image quality, SXGA/VGA/CIF etc ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] mode mode value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2643_set_mode(T_CAMERA_MODE mode) ++{ ++ s_ov2643_CurMode = mode; ++ switch(mode) ++ { ++ case CAMERA_MODE_UXGA: ++ camera_setup(UXGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_SXGA: ++ camera_setup(SXGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_VGA: ++ camera_setup(VGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_CIF: ++ camera_setup(CIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QVGA: ++ camera_setup(QVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_QCIF: ++ camera_setup(QCIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QQVGA: ++ camera_setup(QQVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_PREV: ++ camera_setup(PREV_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_REC: ++ camera_setup(RECORD_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_720P: ++ camera_setup(RECORD_720P_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ default: ++ s_ov2643_CurMode = CAMERA_MODE_VGA; ++ akprintf(C1, M_DRVSYS, "set camera mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera exposure mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] exposure exposure mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2643_set_exposure(T_CAMERA_EXPOSURE exposure) ++{ ++ switch(exposure) ++ { ++ case EXPOSURE_WHOLE: ++ camera_setup(EXPOSURE_WHOLE_TAB); ++ break; ++ case EXPOSURE_CENTER: ++ camera_setup(EXPOSURE_CENTER_TAB); ++ break; ++ case EXPOSURE_MIDDLE: ++ camera_setup(EXPOSURE_MIDDLE_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set exposure parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera brightness level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] brightness brightness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2643_set_brightness(T_CAMERA_BRIGHTNESS brightness) ++{ ++ switch(brightness) ++ { ++ case CAMERA_BRIGHTNESS_0: ++ camera_setup(BRIGHTNESS_0_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_1: ++ camera_setup(BRIGHTNESS_1_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_2: ++ camera_setup(BRIGHTNESS_2_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_3: ++ camera_setup(BRIGHTNESS_3_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_4: ++ camera_setup(BRIGHTNESS_4_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_5: ++ camera_setup(BRIGHTNESS_5_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_6: ++ camera_setup(BRIGHTNESS_6_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set brightness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera contrast level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] contrast contrast value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2643_set_contrast(T_CAMERA_CONTRAST contrast) ++{ ++ switch(contrast) ++ { ++ case CAMERA_CONTRAST_1: ++ camera_setup(CONTRAST_1_TAB); ++ break; ++ case CAMERA_CONTRAST_2: ++ camera_setup(CONTRAST_2_TAB); ++ break; ++ case CAMERA_CONTRAST_3: ++ camera_setup(CONTRAST_3_TAB); ++ break; ++ case CAMERA_CONTRAST_4: ++ camera_setup(CONTRAST_4_TAB); ++ break; ++ case CAMERA_CONTRAST_5: ++ camera_setup(CONTRAST_5_TAB); ++ break; ++ case CAMERA_CONTRAST_6: ++ camera_setup(CONTRAST_6_TAB); ++ break; ++ case CAMERA_CONTRAST_7: ++ camera_setup(CONTRAST_7_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set contrast parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera saturation level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] saturation saturation value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2643_set_saturation(T_CAMERA_SATURATION saturation) ++{ ++ switch(saturation) ++ { ++ case CAMERA_SATURATION_1: ++ camera_setup(SATURATION_1_TAB); ++ break; ++ case CAMERA_SATURATION_2: ++ camera_setup(SATURATION_2_TAB); ++ break; ++ case CAMERA_SATURATION_3: ++ camera_setup(SATURATION_3_TAB); ++ break; ++ case CAMERA_SATURATION_4: ++ camera_setup(SATURATION_4_TAB); ++ break; ++ case CAMERA_SATURATION_5: ++ camera_setup(SATURATION_5_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set saturation parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera sharpness level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] sharpness sharpness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2643_set_sharpness(T_CAMERA_SHARPNESS sharpness) ++{ ++ switch(sharpness) ++ { ++ case CAMERA_SHARPNESS_0: ++ camera_setup(SHARPNESS_0_TAB); ++ break; ++ case CAMERA_SHARPNESS_1: ++ camera_setup(SHARPNESS_1_TAB); ++ break; ++ case CAMERA_SHARPNESS_2: ++ camera_setup(SHARPNESS_2_TAB); ++ break; ++ case CAMERA_SHARPNESS_3: ++ camera_setup(SHARPNESS_3_TAB); ++ break; ++ case CAMERA_SHARPNESS_4: ++ camera_setup(SHARPNESS_4_TAB); ++ break; ++ case CAMERA_SHARPNESS_5: ++ camera_setup(SHARPNESS_5_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set sharpness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera AWB mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] awb AWB mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2643_set_AWB(T_CAMERA_AWB awb) ++{ ++ switch(awb) ++ { ++ case AWB_AUTO: ++ camera_setup(AWB_AUTO_TAB); ++ break; ++ case AWB_SUNNY: ++ camera_setup(AWB_SUNNY_TAB); ++ break; ++ case AWB_CLOUDY: ++ camera_setup(AWB_CLOUDY_TAB); ++ break; ++ case AWB_OFFICE: ++ camera_setup(AWB_OFFICE_TAB); ++ break; ++ case AWB_HOME: ++ camera_setup(AWB_HOME_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set AWB mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera mirror mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] mirror mirror mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2643_set_mirror(T_CAMERA_MIRROR mirror) ++{ ++ switch(mirror) ++ { ++ case CAMERA_MIRROR_V: ++ camera_setbit(0x12, 4, 1); ++ camera_setbit(0x12, 5, 0); ++ break; ++ case CAMERA_MIRROR_H: ++ camera_setbit(0x12, 4, 0); ++ camera_setbit(0x12, 5, 1); ++ break; ++ case CAMERA_MIRROR_NORMAL: ++ camera_setbit(0x12, 4, 0); ++ camera_setbit(0x12, 5, 0); ++ break; ++ case CAMERA_MIRROR_FLIP: ++ camera_setbit(0x12, 4, 1); ++ camera_setbit(0x12, 5, 1); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set mirror parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera effect mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] effect effect mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2643_set_effect(T_CAMERA_EFFECT effect) ++{ ++ switch(effect) ++ { ++ case CAMERA_EFFECT_NORMAL: ++ camera_setup(EFFECT_NORMAL_TAB); ++ break; ++ case CAMERA_EFFECT_SEPIA: ++ camera_setup(EFFECT_SEPIA_TAB); ++ break; ++ case CAMERA_EFFECT_ANTIQUE: ++ camera_setup(EFFECT_ANTIQUE_TAB); ++ break; ++ case CAMERA_EFFECT_BLUE: ++ camera_setup(EFFECT_BLUISH_TAB); ++ break; ++ case CAMERA_EFFECT_GREEN: ++ camera_setup(EFFECT_GREENISH_TAB); ++ break; ++ case CAMERA_EFFECT_RED: ++ camera_setup(EFFECT_REDDISH_TAB); ++ break; ++ case CAMERA_EFFECT_NEGATIVE: ++ camera_setup(EFFECT_NEGATIVE_TAB); ++ break; ++ case CAMERA_EFFECT_BW: ++ camera_setup(EFFECT_BW_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set camer effect parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief set camera window ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] srcWidth window width ++ * @param[in] srcHeight window height ++ * @return T_S32 ++ * @retval 0 if error mode ++ * @retval 1 if success ++ * @retval -1 if failed ++ */ ++static T_S32 cam_ov2643_set_digital_zoom(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ return 1; ++} ++ ++static T_VOID cam_ov2643_set_night_mode(T_NIGHT_MODE mode) ++{ ++ switch(mode) ++ { ++ case CAMERA_DAY_MODE: ++ camera_setup(DAY_MODE_TAB); ++ night_mode = CAMERA_DAY_MODE; ++ break; ++ case CAMERA_NIGHT_MODE: ++ camera_setup(NIGHT_MODE_TAB); ++ night_mode = CAMERA_NIGHT_MODE; ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set night mode parameter error!\n"); ++ break; ++ } ++} ++ ++static T_BOOL cam_ov2643_set_to_cap(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ ++ if ((srcWidth <= 160) && (srcHeight <= 120)) ++ { ++ Cammode = CAMERA_MODE_QQVGA; ++ } ++ else if ((srcWidth <= 176) && (srcHeight <= 144)) ++ { ++ Cammode = CAMERA_MODE_QCIF; ++ } ++ else if ((srcWidth <= 320) && (srcHeight <= 240)) ++ { ++ Cammode = CAMERA_MODE_QVGA; ++ } ++ else if ((srcWidth <= 352) && (srcHeight <= 288)) ++ { ++ Cammode = CAMERA_MODE_CIF; ++ } ++ else if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_VGA; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 720)) ++ { ++ Cammode = CAMERA_MODE_720P; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 1024)) ++ { ++ Cammode = CAMERA_MODE_SXGA; ++ } ++ else if ((srcWidth <= 1600) && (srcHeight <= 1200)) ++ { ++ Cammode = CAMERA_MODE_UXGA; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "ov2643 unsupport %d & %d mode!\n", srcWidth, srcHeight); ++ return AK_FALSE; ++ } ++ ++ cam_ov2643_set_mode(Cammode); ++ cam_ov2643_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_ov2643_set_to_prev(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ cam_ov2643_set_mode(CAMERA_MODE_PREV); ++ cam_ov2643_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_ov2643_set_to_record(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_REC; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 720)) ++ { ++ Cammode = CAMERA_MODE_720P; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "200W camera dose not support such mode"); ++ return AK_FALSE; ++ } ++ ++ cam_ov2643_set_mode(Cammode); ++ cam_ov2643_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_CAMERA_TYPE cam_ov2643_get_type(T_VOID) ++{ ++ return camera_ov2643_type; ++} ++ ++static T_VOID cam_ov2643_set_sensor_param(T_U32 cmd, T_U32 data) ++{ ++ T_U8 value; ++ ++ value = (T_U8)data; ++ sccb_write_data(CAMERA_SCCB_ADDR, (T_U8)cmd, &value, 1); ++} ++ ++static T_CAMERA_FUNCTION_HANDLER ov2643_function_handler = ++{ ++ OV2643_CAMERA_MCLK, ++ cam_ov2643_open, ++ cam_ov2643_close, ++ cam_ov2643_read_id, ++ cam_ov2643_init, ++ cam_ov2643_set_mode, ++ cam_ov2643_set_exposure, ++ cam_ov2643_set_brightness, ++ cam_ov2643_set_contrast, ++ cam_ov2643_set_saturation, ++ cam_ov2643_set_sharpness, ++ cam_ov2643_set_AWB, ++ cam_ov2643_set_mirror, ++ cam_ov2643_set_effect, ++ cam_ov2643_set_digital_zoom, ++ cam_ov2643_set_night_mode, ++ AK_NULL, ++ AK_NULL, ++ cam_ov2643_set_to_cap, ++ cam_ov2643_set_to_prev, ++ cam_ov2643_set_to_record, ++ cam_ov2643_get_type, ++ cam_ov2643_set_sensor_param ++}; ++ ++#ifndef CONFIG_LINUX_AKSENSOR ++static int camera_ov2643_reg(void) ++{ ++ camera_reg_dev(CAMERA_OV2643_ID, &ov2643_function_handler); ++ return 0; ++} ++ ++#ifdef __CC_ARM ++#pragma arm section rwdata = "__initcall_", zidata = "__initcall_" ++#endif ++module_init(camera_ov2643_reg) ++#ifdef __CC_ARM ++#pragma arm section ++#endif ++ ++#else ++static const char * awb_menu[] = { ++ [AWB_AUTO] = "auto", ++ [AWB_SUNNY] = "sunny", ++ [AWB_CLOUDY] = "cloudy", ++ [AWB_OFFICE] = "office", ++ [AWB_HOME] = "home", ++ [AWB_NIGHT] = "night", ++}; ++ ++static const char * effect_menu[] = { ++ [CAMERA_EFFECT_NORMAL] = "normal", ++ [CAMERA_EFFECT_SEPIA] = "sepia", ++ [CAMERA_EFFECT_ANTIQUE] = "antique", ++ [CAMERA_EFFECT_BLUE] = "blue", ++ [CAMERA_EFFECT_GREEN] = "green", ++ [CAMERA_EFFECT_RED] = "red", ++ [CAMERA_EFFECT_NEGATIVE] = "negative", ++ [CAMERA_EFFECT_BW] = "bw", ++ [CAMERA_EFFECT_BWN] = "bwn", ++ [CAMERA_EFFECT_AQUA] = "aqua", ++ [CAMERA_EFFECT_COOL] = "cool", ++ [CAMERA_EFFECT_WARM] = "warm", ++}; ++ ++static const char * resolution_menu[] = { ++ [0] = "1600x1200", ++ [1] = "1280x720", ++ [2] = "800x600", ++ [3] = "640x480", ++}; ++ ++static const char * hflip_menu[] = { ++ [0] = "normal", ++ [1] = "horizontal flip", ++}; ++ ++static const char * vflip_menu[] = { ++ [0] = "normal", ++ [1] = "vertical flip", ++}; ++ ++static const char * night_menu[] = { ++ [CAMERA_DAY_MODE] = "daylight", ++ [CAMERA_NIGHT_MODE] = "night", ++}; ++ ++static int ov2643_s_ctl(struct v4l2_ctrl *ctrl) ++{ ++ int ret = -EINVAL; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ if (ov2643_function_handler.cam_set_AWB_func) { ++ ov2643_function_handler.cam_set_AWB_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_COLORFX: ++ if (ov2643_function_handler.cam_set_effect_func) { ++ ov2643_function_handler.cam_set_effect_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ ++ case V4L2_CID_BRIGHTNESS: ++ if (ov2643_function_handler.cam_set_brightness_func) { ++ ov2643_function_handler.cam_set_brightness_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_CONTRAST: ++ if (ov2643_function_handler.cam_set_contrast_func) { ++ ov2643_function_handler.cam_set_contrast_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SATURATION: ++ if (ov2643_function_handler.cam_set_saturation_func) { ++ ov2643_function_handler.cam_set_saturation_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SHARPNESS: ++ if (ov2643_function_handler.cam_set_sharpness_func) { ++ ov2643_function_handler.cam_set_sharpness_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_HFLIP: ++ if (ov2643_function_handler.cam_set_mirror_func) { ++ ov2643_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_H : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_VFLIP: ++ if (ov2643_function_handler.cam_set_mirror_func) { ++ ov2643_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_V : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_NIGHTMODE: ++ if (ov2643_function_handler.cam_set_night_mode_func) { ++ ov2643_function_handler.cam_set_night_mode_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++}; ++ ++static struct v4l2_ctrl_ops ov2643_ctrl_ops = { ++ .s_ctrl = ov2643_s_ctl, ++}; ++ ++static const struct v4l2_ctrl_config ov2643_ctrls[] = { ++ { ++ .ops = &ov2643_ctrl_ops, ++ .id = V4L2_CID_AUTO_WHITE_BALANCE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "AWB", ++ .min = 0, ++ .max = ARRAY_SIZE(awb_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = awb_menu, ++ }, ++ { ++ .ops = &ov2643_ctrl_ops, ++ .id = V4L2_CID_COLORFX, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Effect", ++ .min = 0, ++ .max = ARRAY_SIZE(effect_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = effect_menu, ++ }, ++ { ++ .ops = &ov2643_ctrl_ops, ++ .id = V4L2_CID_HFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Horizontal Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(hflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = hflip_menu, ++ }, ++ { ++ .ops = &ov2643_ctrl_ops, ++ .id = V4L2_CID_VFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Vetical Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(vflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = vflip_menu, ++ }, ++ { ++ .ops = &ov2643_ctrl_ops, ++ .id = V4L2_CID_PICTURE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Resolution", ++ .min = 0, ++ .max = ARRAY_SIZE(resolution_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = resolution_menu, ++ }, ++ { ++ .ops = &ov2643_ctrl_ops, ++ .id = V4L2_CID_NIGHTMODE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Night mode", ++ .min = 0, ++ .max = ARRAY_SIZE(night_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = night_menu, ++ }, ++ { ++ .ops = &ov2643_ctrl_ops, ++ .id = V4L2_CID_BRIGHTNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Brightness", ++ .min = 0, ++ .max = CAMERA_BRIGHTNESS_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ov2643_ctrl_ops, ++ .id = V4L2_CID_CONTRAST, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Contrast", ++ .min = 0, ++ .max = CAMERA_CONTRAST_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ov2643_ctrl_ops, ++ .id = V4L2_CID_SATURATION, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Saturation", ++ .min = 0, ++ .max = CAMERA_SATURATION_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ov2643_ctrl_ops, ++ .id = V4L2_CID_SHARPNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Sharpness", ++ .min = 0, ++ .max = CAMERA_SHARPNESS_NUM -1, ++ .step = 1, ++ .def = 0, ++ } ++}; ++ ++ ++/* ++ * supported format list ++ */ ++static const struct aksensor_color_format ov2643_formats[] = { ++ { ++ .code = V4L2_MBUS_FMT_YUYV8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_YVYU8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_UYVY8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++}; ++ ++static const struct aksensor_win_size ov2643_win[] = { ++ {.name = "VGA", .width = 640, .height = 480}, ++ {.name = "SVGA", .width = 800, .height = 600}, ++ {.name = "720P", .width = 1280, .height = 720}, ++ {.name = "UXGA", .width = 1600, .height = 1200}, ++}; ++ ++ ++static struct sensor_info ov2643_sensor_info = { ++ .sensor_name = "ov2643", ++ .sensor_id = CAMERA_OV2643_ID, ++ .ctrls = ov2643_ctrls, ++ .nr_ctrls = ARRAY_SIZE(ov2643_ctrls), ++ .formats = ov2643_formats, ++ .num_formats = ARRAY_SIZE(ov2643_formats), ++ .resolution = ov2643_win, ++ .num_resolution = ARRAY_SIZE(ov2643_win), ++ .handler = &ov2643_function_handler, ++}; ++ ++static int ov2643_module_init(void) ++{ ++ return register_sensor(&ov2643_sensor_info); ++} ++module_init(ov2643_module_init) ++#endif ++ ++#endif ++ ++ +diff --git a/drivers/media/video/plat-anyka/camera_ov2643.h b/drivers/media/video/plat-anyka/camera_ov2643.h +new file mode 100755 +index 00000000..4f1f96a8 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_ov2643.h +@@ -0,0 +1,1216 @@ ++/** ++ * @file camera_ov2643.h ++ * @brief camera driver file ++ * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author xia_wenting ++ * @date 2011-10-26 ++ * @version 1.0 ++ * @ref ++ */ ++#ifndef __CAMERA_OV2643_H__ ++#define __CAMERA_OV2643_H__ ++ ++ ++#if defined (USE_CAMERA_OV2643) || defined (CONFIG_SENSOR_OV2643) ++ ++#undef DELAY_FLAG ++#undef END_FLAG ++#define DELAY_FLAG 0xfd // first parameter is 0xfe, then 2nd parameter is delay time count ++#define END_FLAG 0xfe // first parameter is 0xff, then parameter table is over ++ ++static const T_U8 INIT_TAB[] = ++{ ++ //640*480, mclk 24mhz, pclk 72mhz, 30fps ++ 0x12, 0x80, ++ 0xc3, 0x1f, ++ 0xc4, 0xff, ++ 0x3d, 0x48, ++ 0xdd, 0xa5, ++ //windows setup ++ 0x20, 0x01, ++ 0x21, 0x98, ++ 0x22, 0x00, ++ 0x23, 0x06, ++ 0x24, 0x28,//0x280=640 ++ 0x25, 0x04, ++ 0x26, 0x1e,//0x1e0=480 ++ 0x27, 0x04, ++ 0x28, 0x40, ++ ++ //format setting ++ 0x12, 0x09, ++ 0x39, 0xd0, ++ 0xcd, 0x13, ++ //frame setting ++ 0x0e, 0x10, ++ 0x0f, 0x14, ++ 0x10, 0x0a, ++ 0x11, 0x00, ++ 0x29, 0x07,//dummy pixels ++ 0x2a, 0x93, ++ 0x2b, 0x02,//dummy lines ++ 0x2c, 0x6a, ++ 0x1d, 0x03, ++ 0x1e, 0x00, ++ 0x1f, 0xb9, ++ ++ 0x13, 0xff, ++ 0x14, 0xa7, ++ 0x15, 0x42, ++ 0x3c, 0xa4, ++ 0x18, 0x60, ++ 0x19, 0x50, ++ 0x1a, 0xe2, ++ 0x37, 0xe8, ++ 0x16, 0x90, ++ 0x43, 0x00, ++ 0x40, 0xfb, ++ 0xa9, 0x44, ++ 0x2f, 0xec, ++ 0x35, 0x10, ++ 0x36, 0x10, ++ 0x0c, 0x00, ++ 0x0d, 0x20, ++ 0xd0, 0x93, ++ 0xdc, 0x2b, ++ 0xd9, 0x41, ++ 0xd3, 0x02, ++ 0xde, 0x7c, ++ 0x3d, 0x08, ++ 0x0c, 0x00, ++ 0x18, 0x2c, ++ 0x19, 0x24, ++ 0x1a, 0x71, ++ 0x9b, 0x69, ++ 0x9c, 0x7d, ++ 0x9d, 0x7d, ++ 0x9e, 0x69, ++ 0x35, 0x04, ++ 0x36, 0x04, ++ //gamma ++ 0x65, 0x04, ++ 0x66, 0x07, ++ 0x67, 0x19, ++ 0x68, 0x34, ++ 0x69, 0x4a, ++ 0x6a, 0x5a, ++ 0x6b, 0x67, ++ 0x6c, 0x71, ++ 0x6d, 0x7c, ++ 0x6e, 0x8c, ++ 0x6f, 0x9b, ++ 0x70, 0xa9, ++ 0x71, 0xc0, ++ 0x72, 0xd5, ++ 0x73, 0xe8, ++ 0x74, 0x20, ++ //color matrix ++ 0xab, 0x28, ++ 0xac, 0x48, ++ 0xad, 0x10, ++ 0xae, 0x12, ++ 0xaf, 0x76, ++ 0xb0, 0x88, ++ 0xb1, 0x80, ++ 0xb2, 0x88, ++ 0xb3, 0x08, ++ 0xb4, 0x98, ++ 0xb5, 0x00, ++ //lens shading ++ 0x40, 0xfb, ++ 0x4c, 0x03, ++ 0x4d, 0x40, ++ 0x4e, 0x02, ++ 0x4f, 0x63, ++ 0x50, 0x44, ++ 0x51, 0x20, ++ 0x52, 0x66, ++ 0x53, 0x03, ++ 0x54, 0x34, ++ 0x55, 0x02, ++ 0x56, 0x5c, ++ 0x57, 0x38, ++ 0x58, 0x00, ++ 0x59, 0x66, ++ 0x5a, 0x03, ++ 0x5b, 0x20, ++ 0x5c, 0x02, ++ 0x5d, 0x5c, ++ 0x5e, 0x36, ++ 0x5f, 0x00, ++ 0x60, 0x66, ++ 0x41, 0x1f, ++ 0xb6, 0x02, ++ 0xb9, 0x40, ++ 0xba, 0x28, ++ 0xbf, 0x0c, ++ 0xc0, 0x3e, ++ 0xa3, 0x0a, ++ 0xa4, 0x0f, ++ 0xa5, 0x10, ++ 0xa6, 0x16, ++ 0x9f, 0x0a, ++ 0xa0, 0x0f, ++ 0xa7, 0x0a, ++ 0xa8, 0x0f, ++ 0xa1, 0x18, ++ 0xa2, 0x10, ++ 0xa9, 0x00, ++ 0xaa, 0xa6, ++ //awb ++ 0x75, 0x68, ++ 0x76, 0x11, ++ 0x77, 0x92, ++ 0x78, 0x21, ++ 0x79, 0xe1, ++ 0x7a, 0x02, ++ 0x7c, 0x0e, ++ 0x7d, 0x12, ++ 0x7e, 0x12, ++ 0x7f, 0x54, ++ 0x80, 0x78, ++ 0x81, 0xa2, ++ 0x82, 0x80, ++ 0x83, 0x4e, ++ 0x84, 0x40, ++ 0x85, 0x4c, ++ 0x86, 0x53, ++ 0x87, 0xf8, ++ 0x88, 0x08, ++ 0x89, 0x70, ++ 0x8a, 0xf0, ++ 0x8b, 0xf0, ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 UXGA_MODE_TAB[] = ++{ ++#if 1 ++ //1600*1200, mclk 24mhz, pclk 72mhz, 15fps, with no lcd ++ 0x3d, 0x48, ++ //windows setup ++ 0x20, 0x01, ++ 0x21, 0x25, ++ 0x22, 0x00, ++ 0x23, 0x0c, ++ 0x24, 0x64, //0x640=1600 ++ 0x25, 0x08, ++ 0x26, 0x4b, //0x4b0=1200 ++ 0x27, 0x06, ++ 0x28, 0x42, ++ //format setting ++ 0x12, 0x08, ++ 0x39, 0x10, ++ 0xcd, 0x12, ++ ++ 0x3d, 0x08, ++ //frame setting ++ 0x0e, 0x10, ++ 0x0f, 0x14, ++ 0x10, 0x0a, ++ 0x11, 0x00, ++ 0x29, 0x07, //dummy pixels ++ 0x2a, 0x93, ++ 0x2b, 0x04, //dummy lines ++ 0x2c, 0xd4, ++ 0x1d, 0x06, //banding ++ 0x1e, 0x00, ++ 0x1f, 0xb9, ++ //other setting ++ 0xde, 0xc4, ++#else ++ //1600*1200, mclk 24mhz, pclk 36mhz, 7.5fps ++ 0x3d, 0x48, ++ //windows setup ++ 0x20, 0x01, ++ 0x21, 0x25, ++ 0x22, 0x00, ++ 0x23, 0x0c, ++ 0x24, 0x64, ++ 0x25, 0x08, ++ 0x26, 0x4b, ++ 0x27, 0x06, ++ 0x28, 0x42, ++ //format setting ++ 0x12, 0x08, ++ 0x39, 0x10, ++ 0xcd, 0x12, ++ ++ 0x3d, 0x08, ++ //frame setting ++ 0x0e, 0x10, ++ 0x0f, 0x14, ++ 0x10, 0x0a, ++ 0x11, 0x01, ++ 0x29, 0x07, //dummy pixels ++ 0x2a, 0x93, ++ 0x2b, 0x04, //dummy lines ++ 0x2c, 0xd4, ++ 0x1d, 0x0c, //banding ++ 0x1e, 0x00, ++ 0x1f, 0x5d, ++ //other setting ++ 0xde, 0xc4, ++#endif ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SXGA_MODE_TAB[] = ++{ ++ //1280*1024, mclk 24mhz, pclk 36mhz, 15fps ++ 0x3d, 0x48, ++ //windows setup ++ 0x20, 0x01, ++ 0x21, 0x25, ++ 0x22, 0x00, ++ 0x23, 0x0c, ++ 0x24, 0x50, //0x500=1280 ++ 0x25, 0x08, ++ 0x26, 0x40, //0x400=1024 ++ 0x27, 0x06, ++ 0x28, 0x42, ++ //format setting ++ 0x12, 0x08, ++ 0x39, 0x10, ++ 0xcd, 0x12, ++ ++ 0x3d, 0x08, ++ //frame setting ++ 0x0e, 0x10, ++ 0x0f, 0x14, ++ 0x10, 0x0a, ++ 0x11, 0x00, ++ 0x29, 0x07, //dummy pixels ++ 0x2a, 0x93, ++ 0x2b, 0x04, //dummy lines ++ 0x2c, 0xd4, ++ 0x1d, 0x06, //banding ++ 0x1e, 0x00, ++ 0x1f, 0xb9, ++ ++ //other setting ++ 0xde, 0xc4, ++ END_FLAG, END_FLAG ++}; ++ ++ ++ ++static const T_U8 VGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CIF_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 QVGA_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 QCIF_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 QQVGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 PREV_MODE_TAB[] = ++{ ++ //640*480, mclk 24mhz, pclk 72mhz, 30fps ++ 0x3d, 0x48, ++ //windows setup ++ 0x20, 0x01, ++ 0x21, 0x98, ++ 0x22, 0x00, ++ 0x23, 0x06, ++ 0x24, 0x28,//0x280=640 ++ 0x25, 0x04, ++ 0x26, 0x1e,//0x1e0=480 ++ 0x27, 0x04, ++ 0x28, 0x40, ++ //format setting ++ 0x12, 0x09, ++ 0x39, 0xd0, ++ 0xcd, 0x13, ++ 0x3d, 0x08, ++ //frame setting ++ 0x0e, 0x10, ++ 0x0f, 0x14, ++ 0x10, 0x0a, ++ 0x11, 0x00, ++ 0x29, 0x07,//dummy pixels ++ 0x2a, 0x93, ++ 0x2b, 0x02,//dummy lines ++ 0x2c, 0x6a, ++ 0x1d, 0x03, ++ 0x1e, 0x00, ++ 0x1f, 0xb9, ++ //other setting ++ 0xde, 0x7c, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 RECORD_MODE_TAB[] = ++{ ++ //640*480, mclk 24mhz, pclk 72mhz, 25fps ++ 0x12, 0x80, ++ 0xc3, 0x5F, ++ 0xc4, 0xff, ++ 0x3d, 0x48, ++ 0xdd, 0xa5, ++ //windows setup ++ 0x20, 0x01, ++ 0x21, 0x98, ++ 0x22, 0x00, ++ 0x23, 0x06, ++ 0x24, 0x28,//0x280=640 ++ 0x25, 0x04, ++ 0x26, 0x1e,//0x1e0=480 ++ 0x27, 0x04, ++ 0x28, 0x40, ++ //format setting ++ 0x12, 0x09, ++ 0x39, 0xd0, ++ 0xcd, 0x13, ++ //frame setting ++ 0x0e, 0x10, ++ 0x0f, 0x14, ++ 0x10, 0x0a, ++ 0x11, 0x00, ++ 0x29, 0x07,//dummy pixels ++ 0x2a, 0x93, ++ 0x2b, 0x02,//dummy lines ++ 0x2c, 0xe6, ++ 0x1d, 0x04, ++ 0x1e, 0x00, ++ 0x1f, 0xb9, ++ ++ 0x13, 0xff, ++ 0x14, 0xa7, ++ 0x15, 0x42, ++ 0x3c, 0xa4, ++ 0x18, 0x60, ++ 0x19, 0x50, ++ 0x1a, 0xe2, ++ 0x37, 0xe8, ++ 0x16, 0x90, ++ 0x43, 0x00, ++ 0x40, 0xfb, ++ 0xa9, 0x44, ++ 0x2f, 0xec, ++ 0x35, 0x10, ++ 0x36, 0x10, ++ 0x0c, 0x00, ++ 0x0d, 0x20, ++ 0xd0, 0x93, ++ 0xdc, 0x2b, ++ 0xd9, 0x41, ++ 0xd3, 0x02, ++ 0xde, 0x7c, ++ 0x3d, 0x08, ++ 0x0c, 0x00, ++ 0x18, 0x34, //3C, //2c, ++ 0x19, 0x2e, //34, //24, ++ 0x1a, 0x71, //82, //71, ++ 0x9b, 0x69, ++ 0x9c, 0x7d, ++ 0x9d, 0x7d, ++ 0x9e, 0x69, ++ 0x35, 0x04, ++ 0x36, 0x04, ++ //gamma ++ 0x65, 0x04, ++ 0x66, 0x07, ++ 0x67, 0x19, ++ 0x68, 0x34, ++ 0x69, 0x4a, ++ 0x6a, 0x5a, ++ 0x6b, 0x67, ++ 0x6c, 0x71, ++ 0x6d, 0x7c, ++ 0x6e, 0x8c, ++ 0x6f, 0x9b, ++ 0x70, 0xa9, ++ 0x71, 0xc0, ++ 0x72, 0xd5, ++ 0x73, 0xe8, ++ 0x74, 0x20, ++ //color matrix ++ 0xab, 0x28, ++ 0xac, 0x48, ++ 0xad, 0x10, ++ 0xae, 0x18, ++ 0xaf, 0x75, ++ 0xb0, 0x8c, ++ 0xb1, 0x8d, ++ 0xb2, 0x8c, ++ 0xb3, 0x00, ++ 0xb4, 0x98, ++ 0xb5, 0x00, ++ //lens shading ++ 0x40, 0xfb, ++ 0x4c, 0x02, ++ 0x4d, 0x90, ++ 0x4e, 0x01, ++ 0x4f, 0x8c, ++ 0x50, 0x55, ++ 0x51, 0x00, ++ 0x52, 0x66, ++ 0x53, 0x02, ++ 0x54, 0x78, ++ 0x55, 0x01, ++ 0x56, 0x70, ++ 0x57, 0x44, ++ 0x58, 0x20, ++ 0x59, 0x66, ++ 0x5a, 0x02, ++ 0x5b, 0x78, ++ 0x5c, 0x01, ++ 0x5d, 0x88, ++ 0x5e, 0x44, ++ 0x5f, 0x00, ++ 0x60, 0x66, ++ ++ 0x41, 0x1f, ++ 0xb6, 0x07, ++ 0xb9, 0x34,//3c saturation ++ 0xba, 0x28, ++ 0xb7, 0x90, ++ 0xb8, 0x08, ++ 0xbf, 0x0c, ++ 0xc0, 0x3e, ++ 0xa3, 0x0a, ++ 0xa4, 0x0f, ++ 0xa5, 0x10, ++ 0xa6, 0x16, ++ 0x9f, 0x0a, ++ 0xa0, 0x0f, ++ 0xa7, 0x0a, ++ 0xa8, 0x0f, ++ 0xa1, 0x18, ++ 0xa2, 0x10, ++ 0xa9, 0x00, ++ 0xaa, 0xa6, ++ //awb ++ 0x75, 0x68, ++ 0x76, 0x11, ++ 0x77, 0x92, ++ 0x78, 0xa1,//21 ++ 0x79, 0xe1, ++ 0x7a, 0x02, ++ 0x7c, 0x0e, ++ 0x7d, 0x12, ++ 0x7e, 0x12, ++ 0x7f, 0x54, ++ 0x80, 0x78, ++ 0x81, 0xa2, ++ 0x82, 0x80, ++ 0x83, 0x4e, ++ 0x84, 0x40, ++ 0x85, 0x4c, ++ 0x86, 0x43, ++ 0x87, 0xf8, ++ 0x88, 0x08, ++ 0x89, 0x70, ++ 0x8a, 0xf0, ++ 0x8b, 0xf0, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 RECORD_720P_TAB[] = ++{ ++#if 1 ++ //mclk 24mhz, pclk 72mhz, 25fps ++ 0x12, 0x80, ++ 0xc3, 0x1f, ++ 0xc4, 0xff, ++ 0x3d, 0x48, ++ 0xdd, 0xa5, ++ //windows setup ++ 0x20, 0x01, ++ 0x21, 0x25, ++ 0x22, 0x00, ++ 0x23, 0x0c, ++ 0x24, 0x50,//0x500=1280 ++ 0x25, 0x08, ++ 0x26, 0x2d,//0x2d0=720 ++ 0x27, 0x04, ++ ++ 0x28, 0x42, ++ //format setting ++ 0x12, 0x48, ++ 0x39, 0x10, ++ 0xcd, 0x12, ++ //frame setting ++ 0x0e, 0x10, ++ 0x0f, 0x14, ++ 0x10, 0x0a, ++ 0x11, 0x00, ++ 0x29, 0x06,//dummy pixels ++ 0x2a, 0x40, ++ 0x2b, 0x03,//dummy lines ++ 0x2c, 0x84, ++ 0x1d, 0x04, ++ 0x1e, 0x00, ++ 0x1f, 0xe1, ++ ++ 0x13, 0xff, ++ 0x14, 0xa7, ++ 0x15, 0x42, ++ 0x3c, 0xa4, ++ 0x18, 0x60, ++ 0x19, 0x50, ++ 0x1a, 0xe2, ++ 0x37, 0xe8, ++ 0x16, 0x90, ++ 0x43, 0x00, ++ 0x40, 0xfb, ++ 0xa9, 0x44, ++ 0x2f, 0xec, ++ 0x35, 0x10, ++ 0x36, 0x10, ++ 0x0c, 0x00, ++ 0x0d, 0x20, ++ 0xd0, 0x93, ++ 0xdc, 0x2b, ++ 0xd9, 0x41, ++ 0xd3, 0x02, ++ 0x3d, 0x08, ++ 0x0c, 0x00, ++ 0x18, 0x2c, ++ 0x19, 0x24, ++ 0x1a, 0x71, ++ 0x9b, 0x69, ++ 0x9c, 0x7d, ++ 0x9d, 0x7d, ++ 0x9e, 0x69, ++ 0x35, 0x04, ++ 0x36, 0x04, ++ //gamma0x ++ 0x65, 0x04, ++ 0x66, 0x07, ++ 0x67, 0x19, ++ 0x68, 0x34, ++ 0x69, 0x4a, ++ 0x6a, 0x5a, ++ 0x6b, 0x67, ++ 0x6c, 0x71, ++ 0x6d, 0x7c, ++ 0x6e, 0x8c, ++ 0x6f, 0x9b, ++ 0x70, 0xa9, ++ 0x71, 0xc0, ++ 0x72, 0xd5, ++ 0x73, 0xe8, ++ 0x74, 0x20, ++ //color matrix ++ 0xab, 0x28, ++ 0xac, 0x48, ++ 0xad, 0x10, ++ 0xae, 0x18, ++ 0xaf, 0x75, ++ 0xb0, 0x8c, ++ 0xb1, 0x8d, ++ 0xb2, 0x8c, ++ 0xb3, 0x00, ++ 0xb4, 0x98, ++ 0xb5, 0x00, ++ //lens shading ++ 0x40, 0xfb, ++ 0x4c, 0x02, ++ 0x4d, 0x90, ++ 0x4e, 0x01, ++ 0x4f, 0x78, ++ 0x50, 0x54, ++ 0x51, 0x28, ++ 0x52, 0x66, ++ 0x53, 0x02, ++ 0x54, 0x92, ++ 0x55, 0x01, ++ 0x56, 0x70, ++ 0x57, 0x44, ++ 0x58, 0x20, ++ 0x59, 0x66, ++ 0x5a, 0x02, ++ 0x5b, 0x90, ++ 0x5c, 0x01, ++ 0x5d, 0x88, ++ 0x5e, 0x44, ++ 0x5f, 0x00, ++ 0x60, 0x66, ++ ++ 0x41, 0x1f, ++ 0xb6, 0x07, ++ 0xb9, 0x34,//3c saturation ++ 0xba, 0x28, ++ 0xb7, 0x90, ++ 0xb8, 0x08, ++ 0xbf, 0x0c, ++ 0xc0, 0x3e, ++ 0xa3, 0x0a, ++ 0xa4, 0x0f, ++ 0xa5, 0x10, ++ 0xa6, 0x16, ++ 0x9f, 0x0a, ++ 0xa0, 0x0f, ++ 0xa7, 0x0a, ++ 0xa8, 0x0f, ++ 0xa1, 0x18, ++ 0xa2, 0x10, ++ 0xa9, 0x00, ++ 0xaa, 0xa6, ++ //awb ++ 0x75, 0x68, ++ 0x76, 0x11, ++ 0x77, 0x92, ++ 0x78, 0xa1, //21 ++ 0x79, 0xe1, ++ 0x7a, 0x02, ++ 0x7c, 0x0e, ++ 0x7d, 0x12, ++ 0x7e, 0x12, ++ 0x7f, 0x54, ++ 0x80, 0x78, ++ 0x81, 0xa2, ++ 0x82, 0x80, ++ 0x83, 0x4e, ++ 0x84, 0x40, ++ 0x85, 0x4c, ++ 0x86, 0x43, ++ 0x87, 0xf8, ++ 0x88, 0x08, ++ 0x89, 0x70, ++ 0x8a, 0xf0, ++ 0x8b, 0xf0, ++#else ++ //mclk 24mhz, pclk 48mhz, 20fps ++ 0x12, 0x80, ++ 0xc3, 0x5F, ++ 0xc4, 0xff, ++ 0x3d, 0x48, ++ 0xdd, 0xa5, ++ //windows setup ++ 0x20, 0x01, ++ 0x21, 0x25, ++ 0x22, 0x00, ++ 0x23, 0x0c, ++ 0x24, 0x50,//0x500=1280 ++ 0x25, 0x08, ++ 0x26, 0x2d,//0x2d0=720 ++ 0x27, 0x04, ++ 0x28, 0x42, ++ //format setting ++ 0x12, 0x48, ++ 0x39, 0x10, ++ 0xcd, 0x12, ++ //frame setting ++ 0x0e, 0x10, ++ 0x0f, 0x24, ++ 0x10, 0x0a, ++ 0x11, 0x82, ++ 0x29, 0x06,//dummy pixels ++ 0x2a, 0x40, ++ 0x2b, 0x02,//dummy lines ++ 0x2c, 0xee, ++ 0x1d, 0x05, ++ 0x1e, 0x00, ++ 0x1f, 0x96, ++ ++ 0x13, 0xff, ++ 0x14, 0xa7, ++ 0x15, 0x42, ++ 0x3c, 0xa4, ++ 0x18, 0x60, ++ 0x19, 0x50, ++ 0x1a, 0xe2, ++ 0x37, 0xe8, ++ 0x16, 0x90, ++ 0x43, 0x00, ++ 0x40, 0xfb, ++ 0xa9, 0x44, ++ 0x2f, 0xec, ++ 0x35, 0x10, ++ 0x36, 0x10, ++ 0x0c, 0x00, ++ 0x0d, 0x20, ++ 0xd0, 0x93, ++ 0xdc, 0x2b, ++ 0xd9, 0x41, ++ 0xd3, 0x02, ++ 0x3d, 0x08, ++ 0x0c, 0x00, ++ 0x18, 0x2c, ++ 0x19, 0x24, ++ 0x1a, 0x71, ++ 0x9b, 0x69, ++ 0x9c, 0x7d, ++ 0x9d, 0x7d, ++ 0x9e, 0x69, ++ 0x35, 0x04, ++ 0x36, 0x04, ++ //gamma ++ 0x65, 0x04, ++ 0x66, 0x07, ++ 0x67, 0x19, ++ 0x68, 0x34, ++ 0x69, 0x4a, ++ 0x6a, 0x5a, ++ 0x6b, 0x67, ++ 0x6c, 0x71, ++ 0x6d, 0x7c, ++ 0x6e, 0x8c, ++ 0x6f, 0x9b, ++ 0x70, 0xa9, ++ 0x71, 0xc0, ++ 0x72, 0xd5, ++ 0x73, 0xe8, ++ 0x74, 0x20, ++ //color matrix ++ 0xab, 0x28, ++ 0xac, 0x48, ++ 0xad, 0x10, ++ 0xae, 0x18, ++ 0xaf, 0x75, ++ 0xb0, 0x8c, ++ 0xb1, 0x8d, ++ 0xb2, 0x8c, ++ 0xb3, 0x00, ++ 0xb4, 0x98, ++ 0xb5, 0x00, ++ //lens shading ++ 0x40, 0xFB, ++ 0x4c, 0x02, ++ 0x4d, 0x90, ++ 0x4e, 0x01, ++ 0x4f, 0x78, ++ 0x50, 0x54, ++ 0x51, 0x28, ++ 0x52, 0x66, ++ 0x53, 0x02, ++ 0x54, 0x92, ++ 0x55, 0x01, ++ 0x56, 0x70, ++ 0x57, 0x44, ++ 0x58, 0x20, ++ 0x59, 0x66, ++ 0x5a, 0x02, ++ 0x5b, 0x90, ++ 0x5c, 0x01, ++ 0x5d, 0x88, ++ 0x5e, 0x44, ++ 0x5f, 0x00, ++ 0x60, 0x66, ++ ++ 0x41, 0x1f, ++ 0xb6, 0x07, ++ 0xb9, 0x34,//3c saturation ++ 0xba, 0x28, ++ 0xb7, 0x90, ++ 0xb8, 0x08, ++ 0xbf, 0x0c, ++ 0xc0, 0x3e, ++ 0xa3, 0x0a, ++ 0xa4, 0x0f, ++ 0xa5, 0x10, ++ 0xa6, 0x16, ++ 0x9f, 0x0a, ++ 0xa0, 0x0f, ++ 0xa7, 0x0a, ++ 0xa8, 0x0f, ++ 0xa1, 0x18, ++ 0xa2, 0x10, ++ 0xa9, 0x00, ++ 0xaa, 0xa6, ++ //awb ++ 0x75, 0x68, ++ 0x76, 0x11, ++ 0x77, 0x92, ++ 0x78, 0xa1,//21 ++ 0x79, 0xe1, ++ 0x7a, 0x02, ++ 0x7c, 0x0e, ++ 0x7d, 0x12, ++ 0x7e, 0x12, ++ 0x7f, 0x54, ++ 0x80, 0x78, ++ 0x81, 0xa2, ++ 0x82, 0x80, ++ 0x83, 0x4e, ++ 0x84, 0x40, ++ 0x85, 0x4c, ++ 0x86, 0x43, ++ 0x87, 0xf8, ++ 0x88, 0x08, ++ 0x89, 0x70, ++ 0x8a, 0xf0, ++ 0x8b, 0xf0, ++#endif ++ END_FLAG, END_FLAG ++}; ++ ++ ++/**************** Camera Exposure Table ****************/ ++static const T_U8 EXPOSURE_WHOLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EXPOSURE_CENTER_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EXPOSURE_MIDDLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Brightness Table ****************/ ++static const T_U8 BRIGHTNESS_0_TAB[] = ++{ ++ //Brightness -3 ++ 0xbd, 0x30, ++ 0xbe, 0x08, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_1_TAB[] = ++{ ++ //Brightness -2 ++ 0xbd, 0x20, ++ 0xbe, 0x08, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_2_TAB[] = ++{ ++ //Brightness -1 ++ 0xbd, 0x10, ++ 0xbe, 0x08, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_3_TAB[] = ++{ ++ //Brightness 0 ++ 0xbd, 0x00, ++ 0xbe, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_4_TAB[] = ++{ ++ //Brightness +1 ++ 0xbd, 0x10, ++ 0xbe, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_5_TAB[] = ++{ ++ //Brightness +2 ++ 0xbd, 0x20, ++ 0xbe, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_6_TAB[] = ++{ ++ //Brightness +3 ++ 0xbd, 0x30, ++ 0xbe, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Contrast Table ****************/ ++static const T_U8 CONTRAST_1_TAB[] = ++{ ++ //Contrast -3 ++ 0xbb, 0x14, ++ 0xbc, 0x14, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_2_TAB[] = ++{ ++ //Contrast -2 ++ 0xbb, 0x18, ++ 0xbc, 0x18, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_3_TAB[] = ++{ ++ //Contrast -1 ++ 0xbb, 0x1c, ++ 0xbc, 0x1c, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_4_TAB[] = ++{ ++ //Contrast 0 ++ 0xbb, 0x20, ++ 0xbc, 0x20, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_5_TAB[] = ++{ ++ //Contrast +1 ++ 0xbb, 0x24, ++ 0xbc, 0x24, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_6_TAB[] = ++{ ++ //Contrast +2 ++ 0xbb, 0x28, ++ 0xbc, 0x28, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_7_TAB[] = ++{ ++ //Contrast +3 ++ 0xbb, 0x2c, ++ 0xbc, 0x2c, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Saturation Table ****************/ ++static const T_U8 SATURATION_1_TAB[] = ++{ ++ //Saturation -2(0.5x) ++ 0xb9, 0x20, ++ 0xba, 0x20, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_2_TAB[] = ++{ ++ //Saturation -1(0.75x) ++ 0xb9, 0x30, ++ 0xba, 0x30, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_3_TAB[] = ++{ ++ //Saturation ++ 0xb9, 0x40, ++ 0xba, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_4_TAB[] = ++{ ++ //Saturation +1(1.25x) ++ 0xb9, 0x50, ++ 0xba, 0x50, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_5_TAB[] = ++{ ++ //Saturation +2(1.75x) ++ 0xb9, 0x70, ++ 0xba, 0x70, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Sharpness Table ****************/ ++static const T_U8 SHARPNESS_0_TAB[] = ++{ ++ //Sharpness -2 ++ 0xa1, 0x10, ++ 0xa2, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_1_TAB[] = ++{ ++ //Sharpness -1 ++ 0xa1, 0x14, ++ 0xa2, 0x02, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_2_TAB[] = ++{ ++ //Sharpness default ++ 0xa1, 0x18, ++ 0xa2, 0x04, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_3_TAB[] = ++{ ++ //Sharpness +1 ++ 0xa1, 0x1c, ++ 0xa2, 0x08, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_4_TAB[] = ++{ ++ //Sharpness +2 ++ 0xa1, 0x1f, ++ 0xa2, 0x0c, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_5_TAB[] = ++{ ++ //Sharpness auto ++ 0x9f, 0x0a, ++ 0xa0, 0x0f, ++ 0xa7, 0x0a, ++ 0xa8, 0x0f, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera AWB Table ****************/ ++static const T_U8 AWB_AUTO_TAB[] = ++{ ++ 0x13, 0xff, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_SUNNY_TAB[] = ++{ ++ /*0x13, 0xfb, ++ 0x05, 0x5e, ++ 0x06, 0x41, ++ 0x07, 0x54, ++ 0x08, 0x00, ++ 0x09, 0x00,*/ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_CLOUDY_TAB[] = ++{ ++ /*0x13, 0xfb, ++ 0x05, 0x65, ++ 0x06, 0x41, ++ 0x07, 0x4f, ++ 0x08, 0x00, ++ 0x09, 0x00,*/ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_OFFICE_TAB[] = ++{ ++ /*0x13, 0xfb, ++ 0x05, 0x52, ++ 0x06, 0x41, ++ 0x07, 0x66, ++ 0x08, 0x00, ++ 0x09, 0x00,*/ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_HOME_TAB[] = ++{ ++ /*0x13, 0xfb, ++ 0x05, 0x42, ++ 0x06, 0x3f, ++ 0x07, 0x71, ++ 0x08, 0x00, ++ 0x09, 0x00,*/ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Effect Table ****************/ ++static const T_U8 EFFECT_NORMAL_TAB[] = ++{ ++ //0xb6, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_SEPIA_TAB[] = ++{ ++ //0xb6, 0x18, ++ //0xb9, 0x40, ++ //0xba, 0xa0, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_ANTIQUE_TAB[] = ++{ ++ //0xb6, 0x18, ++ //0xb9, 0x40, ++ //0xba, 0xa0, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_BLUISH_TAB[] = ++{ ++ //0xb6, 0x18, ++ //0xb9, 0xa0, ++ //0xba, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_GREENISH_TAB[] = ++{ ++ //0xb6, 0x18, ++ //0xb9, 0x60, ++ //0xba, 0x60, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_REDDISH_TAB[] = ++{ ++ //0xb6, 0x18, ++ //0xb9, 0x80, ++ //0xba, 0xc0, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_NEGATIVE_TAB[] = ++{ ++ //0xb6, 0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_BW_TAB[] = ++{ ++ //0xb6, 0x20, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera night/day mode ****************/ ++static const T_U8 DAY_MODE_TAB[] = ++{ ++ //0x14, 0xa0, ++ //0x2e, 0x00, ++ //0x2d, 0x00, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 NIGHT_MODE_TAB[] = ++{ ++ //0x0e, 0xb8, ++ //0x0f, 0x14, ++ //0x14, 0xad, ++ END_FLAG, END_FLAG ++}; ++#endif ++#endif +diff --git a/drivers/media/video/plat-anyka/camera_ov2710.c b/drivers/media/video/plat-anyka/camera_ov2710.c +new file mode 100755 +index 00000000..dd8b73d5 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_ov2710.c +@@ -0,0 +1,1005 @@ ++/** ++ * @file camera_hm1375.c ++ * @brief camera driver file ++ * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author xia_wenting ++ * @date 2011-09-21 ++ * @version 1.0 ++ * @ref ++ */ ++#ifdef CONFIG_LINUX_AKSENSOR ++#include ++#include ++#include ++#include "camera_ov2710.h" ++#else ++#include "akdefine.h" ++#include "cam_com_sensor.h" ++#include "camera_ov2710.h" ++#include "Gpio_config.h" ++#endif ++ ++#if defined (USE_CAMERA_OV2710) || defined (CONFIG_SENSOR_OV2710) ++ ++#define CAM_EN_LEVEL 0 ++#define CAM_RESET_LEVEL 0 ++ ++#define CAMERA_SCCB_ADDR 0x6c ++ ++#define CAMERA_OV2710_ID 0x2710 ++ ++#define OV2710_CAMERA_MCLK 24 ++ ++static T_CAMERA_TYPE camera_ov2710_type = CAMERA_2M; ++static T_NIGHT_MODE night_mode = CAMERA_DAY_MODE; ++static T_CAMERA_MODE s_ov2710_CurMode = CAMERA_MODE_VGA; ++ ++#if 0 ++static T_VOID camera_setbit(T_U16 reg, T_U8 bit, T_U8 value) ++{ ++ T_U8 tmp; ++ ++ tmp = sccb_read_short(CAMERA_SCCB_ADDR, reg); ++ if (value == 1) ++ { ++ tmp |= 0x1 << bit; ++ } ++ else ++ { ++ tmp &= ~(0x1 << bit); ++ } ++ ++ sccb_write_short(CAMERA_SCCB_ADDR, reg, &tmp, 1); ++} ++#endif ++static T_U32 cam_ov2710_read_id(T_VOID); ++ ++static T_BOOL camera_set_param(const T_U16 tabParameter[]) ++{ ++ int i = 0; ++ T_U8 temp_value; ++ T_U8 data; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ data = tabParameter[i + 1]; ++ sccb_write_short(CAMERA_SCCB_ADDR, tabParameter[i], &data, 1); ++ ++ if ((tabParameter[i] != 0x0000) || (tabParameter[i] != 0x0022) ++ || (tabParameter[i] != 0x0100) || (tabParameter[i] != 0x0101)) ++ { ++ temp_value = sccb_read_short(CAMERA_SCCB_ADDR, tabParameter[i]); ++ if (temp_value != tabParameter[i + 1]) ++ { ++ akprintf(C1, M_DRVSYS, "set parameter error!\n"); ++ akprintf(C1, M_DRVSYS, "reg 0x%x write data is 0x%x, read data is 0x%x!\n", tabParameter[i], tabParameter[i + 1], temp_value); ++ ++ return AK_FALSE; ++ } ++ } ++ } ++ ++ i += 2; ++ } ++ ++ return AK_TRUE; ++} ++ ++static T_VOID camera_setup(const T_U16 tabParameter[]) ++{ ++ int i = 0; ++ T_U8 data; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ data = tabParameter[i + 1]; ++ sccb_write_short(CAMERA_SCCB_ADDR, tabParameter[i], &data, 1); ++ ++ //printk("0x%04x, 0x%02x, 0x%02x\n", tabParameter[i], data, sccb_read_short(CAMERA_SCCB_ADDR, tabParameter[i])); ++ } ++ i += 2; ++ } ++ ++} ++ ++static T_VOID cam_ov2710_open(T_VOID) ++{ ++ gpio_set_pin_dir(GPIO_CAMERA_AVDD, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_CHIP_ENABLE); ++ gpio_set_pin_dir(GPIO_CAMERA_CHIP_ENABLE, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, CAM_EN_LEVEL); ++ mini_delay(10); ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_RESET); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, CAM_RESET_LEVEL); ++ mini_delay(10); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, !CAM_RESET_LEVEL); ++ ++ mini_delay(20); ++} ++ ++static T_BOOL cam_ov2710_close(T_VOID) ++{ ++ //sccb software standby mode ++// T_U8 Reg0x3d = 0x48; ++// T_U8 Reg0xc3 = 0x00; ++ ++// sccb_write_short(CAMERA_SCCB_ADDR, 0x3d, &Reg0x3d, 1); ++// sccb_write_short(CAMERA_SCCB_ADDR, 0xc3, &Reg0xc3, 1); ++ ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, !CAM_EN_LEVEL); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, !gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_INPUT); ++ ++ gpio_set_pin_dir(GPIO_I2C_SCL, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SCL, GPIO_LEVEL_LOW); ++ gpio_set_pin_dir(GPIO_I2C_SDA, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SDA, GPIO_LEVEL_LOW); ++ ++ return AK_TRUE; ++} ++ ++static T_U32 cam_ov2710_read_id(T_VOID) ++{ ++ T_U8 value = 0x00; ++ T_U32 id = 0; ++ ++ sccb_init(GPIO_I2C_SCL, GPIO_I2C_SDA); //init sccb first here!! ++ ++ value = sccb_read_short(CAMERA_SCCB_ADDR, 0x300a); ++ id = value << 8; ++ value = sccb_read_short(CAMERA_SCCB_ADDR, 0x300b); ++ id |= value; ++ ++ return id; ++} ++ ++/** ++ * @brief initialize the parameters of camera, should be done after reset and open camera to initialize ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @return T_BOOL ++ * @retval AK_TRUE if success, else AK_FALSE ++ */ ++static T_BOOL cam_ov2710_init(void) ++{ ++ if (!camera_set_param(INIT_TAB)) ++ { ++ return AK_FALSE; ++ } ++ else ++ { ++ night_mode = CAMERA_DAY_MODE; ++ return AK_TRUE; ++ } ++} ++ ++/** ++ * @brief Set camera mode to specify image quality, SXGA/VGA/CIF etc ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] mode mode value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2710_set_mode(T_CAMERA_MODE mode) ++{ ++ s_ov2710_CurMode = mode; ++ switch(mode) ++ { ++ case CAMERA_MODE_UXGA: ++ camera_setup(UXGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_SXGA: ++ camera_setup(SXGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_VGA: ++ camera_setup(VGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_CIF: ++ camera_setup(CIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QVGA: ++ camera_setup(QVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_QCIF: ++ camera_setup(QCIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QQVGA: ++ camera_setup(QQVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_PREV: ++ camera_setup(PREV_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_REC: ++ camera_setup(RECORD_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_720P: ++ camera_setup(RECORD_720P_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ default: ++ s_ov2710_CurMode = CAMERA_MODE_VGA; ++ akprintf(C1, M_DRVSYS, "set camera mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera exposure mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] exposure exposure mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2710_set_exposure(T_CAMERA_EXPOSURE exposure) ++{ ++ switch(exposure) ++ { ++ case EXPOSURE_WHOLE: ++ camera_setup(EXPOSURE_WHOLE_TAB); ++ break; ++ case EXPOSURE_CENTER: ++ camera_setup(EXPOSURE_CENTER_TAB); ++ break; ++ case EXPOSURE_MIDDLE: ++ camera_setup(EXPOSURE_MIDDLE_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set exposure parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera brightness level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] brightness brightness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2710_set_brightness(T_CAMERA_BRIGHTNESS brightness) ++{ ++ switch(brightness) ++ { ++ case CAMERA_BRIGHTNESS_0: ++ camera_setup(BRIGHTNESS_0_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_1: ++ camera_setup(BRIGHTNESS_1_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_2: ++ camera_setup(BRIGHTNESS_2_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_3: ++ camera_setup(BRIGHTNESS_3_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_4: ++ camera_setup(BRIGHTNESS_4_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_5: ++ camera_setup(BRIGHTNESS_5_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_6: ++ camera_setup(BRIGHTNESS_6_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set brightness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera contrast level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] contrast contrast value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2710_set_contrast(T_CAMERA_CONTRAST contrast) ++{ ++ switch(contrast) ++ { ++ case CAMERA_CONTRAST_1: ++ camera_setup(CONTRAST_1_TAB); ++ break; ++ case CAMERA_CONTRAST_2: ++ camera_setup(CONTRAST_2_TAB); ++ break; ++ case CAMERA_CONTRAST_3: ++ camera_setup(CONTRAST_3_TAB); ++ break; ++ case CAMERA_CONTRAST_4: ++ camera_setup(CONTRAST_4_TAB); ++ break; ++ case CAMERA_CONTRAST_5: ++ camera_setup(CONTRAST_5_TAB); ++ break; ++ case CAMERA_CONTRAST_6: ++ camera_setup(CONTRAST_6_TAB); ++ break; ++ case CAMERA_CONTRAST_7: ++ camera_setup(CONTRAST_7_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set contrast parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera saturation level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] saturation saturation value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2710_set_saturation(T_CAMERA_SATURATION saturation) ++{ ++ switch(saturation) ++ { ++ case CAMERA_SATURATION_1: ++ camera_setup(SATURATION_1_TAB); ++ break; ++ case CAMERA_SATURATION_2: ++ camera_setup(SATURATION_2_TAB); ++ break; ++ case CAMERA_SATURATION_3: ++ camera_setup(SATURATION_3_TAB); ++ break; ++ case CAMERA_SATURATION_4: ++ camera_setup(SATURATION_4_TAB); ++ break; ++ case CAMERA_SATURATION_5: ++ camera_setup(SATURATION_5_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set saturation parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera sharpness level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] sharpness sharpness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2710_set_sharpness(T_CAMERA_SHARPNESS sharpness) ++{ ++ switch(sharpness) ++ { ++ case CAMERA_SHARPNESS_0: ++ camera_setup(SHARPNESS_0_TAB); ++ break; ++ case CAMERA_SHARPNESS_1: ++ camera_setup(SHARPNESS_1_TAB); ++ break; ++ case CAMERA_SHARPNESS_2: ++ camera_setup(SHARPNESS_2_TAB); ++ break; ++ case CAMERA_SHARPNESS_3: ++ camera_setup(SHARPNESS_3_TAB); ++ break; ++ case CAMERA_SHARPNESS_4: ++ camera_setup(SHARPNESS_4_TAB); ++ break; ++ case CAMERA_SHARPNESS_5: ++ camera_setup(SHARPNESS_5_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set sharpness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera AWB mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] awb AWB mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2710_set_AWB(T_CAMERA_AWB awb) ++{ ++ switch(awb) ++ { ++ case AWB_AUTO: ++ camera_setup(AWB_AUTO_TAB); ++ break; ++ case AWB_SUNNY: ++ camera_setup(AWB_SUNNY_TAB); ++ break; ++ case AWB_CLOUDY: ++ camera_setup(AWB_CLOUDY_TAB); ++ break; ++ case AWB_OFFICE: ++ camera_setup(AWB_OFFICE_TAB); ++ break; ++ case AWB_HOME: ++ camera_setup(AWB_HOME_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set AWB mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera mirror mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] mirror mirror mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2710_set_mirror(T_CAMERA_MIRROR mirror) ++{ ++ switch(mirror) ++ { ++ case CAMERA_MIRROR_V: ++ break; ++ case CAMERA_MIRROR_H: ++ break; ++ case CAMERA_MIRROR_NORMAL: ++ break; ++ case CAMERA_MIRROR_FLIP: ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set mirror parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera effect mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] effect effect mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov2710_set_effect(T_CAMERA_EFFECT effect) ++{ ++ switch(effect) ++ { ++ case CAMERA_EFFECT_NORMAL: ++ camera_setup(EFFECT_NORMAL_TAB); ++ break; ++ case CAMERA_EFFECT_SEPIA: ++ camera_setup(EFFECT_SEPIA_TAB); ++ break; ++ case CAMERA_EFFECT_ANTIQUE: ++ camera_setup(EFFECT_ANTIQUE_TAB); ++ break; ++ case CAMERA_EFFECT_BLUE: ++ camera_setup(EFFECT_BLUISH_TAB); ++ break; ++ case CAMERA_EFFECT_GREEN: ++ camera_setup(EFFECT_GREENISH_TAB); ++ break; ++ case CAMERA_EFFECT_RED: ++ camera_setup(EFFECT_REDDISH_TAB); ++ break; ++ case CAMERA_EFFECT_NEGATIVE: ++ camera_setup(EFFECT_NEGATIVE_TAB); ++ break; ++ case CAMERA_EFFECT_BW: ++ camera_setup(EFFECT_BW_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set camer effect parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief set camera window ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] srcWidth window width ++ * @param[in] srcHeight window height ++ * @return T_S32 ++ * @retval 0 if error mode ++ * @retval 1 if success ++ * @retval -1 if failed ++ */ ++static T_S32 cam_ov2710_set_digital_zoom(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ return 1; ++} ++ ++static T_VOID cam_ov2710_set_night_mode(T_NIGHT_MODE mode) ++{ ++ switch(mode) ++ { ++ case CAMERA_DAY_MODE: ++ camera_setup(DAY_MODE_TAB); ++ night_mode = CAMERA_DAY_MODE; ++ break; ++ case CAMERA_NIGHT_MODE: ++ camera_setup(NIGHT_MODE_TAB); ++ night_mode = CAMERA_NIGHT_MODE; ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set night mode parameter error!\n"); ++ break; ++ } ++} ++ ++static T_BOOL cam_ov2710_set_to_cap(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ ++ if ((srcWidth <= 160) && (srcHeight <= 120)) ++ { ++ Cammode = CAMERA_MODE_QQVGA; ++ } ++ else if ((srcWidth <= 176) && (srcHeight <= 144)) ++ { ++ Cammode = CAMERA_MODE_QCIF; ++ } ++ else if ((srcWidth <= 320) && (srcHeight <= 240)) ++ { ++ Cammode = CAMERA_MODE_QVGA; ++ } ++ else if ((srcWidth <= 352) && (srcHeight <= 288)) ++ { ++ Cammode = CAMERA_MODE_CIF; ++ } ++ else if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_VGA; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 720)) ++ { ++ Cammode = CAMERA_MODE_720P; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 1024)) ++ { ++ Cammode = CAMERA_MODE_SXGA; ++ } ++ else if ((srcWidth <= 1600) && (srcHeight <= 1200)) ++ { ++ Cammode = CAMERA_MODE_UXGA; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "ov2710 unsupport %d & %d mode!\n", srcWidth, srcHeight); ++ return AK_FALSE; ++ } ++ ++ cam_ov2710_set_mode(Cammode); ++ cam_ov2710_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_ov2710_set_to_prev(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ cam_ov2710_set_mode(CAMERA_MODE_PREV); ++ cam_ov2710_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_ov2710_set_to_record(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ if ((srcWidth <= 320) && (srcHeight <= 240)) ++ { ++ Cammode = CAMERA_MODE_QVGA; ++ } ++ else if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_REC; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 720)) ++ { ++ Cammode = CAMERA_MODE_720P; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "200W camera dose not support such mode"); ++ return AK_FALSE; ++ } ++ ++ cam_ov2710_set_mode(Cammode); ++ cam_ov2710_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_CAMERA_TYPE cam_ov2710_get_type(T_VOID) ++{ ++ return camera_ov2710_type; ++} ++ ++static T_VOID cam_ov2710_set_sensor_param(T_U32 cmd, T_U32 data) ++{ ++ T_U8 value; ++ ++ value = (T_U8)data; ++ sccb_write_short(CAMERA_SCCB_ADDR, (T_U16)cmd, &value, 1); ++} ++ ++static T_CAMERA_FUNCTION_HANDLER ov2710_function_handler = ++{ ++ OV2710_CAMERA_MCLK, ++ cam_ov2710_open, ++ cam_ov2710_close, ++ cam_ov2710_read_id, ++ cam_ov2710_init, ++ cam_ov2710_set_mode, ++ cam_ov2710_set_exposure, ++ cam_ov2710_set_brightness, ++ cam_ov2710_set_contrast, ++ cam_ov2710_set_saturation, ++ cam_ov2710_set_sharpness, ++ cam_ov2710_set_AWB, ++ cam_ov2710_set_mirror, ++ cam_ov2710_set_effect, ++ cam_ov2710_set_digital_zoom, ++ cam_ov2710_set_night_mode, ++ AK_NULL, ++ AK_NULL, ++ cam_ov2710_set_to_cap, ++ cam_ov2710_set_to_prev, ++ cam_ov2710_set_to_record, ++ cam_ov2710_get_type, ++ cam_ov2710_set_sensor_param ++}; ++ ++#ifndef CONFIG_LINUX_AKSENSOR ++static int camera_ov2710_reg(void) ++{ ++ camera_reg_dev(CAMERA_OV2710_ID, &ov2710_function_handler); ++ return 0; ++} ++ ++#ifdef __CC_ARM ++#pragma arm section rwdata = "__initcall_", zidata = "__initcall_" ++#endif ++module_init(camera_ov2710_reg) ++#ifdef __CC_ARM ++#pragma arm section ++#endif ++ ++#else ++static const char * awb_menu[] = { ++ [AWB_AUTO] = "auto", ++ [AWB_SUNNY] = "sunny", ++ [AWB_CLOUDY] = "cloudy", ++ [AWB_OFFICE] = "office", ++ [AWB_HOME] = "home", ++ [AWB_NIGHT] = "night", ++}; ++ ++static const char * effect_menu[] = { ++ [CAMERA_EFFECT_NORMAL] = "normal", ++ [CAMERA_EFFECT_SEPIA] = "sepia", ++ [CAMERA_EFFECT_ANTIQUE] = "antique", ++ [CAMERA_EFFECT_BLUE] = "blue", ++ [CAMERA_EFFECT_GREEN] = "green", ++ [CAMERA_EFFECT_RED] = "red", ++ [CAMERA_EFFECT_NEGATIVE] = "negative", ++ [CAMERA_EFFECT_BW] = "bw", ++ [CAMERA_EFFECT_BWN] = "bwn", ++ [CAMERA_EFFECT_AQUA] = "aqua", ++ [CAMERA_EFFECT_COOL] = "cool", ++ [CAMERA_EFFECT_WARM] = "warm", ++}; ++ ++static const char * resolution_menu[] = { ++ [0] = "1920x1080", ++ [1] = "1280x720", ++ [2] = "640x480", ++ [3] = "320x240", ++}; ++ ++static const char * hflip_menu[] = { ++ [0] = "normal", ++ [1] = "horizontal flip", ++}; ++ ++static const char * vflip_menu[] = { ++ [0] = "normal", ++ [1] = "vertical flip", ++}; ++ ++static const char * night_menu[] = { ++ [CAMERA_DAY_MODE] = "daylight", ++ [CAMERA_NIGHT_MODE] = "night", ++}; ++ ++static int ov2710_s_ctl(struct v4l2_ctrl *ctrl) ++{ ++ int ret = -EINVAL; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ if (ov2710_function_handler.cam_set_AWB_func) { ++ ov2710_function_handler.cam_set_AWB_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_COLORFX: ++ if (ov2710_function_handler.cam_set_effect_func) { ++ ov2710_function_handler.cam_set_effect_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ ++ case V4L2_CID_BRIGHTNESS: ++ if (ov2710_function_handler.cam_set_brightness_func) { ++ ov2710_function_handler.cam_set_brightness_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_CONTRAST: ++ if (ov2710_function_handler.cam_set_contrast_func) { ++ ov2710_function_handler.cam_set_contrast_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SATURATION: ++ if (ov2710_function_handler.cam_set_saturation_func) { ++ ov2710_function_handler.cam_set_saturation_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SHARPNESS: ++ if (ov2710_function_handler.cam_set_sharpness_func) { ++ ov2710_function_handler.cam_set_sharpness_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_HFLIP: ++ if (ov2710_function_handler.cam_set_mirror_func) { ++ ov2710_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_H : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_VFLIP: ++ if (ov2710_function_handler.cam_set_mirror_func) { ++ ov2710_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_V : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_NIGHTMODE: ++ if (ov2710_function_handler.cam_set_night_mode_func) { ++ ov2710_function_handler.cam_set_night_mode_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++}; ++ ++static struct v4l2_ctrl_ops ov2710_ctrl_ops = { ++ .s_ctrl = ov2710_s_ctl, ++}; ++ ++static const struct v4l2_ctrl_config ov2710_ctrls[] = { ++ { ++ .ops = &ov2710_ctrl_ops, ++ .id = V4L2_CID_AUTO_WHITE_BALANCE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "AWB", ++ .min = 0, ++ .max = ARRAY_SIZE(awb_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = awb_menu, ++ }, ++ { ++ .ops = &ov2710_ctrl_ops, ++ .id = V4L2_CID_COLORFX, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Effect", ++ .min = 0, ++ .max = ARRAY_SIZE(effect_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = effect_menu, ++ }, ++ { ++ .ops = &ov2710_ctrl_ops, ++ .id = V4L2_CID_HFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Horizontal Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(hflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = hflip_menu, ++ }, ++ { ++ .ops = &ov2710_ctrl_ops, ++ .id = V4L2_CID_VFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Vetical Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(vflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = vflip_menu, ++ }, ++ { ++ .ops = &ov2710_ctrl_ops, ++ .id = V4L2_CID_PICTURE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Resolution", ++ .min = 0, ++ .max = ARRAY_SIZE(resolution_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = resolution_menu, ++ }, ++ { ++ .ops = &ov2710_ctrl_ops, ++ .id = V4L2_CID_NIGHTMODE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Night mode", ++ .min = 0, ++ .max = ARRAY_SIZE(night_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = night_menu, ++ }, ++ { ++ .ops = &ov2710_ctrl_ops, ++ .id = V4L2_CID_BRIGHTNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Brightness", ++ .min = 0, ++ .max = CAMERA_BRIGHTNESS_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ov2710_ctrl_ops, ++ .id = V4L2_CID_CONTRAST, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Contrast", ++ .min = 0, ++ .max = CAMERA_CONTRAST_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ov2710_ctrl_ops, ++ .id = V4L2_CID_SATURATION, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Saturation", ++ .min = 0, ++ .max = CAMERA_SATURATION_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ov2710_ctrl_ops, ++ .id = V4L2_CID_SHARPNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Sharpness", ++ .min = 0, ++ .max = CAMERA_SHARPNESS_NUM -1, ++ .step = 1, ++ .def = 0, ++ } ++}; ++ ++ ++/* ++ * supported format list ++ */ ++static const struct aksensor_color_format ov2710_formats[] = { ++ { ++ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB565_2X8_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB565_2X8_BE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++}; ++ ++static const struct aksensor_win_size ov2710_win[] = { ++ {.name = "QVGA", .width = 320, .height = 240}, ++ {.name = "VGA", .width = 640, .height = 480}, ++ {.name = "720P", .width = 1280, .height = 720}, ++ {.name = "1080P", .width = 1920, .height = 1080}, ++}; ++ ++ ++static struct sensor_info ov2710_sensor_info = { ++ .sensor_name = "ov2710", ++ .sensor_id = CAMERA_OV2710_ID, ++ .ctrls = ov2710_ctrls, ++ .nr_ctrls = ARRAY_SIZE(ov2710_ctrls), ++ .formats = ov2710_formats, ++ .num_formats = ARRAY_SIZE(ov2710_formats), ++ .resolution = ov2710_win, ++ .num_resolution = ARRAY_SIZE(ov2710_win), ++ .handler = &ov2710_function_handler, ++}; ++ ++static int ov2710_module_init(void) ++{ ++ return register_sensor(&ov2710_sensor_info); ++} ++module_init(ov2710_module_init) ++#endif ++ ++#endif ++ ++ +diff --git a/drivers/media/video/plat-anyka/camera_ov2710.h b/drivers/media/video/plat-anyka/camera_ov2710.h +new file mode 100755 +index 00000000..65646ef5 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_ov2710.h +@@ -0,0 +1,495 @@ ++/** ++ * @file camera_hm1375.h ++ * @brief camera driver file ++ * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author wudaochao ++ * @date 2013-04-26 ++ * @version 1.0 ++ * @ref ++ */ ++#ifndef __CAMERA_OV2710_H__ ++#define __CAMERA_OV2710_H__ ++ ++ ++#if defined (USE_CAMERA_OV2710) || defined (CONFIG_SENSOR_OV2710) ++ ++#undef DELAY_FLAG ++#undef END_FLAG ++#define DELAY_FLAG 0xfd // first parameter is 0xfe, then 2nd parameter is delay time count ++#define END_FLAG 0xfe // first parameter is 0xff, then parameter table is over ++ ++static const T_U16 INIT_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 UXGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SXGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 VGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CIF_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 QVGA_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 QCIF_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 QQVGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 PREV_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 RECORD_MODE_TAB[] = ++{ ++ 0x3103, 0x03, ++ 0x3008, 0x82, //reset ++ 0x3017, 0x7f, ++ 0x3018, 0xfc, ++ 0x3706, 0x61, ++ 0x3712, 0x0c, ++ 0x3630, 0x6d, ++ 0x3801, 0xb4, ++ 0x3621, 0x04, ++ 0x3604, 0x60, ++ 0x3603, 0xa7, ++ 0x3631, 0x26, ++ 0x3600, 0x04, ++ 0x3620, 0x37, ++ 0x3623, 0x00, ++ 0x3702, 0x9e, ++ 0x3703, 0x74, ++ 0x3704, 0x10, ++ 0x370d, 0x0f, ++ 0x3713, 0x8b, ++ 0x3714, 0x74, ++ 0x3710, 0x9e, ++ 0x3801, 0xc4, ++ 0x3605, 0x05, ++ 0x3606, 0x12, ++ 0x302d, 0x90, ++ 0x370b, 0x40, ++ 0x3716, 0x31, ++ 0x380d, 0x74, ++ 0x5181, 0x20, ++ 0x518f, 0x00, ++ 0x4301, 0xff, ++ 0x4303, 0x00, ++ 0x3a00, 0x78, ++ 0x300f, 0x88, ++ 0x3011, 0x28, ++ 0x3a1a, 0x06, ++ 0x3a18, 0x00, ++ 0x3a19, 0x7a, ++ 0x3a13, 0x54, ++ 0x382e, 0x0f, ++ 0x381a, 0x1a, ++ //aec ++ 0x3a0f, 0x40, ++ 0x3a10, 0x38, ++ 0x3a1b, 0x48, ++ 0x3a1e, 0x30, ++ 0x3a11, 0x90, ++ 0x3a1f, 0x10, ++ ++ //VGA_binning(640*480) Reference Setting 24M MCLK 30fps ++ //window ++ 0x381c, 0x10, ++ 0x381d, 0x42, ++ 0x381e, 0x3, ++ 0x381f, 0xc8, ++ 0x3820, 0xa, ++ 0x3821, 0x29, ++ 0x3800, 0x2, ++ 0x3801, 0xd6, ++ 0x3802, 0x0, ++ 0x3803, 0x5, ++ 0x3804, 0x2, ++ 0x3805, 0x80, ++ 0x3806, 0x1, ++ 0x3807, 0xe0, ++ 0x3808, 0x2, ++ 0x3809, 0x80, ++ 0x380a, 0x1, ++ 0x380b, 0xe0, ++ 0x380c, 0xa, ++ 0x380d, 0x84, ++ 0x380e, 0x1, ++ 0x380f, 0xf0, ++ 0x3810, 0x8, ++ 0x3811, 0x2, ++ //timing ++ 0x3818, 0xe1, //flip on, mirror on ++ 0x3621, 0xd4, ++ 0x3622, 0x8, ++ 0x370d, 0x4f, ++ 0x401c, 0x4, ++ 0x3012, 0x1, ++ 0x300f, 0x88, ++ 0x3011, 0x28, ++ 0x3010, 0x10, ++ //banding ++ 0x3a0a, 0x12, ++ 0x3a0b, 0x99, ++ 0x3a08, 0xf, ++ 0x3a09, 0x80, ++ 0x3a0d, 0x01, ++ 0x3a0e, 0x00, ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 RECORD_720P_TAB[] = ++{ ++ 0x3103, 0x03, ++ 0x3008, 0x82, //reset ++ 0x3017, 0x7f, ++ 0x3018, 0xfc, ++ 0x3706, 0x61, ++ 0x3712, 0x0c, ++ 0x3630, 0x6d, ++ 0x3801, 0xb4, ++ 0x3621, 0x04, ++ 0x3604, 0x60, ++ 0x3603, 0xa7, ++ 0x3631, 0x26, ++ 0x3600, 0x04, ++ 0x3620, 0x37, ++ 0x3623, 0x00, ++ 0x3702, 0x9e, ++ 0x3703, 0x74, ++ 0x3704, 0x10, ++ 0x370d, 0x0f, ++ 0x3713, 0x8b, ++ 0x3714, 0x74, ++ 0x3710, 0x9e, ++ 0x3801, 0xc4, ++ 0x3605, 0x05, ++ 0x3606, 0x12, ++ 0x302d, 0x90, ++ 0x370b, 0x40, ++ 0x3716, 0x31, ++ 0x380d, 0x74, ++ 0x5181, 0x20, ++ 0x518f, 0x00, ++ 0x4301, 0xff, ++ 0x4303, 0x00, ++ 0x3a00, 0x78, ++ 0x300f, 0x88, ++ 0x3011, 0x28, ++ 0x3a1a, 0x06, ++ 0x3a18, 0x00, ++ 0x3a19, 0x7a, ++ 0x3a13, 0x54, ++ 0x382e, 0x0f, ++ 0x381a, 0x1a, ++ //aec ++ 0x3a0f, 0x40, ++ 0x3a10, 0x38, ++ 0x3a1b, 0x48, ++ 0x3a1e, 0x30, ++ 0x3a11, 0x90, ++ 0x3a1f, 0x10, ++ ++ //720p(1280*720) Reference Setting 24M MCLK 20fps ++ //window ++ 0x381c, 0x10, ++ 0x381d, 0xb8, ++ 0x381e, 0x2, ++ 0x381f, 0xdc, ++ 0x3820, 0xa, ++ 0x3821, 0x29, ++ 0x3800, 0x1, ++ 0x3801, 0xc4, ++ 0x3802, 0x0, ++ 0x3803, 0x09, ++ 0x3804, 0x5, ++ 0x3805, 0x0, ++ 0x3806, 0x2, ++ 0x3807, 0xd0, ++ 0x3808, 0x5, ++ 0x3809, 0x0, ++ 0x380a, 0x2, ++ 0x380b, 0xd0, ++ 0x380c, 0x7, ++ 0x380d, 0x0, ++ 0x380e, 0x2, ++ 0x380f, 0xe8, ++ 0x3810, 0x10, ++ 0x3811, 0x6, ++ //timing ++ 0x3818, 0xe0, //flip on, mirror on ++ 0x3621, 0x14, ++ 0x3622, 0x8, ++ 0x370d, 0xf, ++ 0x401c, 0x8, ++ 0x3012, 0x1, ++ 0x300f, 0x88, ++ 0x3011, 0x28, ++ 0x3010, 0x20, ++ //banding ++ 0x3a0a, 0x9, ++ 0x3a0b, 0x4c, ++ 0x3a08, 0x7, ++ 0x3a09, 0xc0, ++ 0x3a0d, 0x04, ++ 0x3a0e, 0x05, ++ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Exposure Table ****************/ ++static const T_U16 EXPOSURE_WHOLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EXPOSURE_CENTER_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EXPOSURE_MIDDLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Brightness Table ****************/ ++static const T_U16 BRIGHTNESS_0_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 BRIGHTNESS_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Contrast Table ****************/ ++static const T_U16 CONTRAST_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 CONTRAST_7_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Saturation Table ****************/ ++static const T_U16 SATURATION_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SATURATION_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Sharpness Table ****************/ ++static const T_U16 SHARPNESS_0_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 SHARPNESS_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera AWB Table ****************/ ++static const T_U16 AWB_AUTO_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 AWB_SUNNY_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 AWB_CLOUDY_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 AWB_OFFICE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 AWB_HOME_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Effect Table ****************/ ++static const T_U16 EFFECT_NORMAL_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_SEPIA_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_ANTIQUE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_BLUISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_GREENISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_REDDISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_NEGATIVE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 EFFECT_BW_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera night/day mode ****************/ ++static const T_U16 DAY_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U16 NIGHT_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++#endif ++#endif +diff --git a/drivers/media/video/plat-anyka/camera_ov7725.c b/drivers/media/video/plat-anyka/camera_ov7725.c +new file mode 100755 +index 00000000..e6019762 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_ov7725.c +@@ -0,0 +1,983 @@ ++/** ++ * @file camera_ov7725.c ++ * @brief camera driver file ++ * Copyright (C) 2010 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author xia_wenting ++ * @date 2010-12-28 ++ * @version 1.0 ++ * @ref ++ */ ++#ifdef CONFIG_LINUX_AKSENSOR ++#include ++#include ++#include ++#include "camera_ov7725.h" ++#else ++#include "akdefine.h" ++#include "cam_com_sensor.h" ++#include "camera_ov7725.h" ++#include "Gpio_config.h" ++#endif ++ ++#if defined (USE_CAMERA_OV7725) || defined (CONFIG_SENSOR_OV7725) ++ ++#define CAM_EN_LEVEL 0 ++#define CAM_RESET_LEVEL 0 ++ ++#define CAMERA_SCCB_ADDR 0x42 ++#define CAMERA_OV7725_ID 0x7721 ++ ++#define CAMERA_MCLK_DIV 3 //192Mhz/(2*(3+1))=24Mhz ++ ++#define OV7725_CAMERA_MCLK 24 //24, 30fps/60fps; //32, 40fps ++ ++static T_CAMERA_TYPE camera_ov7725_type = CAMERA_P3M; ++static T_NIGHT_MODE night_mode = CAMERA_DAY_MODE; ++static T_CAMERA_MODE s_ov7725_CurMode = CAMERA_MODE_VGA; ++ ++static T_VOID camera_setbit(T_U8 reg, T_U8 bit, T_U8 value) ++{ ++ T_U8 tmp; ++ ++ tmp = sccb_read_data(CAMERA_SCCB_ADDR, reg); ++ if (value == 1) ++ tmp |= 0x1 << bit; ++ else ++ tmp &= ~(0x1 << bit); ++ sccb_write_data(CAMERA_SCCB_ADDR, reg, &tmp, 1); ++} ++ ++static T_BOOL camera_set_param(const T_U8 tabParameter[]) ++{ ++ int i = 0; ++ T_U8 temp_value; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ sccb_write_data(CAMERA_SCCB_ADDR, tabParameter[i], (T_U8 *)(&tabParameter[i + 1]), 1); ++ ++ if (!((tabParameter[i] == 0x12) && (tabParameter[i + 1] & 0x80)) ++ && !((tabParameter[i] == 0xc9) && (tabParameter[i + 1] & 0x60))) ++ { ++ temp_value = sccb_read_data(CAMERA_SCCB_ADDR, tabParameter[i]); ++ if (temp_value != tabParameter[i + 1]) ++ { ++ akprintf(C1, M_DRVSYS, "set parameter error!\n"); ++ akprintf(C1, M_DRVSYS, "reg 0x%x write data is 0x%x, read data is 0x%x!\n", tabParameter[i], tabParameter[i + 1], temp_value); ++ ++ return AK_FALSE; ++ } ++ } ++ } ++ i += 2; ++ } ++ ++ return AK_TRUE; ++} ++ ++static T_VOID camera_setup(const T_U8 tabParameter[]) ++{ ++ int i = 0; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ sccb_write_data(CAMERA_SCCB_ADDR, tabParameter[i], (T_U8 *)&tabParameter[i + 1], 1); ++ } ++ i += 2; ++ } ++} ++ ++static T_VOID cam_ov7725_open(T_VOID) ++{ ++ gpio_set_pin_dir(GPIO_CAMERA_AVDD, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_CHIP_ENABLE); ++ gpio_set_pin_dir(GPIO_CAMERA_CHIP_ENABLE, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, CAM_EN_LEVEL); ++ mini_delay(10); ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_RESET); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, CAM_RESET_LEVEL); ++ mini_delay(10); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, !CAM_RESET_LEVEL); ++ ++ mini_delay(20); ++} ++ ++static T_BOOL cam_ov7725_close(T_VOID) ++{ ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, !CAM_EN_LEVEL); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, !gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_INPUT); ++ ++ gpio_set_pin_dir(GPIO_I2C_SCL, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SCL, GPIO_LEVEL_LOW); ++ gpio_set_pin_dir(GPIO_I2C_SDA, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SDA, GPIO_LEVEL_LOW); ++ ++ return AK_TRUE; ++} ++ ++static T_U32 cam_ov7725_read_id(T_VOID) ++{ ++ T_U8 value = 0x00; ++ T_U32 id = 0; ++ ++ sccb_init(GPIO_I2C_SCL, GPIO_I2C_SDA); //init sccb first here!! ++ ++ value = sccb_read_data(CAMERA_SCCB_ADDR, 0x0a); ++ id = value << 8; ++ value = sccb_read_data(CAMERA_SCCB_ADDR, 0x0b); ++ id |= value; ++ ++ return id; ++} ++ ++/** ++ * @brief initialize the parameters of camera, should be done after reset and open camera to initialize ++ * @author xia_wenting ++ * @date 2010-12-28 ++ * @return T_BOOL ++ * @retval AK_TRUE if success, else AK_FALSE ++ */ ++static T_BOOL cam_ov7725_init(void) ++{ ++ if (!camera_set_param(INIT_TAB)) ++ { ++ return AK_FALSE; ++ } ++ else ++ { ++ night_mode = CAMERA_DAY_MODE; ++ return AK_TRUE; ++ } ++} ++ ++/** ++ * @brief Set camera mode to specify image quality, SXGA/VGA/CIF/ etc ++ * @author xia_wenting ++ * @date 2010-12-28 ++ * @param[in] mode mode value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov7725_set_mode(T_CAMERA_MODE mode) ++{ ++ s_ov7725_CurMode = mode; ++ switch(mode) ++ { ++ case CAMERA_MODE_VGA: ++ camera_setup(VGA_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_CIF: ++// camera_setup(CIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QVGA: ++// camera_setup(QVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_QCIF: ++// camera_setup(QCIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QQVGA: ++// camera_setup(QQVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_PREV: //preview mode ++ camera_setup(PREV_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_REC: //record mode ++ camera_setup(RECORD_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ default: ++ s_ov7725_CurMode = CAMERA_MODE_VGA; ++ akprintf(C1, M_DRVSYS, "set camera mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera exposure mode ++ * @author xia_wenting ++ * @date 2010-12-28 ++ * @param[in] exposure exposure mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov7725_set_exposure(T_CAMERA_EXPOSURE exposure) ++{ ++ switch(exposure) ++ { ++ case EXPOSURE_WHOLE: ++ camera_setup(EXPOSURE_WHOLE_TAB); ++ break; ++ case EXPOSURE_CENTER: ++ camera_setup(EXPOSURE_CENTER_TAB); ++ break; ++ case EXPOSURE_MIDDLE: ++ camera_setup(EXPOSURE_MIDDLE_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set exposure parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera brightness level ++ * @author xia_wenting ++ * @date 2010-12-28 ++ * @param[in] brightness brightness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov7725_set_brightness(T_CAMERA_BRIGHTNESS brightness) ++{ ++ switch(brightness) ++ { ++ case CAMERA_BRIGHTNESS_0: ++ camera_setup(BRIGHTNESS_0_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_1: ++ camera_setup(BRIGHTNESS_1_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_2: ++ camera_setup(BRIGHTNESS_2_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_3: ++ camera_setup(BRIGHTNESS_3_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_4: ++ camera_setup(BRIGHTNESS_4_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_5: ++ camera_setup(BRIGHTNESS_5_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_6: ++ camera_setup(BRIGHTNESS_6_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set brightness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera contrast level ++ * @author xia_wenting ++ * @date 2010-12-28 ++ * @param[in] contrast contrast value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov7725_set_contrast(T_CAMERA_CONTRAST contrast) ++{ ++ switch(contrast) ++ { ++ case CAMERA_CONTRAST_1: ++ camera_setup(CONTRAST_1_TAB); ++ break; ++ case CAMERA_CONTRAST_2: ++ camera_setup(CONTRAST_2_TAB); ++ break; ++ case CAMERA_CONTRAST_3: ++ camera_setup(CONTRAST_3_TAB); ++ break; ++ case CAMERA_CONTRAST_4: ++ camera_setup(CONTRAST_4_TAB); ++ break; ++ case CAMERA_CONTRAST_5: ++ camera_setup(CONTRAST_5_TAB); ++ break; ++ case CAMERA_CONTRAST_6: ++ camera_setup(CONTRAST_6_TAB); ++ break; ++ case CAMERA_CONTRAST_7: ++ camera_setup(CONTRAST_7_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set contrast parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera saturation level ++ * @author xia_wenting ++ * @date 2010-12-28 ++ * @param[in] saturation saturation value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov7725_set_saturation(T_CAMERA_SATURATION saturation) ++{ ++ switch(saturation) ++ { ++ case CAMERA_SATURATION_1: ++ camera_setup(SATURATION_1_TAB); ++ break; ++ case CAMERA_SATURATION_2: ++ camera_setup(SATURATION_2_TAB); ++ break; ++ case CAMERA_SATURATION_3: ++ camera_setup(SATURATION_3_TAB); ++ break; ++ case CAMERA_SATURATION_4: ++ camera_setup(SATURATION_4_TAB); ++ break; ++ case CAMERA_SATURATION_5: ++ camera_setup(SATURATION_5_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set saturation parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera sharpness level ++ * @author xia_wenting ++ * @date 2010-12-28 ++ * @param[in] sharpness sharpness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov7725_set_sharpness(T_CAMERA_SHARPNESS sharpness) ++{ ++ switch(sharpness) ++ { ++ case CAMERA_SHARPNESS_0: ++ camera_setup(SHARPNESS_0_TAB); ++ break; ++ case CAMERA_SHARPNESS_1: ++ camera_setup(SHARPNESS_1_TAB); ++ break; ++ case CAMERA_SHARPNESS_2: ++ camera_setup(SHARPNESS_2_TAB); ++ break; ++ case CAMERA_SHARPNESS_3: ++ camera_setup(SHARPNESS_3_TAB); ++ break; ++ case CAMERA_SHARPNESS_4: ++ camera_setup(SHARPNESS_4_TAB); ++ break; ++ case CAMERA_SHARPNESS_5: ++ camera_setup(SHARPNESS_5_TAB); ++ break; ++ case CAMERA_SHARPNESS_6: ++ camera_setup(SHARPNESS_6_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set sharpness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera AWB mode ++ * @author xia_wenting ++ * @date 2010-12-28 ++ * @param[in] awb AWB mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov7725_set_AWB(T_CAMERA_AWB awb) ++{ ++ switch(awb) ++ { ++ case AWB_AUTO: ++ camera_setup(AWB_AUTO_TAB); ++ break; ++ case AWB_SUNNY: ++ camera_setup(AWB_SUNNY_TAB); ++ break; ++ case AWB_CLOUDY: ++ camera_setup(AWB_CLOUDY_TAB); ++ break; ++ case AWB_OFFICE: ++ camera_setup(AWB_OFFICE_TAB); ++ break; ++ case AWB_HOME: ++ camera_setup(AWB_HOME_TAB); ++ break; ++ case AWB_NIGHT: ++ camera_setup(AWB_NIGHT_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set AWB mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera mirror mode ++ * @author xia_wenting ++ * @date 2010-12-28 ++ * @param[in] mirror mirror mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov7725_set_mirror(T_CAMERA_MIRROR mirror) ++{ ++ switch(mirror) ++ { ++ case CAMERA_MIRROR_V: ++ camera_setbit(0x1e, 4, 1); ++ camera_setbit(0x1e, 5, 0); ++ break; ++ case CAMERA_MIRROR_H: ++ camera_setbit(0x1e, 4, 0); ++ camera_setbit(0x1e, 5, 1); ++ break; ++ case CAMERA_MIRROR_NORMAL: ++ camera_setbit(0x1e, 4, 0); ++ camera_setbit(0x1e, 5, 0); ++ break; ++ case CAMERA_MIRROR_FLIP: ++ camera_setbit(0x1e, 4, 1); ++ camera_setbit(0x1e, 5, 1); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set mirror parameter error!\n"); ++ break; ++ } ++} ++ ++ ++/** ++ * @brief Set camera effect mode ++ * @author xia_wenting ++ * @date 2010-12-28 ++ * @param[in] effect effect mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov7725_set_effect(T_CAMERA_EFFECT effect) ++{ ++ switch(effect) ++ { ++ case CAMERA_EFFECT_NORMAL: ++ camera_setup(EFFECT_NORMAL_TAB); ++ break; ++ case CAMERA_EFFECT_SEPIA: ++ camera_setup(EFFECT_SEPIA_TAB); ++ break; ++ case CAMERA_EFFECT_ANTIQUE: ++ camera_setup(EFFECT_ANTIQUE_TAB); ++ break; ++ case CAMERA_EFFECT_BLUE: ++ camera_setup(EFFECT_BLUISH_TAB); ++ break; ++ case CAMERA_EFFECT_GREEN: ++ camera_setup(EFFECT_GREENISH_TAB); ++ break; ++ case CAMERA_EFFECT_RED: ++ camera_setup(EFFECT_REDDISH_TAB); ++ break; ++ case CAMERA_EFFECT_NEGATIVE: ++ camera_setup(EFFECT_NEGATIVE_TAB); ++ break; ++ case CAMERA_EFFECT_BW: ++ camera_setup(EFFECT_BW_TAB); ++ break; ++ case CAMERA_EFFECT_BWN: ++ camera_setup(EFFECT_BWN_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set camer effect parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief set camera window ++ * @author ++ * @date 2010-07-30 ++ * @param[in] srcWidth window width ++ * @param[in] srcHeight window height ++ * @return T_S32 ++ * @retval 0 if error mode ++ * @retval 1 if success ++ * @retval -1 if fail ++ */ ++static T_S32 cam_ov7725_set_digital_zoom(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_U16 hrefstart = 0, hrefstop = 0, vrefstart = 0, vrefstop = 0; ++ T_U8 hbit = 0, lstartbit = 0, lstopbit = 0; ++ T_CAMERA_MODE Cammode = s_ov7725_CurMode; ++ T_U8 Camera_window_table[] = ++ { ++ 0x17, 0, ++ 0x18, 0, ++ 0x32, 0, ++ 0x19, 0, ++ 0x1a, 0, ++ 0x03, 0, ++ END_FLAG, END_FLAG ++ }; ++ ++ akprintf(C1, M_DRVSYS, "set window size %d, %d, %d\r\n", Cammode, srcWidth, srcHeight); ++ ++ if(Cammode == CAMERA_MODE_VGA )//VGA_MODE ++ { ++ if((srcWidth == 640) && (srcHeight == 480)) ++ { ++ Camera_window_table[1] = 0x22;//0x13; //0x17; ++ Camera_window_table[3] = 0xa4;//0x01; //0x18 ++ Camera_window_table[5] = 0x00;//0xb6; //0x92; //0x32 from 0x92 to 0xb6 by lujie @061009 ++ Camera_window_table[7] = 0x07; //0x19 ++ Camera_window_table[9] = 0xf0; //0x1a ++ Camera_window_table[11] = 0x00; //0x00; //0x03 //0x00 from 0x0a to 0xb6 by lujie @061009 ++ if (camera_set_param(Camera_window_table) == AK_TRUE) ++ { ++ return 1; ++ } ++ else ++ { ++ return -1; ++ } ++ } ++ else ++ { ++ hrefstart = 158 + (640 - srcWidth) / 2; // by lujie 154 to 156 @061009 ++ hrefstop = hrefstart + srcWidth; ++ ++ vrefstart = 8 + (480 - srcHeight) / 2; ++ vrefstop = vrefstart + srcHeight; ++ } ++ } ++ else if(Cammode == CAMERA_MODE_QVGA )//QVGA_MODE ++ { ++ if((srcWidth == 320) && (srcHeight == 240)) ++ { ++ Camera_window_table[1] = 0x15; //0x17; ++ Camera_window_table[3] = 0x03; //0x18 ++ Camera_window_table[5] = 0x36; //0x32 ++ Camera_window_table[7] = 0x02; //0x19 ++ Camera_window_table[9] = 0x7a; //0x1a ++ Camera_window_table[11]= 0x0a; //0x03 ++ if (camera_set_param(Camera_window_table) == AK_TRUE) ++ { ++ return 1; ++ } ++ else ++ { ++ return -1; ++ } ++ } ++ else ++ { ++ hrefstart = 282 + (320 - srcWidth) / 2;//196 ++ hrefstop = hrefstart + srcWidth; //836 ++ ++ vrefstart = 8 + (240 - srcHeight) / 2 ; ++ vrefstop = vrefstart + srcHeight; ++ } ++ } ++ else if(Cammode == CAMERA_MODE_QQVGA ) //QQVGA_MODE ++ { ++ if((srcWidth == 160) && (srcHeight == 120)) ++ { ++ cam_ov7725_set_mode(CAMERA_MODE_QQVGA); ++ return 1; ++ } ++ hrefstart = 282 + (160 - srcWidth) * 2; ++ hrefstop = hrefstart + srcWidth; ++ ++ vrefstart = 8 + (120 - srcHeight) / 2 ; ++ vrefstop = vrefstart + srcHeight; ++ } ++ else ++ { ++ return 0; ++ } ++ ++ hbit = hrefstart >> 3; //Horizontal Frame start high 8-bit ++ lstartbit = hrefstart & 0x7; //Horizontal Frame start low 3-bit ++ ++ Camera_window_table[1] = hbit; ++ ++ if(Cammode != CAMERA_MODE_VGA && Cammode != CAMERA_MODE_SXGA) ++ { ++ if (hrefstop > 800) ++ { ++ hrefstop -=800; ++ } ++ } ++ hbit = hrefstop >> 3; //Horizontal Frame end high 8-bit ++ lstopbit = hrefstop & 0x7; //Horizontal Frame end low 3-bit ++ ++ Camera_window_table[3] = hbit; ++ ++ Camera_window_table[5] = 0x80 | lstartbit | (lstopbit << 3) ; ++ ++ hbit = vrefstart >> 2; //Vertical Frame start high 8-bit ++ lstartbit = vrefstart & 0x2; //Vertical Frame start low 2-bit ++ ++ Camera_window_table[7] = hbit; ++ ++ hbit = vrefstop >> 2; //Vertical Frame end high 8-bit ++ lstopbit = vrefstop & 0x2; //Vertical Frame end low 2-bit ++ ++ Camera_window_table[9] = hbit; ++ ++ Camera_window_table[11] = 0x0 | lstartbit | (lstopbit << 2); ++ ++ if (camera_set_param(Camera_window_table) == AK_TRUE) ++ { ++ return 1; ++ } ++ else ++ { ++ return -1; ++ } ++} ++ ++static T_VOID cam_ov7725_set_night_mode(T_NIGHT_MODE mode) ++{ ++ switch(mode) ++ { ++ case CAMERA_DAY_MODE: ++ camera_setup(DAY_MODE_TAB); ++ night_mode = CAMERA_DAY_MODE; ++ break; ++ case CAMERA_NIGHT_MODE: ++ camera_setup(NIGHT_MODE_TAB); ++ night_mode = CAMERA_NIGHT_MODE; ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set night mode parameter error!\n"); ++ break; ++ } ++} ++ ++static T_BOOL cam_ov7725_set_to_cap(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ ++ if ((srcWidth <= 160) && (srcHeight <= 120)) ++ { ++ Cammode = CAMERA_MODE_QQVGA; ++ } ++ else if ((srcWidth <= 176) && (srcHeight <= 144)) ++ { ++ Cammode = CAMERA_MODE_QCIF; ++ } ++ else if ((srcWidth <= 320) && (srcHeight <= 240)) ++ { ++ Cammode = CAMERA_MODE_QVGA; ++ } ++ else if ((srcWidth <= 352) && (srcHeight <= 288)) ++ { ++ Cammode = CAMERA_MODE_CIF; ++ } ++ else if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_VGA; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "30W camera dose not support such mode"); ++ return AK_FALSE; ++ } ++ ++ cam_ov7725_set_mode(Cammode); ++ cam_ov7725_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(200); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_ov7725_set_to_prev(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ cam_ov7725_set_mode(CAMERA_MODE_PREV); ++ cam_ov7725_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(200); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_ov7725_set_to_record(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ cam_ov7725_set_mode(CAMERA_MODE_REC); ++ cam_ov7725_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(200); ++ return AK_TRUE; ++} ++ ++static T_CAMERA_TYPE cam_ov7725_get_type(T_VOID) ++{ ++ return camera_ov7725_type; ++} ++ ++static T_VOID cam_ov7725_set_sensor_param(T_U32 cmd, T_U32 data) ++{ ++ T_U8 value; ++ ++ value = (T_U8)data; ++ sccb_write_data(CAMERA_SCCB_ADDR, (T_U8)cmd, &value, 1); ++} ++ ++static T_CAMERA_FUNCTION_HANDLER ov7725_function_handler = ++{ ++ OV7725_CAMERA_MCLK, ++ cam_ov7725_open, ++ cam_ov7725_close, ++ cam_ov7725_read_id, ++ cam_ov7725_init, ++ cam_ov7725_set_mode, ++ cam_ov7725_set_exposure, ++ cam_ov7725_set_brightness, ++ cam_ov7725_set_contrast, ++ cam_ov7725_set_saturation, ++ cam_ov7725_set_sharpness, ++ cam_ov7725_set_AWB, ++ cam_ov7725_set_mirror, ++ cam_ov7725_set_effect, ++ cam_ov7725_set_digital_zoom, ++ cam_ov7725_set_night_mode, ++ AK_NULL, ++ AK_NULL, ++ cam_ov7725_set_to_cap, ++ cam_ov7725_set_to_prev, ++ cam_ov7725_set_to_record, ++ cam_ov7725_get_type, ++ cam_ov7725_set_sensor_param ++}; ++ ++#ifndef CONFIG_LINUX_AKSENSOR ++static int camera_ov7725_reg(void) ++{ ++ camera_reg_dev(CAMERA_OV7725_ID, &ov7725_function_handler); ++ return 0; ++} ++ ++#ifdef __CC_ARM ++#pragma arm section rwdata = "__initcall_", zidata = "__initcall_" ++#endif ++module_init(camera_ov7725_reg) ++#ifdef __CC_ARM ++#pragma arm section ++#endif ++ ++#else ++ ++static const char * resolution_menu[] = { ++ [0] = "640x480", ++}; ++ ++static const char * hflip_menu[] = { ++ [0] = "normal", ++ [1] = "horizontal flip", ++}; ++ ++static const char * vflip_menu[] = { ++ [0] = "normal", ++ [1] = "vertical flip", ++}; ++ ++static const char * night_menu[] = { ++ [CAMERA_DAY_MODE] = "daylight", ++ [CAMERA_NIGHT_MODE] = "night", ++}; ++ ++static int ov7725_s_ctl(struct v4l2_ctrl *ctrl) ++{ ++ int ret = -EINVAL; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_CONTRAST: ++ if (ov7725_function_handler.cam_set_contrast_func) { ++ ov7725_function_handler.cam_set_contrast_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SATURATION: ++ if (ov7725_function_handler.cam_set_saturation_func) { ++ ov7725_function_handler.cam_set_saturation_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_HFLIP: ++ if (ov7725_function_handler.cam_set_mirror_func) { ++ ov7725_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_H : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_VFLIP: ++ if (ov7725_function_handler.cam_set_mirror_func) { ++ ov7725_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_V : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_NIGHTMODE: ++ if (ov7725_function_handler.cam_set_night_mode_func) { ++ ov7725_function_handler.cam_set_night_mode_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++}; ++ ++static struct v4l2_ctrl_ops ov7725_ctrl_ops = { ++ .s_ctrl = ov7725_s_ctl, ++}; ++ ++static const struct v4l2_ctrl_config ov7725_ctrls[] = { ++ { ++ .ops = &ov7725_ctrl_ops, ++ .id = V4L2_CID_HFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Horizontal Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(hflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = hflip_menu, ++ }, ++ { ++ .ops = &ov7725_ctrl_ops, ++ .id = V4L2_CID_VFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Vetical Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(vflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = vflip_menu, ++ }, ++ { ++ .ops = &ov7725_ctrl_ops, ++ .id = V4L2_CID_PICTURE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Resolution", ++ .min = 0, ++ .max = ARRAY_SIZE(resolution_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = resolution_menu, ++ }, ++ { ++ .ops = &ov7725_ctrl_ops, ++ .id = V4L2_CID_NIGHTMODE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Night mode", ++ .min = 0, ++ .max = ARRAY_SIZE(night_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = night_menu, ++ }, ++ { ++ .ops = &ov7725_ctrl_ops, ++ .id = V4L2_CID_CONTRAST, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Contrast", ++ .min = 0, ++ .max = CAMERA_CONTRAST_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ov7725_ctrl_ops, ++ .id = V4L2_CID_SATURATION, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Saturation", ++ .min = 0, ++ .max = CAMERA_SATURATION_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++}; ++ ++ ++/* ++ * supported format list ++ */ ++static const struct aksensor_color_format ov7725_formats[] = { ++ { ++ .code = V4L2_MBUS_FMT_YUYV8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_YVYU8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_UYVY8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++}; ++ ++static const struct aksensor_win_size ov7725_win[] = { ++ {.name = "VGA", .width = 640, .height = 480}, ++}; ++ ++ ++static struct sensor_info ov7725_sensor_info = { ++ .sensor_name = "ov7725", ++ .sensor_id = CAMERA_OV7725_ID, ++ .ctrls = ov7725_ctrls, ++ .nr_ctrls = ARRAY_SIZE(ov7725_ctrls), ++ .formats = ov7725_formats, ++ .num_formats = ARRAY_SIZE(ov7725_formats), ++ .resolution = ov7725_win, ++ .num_resolution = ARRAY_SIZE(ov7725_win), ++ .handler = &ov7725_function_handler, ++}; ++ ++static int ov7725_module_init(void) ++{ ++ return register_sensor(&ov7725_sensor_info); ++} ++module_init(ov7725_module_init) ++#endif ++ ++#endif +diff --git a/drivers/media/video/plat-anyka/camera_ov7725.h b/drivers/media/video/plat-anyka/camera_ov7725.h +new file mode 100755 +index 00000000..d658cf78 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_ov7725.h +@@ -0,0 +1,406 @@ ++/** ++ * @file camera_ov7725.h ++ * @brief camera driver file ++ * Copyright (C) 2010 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author xia_wenting ++ * @date 2011-04-22 ++ * @version 1.0 ++ * @ref ++ */ ++#ifndef __CAMERA_OV7725_H__ ++#define __CAMERA_OV7725_H__ ++ ++ ++#if defined (USE_CAMERA_OV7725) || defined (CONFIG_SENSOR_OV7725) ++ ++#undef DELAY_FLAG ++#undef END_FLAG ++#define DELAY_FLAG 0xFE // first parameter is 0xfe, then 2nd parameter is delay time count ++#define END_FLAG 0xFF // first parameter is 0xff, then parameter table is over ++ ++ ++static const T_U8 INIT_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 VGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CIF_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 QVGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 QCIF_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 QQVGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 PREV_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 RECORD_MODE_TAB[] = ++{ ++ //MCLK 24MHz, 30fps/60fps; MCLK 32MHz, 40fps ++ 0x12, 0x80, ++ 0x3d, 0x03, ++ ++ 0x17, 0x22, ++ 0x18, 0xa4, ++ 0x32, 0x00, ++ 0x19, 0x07, ++ 0x1a, 0xf0, ++ 0x03, 0x00, ++ ++ 0x29, 0xa0, ++ 0x2c, 0xf0, ++ 0x2a, 0x00, ++ 0x11, 0x01,//01, 30fps/40fps; //00, 60fps ++ 0x33, 0x00,//30fps; 0x66, 25fps, dummy row ++ 0x42, 0x7f, ++ 0x4d, 0x09, ++ 0x63, 0xe0, ++ 0x64, 0xff, ++ 0x65, 0x20, ++ 0x66, 0x00, ++ 0x67, 0x48, ++ 0x13, 0xff, ++ 0x0d, 0x41, ++ 0x0f, 0xc5, ++ 0x14, 0x11,//21, //4x ++ ++ 0x22, 0x97,//97, 30fps/60fps; //ca, 40fps ++ 0x23, 0x02,//02, 30fps; //01, 40fps/60fps ++ 0x24, 0x50,//70,//40, ++ 0x25, 0x40,//60,//30, ++ 0x26, 0xb2,//c3,//a1, ++ ++ 0x2b, 0x00, ++ 0x6b, 0xaa, ++ 0x90, 0x05, ++ 0x91, 0x01, ++ 0x92, 0x03, ++ 0x93, 0x00, ++ 0x94, 0xb0, ++ 0x95, 0x9d, ++ 0x96, 0x13, ++ 0x97, 0x16, ++ 0x98, 0x7b, ++ 0x99, 0x91, ++ 0x9a, 0x1e, ++ 0x9b, 0x08, ++ 0x9c, 0x25,//20, ++ 0x9e, 0x81, ++ 0xa6, 0x04, ++ 0x7e, 0x0c, ++ 0x7f, 0x16, ++ 0x80, 0x2a, ++ 0x81, 0x4e, ++ 0x82, 0x61, ++ 0x83, 0x6f, ++ 0x84, 0x7b, ++ 0x85, 0x86, ++ 0x86, 0x8e, ++ 0x87, 0x97, ++ 0x88, 0xa4, ++ 0x89, 0xaf, ++ 0x8a, 0xc5, ++ 0x8b, 0xd7, ++ 0x8c, 0xe8, ++ 0x8d, 0x20, ++ 0x0c, 0x00, ++ 0x6b, 0x90, ++ 0xa7, 0x70, ++ 0xa8, 0x70, ++ ++ //LC ++ 0x47, 0x90, ++ 0x48, 0x14, ++ 0x4a, 0x00, ++ 0x49, 0x08, //07, //g ++ 0x4b, 0x09, //08, //b ++ 0x4c, 0x0b, //0d, //r ++ 0x46, 0x05, ++ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Exposure Table ****************/ ++static const T_U8 EXPOSURE_WHOLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EXPOSURE_CENTER_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EXPOSURE_MIDDLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Brightness Table ****************/ ++static const T_U8 BRIGHTNESS_0_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Contrast Table ****************/ ++static const T_U8 CONTRAST_1_TAB[] = ++{ ++ 0x9c,0x08, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_2_TAB[] = ++{ ++ 0x9c,0x10, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_3_TAB[] = ++{ ++ 0x9c,0x15, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_4_TAB[] = ++{ ++ 0x9c,0x20, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_5_TAB[] = ++{ ++ 0x9c,0x24, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_6_TAB[] = ++{ ++ 0x9c,0x28, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_7_TAB[] = ++{ ++ 0x9c,0x40, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Saturation Table ****************/ ++static const T_U8 SATURATION_1_TAB[] = ++{ ++ 0xa6,0x06, ++ 0xa7,0x10, ++ 0xa8,0x30, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_2_TAB[] = ++{ ++ 0xa6,0x06, ++ 0xa7,0x40, ++ 0xa8,0x40, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_3_TAB[] = ++{ ++ 0xa6,0x04, ++ 0xa7,0x70, ++ 0xa8,0x70, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_4_TAB[] = ++{ ++ 0xa6,0x06, ++ 0xa7,0x80, ++ 0xa8,0x80, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_5_TAB[] = ++{ ++ 0xa6,0x06, ++ 0xa7,0x98, ++ 0xa8,0x98, ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Sharpness Table ****************/ ++static const T_U8 SHARPNESS_0_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera AWB Table ****************/ ++static const T_U8 AWB_AUTO_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_SUNNY_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_CLOUDY_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_OFFICE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_HOME_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_NIGHT_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Effect Table ****************/ ++static const T_U8 EFFECT_NORMAL_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_SEPIA_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_ANTIQUE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_BLUISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_GREENISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_REDDISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_NEGATIVE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_BW_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_BWN_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera night/day mode ****************/ ++static const T_U8 DAY_MODE_TAB[] = ++{ ++ 0x0e,0x79, ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 NIGHT_MODE_TAB[] = ++{ ++ 0x0e,0xF9, ++ END_FLAG, END_FLAG ++}; ++ ++#endif ++#endif +diff --git a/drivers/media/video/plat-anyka/camera_ov9712.c b/drivers/media/video/plat-anyka/camera_ov9712.c +new file mode 100755 +index 00000000..23f17522 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_ov9712.c +@@ -0,0 +1,1000 @@ ++/** ++ * @file camera_ov9712.c ++ * @brief camera driver file ++ * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author xia_wenting ++ * @date 2011-09-21 ++ * @version 1.0 ++ * @ref ++ */ ++#ifdef CONFIG_LINUX_AKSENSOR ++#include ++#include ++#include ++#include "camera_ov9712.h" ++#else ++#include "akdefine.h" ++#include "cam_com_sensor.h" ++#include "camera_ov9712.h" ++#include "Gpio_config.h" ++#endif ++ ++#if defined (USE_CAMERA_OV9712) || defined (CONFIG_SENSOR_OV9712) ++ ++#define CAM_EN_LEVEL 0 ++#define CAM_RESET_LEVEL 0 ++ ++#define CAMERA_SCCB_ADDR 0x60 ++#define CAMERA_OV9712_ID 0x9711 ++ ++#define OV9712_CAMERA_MCLK 24 ++ ++static T_CAMERA_TYPE camera_ov9712_type = CAMERA_2M; ++static T_NIGHT_MODE night_mode = CAMERA_DAY_MODE; ++static T_CAMERA_MODE s_ov9712_CurMode = CAMERA_MODE_VGA; ++ ++static T_VOID camera_setbit(T_U8 reg, T_U8 bit, T_U8 value) ++{ ++ T_U8 tmp; ++ ++ tmp = sccb_read_data(CAMERA_SCCB_ADDR, reg); ++ if (value == 1) ++ { ++ tmp |= 0x1 << bit; ++ } ++ else ++ { ++ tmp &= ~(0x1 << bit); ++ } ++ ++ sccb_write_data(CAMERA_SCCB_ADDR, reg, &tmp, 1); ++} ++ ++static T_BOOL camera_set_param(const T_U8 tabParameter[]) ++{ ++ int i = 0; ++ T_U8 temp_value; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ sccb_write_data(CAMERA_SCCB_ADDR, tabParameter[i], (T_U8 *)(&tabParameter[i + 1]), 1); ++ ++ if (!((tabParameter[i] == 0x12) && (tabParameter[i + 1] & 0x80))) ++ { ++ temp_value = sccb_read_data(CAMERA_SCCB_ADDR, tabParameter[i]); ++ if (temp_value != tabParameter[i + 1]) ++ { ++ akprintf(C1, M_DRVSYS, "set parameter error!\n"); ++ akprintf(C1, M_DRVSYS, "reg 0x%x write data is 0x%x, read data is 0x%x!\n", tabParameter[i], tabParameter[i + 1], temp_value); ++ ++ return AK_FALSE; ++ } ++ } ++ } ++ ++ i += 2; ++ } ++ ++ return AK_TRUE; ++} ++ ++static T_VOID camera_setup(const T_U8 tabParameter[]) ++{ ++ int i = 0; ++ ++ while (1) ++ { ++ if ((END_FLAG == tabParameter[i]) && (END_FLAG == tabParameter[i + 1])) ++ { ++ break; ++ } ++ else if (DELAY_FLAG == tabParameter[i]) ++ { ++ mini_delay(tabParameter[i + 1]); ++ } ++ else ++ { ++ sccb_write_data(CAMERA_SCCB_ADDR, tabParameter[i], (T_U8 *)&tabParameter[i + 1], 1); ++ } ++ ++ i += 2; ++ } ++} ++ ++static T_VOID cam_ov9712_open(T_VOID) ++{ ++ gpio_set_pin_dir(GPIO_CAMERA_AVDD, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_CHIP_ENABLE); ++ gpio_set_pin_dir(GPIO_CAMERA_CHIP_ENABLE, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, CAM_EN_LEVEL); ++ mini_delay(10); ++ ++ gpio_set_pin_as_gpio(GPIO_CAMERA_RESET); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, CAM_RESET_LEVEL); ++ mini_delay(10); ++ gpio_set_pin_level(GPIO_CAMERA_RESET, !CAM_RESET_LEVEL); ++ ++ mini_delay(20); ++} ++ ++static T_BOOL cam_ov9712_close(T_VOID) ++{ ++ //sccb software standby mode ++ T_U8 Reg0x3d = 0x48; ++ T_U8 Reg0xc3 = 0x00; ++ ++ sccb_write_data(CAMERA_SCCB_ADDR, 0x3d, &Reg0x3d, 1); ++ sccb_write_data(CAMERA_SCCB_ADDR, 0xc3, &Reg0xc3, 1); ++ ++ gpio_set_pin_level(GPIO_CAMERA_CHIP_ENABLE, !CAM_EN_LEVEL); ++ gpio_set_pin_level(GPIO_CAMERA_AVDD, !gpio_pin_get_ActiveLevel(GPIO_CAMERA_AVDD)); ++ gpio_set_pin_dir(GPIO_CAMERA_RESET, GPIO_DIR_INPUT); ++ ++ gpio_set_pin_dir(GPIO_I2C_SCL, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SCL, GPIO_LEVEL_LOW); ++ gpio_set_pin_dir(GPIO_I2C_SDA, GPIO_DIR_OUTPUT); ++ gpio_set_pin_level(GPIO_I2C_SDA, GPIO_LEVEL_LOW); ++ ++ return AK_TRUE; ++} ++ ++static T_U32 cam_ov9712_read_id(T_VOID) ++{ ++ T_U8 value = 0x00; ++ T_U32 id = 0; ++ ++ sccb_init(GPIO_I2C_SCL, GPIO_I2C_SDA); //init sccb first here!! ++ ++ value = sccb_read_data(CAMERA_SCCB_ADDR, 0x0a); ++ id = value << 8; ++ value = sccb_read_data(CAMERA_SCCB_ADDR, 0x0b); ++ id |= value; ++ ++ return id; ++} ++ ++/** ++ * @brief initialize the parameters of camera, should be done after reset and open camera to initialize ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @return T_BOOL ++ * @retval AK_TRUE if success, else AK_FALSE ++ */ ++static T_BOOL cam_ov9712_init(void) ++{ ++ if (!camera_set_param(INIT_TAB)) ++ { ++ return AK_FALSE; ++ } ++ else ++ { ++ night_mode = CAMERA_DAY_MODE; ++ return AK_TRUE; ++ } ++} ++ ++/** ++ * @brief Set camera mode to specify image quality, SXGA/VGA/CIF etc ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] mode mode value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov9712_set_mode(T_CAMERA_MODE mode) ++{ ++ s_ov9712_CurMode = mode; ++ ++ switch(mode) ++ { ++ case CAMERA_MODE_UXGA: ++ camera_setup(UXGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_SXGA: ++ camera_setup(SXGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_VGA: ++ camera_setup(VGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_CIF: ++ camera_setup(CIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QVGA: ++ camera_setup(QVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_QCIF: ++ camera_setup(QCIF_MODE_TAB); ++ break; ++ case CAMERA_MODE_QQVGA: ++ camera_setup(QQVGA_MODE_TAB); ++ break; ++ case CAMERA_MODE_PREV: ++ camera_setup(PREV_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_REC: ++ camera_setup(RECORD_MODE_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ case CAMERA_MODE_720P: ++ camera_setup(RECORD_720P_TAB); ++ ++ if (CAMERA_NIGHT_MODE == night_mode) ++ { ++ camera_setup(NIGHT_MODE_TAB); ++ } ++ break; ++ default: ++ s_ov9712_CurMode = CAMERA_MODE_VGA; ++ akprintf(C1, M_DRVSYS, "set camera mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera exposure mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] exposure exposure mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov9712_set_exposure(T_CAMERA_EXPOSURE exposure) ++{ ++ switch(exposure) ++ { ++ case EXPOSURE_WHOLE: ++ camera_setup(EXPOSURE_WHOLE_TAB); ++ break; ++ case EXPOSURE_CENTER: ++ camera_setup(EXPOSURE_CENTER_TAB); ++ break; ++ case EXPOSURE_MIDDLE: ++ camera_setup(EXPOSURE_MIDDLE_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set exposure parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera brightness level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] brightness brightness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov9712_set_brightness(T_CAMERA_BRIGHTNESS brightness) ++{ ++ switch(brightness) ++ { ++ case CAMERA_BRIGHTNESS_0: ++ camera_setup(BRIGHTNESS_0_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_1: ++ camera_setup(BRIGHTNESS_1_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_2: ++ camera_setup(BRIGHTNESS_2_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_3: ++ camera_setup(BRIGHTNESS_3_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_4: ++ camera_setup(BRIGHTNESS_4_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_5: ++ camera_setup(BRIGHTNESS_5_TAB); ++ break; ++ case CAMERA_BRIGHTNESS_6: ++ camera_setup(BRIGHTNESS_6_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set brightness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera contrast level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] contrast contrast value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov9712_set_contrast(T_CAMERA_CONTRAST contrast) ++{ ++ switch(contrast) ++ { ++ case CAMERA_CONTRAST_1: ++ camera_setup(CONTRAST_1_TAB); ++ break; ++ case CAMERA_CONTRAST_2: ++ camera_setup(CONTRAST_2_TAB); ++ break; ++ case CAMERA_CONTRAST_3: ++ camera_setup(CONTRAST_3_TAB); ++ break; ++ case CAMERA_CONTRAST_4: ++ camera_setup(CONTRAST_4_TAB); ++ break; ++ case CAMERA_CONTRAST_5: ++ camera_setup(CONTRAST_5_TAB); ++ break; ++ case CAMERA_CONTRAST_6: ++ camera_setup(CONTRAST_6_TAB); ++ break; ++ case CAMERA_CONTRAST_7: ++ camera_setup(CONTRAST_7_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set contrast parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera saturation level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] saturation saturation value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov9712_set_saturation(T_CAMERA_SATURATION saturation) ++{ ++ switch(saturation) ++ { ++ case CAMERA_SATURATION_1: ++ camera_setup(SATURATION_1_TAB); ++ break; ++ case CAMERA_SATURATION_2: ++ camera_setup(SATURATION_2_TAB); ++ break; ++ case CAMERA_SATURATION_3: ++ camera_setup(SATURATION_3_TAB); ++ break; ++ case CAMERA_SATURATION_4: ++ camera_setup(SATURATION_4_TAB); ++ break; ++ case CAMERA_SATURATION_5: ++ camera_setup(SATURATION_5_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set saturation parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera sharpness level ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] sharpness sharpness value ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov9712_set_sharpness(T_CAMERA_SHARPNESS sharpness) ++{ ++ switch(sharpness) ++ { ++ case CAMERA_SHARPNESS_0: ++ camera_setup(SHARPNESS_0_TAB); ++ break; ++ case CAMERA_SHARPNESS_1: ++ camera_setup(SHARPNESS_1_TAB); ++ break; ++ case CAMERA_SHARPNESS_2: ++ camera_setup(SHARPNESS_2_TAB); ++ break; ++ case CAMERA_SHARPNESS_3: ++ camera_setup(SHARPNESS_3_TAB); ++ break; ++ case CAMERA_SHARPNESS_4: ++ camera_setup(SHARPNESS_4_TAB); ++ break; ++ case CAMERA_SHARPNESS_5: ++ camera_setup(SHARPNESS_5_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set sharpness parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera AWB mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] awb AWB mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov9712_set_AWB(T_CAMERA_AWB awb) ++{ ++ switch(awb) ++ { ++ case AWB_AUTO: ++ camera_setup(AWB_AUTO_TAB); ++ break; ++ case AWB_SUNNY: ++ camera_setup(AWB_SUNNY_TAB); ++ break; ++ case AWB_CLOUDY: ++ camera_setup(AWB_CLOUDY_TAB); ++ break; ++ case AWB_OFFICE: ++ camera_setup(AWB_OFFICE_TAB); ++ break; ++ case AWB_HOME: ++ camera_setup(AWB_HOME_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set AWB mode parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera mirror mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] mirror mirror mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov9712_set_mirror(T_CAMERA_MIRROR mirror) ++{ ++ switch(mirror) ++ { ++ case CAMERA_MIRROR_V: ++ camera_setbit(0x12, 4, 1); ++ camera_setbit(0x12, 5, 0); ++ break; ++ case CAMERA_MIRROR_H: ++ camera_setbit(0x12, 4, 0); ++ camera_setbit(0x12, 5, 1); ++ break; ++ case CAMERA_MIRROR_NORMAL: ++ camera_setbit(0x12, 4, 0); ++ camera_setbit(0x12, 5, 0); ++ break; ++ case CAMERA_MIRROR_FLIP: ++ camera_setbit(0x12, 4, 1); ++ camera_setbit(0x12, 5, 1); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set mirror parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief Set camera effect mode ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] effect effect mode ++ * @return T_VOID ++ * @retval ++ */ ++static T_VOID cam_ov9712_set_effect(T_CAMERA_EFFECT effect) ++{ ++ switch(effect) ++ { ++ case CAMERA_EFFECT_NORMAL: ++ camera_setup(EFFECT_NORMAL_TAB); ++ break; ++ case CAMERA_EFFECT_SEPIA: ++ camera_setup(EFFECT_SEPIA_TAB); ++ break; ++ case CAMERA_EFFECT_ANTIQUE: ++ camera_setup(EFFECT_ANTIQUE_TAB); ++ break; ++ case CAMERA_EFFECT_BLUE: ++ camera_setup(EFFECT_BLUISH_TAB); ++ break; ++ case CAMERA_EFFECT_GREEN: ++ camera_setup(EFFECT_GREENISH_TAB); ++ break; ++ case CAMERA_EFFECT_RED: ++ camera_setup(EFFECT_REDDISH_TAB); ++ break; ++ case CAMERA_EFFECT_NEGATIVE: ++ camera_setup(EFFECT_NEGATIVE_TAB); ++ break; ++ case CAMERA_EFFECT_BW: ++ camera_setup(EFFECT_BW_TAB); ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set camer effect parameter error!\n"); ++ break; ++ } ++} ++ ++/** ++ * @brief set camera window ++ * @author xia_wenting ++ * @date 2011-03-22 ++ * @param[in] srcWidth window width ++ * @param[in] srcHeight window height ++ * @return T_S32 ++ * @retval 0 if error mode ++ * @retval 1 if success ++ * @retval -1 if failed ++ */ ++static T_S32 cam_ov9712_set_digital_zoom(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ return 1; ++} ++ ++static T_VOID cam_ov9712_set_night_mode(T_NIGHT_MODE mode) ++{ ++ switch(mode) ++ { ++ case CAMERA_DAY_MODE: ++ camera_setup(DAY_MODE_TAB); ++ night_mode = CAMERA_DAY_MODE; ++ break; ++ case CAMERA_NIGHT_MODE: ++ camera_setup(NIGHT_MODE_TAB); ++ night_mode = CAMERA_NIGHT_MODE; ++ break; ++ default: ++ akprintf(C1, M_DRVSYS, "set night mode parameter error!\n"); ++ break; ++ } ++} ++ ++static T_BOOL cam_ov9712_set_to_cap(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ ++ if ((srcWidth <= 160) && (srcHeight <= 120)) ++ { ++ Cammode = CAMERA_MODE_QQVGA; ++ } ++ else if ((srcWidth <= 176) && (srcHeight <= 144)) ++ { ++ Cammode = CAMERA_MODE_QCIF; ++ } ++ else if ((srcWidth <= 320) && (srcHeight <= 240)) ++ { ++ Cammode = CAMERA_MODE_QVGA; ++ } ++ else if ((srcWidth <= 352) && (srcHeight <= 288)) ++ { ++ Cammode = CAMERA_MODE_CIF; ++ } ++ else if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_VGA; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 720)) ++ { ++ Cammode = CAMERA_MODE_720P; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 1024)) ++ { ++ Cammode = CAMERA_MODE_SXGA; ++ } ++ else if ((srcWidth <= 1600) && (srcHeight <= 1200)) ++ { ++ Cammode = CAMERA_MODE_UXGA; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "ov9712 unsupport %d & %d mode!\n", srcWidth, srcHeight); ++ return AK_FALSE; ++ } ++ ++ cam_ov9712_set_mode(Cammode); ++ cam_ov9712_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_ov9712_set_to_prev(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ cam_ov9712_set_mode(CAMERA_MODE_PREV); ++ cam_ov9712_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_BOOL cam_ov9712_set_to_record(T_U32 srcWidth, T_U32 srcHeight) ++{ ++ T_CAMERA_MODE Cammode; ++ ++ if ((srcWidth <= 320) && (srcHeight <= 240)) ++ { ++ Cammode = CAMERA_MODE_QVGA; ++ } ++ else if ((srcWidth <= 640) && (srcHeight <= 480)) ++ { ++ Cammode = CAMERA_MODE_REC; ++ } ++ else if ((srcWidth <= 1280) && (srcHeight <= 720)) ++ { ++ Cammode = CAMERA_MODE_720P; ++ } ++ else ++ { ++ akprintf(C1, M_DRVSYS, "200W camera dose not support such mode"); ++ return AK_FALSE; ++ } ++ ++ cam_ov9712_set_mode(Cammode); ++ cam_ov9712_set_digital_zoom(srcWidth, srcHeight); ++ mini_delay(300); ++ return AK_TRUE; ++} ++ ++static T_CAMERA_TYPE cam_ov9712_get_type(T_VOID) ++{ ++ return camera_ov9712_type; ++} ++ ++static T_VOID cam_ov9712_set_sensor_param(T_U32 cmd, T_U32 data) ++{ ++ T_U8 value; ++ ++ value = (T_U8)data; ++ sccb_write_data(CAMERA_SCCB_ADDR, (T_U8)cmd, &value, 1); ++} ++ ++static T_CAMERA_FUNCTION_HANDLER ov9712_function_handler = ++{ ++ OV9712_CAMERA_MCLK, ++ cam_ov9712_open, ++ cam_ov9712_close, ++ cam_ov9712_read_id, ++ cam_ov9712_init, ++ cam_ov9712_set_mode, ++ cam_ov9712_set_exposure, ++ cam_ov9712_set_brightness, ++ cam_ov9712_set_contrast, ++ cam_ov9712_set_saturation, ++ cam_ov9712_set_sharpness, ++ cam_ov9712_set_AWB, ++ cam_ov9712_set_mirror, ++ cam_ov9712_set_effect, ++ cam_ov9712_set_digital_zoom, ++ cam_ov9712_set_night_mode, ++ AK_NULL, ++ AK_NULL, ++ cam_ov9712_set_to_cap, ++ cam_ov9712_set_to_prev, ++ cam_ov9712_set_to_record, ++ cam_ov9712_get_type, ++ cam_ov9712_set_sensor_param ++}; ++ ++#ifndef CONFIG_LINUX_AKSENSOR ++static int camera_ov9712_reg(void) ++{ ++ camera_reg_dev(CAMERA_OV9712_ID, &ov9712_function_handler); ++ return 0; ++} ++ ++#ifdef __CC_ARM ++#pragma arm section rwdata = "__initcall_", zidata = "__initcall_" ++#endif ++module_init(camera_ov9712_reg) ++#ifdef __CC_ARM ++#pragma arm section ++#endif ++ ++#else ++static const char * awb_menu[] = { ++ [AWB_AUTO] = "auto", ++ [AWB_SUNNY] = "sunny", ++ [AWB_CLOUDY] = "cloudy", ++ [AWB_OFFICE] = "office", ++ [AWB_HOME] = "home", ++ [AWB_NIGHT] = "night", ++}; ++ ++static const char * effect_menu[] = { ++ [CAMERA_EFFECT_NORMAL] = "normal", ++ [CAMERA_EFFECT_SEPIA] = "sepia", ++ [CAMERA_EFFECT_ANTIQUE] = "antique", ++ [CAMERA_EFFECT_BLUE] = "blue", ++ [CAMERA_EFFECT_GREEN] = "green", ++ [CAMERA_EFFECT_RED] = "red", ++ [CAMERA_EFFECT_NEGATIVE] = "negative", ++ [CAMERA_EFFECT_BW] = "bw", ++ [CAMERA_EFFECT_BWN] = "bwn", ++ [CAMERA_EFFECT_AQUA] = "aqua", ++ [CAMERA_EFFECT_COOL] = "cool", ++ [CAMERA_EFFECT_WARM] = "warm", ++}; ++ ++static const char * resolution_menu[] = { ++ [0] = "1280x720", ++ [1] = "640x480", ++}; ++ ++static const char * hflip_menu[] = { ++ [0] = "normal", ++ [1] = "horizontal flip", ++}; ++ ++static const char * vflip_menu[] = { ++ [0] = "normal", ++ [1] = "vertical flip", ++}; ++ ++static const char * night_menu[] = { ++ [CAMERA_DAY_MODE] = "daylight", ++ [CAMERA_NIGHT_MODE] = "night", ++}; ++ ++static int ov9712_s_ctl(struct v4l2_ctrl *ctrl) ++{ ++ int ret = -EINVAL; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ if (ov9712_function_handler.cam_set_AWB_func) { ++ ov9712_function_handler.cam_set_AWB_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_COLORFX: ++ if (ov9712_function_handler.cam_set_effect_func) { ++ ov9712_function_handler.cam_set_effect_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ ++ case V4L2_CID_BRIGHTNESS: ++ if (ov9712_function_handler.cam_set_brightness_func) { ++ ov9712_function_handler.cam_set_brightness_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_CONTRAST: ++ if (ov9712_function_handler.cam_set_contrast_func) { ++ ov9712_function_handler.cam_set_contrast_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SATURATION: ++ if (ov9712_function_handler.cam_set_saturation_func) { ++ ov9712_function_handler.cam_set_saturation_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_SHARPNESS: ++ if (ov9712_function_handler.cam_set_sharpness_func) { ++ ov9712_function_handler.cam_set_sharpness_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_HFLIP: ++ if (ov9712_function_handler.cam_set_mirror_func) { ++ ov9712_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_H : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_VFLIP: ++ if (ov9712_function_handler.cam_set_mirror_func) { ++ ov9712_function_handler.cam_set_mirror_func( ++ ctrl->val ? CAMERA_MIRROR_V : CAMERA_MIRROR_NORMAL); ++ ret = 0; ++ } ++ break; ++ case V4L2_CID_NIGHTMODE: ++ if (ov9712_function_handler.cam_set_night_mode_func) { ++ ov9712_function_handler.cam_set_night_mode_func(ctrl->val); ++ ret = 0; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++}; ++ ++static struct v4l2_ctrl_ops ov9712_ctrl_ops = { ++ .s_ctrl = ov9712_s_ctl, ++}; ++ ++static const struct v4l2_ctrl_config ov9712_ctrls[] = { ++ { ++ .ops = &ov9712_ctrl_ops, ++ .id = V4L2_CID_AUTO_WHITE_BALANCE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "AWB", ++ .min = 0, ++ .max = ARRAY_SIZE(awb_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = awb_menu, ++ }, ++ { ++ .ops = &ov9712_ctrl_ops, ++ .id = V4L2_CID_COLORFX, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Effect", ++ .min = 0, ++ .max = ARRAY_SIZE(effect_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = effect_menu, ++ }, ++ { ++ .ops = &ov9712_ctrl_ops, ++ .id = V4L2_CID_HFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Horizontal Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(hflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = hflip_menu, ++ }, ++ { ++ .ops = &ov9712_ctrl_ops, ++ .id = V4L2_CID_VFLIP, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Vetical Flip", ++ .min = 0, ++ .max = ARRAY_SIZE(vflip_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = vflip_menu, ++ }, ++ { ++ .ops = &ov9712_ctrl_ops, ++ .id = V4L2_CID_PICTURE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Resolution", ++ .min = 0, ++ .max = ARRAY_SIZE(resolution_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = resolution_menu, ++ }, ++ { ++ .ops = &ov9712_ctrl_ops, ++ .id = V4L2_CID_NIGHTMODE, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Night mode", ++ .min = 0, ++ .max = ARRAY_SIZE(night_menu) - 1, ++ .step = 0, ++ .def = 0, ++ .flags = 0, ++ .menu_skip_mask = 0, ++ .qmenu = night_menu, ++ }, ++ { ++ .ops = &ov9712_ctrl_ops, ++ .id = V4L2_CID_BRIGHTNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Brightness", ++ .min = 0, ++ .max = CAMERA_BRIGHTNESS_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ov9712_ctrl_ops, ++ .id = V4L2_CID_CONTRAST, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Contrast", ++ .min = 0, ++ .max = CAMERA_CONTRAST_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ov9712_ctrl_ops, ++ .id = V4L2_CID_SATURATION, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Saturation", ++ .min = 0, ++ .max = CAMERA_SATURATION_NUM -1, ++ .step = 1, ++ .def = 0, ++ }, ++ { ++ .ops = &ov9712_ctrl_ops, ++ .id = V4L2_CID_SHARPNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Sharpness", ++ .min = 0, ++ .max = CAMERA_SHARPNESS_NUM -1, ++ .step = 1, ++ .def = 0, ++ } ++}; ++ ++ ++/* ++ * supported format list ++ */ ++static const struct aksensor_color_format ov9712_formats[] = { ++ { ++ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB565_2X8_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB565_2X8_BE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++}; ++ ++static const struct aksensor_win_size ov9712_win[] = { ++ {.name = "VGA", .width = 640, .height = 480}, ++ {.name = "720P", .width = 1280, .height = 720}, ++}; ++ ++ ++static struct sensor_info ov9712_sensor_info = { ++ .sensor_name = "ov9712", ++ .sensor_id = CAMERA_OV9712_ID, ++ .ctrls = ov9712_ctrls, ++ .nr_ctrls = ARRAY_SIZE(ov9712_ctrls), ++ .formats = ov9712_formats, ++ .num_formats = ARRAY_SIZE(ov9712_formats), ++ .resolution = ov9712_win, ++ .num_resolution = ARRAY_SIZE(ov9712_win), ++ .handler = &ov9712_function_handler, ++}; ++ ++static int ov9712_module_init(void) ++{ ++ return register_sensor(&ov9712_sensor_info); ++} ++module_init(ov9712_module_init) ++#endif ++ ++#endif ++ ++ +diff --git a/drivers/media/video/plat-anyka/camera_ov9712.h b/drivers/media/video/plat-anyka/camera_ov9712.h +new file mode 100755 +index 00000000..a8711fc2 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/camera_ov9712.h +@@ -0,0 +1,458 @@ ++/** ++ * @file camera_ov9712.h ++ * @brief camera driver file ++ * Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd ++ * @author xia_wenting ++ * @date 2011-10-26 ++ * @version 1.0 ++ * @ref ++ */ ++#ifndef __CAMERA_OV9712_H__ ++#define __CAMERA_OV9712_H__ ++ ++ ++#if defined (USE_CAMERA_OV9712) || defined (CONFIG_SENSOR_OV9712) ++ ++#undef DELAY_FLAG ++#undef END_FLAG ++#define DELAY_FLAG 0xfd // first parameter is 0xfe, then 2nd parameter is delay time count ++#define END_FLAG 0xfe // first parameter is 0xff, then parameter table is over ++ ++static const T_U8 INIT_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 UXGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SXGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 VGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CIF_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 QVGA_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 QCIF_MODE_TAB[] = ++{ ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 QQVGA_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 PREV_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 RECORD_MODE_TAB[] = ++{ ++ //640*480 Reference Setting 24M MCLK 15fps ++ //Reset ++ 0x12, 0x80, ++ 0x09, 0x10, ++ //Core Settings ++ 0x1e, 0x07, ++ 0x5f, 0x18, ++ 0x69, 0x04, ++ 0x65, 0x2a, ++ 0x68, 0x0a, ++ 0x39, 0x28, ++ 0x4d, 0x90, ++ 0xc1, 0x80, ++ 0x0c, 0x30, ++ 0x6d, 0x02, ++ //DSP ++ 0x96, 0xf1, //DSP options enable ++ 0xbc, 0x68, ++ //Resolution and Format ++ 0x12, 0x00, ++ 0x3b, 0x00, ++ 0x97, 0x80, ++ 0x17, 0x25, ++ 0x18, 0xA2, ++ 0x19, 0x01, ++ 0x1a, 0xCA, ++ 0x03, 0x03, ++ 0x04, 0xc8,//flip on, mirror on ++ 0x32, 0x07, ++ 0x98, 0x40, ++ 0x99, 0xA0, ++ 0x9a, 0x01, ++ 0x57, 0x00, ++ 0x58, 0x78, ++ 0x59, 0x50, ++ 0x4c, 0x13, ++ 0x4b, 0x36, ++ 0x3d, 0x3c, ++ 0x3e, 0x03, ++ 0xbd, 0x50, ++ 0xbe, 0x78, ++ //AWB ++ //Lens Correction ++ //YAVG ++ 0x4e, 0x55, //AVERAGE ++ 0x4f, 0x55, ++ 0x50, 0x55, ++ 0x51, 0x55, ++ 0x24, 0x60, //Exposure windows ++ 0x25, 0x50, ++ 0x26, 0xa1, ++ //Clock ++ 0x5c, 0x59, ++ 0x5d, 0x00, ++ 0x11, 0x01, ++ 0x2a, 0x98, ++ 0x2b, 0x06, ++ 0x2d, 0x00, ++ 0x2e, 0x00, ++ //General ++ 0x13, 0x85, ++ 0x14, 0x40, //Gain Ceiling 8X ++ 0x09, 0x00, ++ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 RECORD_720P_TAB[] = ++{ ++ //Reset ++ 0x12, 0x80, ++ 0x09, 0x10, ++ //Core Settings ++ 0x1e, 0x07, ++ 0x5f, 0x18, ++ 0x69, 0x04, ++ 0x65, 0x2a, ++ 0x68, 0x0a, ++ 0x39, 0x28, ++ 0x4d, 0x90, ++ 0xc1, 0x80, ++ 0x0c, 0x30, ++ 0x6d, 0x02, ++ //DSP ++ 0x96, 0xf9, //0x01 -->manual:0xf9 ++ //0x96, 0xcf, //0x01 -->manual:0xf9 ++ 0xbc, 0x68, ++ //Resolution and Format ++ 0x12, 0x00, ++ 0x3b, 0x00, ++ 0x97, 0x80, ++ 0x17, 0x25, ++ 0x18, 0xA2, ++ 0x19, 0x01, ++ 0x1a, 0xCA, ++ 0x03, 0x01, ++ 0x04, 0xc8, //flip on, mirror on ++ 0x32, 0x07, ++ 0x98, 0x00, ++ 0x99, 0x28, ++ 0x9a, 0x00, ++ 0x57, 0x00, ++ 0x58, 0xB4, ++ 0x59, 0xA0, ++ 0x4c, 0x13, ++ 0x4b, 0x36, ++ 0x3d, 0x3c, ++ 0x3e, 0x03, ++ 0xbd, 0xA0, ++ 0xbe, 0xb4, ++ ++ //YAVG ++ 0x4e, 0x55, ++ 0x4f, 0x55, ++ 0x50, 0x55, ++ 0x51, 0x55, ++ 0x24, 0x60, ++ 0x25, 0x50, ++ 0x26, 0xa1, ++ ++ //Clock ++ 0x5c, 0x52, //0x52-->manual:0x59 ++ 0x5d, 0x00, ++ 0x11, 0x01, ++ 0x2a, 0x98, ++ 0x2b, 0x06, ++ 0x2d, 0x00, ++ 0x2e, 0x00, ++ //General ++// 0x13, 0xa5, //0xa5 -->manual: 0x85 ++// 0x14, 0x40, ++ 0x13, 0xad, ++ 0x14, 0x48, ++ //Banding ++ 0x4a, 0x00, ++ 0x49, 0xfa, ++ 0x22, 0x03, ++ 0x09, 0x00, ++#if 0 ++ //close AE_AWB ++ 0x13, 0x80, ++ 0x16, 0x00, ++ 0x10, 0xf0, ++ 0x00, 0x3f, ++ 0x38, 0x00, ++ 0x01, 0x40, ++ 0x02, 0x40, ++ 0x05, 0x40, ++ 0x06, 0x00, ++ 0x07, 0x00, ++#endif ++ //BLC ++ 0x41, 0x84, ++ ++ END_FLAG, END_FLAG ++}; ++ ++ ++/**************** Camera Exposure Table ****************/ ++static const T_U8 EXPOSURE_WHOLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EXPOSURE_CENTER_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EXPOSURE_MIDDLE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Brightness Table ****************/ ++static const T_U8 BRIGHTNESS_0_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 BRIGHTNESS_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Contrast Table ****************/ ++static const T_U8 CONTRAST_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 CONTRAST_7_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Saturation Table ****************/ ++static const T_U8 SATURATION_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SATURATION_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Sharpness Table ****************/ ++static const T_U8 SHARPNESS_0_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_1_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_2_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_3_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_4_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_5_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 SHARPNESS_6_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera AWB Table ****************/ ++static const T_U8 AWB_AUTO_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_SUNNY_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_CLOUDY_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_OFFICE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 AWB_HOME_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera Effect Table ****************/ ++static const T_U8 EFFECT_NORMAL_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_SEPIA_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_ANTIQUE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_BLUISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_GREENISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_REDDISH_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_NEGATIVE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 EFFECT_BW_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++/**************** Camera night/day mode ****************/ ++static const T_U8 DAY_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++ ++static const T_U8 NIGHT_MODE_TAB[] = ++{ ++ END_FLAG, END_FLAG ++}; ++#endif ++#endif +diff --git a/drivers/media/video/plat-anyka/wrap_sensor.c b/drivers/media/video/plat-anyka/wrap_sensor.c +new file mode 100755 +index 00000000..37173af4 +--- /dev/null ++++ b/drivers/media/video/plat-anyka/wrap_sensor.c +@@ -0,0 +1,139 @@ ++ ++#include ++#include ++#include ++#include ++ ++/** ++ * @brief millisecond delay ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param[in] minisecond minisecond delay number ++ * @return T_VOID ++ */ ++T_VOID mini_delay(T_U32 minisecond) ++{ ++ mdelay(minisecond); ++} ++ ++/** ++ * @brief anyka specific printf ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param[in] level forbidden level ++ * @param[in] mStr module string ++ * @param[in] s format string ++ * @return T_S32 ++ * @retval 0 is print ok, -1 is forbidden to print ++ */ ++T_S32 akprintf(T_U8 level, T_pCSTR mStr, T_pCSTR s, ...) ++{ ++ va_list args; ++ T_S32 r; ++ ++ va_start(args, s); ++ r = vprintk(s, args); ++ va_end(args); ++ ++ return r; ++} ++ ++/** ++ * @brief write data to SCCB device ++ * ++ * write size length data to daddr's raddr register ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param[in] daddr SCCB device address ++ * @param[in] raddr register address ++ * @param[in] data write data's point ++ * @param[in] size write data's length ++ * @return T_BOOL return write success or failed ++ * @retval AK_FALSE operate failed ++ * @retval AK_TRUE operate success ++ */ ++T_BOOL sccb_write_data(T_U8 daddr, T_U8 raddr, T_U8 *data, T_U32 size) ++{ ++ int ret = aksensor_i2c_write_byte_data(daddr, raddr, data, size); ++ ++ if (ret != 0) ++ { ++ return AK_FALSE; ++ } ++ else ++ { ++ return AK_TRUE; ++ } ++} ++ ++/** ++ * @brief read data from SCCB device function ++ * ++ * read data from daddr's raddr register ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param[in] daddr SCCB device address ++ * @param[in] raddr register address ++ * @return T_U8 ++ * @retval read-back data ++ */ ++T_U8 sccb_read_data(T_U8 daddr, T_U8 raddr) ++{ ++ return aksensor_i2c_read_byte_data(daddr, raddr); ++} ++ ++T_BOOL sccb_write_short(T_U8 daddr, T_U16 raddr, T_U8 *data, T_U32 size) ++{ ++ int ret = aksensor_i2c_write_byte_short(daddr, raddr, data, size); ++ ++ if (ret != 0) ++ { ++ return AK_FALSE; ++ } ++ else ++ { ++ return AK_TRUE; ++ } ++} ++ ++T_U8 sccb_read_short(T_U8 daddr, T_U16 raddr) ++{ ++ return aksensor_i2c_read_byte_short(daddr, raddr); ++} ++ ++T_BOOL sccb_write_word(T_U8 daddr, T_U16 raddr, T_U16 *data, T_U32 size) ++{ ++ int ret = aksensor_i2c_write_word_data(daddr, raddr, data, size); ++ if (ret != 0) ++ { ++ return AK_FALSE; ++ } ++ else ++ { ++ return AK_TRUE; ++ } ++} ++ ++T_U16 sccb_read_word(T_U8 daddr, T_U16 raddr) ++{ ++ return aksensor_i2c_read_word_data(daddr, raddr); ++} ++ ++/*@{*/ ++/** ++ * @brief SCCB interface initialize function ++ * ++ * setup SCCB interface ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param[in] pin_scl the pin assigned to SCL ++ * @param[in] pin_sda the pin assigned to SDA ++ * @return T_VOID ++ */ ++T_VOID sccb_init(T_U32 pin_scl, T_U32 pin_sda) ++{ ++ ++} ++ ++ ++ +diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c +index 5b2ec1fd..22c3e7af 100644 +--- a/drivers/media/video/v4l2-ioctl.c ++++ b/drivers/media/video/v4l2-ioctl.c +@@ -2417,7 +2417,10 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, + err = -EFAULT; + if (_IOC_DIR(cmd) & _IOC_WRITE) { + unsigned long n = cmd_input_size(cmd); +- ++ ++ if (cmd == VIDIOC_G_PARM) ++ n+=4; ++ + if (copy_from_user(parg, (void __user *)arg, n)) + goto out; + +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index c7795096..49705182 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -382,6 +382,14 @@ config HMC6352 + This driver provides support for the Honeywell HMC6352 compass, + providing configuration and heading data via sysfs. + ++config SENSORS_AK8975 ++ tristate "AK8975 compass support" ++ default n ++ depends on I2C ++ help ++ If you say yes here you get support for Asahi Kasei's ++ orientation sensor AK8975. ++ + config EP93XX_PWM + tristate "EP93xx PWM support" + depends on ARCH_EP93XX +@@ -425,6 +433,10 @@ config TI_DAC7512 + This driver can also be built as a module. If so, the module + will be called ti_dac7512. + ++config UID_STAT ++ bool "UID based statistics tracking exported to /proc/uid_stat" ++ default n ++ + config VMWARE_BALLOON + tristate "VMware Balloon Driver" + depends on X86 +@@ -498,6 +510,40 @@ config MAX8997_MUIC + Maxim MAX8997 PMIC. + The MAX8997 MUIC is a USB port accessory detector and switch. + ++config WL127X_RFKILL ++ tristate "Bluetooth power control driver for TI wl127x" ++ depends on RFKILL ++ default n ++ ---help--- ++ Creates an rfkill entry in sysfs for power control of Bluetooth ++ TI wl127x chips. ++ ++config AK_SERIAL_NUMBER ++ tristate "Anyka Serial Number Interface" ++ depends on ARCH_AK39 ++ ---help--- ++ If you say yes here you get an sysfs interface to read device serial number ++ on Anyka platform. ++ ++config AK_MOTOR ++ tristate "Anyka Motor Support" ++ default n ++ help ++ If you say yes here you get support for the Anyka Motor device. ++ ++comment "user space generic gpio controller" ++config GPIOS_AKCUSTOM ++ tristate "Generic akgpio custom support" ++ depends on ARCH_AK39 ++ help ++ This enables anyka's generic GPIO for setting support through user space controller. ++ if you want need to enable this, you can control attribute of akgpio, ++ example dir[in/out], out[high/low], pull[up/down], get gpio level, ++ irq polarity[low/high], request gpio irq and delete irq etc. ++ ++ If unsure, say N. ++ ++ + source "drivers/misc/c2port/Kconfig" + source "drivers/misc/eeprom/Kconfig" + source "drivers/misc/cb710/Kconfig" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index 3e1d8010..b1b215fc 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -33,6 +33,7 @@ obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o + obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o + obj-$(CONFIG_DS1682) += ds1682.o + obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o ++obj-$(CONFIG_UID_STAT) += uid_stat.o + obj-$(CONFIG_C2PORT) += c2port/ + obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/ + obj-$(CONFIG_HMC6352) += hmc6352.o +@@ -49,3 +50,8 @@ obj-y += carma/ + obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o + obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ + obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o ++obj-$(CONFIG_WL127X_RFKILL) += wl127x-rfkill.o ++obj-$(CONFIG_SENSORS_AK8975) += akm8975.o ++obj-$(CONFIG_AK_MOTOR) += ak_motor.o ++obj-$(CONFIG_AK_SERIAL_NUMBER) += ak_sn.o ++obj-$(CONFIG_GPIOS_AKCUSTOM) += akgpios.o +diff --git a/drivers/misc/ak_motor.c b/drivers/misc/ak_motor.c +new file mode 100644 +index 00000000..5d8c9338 +--- /dev/null ++++ b/drivers/misc/ak_motor.c +@@ -0,0 +1,723 @@ ++/* ++ * @file /driver/misc/ak_motor.c ++ * @brief AK On-chip motor driver ++ * Copyright C 2013 Anyka CO.,LTD ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * @author lixinhai ++ * @date 2013-05-11 ++ * @note 2013-05-11 created ++ * @note 2013-05-14 add more comments ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define AK_MOTOR_DEVNAME "ak-motor" ++ ++//#define MOTOR_DEBUG ++ ++#ifdef MOTOR_DEBUG ++#define PDEBUG(fmt, args...) printk(KERN_INFO "ak-motor:" fmt, ##args) ++#else ++#define PDEBUG(fmt, args...) ++#endif ++ ++ ++#define MOTOR_TURN_CLKWISE (0) /*turn by clock wise*/ ++#define MOTOR_TURN_ANTICLKWISE (1) /*turn by anti clock wise*/ ++#define MOTOR_STEP_PERIOD (64) ++#define MOTOR_STEP_ANGLE (360/MOTOR_STEP_PERIOD) ++#define MOTOR_STEP_REMAIN (360%MOTOR_STEP_PERIOD) ++#define MOTOR_DEFAULT_DELAY_MS (2) ++ ++/*motor device list*/ ++static LIST_HEAD(motor_list); ++static DEFINE_MUTEX(list_lock); ++ ++struct ak_motor_runtime ++{ ++ int cw; ++ int angle; ++ int remain_angle; ++ int count; ++ int delay_jiffies; ++ int hit_flags; ++ int running; ++#define MOTOR_STATUS_RUNNING (1) ++#define MOTOR_STATUS_STOPING (2) ++#define MOTOR_STATUS_STOPED (3) ++}; ++ ++struct ak_motor { ++ struct miscdevice miscdev; ++ struct ak_motor_plat_data *pdata; ++ int irq_hit[AK_MOTOR_HIT_NUM]; /*hit feedback irq descriptor code*/ ++ int hit_pin[AK_MOTOR_HIT_NUM]; /*hit feedback irq pin.*/ ++ int trigger_level[AK_MOTOR_HIT_NUM]; /*irq trigger level*/ ++ ++ int irq_hit_type[AK_MOTOR_HIT_NUM]; /*current trigger level*/ ++ int trigger_irq; /*in course of trigger irq index.*/ ++ int phase_pin[AK_MOTOR_PHASE_NUM]; /*phase pin*/ ++ spinlock_t lock; ++ u32 angular_speed; ++ u32 delay; ++ struct timer_list detect_timer; ++ struct timer_list work_timer; ++ ++ struct ak_motor_runtime runtime; ++ struct list_head list; ++ int index; ++ ++ struct ak_motor_dev *curr_dev; ++}; ++ ++ ++struct ak_motor_dev { ++ struct ak_motor *motor; ++ spinlock_t lock; ++ int rd_flags; ++ struct notify_data data; /*notify data to user.*/ ++ int is_open; ++ wait_queue_head_t event; ++}; ++ ++/*clockwise*/ ++static unsigned char ctrl_tbl_cw[] = {0x03, 0x06, 0x0c, 0x09}; ++ ++/*anticlockwise*/ ++static unsigned char ctrl_tbl_acw[] = {0x03, 0x09, 0x0c, 0x06}; ++ ++static void ak_motor_stop(struct ak_motor_dev *motor_dev); ++static int ak_motor_wait_for_stop(struct ak_motor_dev *motor_dev); ++ ++static inline int motor_step_count(int angle) ++{ ++ int count; ++ ++/* ++ int frag; ++ ++ count = angle/MOTOR_STEP_ANGLE; ++ frag = angle%MOTOR_STEP_ANGLE; ++ ++ if((frag*2 >= MOTOR_STEP_ANGLE) || ((count == 0)&& angle)) { ++ count++; ++ } ++*/ ++ count = angle*MOTOR_STEP_ANGLE; ++ count += (angle*MOTOR_STEP_REMAIN)/MOTOR_STEP_PERIOD; ++ return count; ++} ++ ++#define ms_unit (1000) ++static inline int get_delay_by_speed(int speed) ++{ ++ int time; ++ ++ time = (1*ms_unit)/speed; ++ /* time *= MOTOR_STEP_ANGLE; */ ++ time /= MOTOR_STEP_ANGLE; ++ if(time<=0) ++ time= MOTOR_DEFAULT_DELAY_MS; ++ return time; ++} ++ ++ ++/** ++ * * @brief ak motor open ++ * * ++ * * open. ++ * * @author lixinhai ++ * * @date 2013-03-20 ++ * * @param[in] inode pointer. ++ * * @param[in] file pointer. ++ * * @return int return exec success or failed ++ * * @retval returns zero on success ++ * * @retval return a non-zero error code if failed ++ * */ ++static int ak_motor_open(struct inode *inode, struct file *file) ++{ ++ int minor; ++ bool found = false; ++ struct ak_motor *motor; ++ struct ak_motor_dev *motor_dev; ++ ++ PDEBUG("open the motor device.\n"); ++ ++ minor = iminor(file->f_path.dentry->d_inode); ++ list_for_each_entry(motor, &motor_list, list) { ++ if(minor == motor->miscdev.minor) { ++ found = true; ++ break; ++ } ++ } ++ ++ if((found == false) || (motor->curr_dev != NULL)) { ++ printk("ak-motor open fail, device busy now.\n"); ++ return -EBUSY; ++ } ++ ++ motor_dev = kzalloc(sizeof *motor_dev, GFP_KERNEL); ++ if(!motor_dev) { ++ printk("ak-motor: alloc the motor dev err, open fail.\n"); ++ } ++ ++ init_waitqueue_head(&motor_dev->event); ++ motor_dev->motor = motor; ++ motor->curr_dev = motor_dev; ++ motor_dev->data.event = AK_MOTOR_EVENT_UNHIT; ++ motor_dev->is_open = 1; ++ spin_lock_init(&motor_dev->lock); ++ ++ file->private_data = motor_dev; ++ printk("open ak motor device success.\n"); ++ return 0; ++} ++ ++/** ++ * @brief ak motor close ++ * ++ * close. ++ * @author lixinhai ++ * @date 2013-03-20 ++ * @param[in] inode pointer. ++ * @param[in] file pointer. ++ * @return int return exec success or failed ++ * @retval returns zero on success ++ * @retval return a non-zero error code if failed ++ */ ++static int ak_motor_close(struct inode *inode, struct file *file) ++{ ++ int ret; ++ struct ak_motor_dev *motor_dev = file->private_data; ++ struct ak_motor *motor = motor_dev->motor; ++ ++ BUG_ON(motor==NULL); ++ ++ motor_dev->is_open = 0; ++ ak_motor_stop(motor_dev); ++ ret = del_timer_sync(&motor->work_timer); ++ if(ret) { /*timer is pending, need wait for timer stop.*/ ++ ret = ak_motor_wait_for_stop(motor_dev); ++ if(ret) ++ printk("warning: ak motor stop fail.\n"); ++ } ++ ++ motor_dev->motor->curr_dev = NULL; ++ kfree(motor_dev); ++ printk("close ak motor device success.\n"); ++ return 0; ++} ++ ++ ++/** ++ * @brief ak motor read ++ * ++ * read. ++ * @author lixinhai ++ * @date 2013-03-20 ++ * @param[in] file pointer. ++ * @param[in] data buf. ++ * @param[in] data count. ++ * @param[in] data offset. ++ * @return int return read data count. ++ */ ++static ssize_t ak_motor_read(struct file *file, char __user *data, size_t len, loff_t *ofs) ++{ ++ unsigned long flags; ++ int ret = 0; ++ struct ak_motor_dev *motor_dev = file->private_data; ++ ++ PDEBUG("read the motor data, len:%d\n", len); ++ wait_event_interruptible(motor_dev->event, motor_dev->rd_flags != 0); ++ ++ spin_lock_irqsave(&motor_dev->lock, flags); ++ ++ ret = copy_to_user(data, &motor_dev->data, sizeof(struct notify_data)); ++ ++ motor_dev->rd_flags = 0; ++ PDEBUG("copy to user data, ret:%d.\n", ret); ++ spin_unlock_irqrestore(&motor_dev->lock, flags); ++ ++ return ret; ++} ++ ++ ++static unsigned int ak_motor_poll(struct file *file, struct poll_table_struct *wait) ++{ ++ unsigned int mask = 0; ++ struct ak_motor_dev *motor_dev = file->private_data; ++ ++ PDEBUG("motor poll.\n"); ++ ++ poll_wait(file, &motor_dev->event, wait); ++ ++ if(motor_dev->rd_flags != 0) ++ mask |= POLLIN; ++ ++ return mask; ++} ++ ++/** ++ * @brief control the motor turn. ++ * ++ * read. ++ * @author lixinhai ++ * @date 2013-03-20 ++ * @param[in] ak_motor_dev. ++ * @param[in] is clockwise. ++ * @param[in] turn angle.. ++ * @return int return exec success or failed ++ * @retval returns zero on success ++ * @retval return a non-zero error code if failed ++ */ ++static int ak_motor_turn(struct ak_motor_dev *motor_dev, int cw, int angle) ++{ ++ struct ak_motor *motor = motor_dev->motor; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&motor_dev->lock, flags); ++ motor->runtime.cw = cw; ++ motor->runtime.angle = angle; ++ motor->runtime.remain_angle = angle; ++ motor->runtime.count = motor_step_count(angle); ++ motor->runtime.delay_jiffies = (motor->delay*HZ)/1000; ++ motor->runtime.running = MOTOR_STATUS_RUNNING; ++ motor->runtime.hit_flags = 0; ++ ++ PDEBUG("ak motor turn request, %sclockwise, angle is %d.\n", cw ?"":"anti", angle); ++ mod_timer(&motor->work_timer, jiffies); ++ spin_unlock_irqrestore(&motor_dev->lock, flags); ++ return 0; ++} ++ ++static void ak_motor_stop(struct ak_motor_dev *motor_dev) ++{ ++ unsigned long flags; ++ struct ak_motor *motor = motor_dev->motor; ++ ++ spin_lock_irqsave(&motor_dev->lock, flags); ++ motor->runtime.running = MOTOR_STATUS_STOPING; ++ spin_unlock_irqrestore(&motor_dev->lock, flags); ++} ++ ++static int ak_motor_wait_for_stop(struct ak_motor_dev *motor_dev) ++{ ++ int timeout = 200; ++ struct ak_motor *motor = motor_dev->motor; ++ ++ while(timeout--) { ++ if(motor->runtime.running == MOTOR_STATUS_STOPED) ++ return 0; ++ msleep(1); ++ } ++ return 1; ++} ++ ++ ++static long ak_motor_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int val; ++ int err = 0, ret = 0; ++ struct ak_motor_dev *motor_dev = file->private_data; ++ struct ak_motor *motor = motor_dev->motor; ++ ++ PDEBUG("exec the motor ioctl.\n"); ++ /* Check type and command number */ ++ if (_IOC_TYPE(cmd) != AK_MOTOR_IOC_MAGIC) ++ return -ENOTTY; ++ ++ /* Check access direction once here; don't repeat below. ++ * IOC_DIR is from the user perspective, while access_ok is ++ * from the kernel perspective; so they look reversed. ++ */ ++ if (_IOC_DIR(cmd) & _IOC_READ) ++ err = !access_ok(VERIFY_WRITE, ++ (void __user *)arg, _IOC_SIZE(cmd)); ++ if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) ++ err = !access_ok(VERIFY_READ, ++ (void __user *)arg, _IOC_SIZE(cmd)); ++ if (err) ++ return -EFAULT; ++ ++ switch(cmd) { ++ case AK_MOTOR_SET_ANG_SPEED: ++ __get_user(val, (int __user*)arg); ++ PDEBUG("ak motor set angular speed %d.\n", val); ++ if((val AK_MOTOR_MAX_SPEED) ++ return -EINVAL; ++ ++ motor->angular_speed = val; ++ motor->delay = get_delay_by_speed(motor->angular_speed); ++ break; ++ case AK_MOTOR_GET_ANG_SPEED: ++ PDEBUG("ak motor get angular speed.\n"); ++ __put_user(motor->angular_speed, (int __user*)arg); ++ break; ++ case AK_MOTOR_TURN_CLKWISE: ++ __get_user(val, (int __user*)arg); ++ PDEBUG("ak motor turn clk wise, angle:%d\n", val); ++ if(val < 0) ++ return -EINVAL; ++ ++ ak_motor_turn(motor_dev, MOTOR_TURN_CLKWISE, val); ++ break; ++ case AK_MOTOR_TURN_ANTICLKWISE: ++ __get_user(val, (int __user*)arg); ++ PDEBUG("ak motor turn anti clkwise, angle:%d\n", val); ++ if(val < 0) ++ return -EINVAL; ++ ++ ak_motor_turn(motor_dev, MOTOR_TURN_ANTICLKWISE, val); ++ break; ++ case AK_MOTOR_GET_HIT_STATUS: ++ val = 0; ++ ret = ak_gpio_getpin(motor->hit_pin[AK_MOTOR_HIT_LEFT]); ++ if(ret == (motor->trigger_level[AK_MOTOR_HIT_LEFT] == IRQ_TYPE_LEVEL_HIGH)) ++ val |= AK_MOTOR_HITTING_LEFT; ++ ++ ret = ak_gpio_getpin(motor->hit_pin[AK_MOTOR_HIT_RIGHT]); ++ if(ret == (motor->trigger_level[AK_MOTOR_HIT_RIGHT] == IRQ_TYPE_LEVEL_HIGH)) ++ val |= AK_MOTOR_HITTING_RIGHT; ++ ++ PDEBUG("ak motor get status, val:%d\n", val); ++ __put_user(val, (int __user*)arg); ++ case AK_MOTOR_TURN_STOP: ++ PDEBUG("ak motor stop request.\n"); ++ ak_motor_stop(motor_dev); ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++static struct file_operations ak_motor_fops = { ++ .owner = THIS_MODULE, ++ .open = ak_motor_open, ++ .release = ak_motor_close, ++ .read = ak_motor_read, ++ .poll = ak_motor_poll, ++ .unlocked_ioctl = ak_motor_ioctl, ++}; ++ ++/*********************************************************************/ ++ ++static void ak_motor_event_notify(struct ak_motor *motor, int num, int event) ++{ ++ struct ak_motor_dev *motor_dev = motor->curr_dev; ++ ++ if(!motor_dev) ++ return; ++ ++ motor_dev->data.hit_num = num; ++ motor_dev->data.event = event; ++ motor_dev->data.remain_angle = motor->runtime.remain_angle; ++ motor_dev->rd_flags = 1; ++ PDEBUG("notify to hit: hit number:%d, event:%d, remain angle:%d.\n", ++ num, event, motor->runtime.remain_angle); ++ ++ if(event == AK_MOTOR_EVENT_HIT) ++ motor->runtime.hit_flags = 1; ++ ++ wake_up_interruptible(&motor_dev->event); ++} ++ ++/** ++ * @brief detect the motor hit the boundary or not. ++ * ++ * @author lixinhai ++ * @date 2013-03-20 ++ * @param[in] timer data pointer. ++ */ ++static void ak_motor_hit_detect(unsigned long data) ++{ ++ int i; ++ int hit_event; ++ unsigned long flags; ++ struct ak_motor *motor = (struct ak_motor*)data; ++ struct ak_motor_dev *motor_dev = motor->curr_dev; ++ ++ PDEBUG("ak motor hit detect.\n"); ++ spin_lock_irqsave(&motor_dev->lock, flags); ++ for(i=0; itrigger_irq == motor->irq_hit[i]) { ++ hit_event = (motor->trigger_level[i] == motor->irq_hit_type[i]) ? ++ AK_MOTOR_EVENT_HIT : AK_MOTOR_EVENT_UNHIT; ++ ++ if(motor->irq_hit_type[i] == IRQ_TYPE_LEVEL_LOW) { ++ motor->irq_hit_type[i] = IRQ_TYPE_LEVEL_HIGH; ++ }else { ++ motor->irq_hit_type[i] = IRQ_TYPE_LEVEL_LOW; ++ } ++ irq_set_irq_type(motor->irq_hit[i], motor->irq_hit_type[i]); ++ enable_irq(motor->irq_hit[i]); ++ ak_motor_event_notify(motor, i, hit_event); ++ break; ++ } ++ } ++ spin_unlock_irqrestore(&motor_dev->lock, flags); ++} ++ ++/** ++ * @brief hitting the boundary interrupt. ++ * ++ * @author lixinhai ++ * @date 2013-03-20 ++ * @param[in] irq description. ++ * @param[in] dev_id. ++ */ ++static irqreturn_t ak_motor_hit_irq(int irq, void *dev_id) ++{ ++ struct ak_motor *motor = dev_id; ++ ++ PDEBUG("receive the motor hit irq.\n"); ++ disable_irq_nosync(irq); ++ motor->trigger_irq = irq; ++ mod_timer(&motor->detect_timer, jiffies + msecs_to_jiffies(50)); ++ ++ return IRQ_HANDLED; ++} ++ ++static void ak_motor_timer_hander(unsigned long data) ++{ ++ unsigned long flags; ++ unsigned char *ctrl_tbl; ++ int i, j, idx; ++ struct ak_motor_dev *motor_dev; ++ int ret = 0; ++ struct ak_motor_runtime *runtime; ++ struct ak_motor *motor = (struct ak_motor*)data; ++ int count; ++ char *err_desc = ""; ++ ++ motor_dev = motor->curr_dev; ++ if(!motor_dev) ++ return; ++ ++ spin_lock_irqsave(&motor_dev->lock, flags); ++ runtime = &motor->runtime; ++ count = motor_step_count(runtime->angle); ++ i = count - runtime->count; ++ ctrl_tbl = runtime->cw ? ctrl_tbl_cw : ctrl_tbl_acw; ++ //printk("[%d:%d]",i, runtime->count); ++/* ++ PDEBUG("ak motor turn: %s, angle:%d, delay:%d, count:%d, runtime count:%d\n", ++ runtime->cw ?"cw":"acw", runtime->angle, motor->delay, count, runtime->count); ++*/ ++ if(unlikely(!motor_dev->is_open)) { ++ err_desc = "not open"; ++ goto out; ++ } ++ ++ if(unlikely(motor->runtime.hit_flags)) { ++ motor->runtime.hit_flags = 0; ++ err_desc = "hitting the boundary"; ++ goto out; ++ } ++ ++ if(unlikely(runtime->running != MOTOR_STATUS_RUNNING)) { ++ ak_motor_event_notify(motor, 0, AK_MOTOR_EVENT_STOP); ++ err_desc = "stop running"; ++ goto out; ++ } ++ ++ idx = i % AK_MOTOR_PHASE_NUM; ++ for(j=0; jphase_pin[j], !!((1<remain_angle = runtime->angle - (i*MOTOR_STEP_PERIOD)/360; ++ ++ if(--runtime->count > 0 && ++ runtime->running == MOTOR_STATUS_RUNNING) { ++ ret = mod_timer(&motor->work_timer, jiffies + runtime->delay_jiffies); ++ if(ret) { ++ printk(KERN_ERR "ak motor: mod timer fail.\n"); ++ } ++ } else { ++ runtime->running = MOTOR_STATUS_STOPED; ++ for(j=0; jphase_pin[j], 0); ++ } ++ ak_motor_event_notify(motor, 0, AK_MOTOR_EVENT_STOP); ++ } ++ ++ spin_unlock_irqrestore(&motor_dev->lock, flags); ++ return; ++out: ++ runtime->running = MOTOR_STATUS_STOPED; ++ spin_unlock_irqrestore(&motor_dev->lock, flags); ++ PDEBUG("ak motor running stop, out reason:%s\n", err_desc); ++ return; ++} ++ ++ ++/** ++ * @brief initilize the motor gpio. ++ * ++ * @author lixinhai ++ * @date 2013-03-20 ++ * @param[in] ak_motor. ++ */ ++static int ak_motor_init_cfg(struct ak_motor *motor) ++{ ++ int i, ret = 0; ++ bool flags = false; ++ struct ak_motor_plat_data *plat = motor->pdata; ++ ++ for(i=0; igpio_phase[i]); ++ motor->phase_pin[i] = plat->gpio_phase[i].pin; ++ ak_setpin_as_gpio(motor->phase_pin[i]); ++ } ++ ++ for(i=0; iirq_hit[i] = 0; ++ if(plat->gpio_hit[i].pin >= 0) { ++ motor->hit_pin[i] = plat->gpio_hit[i].pin; ++ ak_gpio_set(&plat->gpio_hit[i]); ++ ++ if(!flags) { ++ setup_timer(&motor->detect_timer, ak_motor_hit_detect, ++ (unsigned long)motor); ++ flags = true; ++ } ++ motor->irq_hit[i] = ak_gpio_to_irq(motor->hit_pin[i]); ++ ++ /*request the hit boundary irq*/ ++ ret = request_irq(motor->irq_hit[i], ak_motor_hit_irq, ++ IRQF_DISABLED|IRQF_TRIGGER_LOW, "motor", motor); ++ if(ret) ++ goto out; ++ ++ printk("ak_motor:request gpio irq ret = %d, irq=%d\n", ++ ret, motor->irq_hit[i]); ++ ++ motor->trigger_level[i] = motor->irq_hit_type[i] = plat->irq_hit_type[i]; ++ } ++ } ++ ++out: ++ return ret; ++} ++ ++static int __devinit ak_motor_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct ak_motor *motor; ++ struct ak_motor_plat_data *pdata; ++ char name[64]; ++ ++ PDEBUG("ak motor driver probe enter.\n"); ++ pdata = pdev->dev.platform_data; ++ if(!pdata) ++ return -ENODEV; ++ ++ motor = kzalloc(sizeof *motor, GFP_KERNEL); ++ if(!motor) { ++ printk("alloc the motor device fail.\n"); ++ return -ENOMEM; ++ } ++ ++ motor->pdata = pdata; ++ platform_set_drvdata(pdev, motor); ++ ++ sprintf(name, "%s%d", pdev->name, pdev->id); ++ ++ motor->angular_speed = pdata->angular_speed; ++ motor->delay = get_delay_by_speed(motor->angular_speed); ++ motor->miscdev.minor = MISC_DYNAMIC_MINOR; ++ motor->miscdev.name = name; ++ motor->miscdev.fops = &ak_motor_fops; ++ motor->miscdev.mode = S_IRWXO; ++ spin_lock_init(&motor->lock); ++ ++ setup_timer(&motor->work_timer, ak_motor_timer_hander, (unsigned long)motor); ++ ++ ak_motor_init_cfg(motor); ++ list_add_tail(&motor->list, &motor_list); ++ ++ /*register to miscdevice subsystem*/ ++ ret = misc_register(&motor->miscdev); ++ if(ret) { ++ dev_err(&pdev->dev, "register misc device fail.\n"); ++ ret = -ENOENT; ++ goto err_misc_dev; ++ } ++ PDEBUG("minor:%d\n", motor->miscdev.minor); ++ printk("init the ak-motor device success.\n"); ++ return 0; ++ ++err_misc_dev: ++ kfree(motor); ++ ++ return ret; ++} ++ ++static int __devexit ak_motor_remove(struct platform_device *pdev) ++{ ++ struct ak_motor *motor = platform_get_drvdata(pdev); ++ ++ dev_info(&pdev->dev, "remove the ak motor driver.\n"); ++ misc_deregister(&motor->miscdev); ++ ++ kfree(motor); ++ return 0; ++} ++ ++static struct platform_driver ak_motor_driver = { ++ .driver = { ++ .name = "ak-motor", ++ .owner = THIS_MODULE, ++ }, ++ .probe = ak_motor_probe, ++ .remove = __devexit_p(ak_motor_remove), ++}; ++ ++ ++static int __init ak_motor_init(void) ++{ ++ printk("AK Motor Driver (c) 2013 ANYKA\n"); ++ return platform_driver_register(&ak_motor_driver); ++} ++ ++static void __exit ak_motor_exit(void) ++{ ++ platform_driver_unregister(&ak_motor_driver); ++} ++ ++module_init(ak_motor_init); ++module_exit(ak_motor_exit); ++ ++MODULE_AUTHOR("Anyka"); ++MODULE_DESCRIPTION("Anyka Motor Device Driver"); ++MODULE_ALIAS("platform:ak-motor"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/misc/ak_sn.c b/drivers/misc/ak_sn.c +new file mode 100644 +index 00000000..dbd91b0a +--- /dev/null ++++ b/drivers/misc/ak_sn.c +@@ -0,0 +1,69 @@ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define SN_FILE_NAME "SERADDR" ++#define SN_MAX_LEN 64 ++ ++static ssize_t sn_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) ++{ ++ unsigned char sn[SN_MAX_LEN] = {0}; ++ unsigned char file_len[4] = {0}; ++ ++ if (FHA_asa_read_file(SN_FILE_NAME, file_len, 4) == AK_FALSE) { ++ goto out; ++ } ++ ++ if (FHA_asa_read_file(SN_FILE_NAME, sn, *(unsigned long*)file_len + 4) == AK_FALSE) { ++ memset(sn, 0, SN_MAX_LEN); ++ goto out; ++ } ++out: ++ return sprintf(buf, "%s\n", sn + 4); ++} ++ ++static struct kobj_attribute sn_attribute = ++ __ATTR(sn, 0666, sn_show, NULL); ++ ++static struct attribute *attrs[] = { ++ &sn_attribute.attr, ++ NULL ++}; ++ ++static struct kobject *sn_kobj; ++ ++static int __init serial_number_init(void) ++{ ++ int ret; ++ ++ sn_kobj = kobject_create_and_add("serial_number", kernel_kobj); ++ if (!sn_kobj) { ++ printk("Create serial number kobject failed\n"); ++ return -ENOMEM; ++ } ++ ++ ret = sysfs_create_file(sn_kobj, *attrs); ++ if (ret) { ++ printk("Create serial number sysfs file failed\n"); ++ kobject_put(sn_kobj); ++ } ++ ++ return ret; ++} ++ ++static void __exit serial_number_exit(void) ++{ ++ kobject_put(sn_kobj); ++} ++ ++module_init(serial_number_init); ++module_exit(serial_number_exit); ++ ++MODULE_DESCRIPTION("Anyka Device Serial Number Interface"); ++MODULE_AUTHOR("Anyka"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/misc/akgpios.c b/drivers/misc/akgpios.c +new file mode 100755 +index 00000000..f3985e65 +--- /dev/null ++++ b/drivers/misc/akgpios.c +@@ -0,0 +1,361 @@ ++/* ++ * drivers/gpio/akgpios.c ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++//#define AKGPIO_DEDUB ++#define DBG(fmt...) //printk(fmt); ++ ++#define AKGPIO_DEV_NAME "akgpio" ++ ++#define init_MUTEX(sem) sema_init(sem, 1) ++ ++struct akgpio_custom_device { ++ struct gpio_info gpioinfo; ++ int gpio; ++ int used; ++ int irq_flag; ++ struct semaphore sem; ++ int locked; ++}; ++ ++static struct custom_gpio_data *pdata = NULL; ++static struct akgpio_custom_device *akgpios = NULL; ++static int gpio_num; ++ ++ ++#ifdef AKGPIO_DEDUB ++static void print_gpio_set_info(struct gpio_info *gpio) ++{ ++ printk("GPIO[%d], dir[%s] out[%s] pulldown[%s] pullup[%s] int_pol[%s]\n", ++ gpio->pin, ++ (gpio->dir == AK_GPIO_DIR_OUTPUT) ? "out" : "in", ++ (gpio->value == AK_GPIO_OUT_HIGH) ? "high" : ++ (gpio->value == AK_GPIO_OUT_LOW) ? "low" : "no set", ++ (gpio->pulldown == AK_PULLDOWN_ENABLE) ? "enable" : "disable", ++ (gpio->pullup == AK_PULLUP_ENABLE) ? "enable" : "disable", ++ (gpio->int_pol == AK_GPIO_INT_HIGHLEVEL) ? "high" : ++ (gpio->int_pol == AK_GPIO_INT_LOWLEVEL) ? "low" : "no set"); ++} ++ ++static void print_gpios_info(struct akgpio_custom_device *gpioirq, int ngpio) ++{ ++ int i; ++ for (i = 0; i < ngpio; i++) { ++ printk("akgpios[%d].gpio=%d, akgpios[%d].used=%d\n," ++ " akgpios[%d].irq_flag=%d, akgpios[%d].locked=%d\n," ++ " akgpios[%d].gpioinfo=%p, akgpios[%d].sem=%p\n\n", ++ i, gpioirq[i].gpio, i, gpioirq[i].used, i, gpioirq[i].irq_flag, ++ i, gpioirq[i].locked, i, &gpioirq[i].gpioinfo, i, &gpioirq[i].sem); ++ } ++} ++ ++#else ++static void print_gpio_set_info(struct gpio_info *gpio) {} ++static void print_gpios_info(struct akgpio_custom_device *gpioirq, int ngpio){} ++#endif ++ ++static irqreturn_t akgpio_custom_irq(int irq, void *dev_id) ++{ ++ struct akgpio_custom_device *gpioirq = dev_id; ++ struct gpio_info *gpio = &gpioirq->gpioinfo; ++ unsigned int gpio_level; ++ ++ DBG("akgpio irq: irq=%d, gpioirq->gpio=%d\n", irq, gpioirq->gpio); ++ ++ BUG_ON(irq != ak_gpio_to_irq(gpio->pin)); ++ ++ gpio_level = ak_gpio_getpin(gpio->pin); ++ //disalbe gpio_irq when the button down ++ if ((gpio_level && gpio->int_pol) || (!gpio_level && !gpio->int_pol)) ++ ak_gpio_intpol(gpio->pin, !gpio->int_pol); ++ ++ //enable gpiot_irq when the button up ++ if ((!gpio_level && gpio->int_pol) || (gpio_level && !gpio->int_pol)) { ++ ak_gpio_intpol(gpio->pin, gpio->int_pol); ++ ++ /* release semaphore lock */ ++ if (gpioirq->locked) { ++ up(&gpioirq->sem); ++ gpioirq->locked = 0; ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static long akgpio_custom_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ unsigned int *gpio_array = pdata->gpiopin; ++ unsigned int ngpio = pdata->ngpiopin; ++ struct gpio_info gpio; ++ struct gpio_group_info gpios_info; ++ unsigned int irq, gpiopin; ++ int i, ret; ++ ++ switch(cmd) { ++ case GET_GPIO_NUM: ++ /* get gpio num */ ++ if (copy_to_user((void __user *)arg, &ngpio, sizeof(unsigned int))) ++ return -EINVAL; ++ break; ++ case GET_AVAILABLE_GPIO: ++ /* get unused gpio num, and report user space */ ++ if (copy_to_user((void __user *)arg, gpio_array, ngpio*sizeof(unsigned int))) ++ return -EINVAL; ++ break; ++ ++ case SET_GPIO_FUNC: ++ /* set gpio attribute, include dir(in/out), out(low/high), pull(up/down) */ ++ if (copy_from_user(&gpio, (void __user *)arg, sizeof(struct gpio_info))) ++ return -EINVAL; ++ ++ ak_gpio_set(&gpio); ++ break; ++ ++ case SET_GPIO_LEVEL: ++ /* set gpio level when the pin as output */ ++ if (copy_from_user(&gpio, (void __user *)arg, sizeof(struct gpio_info))) ++ return -EINVAL; ++ ++ ak_gpio_setpin(gpio.pin, gpio.value); ++ break; ++ ++ case SET_GROUP_GPIO_LEVEL: ++ /* set gpio level when the pin as output */ ++ if (copy_from_user(&gpios_info, (void __user *)arg, sizeof(struct gpio_group_info))) ++ return -EINVAL; ++ ++ for(i = 0; i < gpios_info.gpio_num; i++) { ++ ak_gpio_setpin(gpios_info.gpio[i].pin, gpios_info.gpio[i].value); ++ } ++ break; ++ ++ case GET_GPIO_VALUE: ++ /* read gpio value when the pin is input */ ++ if (copy_from_user(&gpio, (void __user *)arg, sizeof(struct gpio_info))) ++ return -EINVAL; ++ ++ gpio.value = ak_gpio_getpin(gpio.pin); ++ if(copy_to_user((void __user *)arg, &gpio, sizeof(struct gpio_info))) ++ return -EINVAL; ++ break; ++ ++ case SET_GPIO_IRQ: ++ { ++ /* set gpio irq func */ ++ struct akgpio_custom_device *gpioirq = NULL; ++ if (copy_from_user(&gpio, (void __user *)arg, sizeof(struct gpio_info))) ++ return -EINVAL; ++ ++ for (i = 0; i < ngpio; i++) { ++ gpioirq = &akgpios[i]; ++ if ((gpioirq->used)&&(gpioirq->gpio == gpio.pin)) { ++ /* gpio irq had set, return for */ ++ break; ++ } else if (!gpioirq->used) { ++ gpioirq->used = 1; ++ gpioirq->gpio = gpio.pin; ++ down(&gpioirq->sem); ++ break; ++ } ++ } ++ ++ for (i = 0; i < ngpio; i++) { ++ gpioirq = &akgpios[i]; ++ if ((gpioirq->used)&&(gpioirq->gpio == gpio.pin)) { ++ memcpy(&gpioirq->gpioinfo, &gpio, sizeof(struct gpio_info)); ++ ++ print_gpio_set_info(&gpio); ++ ak_gpio_set(&gpio); ++ ++ if (!gpioirq->irq_flag) { ++ irq = ak_gpio_to_irq(gpio.pin); ++ DBG("akgpio: gpio[%d], irq[%d]\n", gpio.pin, irq); ++ ret = request_irq(irq, akgpio_custom_irq, ++ ((gpio.int_pol == AK_GPIO_INT_HIGHLEVEL)?(IRQF_TRIGGER_HIGH):(IRQF_TRIGGER_LOW)), ++ "akgpio", gpioirq); ++ if (ret) { ++ printk("Request irq failed. ret=%d\n", ret); ++ return -EINVAL; ++ } ++ gpioirq->irq_flag = 1; ++ } ++ break; ++ } ++ } ++ } ++ break; ++ ++ case LISTEN_GPIO_IRQ: ++ /* wait irq occurs */ ++ gpiopin = (unsigned int)arg; ++ for (i = 0; i < ngpio; i++) { ++ struct akgpio_custom_device *gpioirq = &akgpios[i]; ++ if ((gpioirq->used)&&(gpioirq->gpio == gpiopin)) { ++ gpioirq->locked = 1; ++ down(&gpioirq->sem); ++ /* blocking, wait irq occurs, then continue */ ++ break; ++ } ++ ++ if (i == ngpio) { ++ printk("listen gpio irq: first request irq, then do this.\n"); ++ return -EINVAL; ++ } ++ } ++ break; ++ ++ case DELETE_GPIO_IRQ: ++ /* delete gpio irq that specific */ ++ gpiopin = (unsigned int)arg; ++ for (i = 0; i < ngpio; i++) { ++ struct akgpio_custom_device *gpioirq = &akgpios[i]; ++ if ((gpioirq->used)&&(gpioirq->gpio == gpiopin)) { ++ irq = ak_gpio_to_irq(gpiopin); ++ free_irq(irq, gpioirq); ++ gpioirq->irq_flag = 0; ++ break; ++ } ++ ++ if (i == ngpio) { ++ printk("delete gpio irq: first request irq, then do this.\n"); ++ return -EINVAL; ++ } ++ } ++ break; ++ ++ default: ++ printk("akgpio: the ioctl is unknow.\n"); ++ break; ++ } ++ ++ //print_gpios_info(akgpios, ngpio); ++ return 0; ++} ++ ++static int akgpio_custom_open(struct inode *node, struct file *file) ++{ ++ /* do nothing, return correct */ ++ printk(KERN_INFO "open akgpio device success.\n"); ++ return 0; ++} ++ ++static int akgpio_custom_release(struct inode *node, struct file *file) ++{ ++ /* do nothing */ ++ return 0; ++} ++ ++static const struct file_operations akgpio_ops = { ++ .owner = THIS_MODULE, ++ .open = akgpio_custom_open, ++ .release = akgpio_custom_release, ++ .unlocked_ioctl = akgpio_custom_ioctl, ++}; ++ ++static struct miscdevice akgpio_dev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AKGPIO_DEV_NAME, ++ .fops = &akgpio_ops, ++ .mode = S_IRWXO, ++}; ++ ++static int akgpio_custom_probe(struct platform_device *pdev) ++{ ++ int i; ++ struct akgpio_custom_device *gpioirq = NULL; ++ ++ /* get platform data of akgpio */ ++ pdata = pdev->dev.platform_data; ++ if ((pdata == NULL)||((pdata)&&(pdata->ngpiopin == 0))) ++ return -ENODEV; ++ ++ /* register misc device */ ++ if (misc_register(&akgpio_dev)) { ++ printk(KERN_ERR "akgpio: Unable register misc device.\n"); ++ return -ENODEV; ++ } ++ ++ gpio_num = pdata->ngpiopin; ++ ++ akgpios = kzalloc(gpio_num * sizeof(struct akgpio_custom_device), GFP_KERNEL); ++ if (akgpios == NULL) ++ return -ENOMEM; ++ gpioirq = akgpios; ++ ++ for (i = 0; i < gpio_num; i++) { ++ gpioirq[i].gpio = -1; ++ gpioirq[i].used = 0; ++ gpioirq[i].irq_flag = 0; ++ init_MUTEX(&gpioirq[i].sem); ++ gpioirq[i].locked = 0; ++ } ++ ++ printk("akgpio driver initialize.\n"); ++ print_gpios_info(gpioirq, gpio_num); ++ return 0; ++} ++ ++static int akgpio_custom_remove(struct platform_device *pdev) ++{ ++ /* release platform data for akgpio */ ++ int i; ++ struct akgpio_custom_device *gpioirq = akgpios; ++ ++ pdata = NULL; ++ misc_deregister(&akgpio_dev); ++ ++ for (i = 0; i < gpio_num; i++) { ++ gpioirq[i].gpio = -1; ++ gpioirq[i].used = 0; ++ gpioirq[i].irq_flag = 0; ++ gpioirq[i].locked = 0; ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver akgpio_custom_driver = { ++ .probe = akgpio_custom_probe, ++ .remove = __devexit_p(akgpio_custom_remove), ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = AKGPIO_DEV_NAME, ++ }, ++}; ++ ++static int __init akgpio_custom_init(void) ++{ ++ return platform_driver_register(&akgpio_custom_driver); ++} ++ ++static void __exit akgpio_custom_exit(void) ++{ ++ platform_driver_unregister(&akgpio_custom_driver); ++} ++ ++module_init(akgpio_custom_init); ++module_exit(akgpio_custom_exit); ++ ++MODULE_AUTHOR("Anyka Microelectronic Ltd."); ++MODULE_DESCRIPTION("Anyka gpio apply for user space control"); ++MODULE_ALIAS("Anyka GPIO Apply"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/misc/akm8975.c b/drivers/misc/akm8975.c +new file mode 100644 +index 00000000..830d2897 +--- /dev/null ++++ b/drivers/misc/akm8975.c +@@ -0,0 +1,732 @@ ++/* drivers/misc/akm8975.c - akm8975 compass driver ++ * ++ * Copyright (C) 2007-2008 HTC Corporation. ++ * Author: Hou-Kun Chen ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++/* ++ * Revised by AKM 2009/04/02 ++ * Revised by Motorola 2010/05/27 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AK8975DRV_CALL_DBG 0 ++#if AK8975DRV_CALL_DBG ++#define FUNCDBG(msg) pr_err("%s:%s\n", __func__, msg); ++#else ++#define FUNCDBG(msg) ++#endif ++ ++#define AK8975DRV_DATA_DBG 0 ++#define MAX_FAILURE_COUNT 10 ++ ++struct akm8975_data { ++ struct i2c_client *this_client; ++ struct akm8975_platform_data *pdata; ++ struct input_dev *input_dev; ++ struct work_struct work; ++ struct mutex flags_lock; ++#ifdef CONFIG_HAS_EARLYSUSPEND ++ struct early_suspend early_suspend; ++#endif ++}; ++ ++/* ++* Because misc devices can not carry a pointer from driver register to ++* open, we keep this global. This limits the driver to a single instance. ++*/ ++struct akm8975_data *akmd_data; ++ ++static DECLARE_WAIT_QUEUE_HEAD(open_wq); ++ ++static atomic_t open_flag; ++ ++static short m_flag; ++static short a_flag; ++static short t_flag; ++static short mv_flag; ++ ++static short akmd_delay; ++ ++static ssize_t akm8975_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ return sprintf(buf, "%u\n", i2c_smbus_read_byte_data(client, ++ AK8975_REG_CNTL)); ++} ++static ssize_t akm8975_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ unsigned long val; ++ strict_strtoul(buf, 10, &val); ++ if (val > 0xff) ++ return -EINVAL; ++ i2c_smbus_write_byte_data(client, AK8975_REG_CNTL, val); ++ return count; ++} ++static DEVICE_ATTR(akm_ms1, S_IWUSR | S_IRUGO, akm8975_show, akm8975_store); ++ ++static int akm8975_i2c_rxdata(struct akm8975_data *akm, char *buf, int length) ++{ ++ struct i2c_msg msgs[] = { ++ { ++ .addr = akm->this_client->addr, ++ .flags = 0, ++ .len = 1, ++ .buf = buf, ++ }, ++ { ++ .addr = akm->this_client->addr, ++ .flags = I2C_M_RD, ++ .len = length, ++ .buf = buf, ++ }, ++ }; ++ ++ FUNCDBG("called"); ++ ++ if (i2c_transfer(akm->this_client->adapter, msgs, 2) < 0) { ++ pr_err("akm8975_i2c_rxdata: transfer error\n"); ++ return EIO; ++ } else ++ return 0; ++} ++ ++static int akm8975_i2c_txdata(struct akm8975_data *akm, char *buf, int length) ++{ ++ struct i2c_msg msgs[] = { ++ { ++ .addr = akm->this_client->addr, ++ .flags = 0, ++ .len = length, ++ .buf = buf, ++ }, ++ }; ++ ++ FUNCDBG("called"); ++ ++ if (i2c_transfer(akm->this_client->adapter, msgs, 1) < 0) { ++ pr_err("akm8975_i2c_txdata: transfer error\n"); ++ return -EIO; ++ } else ++ return 0; ++} ++ ++static void akm8975_ecs_report_value(struct akm8975_data *akm, short *rbuf) ++{ ++ struct akm8975_data *data = i2c_get_clientdata(akm->this_client); ++ ++ FUNCDBG("called"); ++ ++#if AK8975DRV_DATA_DBG ++ pr_info("akm8975_ecs_report_value: yaw = %d, pitch = %d, roll = %d\n", ++ rbuf[0], rbuf[1], rbuf[2]); ++ pr_info("tmp = %d, m_stat= %d, g_stat=%d\n", rbuf[3], rbuf[4], rbuf[5]); ++ pr_info("Acceleration: x = %d LSB, y = %d LSB, z = %d LSB\n", ++ rbuf[6], rbuf[7], rbuf[8]); ++ pr_info("Magnetic: x = %d LSB, y = %d LSB, z = %d LSB\n\n", ++ rbuf[9], rbuf[10], rbuf[11]); ++#endif ++ mutex_lock(&akm->flags_lock); ++ /* Report magnetic sensor information */ ++ if (m_flag) { ++ input_report_abs(data->input_dev, ABS_RX, rbuf[0]); ++ input_report_abs(data->input_dev, ABS_RY, rbuf[1]); ++ input_report_abs(data->input_dev, ABS_RZ, rbuf[2]); ++ input_report_abs(data->input_dev, ABS_RUDDER, rbuf[4]); ++ } ++ ++ /* Report acceleration sensor information */ ++ if (a_flag) { ++ input_report_abs(data->input_dev, ABS_X, rbuf[6]); ++ input_report_abs(data->input_dev, ABS_Y, rbuf[7]); ++ input_report_abs(data->input_dev, ABS_Z, rbuf[8]); ++ input_report_abs(data->input_dev, ABS_WHEEL, rbuf[5]); ++ } ++ ++ /* Report temperature information */ ++ if (t_flag) ++ input_report_abs(data->input_dev, ABS_THROTTLE, rbuf[3]); ++ ++ if (mv_flag) { ++ input_report_abs(data->input_dev, ABS_HAT0X, rbuf[9]); ++ input_report_abs(data->input_dev, ABS_HAT0Y, rbuf[10]); ++ input_report_abs(data->input_dev, ABS_BRAKE, rbuf[11]); ++ } ++ mutex_unlock(&akm->flags_lock); ++ ++ input_sync(data->input_dev); ++} ++ ++static void akm8975_ecs_close_done(struct akm8975_data *akm) ++{ ++ FUNCDBG("called"); ++ mutex_lock(&akm->flags_lock); ++ m_flag = 1; ++ a_flag = 1; ++ t_flag = 1; ++ mv_flag = 1; ++ mutex_unlock(&akm->flags_lock); ++} ++ ++static int akm_aot_open(struct inode *inode, struct file *file) ++{ ++ int ret = -1; ++ ++ FUNCDBG("called"); ++ if (atomic_cmpxchg(&open_flag, 0, 1) == 0) { ++ wake_up(&open_wq); ++ ret = 0; ++ } ++ ++ ret = nonseekable_open(inode, file); ++ if (ret) ++ return ret; ++ ++ file->private_data = akmd_data; ++ ++ return ret; ++} ++ ++static int akm_aot_release(struct inode *inode, struct file *file) ++{ ++ FUNCDBG("called"); ++ atomic_set(&open_flag, 0); ++ wake_up(&open_wq); ++ return 0; ++} ++ ++static int akm_aot_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ void __user *argp = (void __user *) arg; ++ short flag; ++ struct akm8975_data *akm = file->private_data; ++ ++ FUNCDBG("called"); ++ ++ switch (cmd) { ++ case ECS_IOCTL_APP_SET_MFLAG: ++ case ECS_IOCTL_APP_SET_AFLAG: ++ case ECS_IOCTL_APP_SET_MVFLAG: ++ if (copy_from_user(&flag, argp, sizeof(flag))) ++ return -EFAULT; ++ if (flag < 0 || flag > 1) ++ return -EINVAL; ++ break; ++ case ECS_IOCTL_APP_SET_DELAY: ++ if (copy_from_user(&flag, argp, sizeof(flag))) ++ return -EFAULT; ++ break; ++ default: ++ break; ++ } ++ ++ mutex_lock(&akm->flags_lock); ++ switch (cmd) { ++ case ECS_IOCTL_APP_SET_MFLAG: ++ m_flag = flag; ++ break; ++ case ECS_IOCTL_APP_GET_MFLAG: ++ flag = m_flag; ++ break; ++ case ECS_IOCTL_APP_SET_AFLAG: ++ a_flag = flag; ++ break; ++ case ECS_IOCTL_APP_GET_AFLAG: ++ flag = a_flag; ++ break; ++ case ECS_IOCTL_APP_SET_MVFLAG: ++ mv_flag = flag; ++ break; ++ case ECS_IOCTL_APP_GET_MVFLAG: ++ flag = mv_flag; ++ break; ++ case ECS_IOCTL_APP_SET_DELAY: ++ akmd_delay = flag; ++ break; ++ case ECS_IOCTL_APP_GET_DELAY: ++ flag = akmd_delay; ++ break; ++ default: ++ return -ENOTTY; ++ } ++ mutex_unlock(&akm->flags_lock); ++ ++ switch (cmd) { ++ case ECS_IOCTL_APP_GET_MFLAG: ++ case ECS_IOCTL_APP_GET_AFLAG: ++ case ECS_IOCTL_APP_GET_MVFLAG: ++ case ECS_IOCTL_APP_GET_DELAY: ++ if (copy_to_user(argp, &flag, sizeof(flag))) ++ return -EFAULT; ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int akmd_open(struct inode *inode, struct file *file) ++{ ++ int err = 0; ++ ++ FUNCDBG("called"); ++ err = nonseekable_open(inode, file); ++ if (err) ++ return err; ++ ++ file->private_data = akmd_data; ++ return 0; ++} ++ ++static int akmd_release(struct inode *inode, struct file *file) ++{ ++ struct akm8975_data *akm = file->private_data; ++ ++ FUNCDBG("called"); ++ akm8975_ecs_close_done(akm); ++ return 0; ++} ++ ++static int akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ void __user *argp = (void __user *) arg; ++ ++ char rwbuf[16]; ++ int ret = -1; ++ int status; ++ short value[12]; ++ short delay; ++ struct akm8975_data *akm = file->private_data; ++ ++ FUNCDBG("called"); ++ ++ switch (cmd) { ++ case ECS_IOCTL_READ: ++ case ECS_IOCTL_WRITE: ++ if (copy_from_user(&rwbuf, argp, sizeof(rwbuf))) ++ return -EFAULT; ++ break; ++ ++ case ECS_IOCTL_SET_YPR: ++ if (copy_from_user(&value, argp, sizeof(value))) ++ return -EFAULT; ++ break; ++ ++ default: ++ break; ++ } ++ ++ switch (cmd) { ++ case ECS_IOCTL_READ: ++ if (rwbuf[0] < 1) ++ return -EINVAL; ++ ++ ret = akm8975_i2c_rxdata(akm, &rwbuf[1], rwbuf[0]); ++ if (ret < 0) ++ return ret; ++ break; ++ ++ case ECS_IOCTL_WRITE: ++ if (rwbuf[0] < 2) ++ return -EINVAL; ++ ++ ret = akm8975_i2c_txdata(akm, &rwbuf[1], rwbuf[0]); ++ if (ret < 0) ++ return ret; ++ break; ++ case ECS_IOCTL_SET_YPR: ++ akm8975_ecs_report_value(akm, value); ++ break; ++ ++ case ECS_IOCTL_GET_OPEN_STATUS: ++ wait_event_interruptible(open_wq, ++ (atomic_read(&open_flag) != 0)); ++ status = atomic_read(&open_flag); ++ break; ++ case ECS_IOCTL_GET_CLOSE_STATUS: ++ wait_event_interruptible(open_wq, ++ (atomic_read(&open_flag) == 0)); ++ status = atomic_read(&open_flag); ++ break; ++ ++ case ECS_IOCTL_GET_DELAY: ++ delay = akmd_delay; ++ break; ++ ++ default: ++ FUNCDBG("Unknown cmd\n"); ++ return -ENOTTY; ++ } ++ ++ switch (cmd) { ++ case ECS_IOCTL_READ: ++ if (copy_to_user(argp, &rwbuf, sizeof(rwbuf))) ++ return -EFAULT; ++ break; ++ case ECS_IOCTL_GET_OPEN_STATUS: ++ case ECS_IOCTL_GET_CLOSE_STATUS: ++ if (copy_to_user(argp, &status, sizeof(status))) ++ return -EFAULT; ++ break; ++ case ECS_IOCTL_GET_DELAY: ++ if (copy_to_user(argp, &delay, sizeof(delay))) ++ return -EFAULT; ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++/* needed to clear the int. pin */ ++static void akm_work_func(struct work_struct *work) ++{ ++ struct akm8975_data *akm = ++ container_of(work, struct akm8975_data, work); ++ ++ FUNCDBG("called"); ++ enable_irq(akm->this_client->irq); ++} ++ ++static irqreturn_t akm8975_interrupt(int irq, void *dev_id) ++{ ++ struct akm8975_data *akm = dev_id; ++ FUNCDBG("called"); ++ ++ disable_irq_nosync(akm->this_client->irq); ++ schedule_work(&akm->work); ++ return IRQ_HANDLED; ++} ++ ++static int akm8975_power_off(struct akm8975_data *akm) ++{ ++#if AK8975DRV_CALL_DBG ++ pr_info("%s\n", __func__); ++#endif ++ if (akm->pdata->power_off) ++ akm->pdata->power_off(); ++ ++ return 0; ++} ++ ++static int akm8975_power_on(struct akm8975_data *akm) ++{ ++ int err; ++ ++#if AK8975DRV_CALL_DBG ++ pr_info("%s\n", __func__); ++#endif ++ if (akm->pdata->power_on) { ++ err = akm->pdata->power_on(); ++ if (err < 0) ++ return err; ++ } ++ return 0; ++} ++ ++static int akm8975_suspend(struct i2c_client *client, pm_message_t mesg) ++{ ++ struct akm8975_data *akm = i2c_get_clientdata(client); ++ ++#if AK8975DRV_CALL_DBG ++ pr_info("%s\n", __func__); ++#endif ++ /* TO DO: might need more work after power mgmt ++ is enabled */ ++ return akm8975_power_off(akm); ++} ++ ++static int akm8975_resume(struct i2c_client *client) ++{ ++ struct akm8975_data *akm = i2c_get_clientdata(client); ++ ++#if AK8975DRV_CALL_DBG ++ pr_info("%s\n", __func__); ++#endif ++ /* TO DO: might need more work after power mgmt ++ is enabled */ ++ return akm8975_power_on(akm); ++} ++ ++#ifdef CONFIG_HAS_EARLYSUSPEND ++static void akm8975_early_suspend(struct early_suspend *handler) ++{ ++ struct akm8975_data *akm; ++ akm = container_of(handler, struct akm8975_data, early_suspend); ++ ++#if AK8975DRV_CALL_DBG ++ pr_info("%s\n", __func__); ++#endif ++ akm8975_suspend(akm->this_client, PMSG_SUSPEND); ++} ++ ++static void akm8975_early_resume(struct early_suspend *handler) ++{ ++ struct akm8975_data *akm; ++ akm = container_of(handler, struct akm8975_data, early_suspend); ++ ++#if AK8975DRV_CALL_DBG ++ pr_info("%s\n", __func__); ++#endif ++ akm8975_resume(akm->this_client); ++} ++#endif ++ ++ ++static int akm8975_init_client(struct i2c_client *client) ++{ ++ struct akm8975_data *data; ++ int ret; ++ ++ data = i2c_get_clientdata(client); ++ ++ ret = request_irq(client->irq, akm8975_interrupt, IRQF_TRIGGER_RISING, ++ "akm8975", data); ++ ++ if (ret < 0) { ++ pr_err("akm8975_init_client: request irq failed\n"); ++ goto err; ++ } ++ ++ init_waitqueue_head(&open_wq); ++ ++ mutex_lock(&data->flags_lock); ++ m_flag = 1; ++ a_flag = 1; ++ t_flag = 1; ++ mv_flag = 1; ++ mutex_unlock(&data->flags_lock); ++ ++ return 0; ++err: ++ return ret; ++} ++ ++static const struct file_operations akmd_fops = { ++ .owner = THIS_MODULE, ++ .open = akmd_open, ++ .release = akmd_release, ++ .ioctl = akmd_ioctl, ++}; ++ ++static const struct file_operations akm_aot_fops = { ++ .owner = THIS_MODULE, ++ .open = akm_aot_open, ++ .release = akm_aot_release, ++ .ioctl = akm_aot_ioctl, ++}; ++ ++static struct miscdevice akm_aot_device = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "akm8975_aot", ++ .fops = &akm_aot_fops, ++}; ++ ++static struct miscdevice akmd_device = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "akm8975_dev", ++ .fops = &akmd_fops, ++}; ++ ++int akm8975_probe(struct i2c_client *client, ++ const struct i2c_device_id *devid) ++{ ++ struct akm8975_data *akm; ++ int err; ++ FUNCDBG("called"); ++ ++ if (client->dev.platform_data == NULL) { ++ dev_err(&client->dev, "platform data is NULL. exiting.\n"); ++ err = -ENODEV; ++ goto exit_platform_data_null; ++ } ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ dev_err(&client->dev, "platform data is NULL. exiting.\n"); ++ err = -ENODEV; ++ goto exit_check_functionality_failed; ++ } ++ ++ akm = kzalloc(sizeof(struct akm8975_data), GFP_KERNEL); ++ if (!akm) { ++ dev_err(&client->dev, ++ "failed to allocate memory for module data\n"); ++ err = -ENOMEM; ++ goto exit_alloc_data_failed; ++ } ++ ++ akm->pdata = client->dev.platform_data; ++ ++ mutex_init(&akm->flags_lock); ++ INIT_WORK(&akm->work, akm_work_func); ++ i2c_set_clientdata(client, akm); ++ ++ err = akm8975_power_on(akm); ++ if (err < 0) ++ goto exit_power_on_failed; ++ ++ akm8975_init_client(client); ++ akm->this_client = client; ++ akmd_data = akm; ++ ++ akm->input_dev = input_allocate_device(); ++ if (!akm->input_dev) { ++ err = -ENOMEM; ++ dev_err(&akm->this_client->dev, ++ "input device allocate failed\n"); ++ goto exit_input_dev_alloc_failed; ++ } ++ ++ set_bit(EV_ABS, akm->input_dev->evbit); ++ ++ /* yaw */ ++ input_set_abs_params(akm->input_dev, ABS_RX, 0, 23040, 0, 0); ++ /* pitch */ ++ input_set_abs_params(akm->input_dev, ABS_RY, -11520, 11520, 0, 0); ++ /* roll */ ++ input_set_abs_params(akm->input_dev, ABS_RZ, -5760, 5760, 0, 0); ++ /* x-axis acceleration */ ++ input_set_abs_params(akm->input_dev, ABS_X, -5760, 5760, 0, 0); ++ /* y-axis acceleration */ ++ input_set_abs_params(akm->input_dev, ABS_Y, -5760, 5760, 0, 0); ++ /* z-axis acceleration */ ++ input_set_abs_params(akm->input_dev, ABS_Z, -5760, 5760, 0, 0); ++ /* temparature */ ++ input_set_abs_params(akm->input_dev, ABS_THROTTLE, -30, 85, 0, 0); ++ /* status of magnetic sensor */ ++ input_set_abs_params(akm->input_dev, ABS_RUDDER, 0, 3, 0, 0); ++ /* status of acceleration sensor */ ++ input_set_abs_params(akm->input_dev, ABS_WHEEL, 0, 3, 0, 0); ++ /* x-axis of raw magnetic vector */ ++ input_set_abs_params(akm->input_dev, ABS_HAT0X, -20480, 20479, 0, 0); ++ /* y-axis of raw magnetic vector */ ++ input_set_abs_params(akm->input_dev, ABS_HAT0Y, -20480, 20479, 0, 0); ++ /* z-axis of raw magnetic vector */ ++ input_set_abs_params(akm->input_dev, ABS_BRAKE, -20480, 20479, 0, 0); ++ ++ akm->input_dev->name = "compass"; ++ ++ err = input_register_device(akm->input_dev); ++ if (err) { ++ pr_err("akm8975_probe: Unable to register input device: %s\n", ++ akm->input_dev->name); ++ goto exit_input_register_device_failed; ++ } ++ ++ err = misc_register(&akmd_device); ++ if (err) { ++ pr_err("akm8975_probe: akmd_device register failed\n"); ++ goto exit_misc_device_register_failed; ++ } ++ ++ err = misc_register(&akm_aot_device); ++ if (err) { ++ pr_err("akm8975_probe: akm_aot_device register failed\n"); ++ goto exit_misc_device_register_failed; ++ } ++ ++ err = device_create_file(&client->dev, &dev_attr_akm_ms1); ++ ++#ifdef CONFIG_HAS_EARLYSUSPEND ++ akm->early_suspend.suspend = akm8975_early_suspend; ++ akm->early_suspend.resume = akm8975_early_resume; ++ register_early_suspend(&akm->early_suspend); ++#endif ++ return 0; ++ ++exit_misc_device_register_failed: ++exit_input_register_device_failed: ++ input_free_device(akm->input_dev); ++exit_input_dev_alloc_failed: ++ akm8975_power_off(akm); ++exit_power_on_failed: ++ kfree(akm); ++exit_alloc_data_failed: ++exit_check_functionality_failed: ++exit_platform_data_null: ++ return err; ++} ++ ++static int __devexit akm8975_remove(struct i2c_client *client) ++{ ++ struct akm8975_data *akm = i2c_get_clientdata(client); ++ FUNCDBG("called"); ++ free_irq(client->irq, NULL); ++ input_unregister_device(akm->input_dev); ++ misc_deregister(&akmd_device); ++ misc_deregister(&akm_aot_device); ++ akm8975_power_off(akm); ++ kfree(akm); ++ return 0; ++} ++ ++static const struct i2c_device_id akm8975_id[] = { ++ { "akm8975", 0 }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(i2c, akm8975_id); ++ ++static struct i2c_driver akm8975_driver = { ++ .probe = akm8975_probe, ++ .remove = akm8975_remove, ++#ifndef CONFIG_HAS_EARLYSUSPEND ++ .resume = akm8975_resume, ++ .suspend = akm8975_suspend, ++#endif ++ .id_table = akm8975_id, ++ .driver = { ++ .name = "akm8975", ++ }, ++}; ++ ++static int __init akm8975_init(void) ++{ ++ pr_info("AK8975 compass driver: init\n"); ++ FUNCDBG("AK8975 compass driver: init\n"); ++ return i2c_add_driver(&akm8975_driver); ++} ++ ++static void __exit akm8975_exit(void) ++{ ++ FUNCDBG("AK8975 compass driver: exit\n"); ++ i2c_del_driver(&akm8975_driver); ++} ++ ++module_init(akm8975_init); ++module_exit(akm8975_exit); ++ ++MODULE_AUTHOR("Hou-Kun Chen "); ++MODULE_DESCRIPTION("AK8975 compass driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c +new file mode 100644 +index 00000000..2141124a +--- /dev/null ++++ b/drivers/misc/uid_stat.c +@@ -0,0 +1,156 @@ ++/* drivers/misc/uid_stat.c ++ * ++ * Copyright (C) 2008 - 2009 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static DEFINE_SPINLOCK(uid_lock); ++static LIST_HEAD(uid_list); ++static struct proc_dir_entry *parent; ++ ++struct uid_stat { ++ struct list_head link; ++ uid_t uid; ++ atomic_t tcp_rcv; ++ atomic_t tcp_snd; ++}; ++ ++static struct uid_stat *find_uid_stat(uid_t uid) { ++ unsigned long flags; ++ struct uid_stat *entry; ++ ++ spin_lock_irqsave(&uid_lock, flags); ++ list_for_each_entry(entry, &uid_list, link) { ++ if (entry->uid == uid) { ++ spin_unlock_irqrestore(&uid_lock, flags); ++ return entry; ++ } ++ } ++ spin_unlock_irqrestore(&uid_lock, flags); ++ return NULL; ++} ++ ++static int tcp_snd_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len; ++ unsigned int bytes; ++ char *p = page; ++ struct uid_stat *uid_entry = (struct uid_stat *) data; ++ if (!data) ++ return 0; ++ ++ bytes = (unsigned int) (atomic_read(&uid_entry->tcp_snd) + INT_MIN); ++ p += sprintf(p, "%u\n", bytes); ++ len = (p - page) - off; ++ *eof = (len <= count) ? 1 : 0; ++ *start = page + off; ++ return len; ++} ++ ++static int tcp_rcv_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len; ++ unsigned int bytes; ++ char *p = page; ++ struct uid_stat *uid_entry = (struct uid_stat *) data; ++ if (!data) ++ return 0; ++ ++ bytes = (unsigned int) (atomic_read(&uid_entry->tcp_rcv) + INT_MIN); ++ p += sprintf(p, "%u\n", bytes); ++ len = (p - page) - off; ++ *eof = (len <= count) ? 1 : 0; ++ *start = page + off; ++ return len; ++} ++ ++/* Create a new entry for tracking the specified uid. */ ++static struct uid_stat *create_stat(uid_t uid) { ++ unsigned long flags; ++ char uid_s[32]; ++ struct uid_stat *new_uid; ++ struct proc_dir_entry *entry; ++ ++ /* Create the uid stat struct and append it to the list. */ ++ if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL) ++ return NULL; ++ ++ new_uid->uid = uid; ++ /* Counters start at INT_MIN, so we can track 4GB of network traffic. */ ++ atomic_set(&new_uid->tcp_rcv, INT_MIN); ++ atomic_set(&new_uid->tcp_snd, INT_MIN); ++ ++ spin_lock_irqsave(&uid_lock, flags); ++ list_add_tail(&new_uid->link, &uid_list); ++ spin_unlock_irqrestore(&uid_lock, flags); ++ ++ sprintf(uid_s, "%d", uid); ++ entry = proc_mkdir(uid_s, parent); ++ ++ /* Keep reference to uid_stat so we know what uid to read stats from. */ ++ create_proc_read_entry("tcp_snd", S_IRUGO, entry , tcp_snd_read_proc, ++ (void *) new_uid); ++ ++ create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc, ++ (void *) new_uid); ++ ++ return new_uid; ++} ++ ++int uid_stat_tcp_snd(uid_t uid, int size) { ++ struct uid_stat *entry; ++ activity_stats_update(); ++ if ((entry = find_uid_stat(uid)) == NULL && ++ ((entry = create_stat(uid)) == NULL)) { ++ return -1; ++ } ++ atomic_add(size, &entry->tcp_snd); ++ return 0; ++} ++ ++int uid_stat_tcp_rcv(uid_t uid, int size) { ++ struct uid_stat *entry; ++ activity_stats_update(); ++ if ((entry = find_uid_stat(uid)) == NULL && ++ ((entry = create_stat(uid)) == NULL)) { ++ return -1; ++ } ++ atomic_add(size, &entry->tcp_rcv); ++ return 0; ++} ++ ++static int __init uid_stat_init(void) ++{ ++ parent = proc_mkdir("uid_stat", NULL); ++ if (!parent) { ++ pr_err("uid_stat: failed to create proc entry\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++__initcall(uid_stat_init); +diff --git a/drivers/misc/wl127x-rfkill.c b/drivers/misc/wl127x-rfkill.c +new file mode 100644 +index 00000000..f5b95152 +--- /dev/null ++++ b/drivers/misc/wl127x-rfkill.c +@@ -0,0 +1,121 @@ ++/* ++ * Bluetooth TI wl127x rfkill power control via GPIO ++ * ++ * Copyright (C) 2009 Motorola, Inc. ++ * Copyright (C) 2008 Texas Instruments ++ * Initial code: Pavan Savoy (wl127x_power.c) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int wl127x_rfkill_set_power(void *data, enum rfkill_state state) ++{ ++ int nshutdown_gpio = (int) data; ++ ++ switch (state) { ++ case RFKILL_STATE_UNBLOCKED: ++ gpio_set_value(nshutdown_gpio, 1); ++ break; ++ case RFKILL_STATE_SOFT_BLOCKED: ++ gpio_set_value(nshutdown_gpio, 0); ++ break; ++ default: ++ printk(KERN_ERR "invalid bluetooth rfkill state %d\n", state); ++ } ++ return 0; ++} ++ ++static int wl127x_rfkill_probe(struct platform_device *pdev) ++{ ++ int rc = 0; ++ struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data; ++ enum rfkill_state default_state = RFKILL_STATE_SOFT_BLOCKED; /* off */ ++ ++ rc = gpio_request(pdata->nshutdown_gpio, "wl127x_nshutdown_gpio"); ++ if (unlikely(rc)) ++ return rc; ++ ++ rc = gpio_direction_output(pdata->nshutdown_gpio, 0); ++ if (unlikely(rc)) ++ return rc; ++ ++ rfkill_set_default(RFKILL_TYPE_BLUETOOTH, default_state); ++ wl127x_rfkill_set_power(NULL, default_state); ++ ++ pdata->rfkill = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH); ++ if (unlikely(!pdata->rfkill)) ++ return -ENOMEM; ++ ++ pdata->rfkill->name = "wl127x"; ++ pdata->rfkill->state = default_state; ++ /* userspace cannot take exclusive control */ ++ pdata->rfkill->user_claim_unsupported = 1; ++ pdata->rfkill->user_claim = 0; ++ pdata->rfkill->data = (void *) pdata->nshutdown_gpio; ++ pdata->rfkill->toggle_radio = wl127x_rfkill_set_power; ++ ++ rc = rfkill_register(pdata->rfkill); ++ ++ if (unlikely(rc)) ++ rfkill_free(pdata->rfkill); ++ ++ return 0; ++} ++ ++static int wl127x_rfkill_remove(struct platform_device *pdev) ++{ ++ struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data; ++ ++ rfkill_unregister(pdata->rfkill); ++ rfkill_free(pdata->rfkill); ++ gpio_free(pdata->nshutdown_gpio); ++ ++ return 0; ++} ++ ++static struct platform_driver wl127x_rfkill_platform_driver = { ++ .probe = wl127x_rfkill_probe, ++ .remove = wl127x_rfkill_remove, ++ .driver = { ++ .name = "wl127x-rfkill", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init wl127x_rfkill_init(void) ++{ ++ return platform_driver_register(&wl127x_rfkill_platform_driver); ++} ++ ++static void __exit wl127x_rfkill_exit(void) ++{ ++ platform_driver_unregister(&wl127x_rfkill_platform_driver); ++} ++ ++module_init(wl127x_rfkill_init); ++module_exit(wl127x_rfkill_exit); ++ ++MODULE_ALIAS("platform:wl127x"); ++MODULE_DESCRIPTION("wl127x-rfkill"); ++MODULE_AUTHOR("Motorola"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig +index 3b1f783b..c9905355 100644 +--- a/drivers/mmc/card/Kconfig ++++ b/drivers/mmc/card/Kconfig +@@ -50,6 +50,15 @@ config MMC_BLOCK_BOUNCE + + If unsure, say Y here. + ++config MMC_BLOCK_DEFERRED_RESUME ++ bool "Deferr MMC layer resume until I/O is requested" ++ depends on MMC_BLOCK ++ default n ++ help ++ Say Y here to enable deferred MMC resume until I/O ++ is requested. This will reduce overall resume latency and ++ save power when theres an SD card inserted but not being used. ++ + config SDIO_UART + tristate "SDIO UART/GPS class support" + help +@@ -67,3 +76,11 @@ config MMC_TEST + + This driver is only of interest to those developing or + testing a host driver. Most people should say N here. ++ ++config SDIO_WIFI ++ tristate "SDIO WIFI support" ++ depends on MMC_ANYKA ++ default n ++ help ++ SDIO function driver for SDIO cards that implements the WIFI ++ module. +diff --git a/drivers/mmc/card/Makefile b/drivers/mmc/card/Makefile +index c73b406a..c6c2f22f 100644 +--- a/drivers/mmc/card/Makefile ++++ b/drivers/mmc/card/Makefile +@@ -7,4 +7,5 @@ mmc_block-objs := block.o queue.o + obj-$(CONFIG_MMC_TEST) += mmc_test.o + + obj-$(CONFIG_SDIO_UART) += sdio_uart.o ++obj-$(CONFIG_SDIO_WIFI) += sdio_wifi.o + +diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c +index 833ff16f..117b98a6 100644 +--- a/drivers/mmc/card/block.c ++++ b/drivers/mmc/card/block.c +@@ -143,11 +143,7 @@ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) + + static inline int mmc_get_devidx(struct gendisk *disk) + { +- int devmaj = MAJOR(disk_devt(disk)); +- int devidx = MINOR(disk_devt(disk)) / perdev_minors; +- +- if (!devmaj) +- devidx = disk->first_minor / perdev_minors; ++ int devidx = disk->first_minor / perdev_minors; + return devidx; + } + +@@ -660,18 +656,22 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error, + req->rq_disk->disk_name, "timed out", name, status); + + /* If the status cmd initially failed, retry the r/w cmd */ +- if (!status_valid) ++ if (!status_valid) { ++ pr_err("%s: status not valid, retrying timeout\n", req->rq_disk->disk_name); + return ERR_RETRY; +- ++ } + /* + * If it was a r/w cmd crc error, or illegal command + * (eg, issued in wrong state) then retry - we should + * have corrected the state problem above. + */ +- if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND)) ++ if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND)) { ++ pr_err("%s: command error, retrying timeout\n", req->rq_disk->disk_name); + return ERR_RETRY; ++ } + + /* Otherwise abort the command */ ++ pr_err("%s: not retrying timeout\n", req->rq_disk->disk_name); + return ERR_ABORT; + + default: +@@ -1405,12 +1405,22 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) + return 0; + } + ++static int ++mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card); ++ + static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) + { + int ret; + struct mmc_blk_data *md = mq->data; + struct mmc_card *card = md->queue.card; + ++#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME ++ if (mmc_bus_needs_resume(card->host)) { ++ mmc_resume_bus(card->host); ++ mmc_blk_set_blksize(md, card); ++ } ++#endif ++ + if (req && !mq->mqrq_prev->req) + /* claim host only for the first request */ + mmc_claim_host(card->host); +@@ -1523,6 +1533,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, + md->disk->queue = md->queue.queue; + md->disk->driverfs_dev = parent; + set_disk_ro(md->disk, md->read_only || default_ro); ++ md->disk->flags = GENHD_FL_EXT_DEVT; + + /* + * As discussed on lkml, GENHD_FL_REMOVABLE should: +@@ -1820,6 +1831,9 @@ static int mmc_blk_probe(struct mmc_card *card) + mmc_set_drvdata(card, md); + mmc_fixup_device(card, blk_fixups); + ++#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME ++ mmc_set_bus_resume_policy(card->host, 1); ++#endif + if (mmc_add_disk(md)) + goto out; + +@@ -1845,6 +1859,9 @@ static void mmc_blk_remove(struct mmc_card *card) + mmc_release_host(card->host); + mmc_blk_remove_req(md); + mmc_set_drvdata(card, NULL); ++#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME ++ mmc_set_bus_resume_policy(card->host, 0); ++#endif + } + + #ifdef CONFIG_PM +diff --git a/drivers/mmc/card/sdio_wifi.c b/drivers/mmc/card/sdio_wifi.c +new file mode 100644 +index 00000000..82ef05b6 +--- /dev/null ++++ b/drivers/mmc/card/sdio_wifi.c +@@ -0,0 +1,103 @@ ++#include ++#include ++#include ++#include ++ ++#define WIFI_DEV_ENABLE 1 ++ ++ ++static int __devinit akplat_wifi_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct akwifi_platform_data *pdata = pdev->dev.platform_data; ++ ++ printk("%s entered.\n", __func__); ++ if(!pdata) ++ { ++ ret = -EINVAL; ++ return ret; ++ } ++ ++ /* akwifi power on */ ++ if (pdata->gpio_on.pin > 0) { ++ pdata->gpio_init(&pdata->gpio_on); ++ ++ msleep(pdata->power_on_delay); ++ printk("wifi power on\n"); ++ ++ } ++// data->power_off(); ++// data->power_on(); ++ return 0; ++} ++ ++static int __devexit akplat_wifi_remove(struct platform_device *pdev) ++{ ++ struct akwifi_platform_data *pdata = pdev->dev.platform_data; ++ ++ printk("%s entered.\n", __func__); ++ ++ if (pdata->gpio_off.pin > 0) { ++ pdata->gpio_init(&pdata->gpio_off); ++ ++ msleep(pdata->power_off_delay); ++ printk("wifi power off\n"); ++ } ++ ++// data->power_off(); ++ return 0; ++} ++ ++static int akplat_wifi_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++// struct wifi_power_platform_data *data = pdev->dev.platform_data; ++ ++ printk("%s entered.\n", __func__); ++ ++ //data->power_off(); ++ ++ return 0; ++} ++ ++static int akplat_wifi_resume(struct platform_device *pdev) ++{ ++// struct wifi_power_platform_data *data = pdev->dev.platform_data; ++ ++ printk("%s entered.\n", __func__); ++ ++ //data->power_on(); ++ ++ return 0; ++} ++ ++struct platform_device_id sdio_wifi_ids[] ={ ++ { ++ .name = "sdio_wifi_ar6302", ++ .driver_data = 0, ++ }, { }, ++}; ++ ++static struct platform_driver akplat_wifi_driver = { ++ .driver = { ++ .name = "anyka-wifi", ++ }, ++// .id_table = sdio_wifi_ids, ++ .probe = akplat_wifi_probe, ++ .remove = akplat_wifi_remove, ++ .suspend = akplat_wifi_suspend, ++ .resume = akplat_wifi_resume, ++}; ++ ++ ++static int __init sdio_wifi_init(void) ++{ ++ return platform_driver_register(&akplat_wifi_driver); ++} ++ ++static void __exit sdio_wifi_exit(void) ++{ ++ platform_driver_unregister(&akplat_wifi_driver); ++} ++ ++module_init(sdio_wifi_init); ++module_exit(sdio_wifi_exit); +diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig +index ef103871..85c2e1ac 100644 +--- a/drivers/mmc/core/Kconfig ++++ b/drivers/mmc/core/Kconfig +@@ -27,3 +27,20 @@ config MMC_CLKGATE + support handling this in order for it to be of any use. + + If unsure, say N. ++ ++config MMC_EMBEDDED_SDIO ++ boolean "MMC embedded SDIO device support (EXPERIMENTAL)" ++ depends on EXPERIMENTAL ++ help ++ If you say Y here, support will be added for embedded SDIO ++ devices which do not contain the necessary enumeration ++ support in hardware to be properly detected. ++ ++config MMC_PARANOID_SD_INIT ++ bool "Enable paranoid SD card initialization (EXPERIMENTAL)" ++ depends on EXPERIMENTAL ++ help ++ If you say Y here, the MMC layer will be extra paranoid ++ about re-trying SD init requests. This can be a useful ++ work-around for buggy controllers and hardware. Enable ++ if you are experiencing issues with SD detection. +diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c +index ba821fe7..27c49568 100644 +--- a/drivers/mmc/core/core.c ++++ b/drivers/mmc/core/core.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -140,13 +141,25 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) + cmd->retries = 0; + } + +- if (err && cmd->retries && !mmc_card_removed(host->card)) { +- /* +- * Request starter must handle retries - see +- * mmc_wait_for_req_done(). +- */ +- if (mrq->done) +- mrq->done(mrq); ++ if ((err && cmd->retries && !mmc_card_removed(host->card)) ++ ||(mrq->data && mrq->data->error && mrq->data->retries)) { ++ if (err && cmd->retries) { ++ pr_debug("%s: req failed (CMD%u): %d, retrying...\n", ++ mmc_hostname(host), cmd->opcode, err); ++ ++ cmd->retries--; ++ cmd->error = 0; ++ host->ops->request(host, mrq); ++ } else { ++ printk("%s: data(%s) req failed (CMD%u): %d, retrying(%d)...\n", ++ mmc_hostname(host), ++ (mrq->data->flags & MMC_DATA_READ) ? "read" : "write", ++ cmd->opcode, mrq->data->error, mrq->data->retries); ++ ++ mrq->data->retries--; ++ mrq->data->error = 0; ++ host->ops->request(host, mrq); ++ } + } else { + mmc_should_fail_request(host, mrq); + +@@ -232,6 +245,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) + + mrq->cmd->data = mrq->data; + mrq->data->error = 0; ++ mrq->data->retries = 3; + mrq->data->mrq = mrq; + if (mrq->stop) { + mrq->data->stop = mrq->stop; +@@ -1285,6 +1299,36 @@ static inline void mmc_bus_put(struct mmc_host *host) + spin_unlock_irqrestore(&host->lock, flags); + } + ++int mmc_resume_bus(struct mmc_host *host) ++{ ++ unsigned long flags; ++ ++ if (!mmc_bus_needs_resume(host)) ++ return -EINVAL; ++ ++ printk("%s: Starting deferred resume\n", mmc_hostname(host)); ++ spin_lock_irqsave(&host->lock, flags); ++ host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME; ++ host->rescan_disable = 0; ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ mmc_bus_get(host); ++ if (host->bus_ops && !host->bus_dead) { ++ mmc_power_up(host); ++ BUG_ON(!host->bus_ops->resume); ++ host->bus_ops->resume(host); ++ } ++ ++ if (host->bus_ops->detect && !host->bus_dead) ++ host->bus_ops->detect(host); ++ ++ mmc_bus_put(host); ++ printk("%s: Deferred resume completed\n", mmc_hostname(host)); ++ return 0; ++} ++ ++EXPORT_SYMBOL(mmc_resume_bus); ++ + /* + * Assign a mmc bus handler to a host. Only one bus handler may control a + * host at any given time. +@@ -1350,6 +1394,8 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay) + spin_unlock_irqrestore(&host->lock, flags); + #endif + host->detect_change = 1; ++ ++ wake_lock(&host->detect_wake_lock); + mmc_schedule_delayed_work(&host->detect, delay); + } + +@@ -2005,10 +2051,11 @@ EXPORT_SYMBOL(mmc_detect_card_removed); + + void mmc_rescan(struct work_struct *work) + { +- static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; ++ static const unsigned freqs[] = {300000, 200000, 100000 }; + struct mmc_host *host = + container_of(work, struct mmc_host, detect.work); + int i; ++ bool extend_wakelock = false; + + if (host->rescan_disable) + return; +@@ -2025,6 +2072,12 @@ void mmc_rescan(struct work_struct *work) + + host->detect_change = 0; + ++ /* If the card was removed the bus will be marked ++ * as dead - extend the wakelock so userspace ++ * can respond */ ++ if (host->bus_dead) ++ extend_wakelock = 1; ++ + /* + * Let mmc_bus_put() free the bus/bus_ops if we've found that + * the card is no longer present. +@@ -2049,16 +2102,24 @@ void mmc_rescan(struct work_struct *work) + + mmc_claim_host(host); + for (i = 0; i < ARRAY_SIZE(freqs); i++) { +- if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) ++ if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) { ++ extend_wakelock = true; + break; ++ } + if (freqs[i] <= host->f_min) + break; + } + mmc_release_host(host); + + out: +- if (host->caps & MMC_CAP_NEEDS_POLL) ++ if (extend_wakelock) ++ wake_lock_timeout(&host->detect_wake_lock, HZ / 2); ++ else ++ wake_unlock(&host->detect_wake_lock); ++ if (host->caps & MMC_CAP_NEEDS_POLL) { ++ wake_lock(&host->detect_wake_lock); + mmc_schedule_delayed_work(&host->detect, HZ); ++ } + } + + void mmc_start_host(struct mmc_host *host) +@@ -2076,7 +2137,8 @@ void mmc_stop_host(struct mmc_host *host) + spin_unlock_irqrestore(&host->lock, flags); + #endif + +- cancel_delayed_work_sync(&host->detect); ++ if (cancel_delayed_work_sync(&host->detect)) ++ wake_unlock(&host->detect_wake_lock); + mmc_flush_scheduled_work(); + + /* clear pm flags now and let card drivers set them as needed */ +@@ -2272,7 +2334,11 @@ int mmc_suspend_host(struct mmc_host *host) + { + int err = 0; + +- cancel_delayed_work(&host->detect); ++ if (mmc_bus_needs_resume(host)) ++ return 0; ++ ++ if (cancel_delayed_work(&host->detect)) ++ wake_unlock(&host->detect_wake_lock); + mmc_flush_scheduled_work(); + + err = mmc_cache_ctrl(host, 0); +@@ -2322,6 +2388,12 @@ int mmc_resume_host(struct mmc_host *host) + int err = 0; + + mmc_bus_get(host); ++ if (mmc_bus_manual_resume(host)) { ++ host->bus_resume_flags |= MMC_BUSRESUME_NEEDS_RESUME; ++ mmc_bus_put(host); ++ return 0; ++ } ++ + if (host->bus_ops && !host->bus_dead) { + if (!mmc_card_keep_power(host)) { + mmc_power_up(host); +@@ -2372,10 +2444,15 @@ int mmc_pm_notify(struct notifier_block *notify_block, + case PM_SUSPEND_PREPARE: + + spin_lock_irqsave(&host->lock, flags); ++ if (mmc_bus_needs_resume(host)) { ++ spin_unlock_irqrestore(&host->lock, flags); ++ break; ++ } + host->rescan_disable = 1; + host->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT; + spin_unlock_irqrestore(&host->lock, flags); +- cancel_delayed_work_sync(&host->detect); ++ if (cancel_delayed_work_sync(&host->detect)) ++ wake_unlock(&host->detect_wake_lock); + + if (!host->bus_ops || host->bus_ops->suspend) + break; +@@ -2396,6 +2473,10 @@ int mmc_pm_notify(struct notifier_block *notify_block, + case PM_POST_RESTORE: + + spin_lock_irqsave(&host->lock, flags); ++ if (mmc_bus_manual_resume(host)) { ++ spin_unlock_irqrestore(&host->lock, flags); ++ break; ++ } + host->rescan_disable = 0; + host->power_notify_type = MMC_HOST_PW_NOTIFY_LONG; + spin_unlock_irqrestore(&host->lock, flags); +@@ -2407,6 +2488,22 @@ int mmc_pm_notify(struct notifier_block *notify_block, + } + #endif + ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++void mmc_set_embedded_sdio_data(struct mmc_host *host, ++ struct sdio_cis *cis, ++ struct sdio_cccr *cccr, ++ struct sdio_embedded_func *funcs, ++ int num_funcs) ++{ ++ host->embedded_sdio_data.cis = cis; ++ host->embedded_sdio_data.cccr = cccr; ++ host->embedded_sdio_data.funcs = funcs; ++ host->embedded_sdio_data.num_funcs = num_funcs; ++} ++ ++EXPORT_SYMBOL(mmc_set_embedded_sdio_data); ++#endif ++ + static int __init mmc_init(void) + { + int ret; +diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c +index 91c84c7a..dd7b1203 100644 +--- a/drivers/mmc/core/host.c ++++ b/drivers/mmc/core/host.c +@@ -329,6 +329,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) + + spin_lock_init(&host->lock); + init_waitqueue_head(&host->wq); ++ wake_lock_init(&host->detect_wake_lock, WAKE_LOCK_SUSPEND, ++ kasprintf(GFP_KERNEL, "%s_detect", mmc_hostname(host))); + INIT_DELAYED_WORK(&host->detect, mmc_rescan); + #ifdef CONFIG_PM + host->pm_notify.notifier_call = mmc_pm_notify; +@@ -381,7 +383,8 @@ int mmc_add_host(struct mmc_host *host) + mmc_host_clk_sysfs_init(host); + + mmc_start_host(host); +- register_pm_notifier(&host->pm_notify); ++ if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) ++ register_pm_notifier(&host->pm_notify); + + return 0; + } +@@ -398,7 +401,9 @@ EXPORT_SYMBOL(mmc_add_host); + */ + void mmc_remove_host(struct mmc_host *host) + { +- unregister_pm_notifier(&host->pm_notify); ++ if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) ++ unregister_pm_notifier(&host->pm_notify); ++ + mmc_stop_host(host); + + #ifdef CONFIG_DEBUG_FS +@@ -425,6 +430,7 @@ void mmc_free_host(struct mmc_host *host) + spin_lock(&mmc_host_lock); + idr_remove(&mmc_host_idr, host->index); + spin_unlock(&mmc_host_lock); ++ wake_lock_destroy(&host->detect_wake_lock); + + put_device(&host->class_dev); + } +diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c +index c272c686..7c76a455 100644 +--- a/drivers/mmc/core/sd.c ++++ b/drivers/mmc/core/sd.c +@@ -806,6 +806,9 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, + bool reinit) + { + int err; ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ int retries; ++#endif + + if (!reinit) { + /* +@@ -832,7 +835,26 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, + /* + * Fetch switch information from card. + */ ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ for (retries = 1; retries <= 3; retries++) { ++ err = mmc_read_switch(card); ++ if (!err) { ++ if (retries > 1) { ++ printk(KERN_WARNING ++ "%s: recovered\n", ++ mmc_hostname(host)); ++ } ++ break; ++ } else { ++ printk(KERN_WARNING ++ "%s: read switch failed (attempt %d)\n", ++ mmc_hostname(host), retries); ++ } ++ } ++#else + err = mmc_read_switch(card); ++#endif ++ + if (err) + return err; + } +@@ -1046,18 +1068,36 @@ static int mmc_sd_alive(struct mmc_host *host) + */ + static void mmc_sd_detect(struct mmc_host *host) + { +- int err; ++ int err = 0; ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ int retries = 5; ++#endif + + BUG_ON(!host); + BUG_ON(!host->card); +- ++ + mmc_claim_host(host); + + /* + * Just check if our card has been removed. + */ ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ while(retries) { ++ err = mmc_send_status(host->card, NULL); ++ if (err) { ++ retries--; ++ udelay(5); ++ continue; ++ } ++ break; ++ } ++ if (!retries) { ++ printk(KERN_ERR "%s(%s): Unable to re-detect card (%d)\n", ++ __func__, mmc_hostname(host), err); ++ } ++#else + err = _mmc_detect_card_removed(host); +- ++#endif + mmc_release_host(host); + + if (err) { +@@ -1096,12 +1136,31 @@ static int mmc_sd_suspend(struct mmc_host *host) + static int mmc_sd_resume(struct mmc_host *host) + { + int err; ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ int retries; ++#endif + + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ retries = 5; ++ while (retries) { ++ err = mmc_sd_init_card(host, host->ocr, host->card); ++ ++ if (err) { ++ printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n", ++ mmc_hostname(host), err, retries); ++ mdelay(5); ++ retries--; ++ continue; ++ } ++ break; ++ } ++#else + err = mmc_sd_init_card(host, host->ocr, host->card); ++#endif + mmc_release_host(host); + + return err; +@@ -1155,6 +1214,9 @@ int mmc_attach_sd(struct mmc_host *host) + { + int err; + u32 ocr; ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ int retries; ++#endif + + BUG_ON(!host); + WARN_ON(!host->claimed); +@@ -1217,9 +1279,27 @@ int mmc_attach_sd(struct mmc_host *host) + /* + * Detect and init the card. + */ ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ retries = 5; ++ while (retries) { ++ err = mmc_sd_init_card(host, host->ocr, NULL); ++ if (err) { ++ retries--; ++ continue; ++ } ++ break; ++ } ++ ++ if (!retries) { ++ printk(KERN_ERR "%s: mmc_sd_init_card() failure (err = %d)\n", ++ mmc_hostname(host), err); ++ goto err; ++ } ++#else + err = mmc_sd_init_card(host, host->ocr, NULL); + if (err) + goto err; ++#endif + + mmc_release_host(host); + err = mmc_add_card(host->card); +diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c +index 13d0e953..341b4c0f 100644 +--- a/drivers/mmc/core/sdio.c ++++ b/drivers/mmc/core/sdio.c +@@ -10,6 +10,7 @@ + */ + + #include ++#include + #include + + #include +@@ -28,6 +29,10 @@ + #include "sdio_ops.h" + #include "sdio_cis.h" + ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++#include ++#endif ++ + static int sdio_read_fbr(struct sdio_func *func) + { + int ret; +@@ -713,19 +718,35 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, + goto finish; + } + +- /* +- * Read the common registers. +- */ +- err = sdio_read_cccr(card, ocr); +- if (err) +- goto remove; ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ if (host->embedded_sdio_data.cccr) ++ memcpy(&card->cccr, host->embedded_sdio_data.cccr, sizeof(struct sdio_cccr)); ++ else { ++#endif ++ /* ++ * Read the common registers. ++ */ ++ err = sdio_read_cccr(card, ocr); ++ if (err) ++ goto remove; ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ } ++#endif + +- /* +- * Read the common CIS tuples. +- */ +- err = sdio_read_common_cis(card); +- if (err) +- goto remove; ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ if (host->embedded_sdio_data.cis) ++ memcpy(&card->cis, host->embedded_sdio_data.cis, sizeof(struct sdio_cis)); ++ else { ++#endif ++ /* ++ * Read the common CIS tuples. ++ */ ++ err = sdio_read_common_cis(card); ++ if (err) ++ goto remove; ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ } ++#endif + + if (oldcard) { + int same = (card->cis.vendor == oldcard->cis.vendor && +@@ -1124,14 +1145,36 @@ int mmc_attach_sdio(struct mmc_host *host) + funcs = (ocr & 0x70000000) >> 28; + card->sdio_funcs = 0; + ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ if (host->embedded_sdio_data.funcs) ++ card->sdio_funcs = funcs = host->embedded_sdio_data.num_funcs; ++#endif ++ + /* + * Initialize (but don't add) all present functions. + */ + for (i = 0; i < funcs; i++, card->sdio_funcs++) { +- err = sdio_init_func(host->card, i + 1); +- if (err) +- goto remove; +- ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ if (host->embedded_sdio_data.funcs) { ++ struct sdio_func *tmp; ++ ++ tmp = sdio_alloc_func(host->card); ++ if (IS_ERR(tmp)) ++ goto remove; ++ tmp->num = (i + 1); ++ card->sdio_func[i] = tmp; ++ tmp->class = host->embedded_sdio_data.funcs[i].f_class; ++ tmp->max_blksize = host->embedded_sdio_data.funcs[i].f_maxblksize; ++ tmp->vendor = card->cis.vendor; ++ tmp->device = card->cis.device; ++ } else { ++#endif ++ err = sdio_init_func(host->card, i + 1); ++ if (err) ++ goto remove; ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ } ++#endif + /* + * Enable Runtime PM for this func (if supported) + */ +@@ -1179,3 +1222,39 @@ err: + return err; + } + ++int sdio_reset_comm(struct mmc_card *card) ++{ ++ struct mmc_host *host = card->host; ++ u32 ocr; ++ int err; ++ ++ printk("%s():\n", __func__); ++ mmc_claim_host(host); ++ ++ mmc_go_idle(host); ++ ++ mmc_set_clock(host, host->f_min); ++ ++ err = mmc_send_io_op_cond(host, 0, &ocr); ++ if (err) ++ goto err; ++ ++ host->ocr = mmc_select_voltage(host, ocr); ++ if (!host->ocr) { ++ err = -EINVAL; ++ goto err; ++ } ++ ++ err = mmc_sdio_init_card(host, host->ocr, card, 0); ++ if (err) ++ goto err; ++ ++ mmc_release_host(host); ++ return 0; ++err: ++ printk("%s: Error resetting SDIO communications (%d)\n", ++ mmc_hostname(host), err); ++ mmc_release_host(host); ++ return err; ++} ++EXPORT_SYMBOL(sdio_reset_comm); +diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c +index 236842ec..56d020e2 100644 +--- a/drivers/mmc/core/sdio_bus.c ++++ b/drivers/mmc/core/sdio_bus.c +@@ -24,6 +24,10 @@ + #include "sdio_cis.h" + #include "sdio_bus.h" + ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++#include ++#endif ++ + /* show configuration fields */ + #define sdio_config_attr(field, format_string) \ + static ssize_t \ +@@ -263,7 +267,14 @@ static void sdio_release_func(struct device *dev) + { + struct sdio_func *func = dev_to_sdio_func(dev); + +- sdio_free_func_cis(func); ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ /* ++ * If this device is embedded then we never allocated ++ * cis tables for this func ++ */ ++ if (!func->card->host->embedded_sdio_data.funcs) ++#endif ++ sdio_free_func_cis(func); + + if (func->info) + kfree(func->info); +diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c +old mode 100644 +new mode 100755 +index 8f6f5ac1..01fa6e86 +--- a/drivers/mmc/core/sdio_io.c ++++ b/drivers/mmc/core/sdio_io.c +@@ -387,6 +387,39 @@ u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret) + } + EXPORT_SYMBOL_GPL(sdio_readb); + ++/** ++ * sdio_readb_ext - read a single byte from a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * @in: value to add to argument ++ * ++ * Reads a single byte from the address space of a given SDIO ++ * function. If there is a problem reading the address, 0xff ++ * is returned and @err_ret will contain the error code. ++ */ ++unsigned char sdio_readb_ext(struct sdio_func *func, unsigned int addr, ++ int *err_ret, unsigned in) ++{ ++ int ret; ++ unsigned char val; ++ ++ BUG_ON(!func); ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = mmc_io_rw_direct(func->card, 0, func->num, addr, (u8)in, &val); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFF; ++ } ++ ++ return val; ++} ++EXPORT_SYMBOL_GPL(sdio_readb_ext); ++ + /** + * sdio_writeb - write a single byte to a SDIO function + * @func: SDIO function to access +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 2bc06e73..99146344 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -619,3 +619,13 @@ config MMC_USHC + + Note: These controllers only support SDIO cards and do not + support MMC or SD memory cards. ++ ++config MMC_ANYKA ++ bool "ANYKA MMC/SD/SDIO Card Interface support" ++ depends on ARCH_AK39 ++ help ++ This selects a driver for the MCI interface found in ANYKA's CPUs. ++ If you have a board based on one of those and a MMC/SD ++ slot, say Y here. If unsure, say N. ++ ++ +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index 3e7e26d0..7160d24c 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -53,6 +53,7 @@ obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o + obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o + obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o + obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o ++obj-$(CONFIG_MMC_ANYKA) +=akmci.o + + ifeq ($(CONFIG_CB710_DEBUG),y) + CFLAGS-cb710-mmc += -DDEBUG +diff --git a/drivers/mmc/host/akmci.c b/drivers/mmc/host/akmci.c +new file mode 100644 +index 00000000..f5b895e8 +--- /dev/null ++++ b/drivers/mmc/host/akmci.c +@@ -0,0 +1,1408 @@ ++/* ++ * linux/drivers/mmc/host/plat-anyka/akmci.c - Anyka MMC/SD/SDIO driver ++ * ++ * Copyright (C) 2010 Anyka, Ltd, All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "akmci" ++//#define DRIVER_NAME "ak-mci" ++ ++#undef PDEBUG ++ ++#define ___hdbg___() //printk("akmci:----func:%s---line:%d----\n", __func__, __LINE__); ++#define DDREGS(host) //dbg_dumpregs(host, __func__, __LINE__) ++#define DDDATA(h, d, s) //dbg_dumpdata(h, d, s) ++#define HDBG(fmt, args...) //printk(fmt, ##args) ++ ++//#define MCI_DBG ++ ++#ifdef MCI_DBG ++#ifdef __KERNEL__ ++#define PDEBUG(fmt, args...) printk(KERN_INFO "akmci:" fmt, ##args) ++#else ++#define PDEBUG(fmt, args...) fprintf(stderr, "%s %d:" fmt,__FILE__, __LINE__, ## args) ++#endif ++#else ++#define PDEBUG(fmt, args...) ++#endif ++ ++ ++static inline void dbg_dumpregs(struct akmci_host *host, ++ const char *prefix, int eflags) ++{ ++ u32 clkcon, cmdarg, cmd, cmdrsp, rsp1, rsp2, rsp3, rsp4; ++ u32 dtimer, datlen, datcon, datcnt, stat, imask, dmamode, cpumode; ++ ++ clkcon = readl(host->base + MCI_CLK_REG); ++ cmdarg = readl(host->base + MCI_ARGUMENT_REG); ++ cmd = readl(host->base + MCI_COMMAND_REG); ++ cmdrsp = readl(host->base + MCI_RESPCMD_REG); ++ ++ rsp1 = readl(host->base + MCI_RESPONSE0_REG); ++ rsp2 = readl(host->base + MCI_RESPONSE1_REG); ++ rsp3 = readl(host->base + MCI_RESPONSE2_REG); ++ rsp4 = readl(host->base + MCI_RESPONSE3_REG); ++ ++ dtimer = readl(host->base + MCI_DATATIMER_REG); ++ datlen = readl(host->base + MCI_DATALENGTH_REG); ++ datcon = readl(host->base + MCI_DATACTRL_REG); ++ datcnt = readl(host->base + MCI_DATACNT_REG); ++ ++ stat = 0; //readl(host->base + MCI_STATUS_REG); ++ imask = readl(host->base + MCI_MASK_REG); ++ dmamode = readl(host->base + MCI_DMACTRL_REG); ++ cpumode = readl(host->base + MCI_FIFO_REG); ++ ++ PDEBUG("current prefix: %s (%d)\n", prefix, eflags); ++ ++ PDEBUG("clkcon:[%08x], cmdarg:[%08x], cmd:[%08x], cmdrsp:[%08x].\n", ++ clkcon, cmdarg, cmd, cmdrsp); ++ PDEBUG("rsp1:[%08x], rsp2:[%08x], rsp3:[%08x], rsp4:[%08x]\n", ++ rsp1, rsp2, rsp3, rsp4); ++ PDEBUG("dtimer:[%08x], datlen:[%08x], datcon:[%08x], datcnt:[%08x]\n", ++ dtimer, datlen, datcon, datcnt); ++ PDEBUG("stat:[%08x], imask:[%08x], dmamode:[%08x], cpumode:[%08x]\n", ++ stat, imask, dmamode, cpumode); ++} ++ ++static inline void dbg_dumpdata(struct akmci_host *host, ++ void *data, int size) ++{ ++ int ii; ++ int dsize = (size +3)/4; ++ u32 *dptr = data; ++ ++ printk("xfer data (size:%d):", size); ++ ++ for(ii = 0; ii < dsize; ii++) { ++ if((ii%10) == 0) ++ printk("\n"); ++ ++ printk("%08x ", *(dptr + ii)); ++ } ++ printk("\n"); ++} ++ ++/** ++ * the data transfer mode description. ++*/ ++static char* xfer_mode_desc[] = { ++ "unknown", ++ "l2dma", ++ "l2pio", ++ "inner pio", ++ }; ++ ++/** ++ * the sd/mmc/sdio card detect mode description. ++*/ ++static char* detect_mode_desc[] = { ++ "plugin alway", ++ "GPIO detect", ++ "AD detect", ++ }; ++ ++ ++static void akmci_drv_lock(struct akmci_host *host) ++{ ++ if(host->mci_mode == MCI_MODE_MMC_SD) { ++ ak_drv_module_protect(DRV_MODULE_SDMMC); ++ } ++} ++static void akmci_drv_unlock(struct akmci_host *host) ++{ ++ if(host->mci_mode == MCI_MODE_MMC_SD) { ++ ak_drv_module_unprotect(DRV_MODULE_SDMMC); ++ } ++} ++ ++static void akmci_init_sharepin(struct akmci_host *host) ++{ ++ if(host->mci_mode == MCI_MODE_MMC_SD) { ++ if(ak_drv_module_lock(DRV_MODULE_SDMMC) < 0) ++ ak_group_config(ePIN_AS_MCI); ++ ak_drv_module_unlock(DRV_MODULE_SDMMC); ++ } else { ++ if(ak_drv_module_lock(DRV_MODULE_SDIO) < 0) ++ ak_group_config(ePIN_AS_SDIO); ++ ak_drv_module_unlock(DRV_MODULE_SDIO); ++ } ++} ++ ++ ++#define MCI_L2_ADDR(host) \ ++ ((host->mci_mode == MCI_MODE_MMC_SD) ? ADDR_MMC_SD : ADDR_SDIO) ++ ++/** ++* akmci_xfer_mode - judgement the mci transfer mode. ++* ret: AKMCI_XFER_L2DMA: use for l2 dma mode ++* AKMCI_XFER_L2PIO: use for l2 fifo mode. ++* AKMCI_XFER_INNERPIO: use for inner fifo mode. ++*/ ++static inline int akmci_xfer_mode(struct akmci_host *host) ++{ ++ return host->xfer_mode; ++} ++ ++static inline int enable_imask(struct akmci_host *host, u32 imask) ++{ ++ u32 newmask; ++ ++ newmask = readl(host->base + MCI_MASK_REG); ++ newmask |= imask; ++ writel(newmask, host->base + MCI_MASK_REG); ++ ++ return newmask; ++} ++ ++static inline int disable_imask(struct akmci_host *host, u32 imask) ++{ ++ u32 newmask; ++ ++ newmask = readl(host->base + MCI_MASK_REG); ++ newmask &= ~imask; ++ writel(newmask, host->base + MCI_MASK_REG); ++ ++ return newmask; ++} ++ ++static inline void clear_imask(struct akmci_host *host) ++{ ++ u32 mask = readl(host->base + MCI_MASK_REG); ++ ++ /* preserve the SDIO IRQ mask state */ ++ mask &= MCI_SDIOINTMASK; ++ writel(mask, host->base + MCI_MASK_REG); ++} ++ ++static void akmci_reset(struct akmci_host *host) ++{ ++ if(host->mci_mode == MCI_MODE_MMC_SD) { ++ ak_soft_reset(AK_SRESET_MMCSD); ++ } else { ++ ak_soft_reset(AK_SRESET_SDIO); ++ } ++} ++ ++/** ++ * @brief transmitting data. ++ * ++ * @author Hanyang ++ * @date 2011-05-10 ++ * @param [in] *host information of data transmitted, including data buf pointer, data len . ++ * @return void. ++*/ ++static void akmci_l2xfer(struct akmci_host *host) ++{ ++ int sg_remain; ++ u32 xferlen; ++ u8 dir; ++ u32 *tempbuf = NULL; ++ dma_addr_t phyaddr = 0; ++ ++ if (host->data->flags & MMC_DATA_WRITE) { ++ dir = MEM2BUF; ++ } else { ++ dir = BUF2MEM; ++ } ++ ++ sg_remain = host->sg_ptr->length - host->sg_off; ++ if (sg_remain <= 0) ++ { ++ host->sg_ptr = sg_next(host->sg_ptr); ++ if (host->sg_ptr == NULL) ++ return; ++ ++ host->sg_off = 0; ++ sg_remain = host->sg_ptr->length - host->sg_off; ++ } ++ ++#ifdef CONFIG_MMC_BLOCK_BOUNCE ++ xferlen = sg_remain; ++#else ++ xferlen = (sg_remain > host->data->blksz) ? host->data->blksz : sg_remain; ++#endif ++ ++ if ((akmci_xfer_mode(host) == AKMCI_XFER_L2DMA) && ++ xferlen >= L2_DMA_ALIGN) ++ { ++ PDEBUG("akmci transfer data: DMA mode.\n"); ++ phyaddr = sg_dma_address(host->sg_ptr) + host->sg_off; ++ l2_combuf_dma(phyaddr, host->l2buf_id, xferlen, dir, AK_FALSE); ++ } else { ++ PDEBUG("akmci transfer data: CPU mode.\n"); ++ tempbuf = sg_virt(host->sg_ptr) + host->sg_off; ++ l2_combuf_cpu((unsigned long)tempbuf, host->l2buf_id, xferlen, dir); ++ } ++ ++ host->sg_off += xferlen; ++ host->data_xfered += xferlen; ++ host->size -= xferlen; ++ ++ /* debug info if data transfer error */ ++ if(host->data_err_flag > 0) { ++ printk("mci_xfer transfered: xferptr = 0x%p, xfer_offset=%d, xfer_bytes=%d\n", ++ sg_virt(host->sg_ptr)+host->sg_off, host->sg_off, host->data_xfered); ++ } ++} ++ ++ ++void akmci_init_sg(struct akmci_host *host, struct mmc_data *data) ++{ ++ /* ++ * Ideally, we want the higher levels to pass us a scatter list. ++ */ ++ host->sg_len = data->sg_len; ++ host->sg_ptr = data->sg; ++ host->sg_off = 0; ++} ++ ++int akmci_next_sg(struct akmci_host *host) ++{ ++ host->sg_ptr++; ++ host->sg_off = 0; ++ return --host->sg_len; ++} ++ ++/** ++ * @brief stop data, close interrupt. ++ * ++ * @author Hanyang ++ * @date 2011-05-10 ++ * @param [in] *host get the base address of resgister. ++ * @return void. ++ */ ++static void akmci_stop_data(struct akmci_host *host) ++{ ++ writel(0, host->base + MCI_DMACTRL_REG); ++ writel(0, host->base + MCI_DATACTRL_REG); ++ ++ /* disable mci data irq */ ++ disable_imask(host, MCI_DATAIRQMASKS|MCI_FIFOFULLMASK|MCI_FIFOEMPTYMASK); ++ ++ if(akmci_xfer_mode(host) ==AKMCI_XFER_L2DMA) { ++ if (host->data->flags & MMC_DATA_WRITE) { ++ dma_sync_sg_for_cpu(mmc_dev(host->mmc), host->data->sg, host->data->sg_len, DMA_TO_DEVICE); ++ dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->data->sg_len, DMA_TO_DEVICE); ++ } else { ++ dma_sync_sg_for_cpu(mmc_dev(host->mmc), host->data->sg, host->data->sg_len, DMA_FROM_DEVICE); ++ dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->data->sg_len, DMA_FROM_DEVICE); ++ } ++ } ++ ++ host->sg_ptr = NULL; ++ host->sg_len = 0; ++ host->sg_off = 0; ++ ++ host->data = NULL; ++} ++ ++/** ++ * @brief finish a request,release resource. ++ * ++ * @author Hanyang ++ * @date 2011-05-10 ++ * @param [in] *host information of sd controller. ++ * @param [in] *mrq information of request. ++ * @return void. ++ */ ++static void akmci_request_end(struct akmci_host *host, struct mmc_request *mrq) ++{ ++ int not_retry = 0; ++ ++ writel(0, host->base + MCI_COMMAND_REG); ++ ++ BUG_ON(host->data); ++ host->mrq = NULL; ++ host->cmd = NULL; ++ ++ if(host->data_err_flag > 0) { ++ akmci_reset(host); ++ ++ writel(MCI_ENABLE|MCI_FAIL_TRIGGER, host->base + MCI_CLK_REG); ++ writel(readl(host->base + MCI_CLK_REG)|host->clkreg, host->base + MCI_CLK_REG); ++ mdelay(10); ++ } ++ ++ if(host->l2buf_id != BUF_NULL) { ++ l2_free(MCI_L2_ADDR(host)); ++ host->l2buf_id = BUF_NULL; ++ } ++ ++ if (mrq->data) ++ mrq->data->bytes_xfered = host->data_xfered; ++ ++ /* ++ * Need to drop the host lock here; mmc_request_done may call ++ * back into the driver... ++ */ ++ spin_unlock(&host->lock); ++ ++ not_retry = (!mrq->cmd->error) || ((mrq->cmd->error && (mrq->cmd->retries == 0))); ++ ++ mmc_request_done(host->mmc, mrq); ++ PDEBUG("finalize the mci request.\n"); ++ ++ /*if request fail,then mmc_request_done send request again, ++ * ak_mci_send_request not down nand_lock in interrupt,so not to up nand_lock. ++ */ ++ if (not_retry) { ++ akmci_drv_unlock(host); ++ } ++ ++#ifdef CONFIG_CPU_FREQ ++ /*if request fail,then mmc_request_done send request again, ak_mci_send_request ++ * not down freq_lock in interrupt,so not to unlock freq_lock. ++ */ ++ if (not_retry) { ++ up(&host->freq_lock); ++ } ++#endif ++ ++ spin_lock(&host->lock); ++} ++ ++#define BOTH_DIR (MMC_DATA_WRITE | MMC_DATA_READ) ++ ++/** ++ * @brief config sd controller, start transmitting data. ++ * ++ * @author Hanyang ++ * @date 2011-05-10 ++ * @param [in] *host information of sd controller. ++ * @param [in] *data information of data transmitted. ++ * @return void. ++ */ ++static void akmci_start_data(struct akmci_host *host, struct mmc_data *data) ++{ ++ unsigned int datactrl, dmacon; ++ ++ PDEBUG("%s: blksz %04x blks %04x flags %08x\n", ++ __func__, data->blksz, data->blocks, data->flags); ++ BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); ++ ++ host->data = data; ++ host->size = data->blksz * data->blocks; ++ host->data_xfered = 0; ++ ++ akmci_init_sg(host, data); ++ ++ if(akmci_xfer_mode(host) == AKMCI_XFER_L2DMA) { ++ /* set dma addr */ ++ if (data->flags & MMC_DATA_WRITE) ++ dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, DMA_TO_DEVICE); ++ else ++ dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, DMA_FROM_DEVICE); ++ } ++ ++ writel(TRANS_DATA_TIMEOUT, host->base + MCI_DATATIMER_REG); ++ writel(host->size, host->base + MCI_DATALENGTH_REG); ++ ++ if(akmci_xfer_mode(host) != AKMCI_XFER_INNERPIO) { ++ /*dma mode register*/ ++ dmacon = MCI_DMA_BUFEN | MCI_DMA_SIZE(MCI_L2FIFO_SIZE/4); ++ ++ if(akmci_xfer_mode(host) == AKMCI_XFER_L2DMA) { ++ dmacon |= MCI_DMA_EN; ++ } ++ writel(dmacon, host->base + MCI_DMACTRL_REG); ++ } ++ ++ /* enable mci data irq */ ++ enable_imask(host, MCI_DATAIRQMASKS); ++ ++ datactrl = MCI_DPSM_ENABLE; ++ ++ switch (host->bus_width) { ++ case MMC_BUS_WIDTH_8: ++ datactrl |= MCI_DPSM_BUSMODE(2); ++ break; ++ case MMC_BUS_WIDTH_4: ++ datactrl |= MCI_DPSM_BUSMODE(1); ++ break; ++ case MMC_BUS_WIDTH_1: ++ default: ++ datactrl |= MCI_DPSM_BUSMODE(0); ++ break; ++ } ++ ++ if (data->flags & MMC_DATA_STREAM) { ++ PDEBUG("STREAM Data\n"); ++ datactrl |= MCI_DPSM_STREAM; ++ } else { ++ PDEBUG("BLOCK Data: %u x %u\n", data->blksz, data->blocks); ++ datactrl |= MCI_DPSM_BLOCKSIZE(data->blksz); ++ datactrl &= ~MCI_DPSM_STREAM; ++ } ++ ++ if (data->flags & MMC_DATA_READ) ++ datactrl |= MCI_DPSM_DIRECTION; ++ else if (data->flags & MMC_DATA_WRITE) ++ datactrl &= ~MCI_DPSM_DIRECTION; ++ ++ /* configurate data controller register */ ++ writel(datactrl, host->base + MCI_DATACTRL_REG); ++ ++ PDEBUG("ENABLE DATA IRQ, datactrl: 0x%08x, timeout: 0x%08x, len: %u\n", ++ datactrl, readl(host->base + MCI_DATATIMER_REG), host->size); ++ ++ if((akmci_xfer_mode(host) != AKMCI_XFER_INNERPIO) && ++ data->flags & MMC_DATA_WRITE) ++ akmci_l2xfer(host); ++} ++ ++/** ++ * @brief config sd controller, start sending command. ++ * ++ * @author Hanyang ++ * @date 2011-05-10 ++ * @param [in] *host information of sd controller. ++ * @param [in] *cmd information of cmd sended. ++ * @return void. ++ */ ++static void akmci_start_command(struct akmci_host *host, struct mmc_command *cmd) ++{ ++ unsigned int ccon; ++ ++ PDEBUG("mci send cmd: op %i arg 0x%08x flags 0x%08x.%s data.\n", ++ cmd->opcode, cmd->arg, cmd->flags, cmd->data ? "contain":"no"); ++ ++ writel(cmd->arg, host->base + MCI_ARGUMENT_REG); ++ /* enable mci cmd irq */ ++ enable_imask(host, MCI_CMDIRQMASKS); ++ ++ ++ ccon = MCI_CPSM_CMD(cmd->opcode) | MCI_CPSM_ENABLE; ++ if (cmd->flags & MMC_RSP_PRESENT) { ++ ccon |= MCI_CPSM_RESPONSE; ++ if (cmd->flags & MMC_RSP_136) ++ ccon |= MCI_CPSM_LONGRSP; ++ } ++ ++ if (cmd->data) ++ ccon |= MCI_CPSM_WITHDATA; ++ ++ host->cmd = cmd; ++ ++ /* configurate cmd controller register */ ++ writel(ccon, host->base + MCI_COMMAND_REG); ++} ++ ++ ++static void print_mci_data_err(struct mmc_data *data, ++ unsigned int status, const char *err) ++{ ++ if (data->flags & MMC_DATA_READ) { ++ printk("akmci: data(read) status=%d %s\n", status, err); ++ } else if (data->flags & MMC_DATA_WRITE) { ++ printk("akmci: data(write) status=%d %s\n", status, err); ++ } ++} ++ ++/** ++ * @brief data handle in sdio interrupt. ++ * ++ * @author Hanyang ++ * @date 2011-05-10 ++ * @param [in] *host information of sd controller. ++ * @param [in] *data information of data transmitting. ++ * @return void. ++ */ ++static void akmci_data_irq(struct akmci_host *host, struct mmc_data *data, ++ unsigned int status) ++{ ++ if(status & MCI_DATABLOCKEND) { ++ if((akmci_xfer_mode(host) == AKMCI_XFER_L2DMA) || ++ (akmci_xfer_mode(host) == AKMCI_XFER_L2PIO)) { ++ //wait L2 dma finish, if need frac dma,start frac dma ++ if((akmci_xfer_mode(host) == AKMCI_XFER_L2DMA) && ++ (AK_FALSE == l2_combuf_wait_dma_finish(host->l2buf_id))) ++ return; ++ ++ if (data->flags & MMC_DATA_WRITE) ++ l2_clr_status(host->l2buf_id); ++ ++ if (host->size > 0) { ++ akmci_l2xfer(host); ++ } ++ } ++ } ++ ++ if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT)) { ++ if (status & MCI_DATACRCFAIL) { ++ data->error = -EILSEQ; ++ print_mci_data_err(data, status, "illeage byte sequence"); ++ } else if (status & MCI_DATATIMEOUT) { ++ data->error = -ETIMEDOUT; ++ print_mci_data_err(data, status, "transfer timeout"); ++ } ++ ++ status |= MCI_DATAEND; ++ host->data_err_flag = 1; ++ ++ printk("akmci need transfer: data->sg = 0x%p, data->sg_len=%d(sg)," ++ " data->sg->length=%d, remain data = %d\n", ++ data->sg, data->sg_len, data->sg->length, ++ __raw_readl(host->base + 0x30)); ++ ++ /* ++ * We hit an error condition. Ensure that any data ++ * partially written to a page is properly coherent. ++ */ ++ if (host->sg_len && data->flags & MMC_DATA_READ) ++ flush_dcache_page(sg_page(host->sg_ptr)); ++ } ++ ++ if (status & MCI_DATAEND) { ++ if ((data->retries > 0) && data->error) { ++ printk("data->error = %d, data->retries = %d\n", ++ data->error, data->retries); ++ ++ /* support retry if error */ ++ akmci_stop_data(host); ++ akmci_request_end(host, data->mrq); ++ } else { ++ ++ //wait L2 dma finish, if need frac dma,start frac dma ++ if((akmci_xfer_mode(host) == AKMCI_XFER_L2DMA) && ++ (AK_FALSE == l2_combuf_wait_dma_finish(host->l2buf_id))) ++ return; ++ ++ host->data_err_flag = 0; ++ akmci_stop_data(host); ++ ++ if (!data->stop) ++ akmci_request_end(host, data->mrq); ++ else ++ akmci_start_command(host, data->stop); ++ } ++ } ++} ++ ++/** ++ * @brief cmd handle in sd interrupt. ++ * ++ * @author Hanyang ++ * @date 2011-05-10 ++ * @param [in] *host information of sd controller. ++ * @param [in] *cmd information of cmd sended. ++ *@param [in] *status the status of sd controller. ++ * @return void. ++ */ ++static void akmci_cmd_irq(struct akmci_host *host, struct mmc_command *cmd, ++ unsigned int status) ++{ ++ host->cmd = NULL; ++ ++ cmd->resp[0] = readl(host->base + MCI_RESPONSE0_REG); ++ cmd->resp[1] = readl(host->base + MCI_RESPONSE1_REG); ++ cmd->resp[2] = readl(host->base + MCI_RESPONSE2_REG); ++ cmd->resp[3] = readl(host->base + MCI_RESPONSE3_REG); ++ ++ PDEBUG("resp[0]=0x%x, [1]=0x%x, resp[2]=0x%x, [3]=0x%x\n", ++ cmd->resp[0],cmd->resp[1],cmd->resp[2],cmd->resp[3]); ++ ++ if (status & MCI_RESPTIMEOUT) { ++ cmd->error = -ETIMEDOUT; ++ PDEBUG("CMD: send timeout\n"); ++ } else if (status & MCI_RESPCRCFAIL && cmd->flags & MMC_RSP_CRC) { ++ cmd->error = -EILSEQ; ++ PDEBUG("CMD: illegal byte sequence\n"); ++ } ++ ++ /* disable mci cmd irq */ ++ disable_imask(host, MCI_CMDIRQMASKS); ++ ++ if (!cmd->data || cmd->error) { ++ if (host->data) ++ akmci_stop_data(host); ++ akmci_request_end(host, cmd->mrq); ++ } else if (!(cmd->data->flags & MMC_DATA_READ)) { ++ /* transfer data from host to sd card */ ++ akmci_start_data(host, cmd->data); ++ } ++} ++ ++/* ++ * Handle completion of command and data transfers. ++ */ ++static irqreturn_t akmci_irq(int irq, void *dev_id) ++{ ++ struct akmci_host *host = dev_id; ++ u32 stat_mask; ++ u32 status; ++ int ret = 0; ++ ++ spin_lock(&host->lock); ++ ++ status = readl(host->base + MCI_STATUS_REG); ++ if (status & MCI_SDIOINT) { ++ /*must disable sdio irq ,than read status to clear the sdio status, ++ * else sdio irq will come again. ++ */ ++ mmc_signal_sdio_irq(host->mmc); ++ status |= readl(host->base + MCI_STATUS_REG); ++ } ++ ++ stat_mask = MCI_RESPCRCFAIL|MCI_RESPTIMEOUT|MCI_CMDSENT|MCI_RESPEND; ++ if ((status & stat_mask) && host->cmd) ++ akmci_cmd_irq(host, host->cmd, status); ++ ++ stat_mask = MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_DATAEND| ++ MCI_DATABLOCKEND|MCI_STARTBIT_ERR; ++ ++ if ((status & stat_mask) && host->data) ++ akmci_data_irq(host, host->data, status); ++ ++ ret = 1; ++ spin_unlock(&host->lock); ++ ++ return IRQ_RETVAL(ret); ++ ++} ++ ++ ++static void akmci_send_request(struct mmc_host *mmc) ++{ ++ struct akmci_host *host = mmc_priv(mmc); ++ struct mmc_request *mrq = host->mrq; ++ unsigned long flags; ++ ++#ifdef CONFIG_CPU_FREQ ++ /* need not to acquire the freq_lock in interrupt. */ ++ if (!in_interrupt()) ++ down(&host->freq_lock); ++#endif ++ ++ /* need not to acquire the nand_lock in interrupt. */ ++ if (!in_interrupt()) { ++ akmci_drv_lock(host); ++ } ++ ++ if(mrq->data || mrq->cmd->data) { ++ host->l2buf_id = l2_alloc(MCI_L2_ADDR(host)); ++ if (BUF_NULL == host->l2buf_id) { ++ printk("L2 buffer malloc fail!\n"); ++ BUG(); ++ } ++ } ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (mrq->data && (mrq->data->flags & MMC_DATA_READ)) ++ akmci_start_data(host, mrq->data); ++ ++ akmci_start_command(host, mrq->cmd); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++ ++/** ++ * @brief detect sdio card's level type . ++ * ++ * @author Hanyang ++ * @date 2011-05-10 ++ * @param [in] data getting the information of sd host. ++ * @return void. ++ */ ++static void akmci_detect_change(unsigned long data) ++{ ++ struct akmci_host *host = (struct akmci_host *)data; ++ ++ PDEBUG("card detect change.\n"); ++ ++ mmc_detect_change(host->mmc, 0); ++ ++ if (host->irq_cd_type == IRQ_TYPE_LEVEL_LOW) { ++ host->irq_cd_type = IRQ_TYPE_LEVEL_HIGH; ++ } else { ++ host->irq_cd_type = IRQ_TYPE_LEVEL_LOW; ++ } ++ irq_set_irq_type(host->irq_cd, host->irq_cd_type); ++ enable_irq(host->irq_cd); ++} ++ ++static irqreturn_t akmci_card_detect_irq(int irq, void *dev) ++{ ++ struct akmci_host *host = dev; ++ ++ disable_irq_nosync(irq); ++ mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(400)); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++/** ++ * @brief detect the sdio card whether or not is in. ++ * ++ * @author Hanyang ++ * @date 2011-05-10 ++ * @param [in] *mmc information of host ,getting the sdio detect gpio. ++ * @return int. ++ * @retal 1 sdio card is in ;0 sdio card is not in ++ */ ++static int set_mci_plugin(struct notifier_block *nb, ++ unsigned long val, void *data) ++{ ++ struct akmci_host *host = container_of(nb, struct akmci_host, detect_nb); ++ ++ if(host->mci_mode == MCI_MODE_MMC_SD) { ++ if (val == ADDETECT_MMC_PLUGIN) ++ host->plugin_flag = 1; ++ else if (val == ADDETECT_MMC_PLUGOUT) ++ host->plugin_flag = 0; ++ } else { ++ if (val == ADDETECT_SDIO_PLUGIN) ++ host->plugin_flag = 1; ++ else if (val == ADDETECT_SDIO_PLUGOUT) ++ host->plugin_flag = 0; ++ } ++ ++ mmc_detect_change(host->mmc, 50); ++ return 0; ++} ++ ++static int akmci_enable(struct mmc_host *mmc) ++{ ++ PDEBUG("akmci_enable:host is claimed.\n"); ++ //akmci_drv_lock(host); ++ return 0; ++} ++ ++static int akmci_disable(struct mmc_host *mmc) ++{ ++ PDEBUG("akmci_disable:host is released.\n"); ++ //akmci_drv_unlock(host); ++ return 0; ++} ++ ++static int akmci_card_present(struct mmc_host *mmc) ++{ ++ struct akmci_host *host = mmc_priv(mmc); ++ ++ if(host->detect_mode == AKMCI_DETECT_MODE_AD) ++ { ++ return host->plugin_flag; ++ } ++ else if(host->detect_mode == AKMCI_DETECT_MODE_GPIO) ++ { ++ if (host->gpio_cd == -ENOSYS) ++ return -ENOSYS; ++ ++ if(host->gpio_cd >= 0) ++ return (ak_gpio_getpin(host->gpio_cd) == 0); ++ else ++ return 1; ++ } ++ else ++ { ++ return 1; //plugin alway. ++ } ++} ++ ++static void akmci_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct akmci_host *host = mmc_priv(mmc); ++ ++ host->mrq = mrq; ++ host->data_err_flag = 0; ++ ++ PDEBUG("start the mci request.\n"); ++ ++ if(akmci_card_present(mmc) == 0) { ++ printk("%s: no medium present.\n", __func__); ++ host->mrq->cmd->error = -ENOMEDIUM; ++ mmc_request_done(mmc, mrq); ++ } else { ++ akmci_send_request(mmc); ++ } ++} ++ ++static void akmci_set_clk(struct akmci_host *host, struct mmc_ios *ios) ++{ ++ u32 clk, div; ++ u32 clk_div_h, clk_div_l; ++ ++ if (ios->clock == 0) { ++ clk = readl(host->base + MCI_CLK_REG); ++ clk &= ~MCI_CLK_ENABLE; ++ writel(clk, host->base + MCI_CLK_REG); ++ ++ host->bus_clock = 0; ++ } else { ++ clk = readl(host->base + MCI_CLK_REG); ++ clk |= MCI_CLK_ENABLE;//|MCI_CLK_PWRSAVE; ++ clk &= ~0xffff; /* clear clk div */ ++ ++ div = host->asic_clk/ios->clock; ++ ++ if (host->asic_clk % ios->clock) ++ div += 1; ++ ++ div -= 2; ++ clk_div_h = div/2; ++ clk_div_l = div - clk_div_h; ++ ++ clk |= MMC_CLK_DIVL(clk_div_l) | MMC_CLK_DIVH(clk_div_h); ++ writel(clk, host->base + MCI_CLK_REG); ++ ++ host->bus_clock = host->asic_clk / ((clk_div_h+1)*(clk_div_l + 1)); ++ ++ PDEBUG("mmc clock is %lu Mhz. asic_clock is %ld MHz(div:l=%d, h=%d).\n", ++ ios->clock/MHz, host->asic_clk/MHz, clk_div_l, clk_div_h); ++ } ++ ++ host->clkreg = clk; ++} ++ ++ ++static void akmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct akmci_host *host = mmc_priv(mmc); ++ ++ switch(ios->power_mode) { ++ case MMC_POWER_ON: ++ PDEBUG("mci power on.\n"); ++ break; ++ case MMC_POWER_UP: ++ PDEBUG("mci power up.\n"); ++ break; ++ case MMC_POWER_OFF: ++ PDEBUG("mci power off.\n"); ++ break; ++ default: ++ break; ++ } ++ ++ host->bus_mode = ios->bus_mode; ++ host->bus_width = ios->bus_width; ++ ++ if(ios->clock != host->bus_clock) { ++ akmci_set_clk(host, ios); ++ } ++} ++ ++/** ++ * @brief detect the sdio card writing protection. ++ * ++ * @author Hanyang ++ * @date 2011-05-10 ++ * @param [in] *mmc information of host ,getting the sdio detect gpio. ++ * @return int. ++ * @retal 1 sdio card writing protected ;0 sdio card writing is not protected ++ */ ++static int akmci_get_ro(struct mmc_host *mmc) ++{ ++ struct akmci_host *host = mmc_priv(mmc); ++ ++ if (host->gpio_wp == -ENOSYS) ++ return -ENOSYS; ++ ++ return (ak_gpio_getpin(host->gpio_wp) == 0); ++} ++ ++/** ++ * @brief enable or disable sdio interrupt, mmc host not use.. ++ * ++ * @author Hanyang ++ * @date 2011-05-10 ++ * @param [in] *mmc information of sd controller. ++ * @param [in] enable 1: enable; 0: disable. ++ * @return void. ++ */ ++static void akmci_enable_sdio_irq(struct mmc_host *mmc, int enable) ++{ ++ unsigned reg1,reg2; ++ unsigned long flags; ++ struct akmci_host *host = mmc_priv(mmc); ++ ++ BUG_ON(host->mci_mode == MCI_MODE_MMC_SD); ++ ++ PDEBUG("%s the sdio interrupt.\n", enable ? "enable" : "disable"); ++ spin_lock_irqsave(&host->lock, flags); ++ ++ reg1 = readl(host->base + MCI_MASK_REG); ++ reg2 = readl(host->base + SDIO_INTRCTR_REG); ++ ++ if (enable) { ++ reg1 |= SDIO_INTR_ENABLE; ++ reg2 |= SDIO_INTR_CTR_ENABLE; ++ } else { ++ reg1 &= ~SDIO_INTR_ENABLE; ++ reg2 &= ~SDIO_INTR_CTR_ENABLE; ++ } ++ ++ writel(reg2, host->base + SDIO_INTRCTR_REG); ++ writel(reg1, host->base + MCI_MASK_REG); ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++/** ++ * register the function of sd/sdio driver. ++ * ++ */ ++static struct mmc_host_ops akmci_ops = { ++ .enable = akmci_enable, ++ .disable = akmci_disable, ++ .request = akmci_request, ++ .set_ios = akmci_set_ios, ++ .get_ro = akmci_get_ro, ++ .get_cd = akmci_card_present, ++ .enable_sdio_irq = akmci_enable_sdio_irq, ++}; ++ ++ ++static int akmci_init_mmc_host(struct akmci_host *host) ++{ ++ struct mmc_host *mmc = host->mmc; ++ struct ak_mci_platform_data *plat = host->plat; ++ ++ mmc->ops = &akmci_ops; ++ ++ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ++ mmc->caps = MMC_CAP_4_BIT_DATA; ++ ++ if(host->mci_mode == MCI_MODE_SDIO) ++ mmc->caps |= MMC_CAP_SDIO_IRQ; ++ ++ if(plat->cap_highspeed) ++ mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; ++ ++// mmc->caps |= MMC_CAP_NEEDS_POLL; ++ mmc->f_min = host->asic_clk / (255+1 + 255+1); ++ mmc->f_max = host->asic_clk / (0+1 + 0+1); ++ mmc->f_max = (mmc->f_max < plat->max_speed_hz) ? ++ mmc->f_max : plat->max_speed_hz; ++ ++#ifdef CONFIG_MMC_BLOCK_BOUNCE ++ /* use block bounce buffer. */ ++ mmc->max_segs = 1; ++#else ++ /* We can do SGIO */ ++ mmc->max_segs = MAX_MCI_REQ_SIZE/MAX_MCI_BLOCK_SIZE; ++#endif ++ ++ /* ++ * Since we only have a 16-bit data length register, we must ++ * ensure that we don't exceed 2^16-1 bytes in a single request. ++ */ ++ mmc->max_req_size = MAX_MCI_REQ_SIZE; ++ ++ /* ++ * Set the maximum segment size. Since we aren't doing DMA ++ * (yet) we are only limited by the data length register. ++ */ ++ mmc->max_seg_size = mmc->max_req_size; ++ ++ mmc->max_blk_size = MAX_MCI_BLOCK_SIZE; ++ ++ /*No limit on the number of blocks transferred.*/ ++ mmc->max_blk_count = mmc->max_req_size / MAX_MCI_BLOCK_SIZE; ++ return 0; ++} ++ ++static void akmci_init_host_cfg(struct akmci_host *host) ++{ ++ akmci_init_sharepin(host); ++ /*enable the mci clock*/ ++ writel(MCI_ENABLE|MCI_FAIL_TRIGGER, host->base + MCI_CLK_REG); ++ ++ clear_imask(host); ++} ++ ++ ++#if defined(CONFIG_CPU_FREQ) ++ ++static int akmci_cpufreq_transition(struct notifier_block *nb, ++ unsigned long val, void *data) ++{ ++ struct akmci_host *host; ++ struct mmc_host *mmc; ++ unsigned long newclk; ++ unsigned long flags; ++ struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; ++ host = container_of(nb, struct ak_mci_host, freq_transition); ++ ++ PDEBUG("%s(): in_interrupt()=%ld\n", __func__, in_interrupt()); ++ PDEBUG("ak_get_asic_clk = %ld\n",ak_get_asic_clk()); ++ PDEBUG("freqs->new_cpufreq.asic_clk = %d\n", ++ freqs->new_cpufreq.asic_clk); ++ ++ mmc = host->mmc; ++ newclk = freqs->new_cpufreq.asic_clk; ++ if ((val == CPUFREQ_PRECHANGE && newclk > host->asic_clock) ++ || (val == CPUFREQ_POSTCHANGE && newclk < host->asic_clock)) ++ { ++ ++ if (mmc->ios.power_mode != MMC_POWER_OFF && ++ mmc->ios.clock != 0) ++ { ++ PDEBUG("%s(): preempt_count()=%d\n", __func__, preempt_count()); ++ ++ down(&host->freq_lock); ++ ++ spin_lock_irqsave(&mmc->lock, flags); ++ ++ host->asic_clock = newclk; ++ PDEBUG("MCI_CLK_REG1 = %d\n",readl(host->base + MCI_CLK_REG)); ++ aksdio_set_clk(host, &mmc->ios); ++ PDEBUG("MCI_CLK_REG2 = %d\n",readl(host->base + MCI_CLK_REG)); ++ ++ spin_unlock_irqrestore(&mmc->lock, flags); ++ ++ up(&host->freq_lock); ++ } ++ } ++ ++ return NOTIFY_DONE; ++} ++ ++ ++static inline int akmci_cpufreq_register(struct akmci_host *host) ++{ ++ // use for requst and cpufreq ++ sema_init(&host->freq_lock, 1); ++ ++ host->freq_transition.notifier_call = akmci_cpufreq_transition; ++ ++ return cpufreq_register_notifier(&host->freq_transition, ++ CPUFREQ_TRANSITION_NOTIFIER); ++} ++ ++static inline void akmci_cpufreq_deregister(struct akmci_host *host) ++{ ++ cpufreq_unregister_notifier(&host->freq_transition, ++ CPUFREQ_TRANSITION_NOTIFIER); ++} ++ ++#else ++static inline int akmci_cpufreq_register(struct akmci_host *host) ++{ ++ return 0; ++} ++ ++static inline void akmci_cpufreq_deregister(struct akmci_host *host) ++{ ++} ++#endif ++ ++ ++/** ++ * @brief sdio driver probe and init. ++ * ++ * @author Hanyang ++ * @date 2011-05-10 ++ * @param [in] *pdev information of platform device ,getting the sd driver resource . ++ * @return int. ++ * @retval -EINVAL no platform data , fail; ++ * @retval -EBUSY requset mem fail; ++ * @retval -ENOMEM alloc mem fail; ++ */ ++static int __devinit akmci_probe(struct platform_device *pdev) ++{ ++ struct akmci_host *host; ++ struct mmc_host *mmc; ++ int ret; ++ struct ak_mci_platform_data *plat; ++ ++ plat = pdev->dev.platform_data; ++ if(!plat) { ++ printk("not found mci platform data."); ++ ret = -EINVAL; ++ goto probe_out; ++ } ++ ++ mmc = mmc_alloc_host(sizeof(struct akmci_host), &pdev->dev); ++ if (!mmc) { ++ ret = -ENOMEM; ++ goto probe_out; ++ } ++ ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ host->pdev = pdev; ++ ++ spin_lock_init(&host->lock); ++ ++ host->plat = plat; ++ host->mci_mode = plat->mci_mode; ++ host->data_err_flag = 0; ++ host->l2buf_id = BUF_NULL; ++ ++ host->gpio_wp = -ENOSYS; ++ host->gpio_cd = -ENOSYS; ++ host->detect_mode = plat->detect_mode; ++ host->xfer_mode = plat->xfer_mode; ++ ++ akmci_reset(host); ++ ++ host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if(!host->mem) { ++ ret = -ENOENT; ++ goto probe_free_host; ++ } ++ ++ host->mem = request_mem_region(host->mem->start, ++ resource_size(host->mem), pdev->name); ++ ++ if (!host->mem) { ++ dev_err(&pdev->dev, "failed to request io memory region.\n"); ++ ret = -ENOENT; ++ goto probe_free_host; ++ } ++ ++ host->base = ioremap(host->mem->start, resource_size(host->mem)); ++ if (!host->base) { ++ dev_err(&pdev->dev, "failed to ioremap() io memory region.\n"); ++ ret = -EINVAL; ++ goto probe_free_mem_region; ++ } ++ ++ host->irq_mci = platform_get_irq(pdev, 0); ++ if(host->irq_mci == 0) { ++ dev_err(&pdev->dev, "failed to get interrupt resouce.\n"); ++ ret = -EINVAL; ++ goto probe_iounmap; ++ } ++ ++ if (request_irq(host->irq_mci, akmci_irq, IRQF_DISABLED, pdev->name, host)) { ++ dev_err(&pdev->dev, "failed to request mci interrupt.\n"); ++ ret = -ENOENT; ++ goto probe_iounmap; ++ } ++ ++ /* We get spurious interrupts even when we have set the IMSK ++ * register to ignore everything, so use disable_irq() to make ++ * ensure we don't lock the system with un-serviceable requests. */ ++ //disable_irq(host->irq_mci); ++ ++ host->clk = clk_get(&pdev->dev, (host->mci_mode == MCI_MODE_MMC_SD)? "mci" : "sdio"); ++ if (IS_ERR(host->clk)) { ++ dev_err(&pdev->dev, "failed to find clock source.\n"); ++ ret = PTR_ERR(host->clk); ++ host->clk = NULL; ++ goto probe_free_irq; ++ } ++ ++ ret = clk_enable(host->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to enable clock source.\n"); ++ goto clk_free; ++ } ++ ++ host->asic_clk = clk_get_rate(host->clk); ++ ++ ret = akmci_init_mmc_host(host); ++ if(ret) { ++ dev_err(&pdev->dev, "failed to init mmc host.\n"); ++ goto clk_disable; ++ } ++ ++ akmci_init_host_cfg(host); ++ ++ if(host->detect_mode == AKMCI_DETECT_MODE_GPIO) { ++ if(plat->gpio_cd.pin >= 0) { ++ host->gpio_cd = plat->gpio_cd.pin; ++ plat->gpio_init(&plat->gpio_cd); ++ ++ setup_timer(&host->detect_timer, akmci_detect_change, ++ (unsigned long)host); ++ ++ host->irq_cd = ak_gpio_to_irq(host->gpio_cd); ++ ret = request_irq(host->irq_cd, akmci_card_detect_irq, ++ IRQF_DISABLED|IRQF_TRIGGER_LOW, pdev->name, host); ++ ++ dev_info(&pdev->dev, "pdev->name:%s request gpio irq ret = %d, irq=%d\n", ++ pdev->name, ret, host->irq_cd); ++ if (ret) ++ goto clk_disable; ++ ++ host->irq_cd_type = plat->irq_cd_type; ++ } ++ } ++ else if(host->detect_mode == AKMCI_DETECT_MODE_AD) { ++ memset(&host->detect_nb, 0, sizeof(host->detect_nb)); ++ host->detect_nb.notifier_call = set_mci_plugin; ++ addetect_register_client(&host->detect_nb); ++ } ++ ++ if (plat->gpio_wp.pin >= 0) { ++ host->gpio_wp = plat->gpio_wp.pin; ++ plat->gpio_init(&plat->gpio_wp); ++ } ++ ++ ret = akmci_cpufreq_register(host); ++ if (ret) { ++ goto detect_irq_free; ++ } ++ ++ platform_set_drvdata(pdev, mmc); ++ ++ ret = mmc_add_host(mmc); ++ if (ret) { ++ goto probe_cpufreq_free; ++ } ++ ++ dev_info(&pdev->dev, "Mci Interface driver.%s." ++ " using %s, %s IRQ. detect mode:%s.\n", ++ mmc_hostname(mmc), xfer_mode_desc[akmci_xfer_mode(host)], ++ mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw", ++ detect_mode_desc[host->detect_mode]); ++ ++ return 0; ++ ++probe_cpufreq_free: ++ ++detect_irq_free: ++ free_irq(host->irq_cd, host); ++ ++clk_disable: ++ clk_disable(host->clk); ++ ++clk_free: ++ clk_put(host->clk); ++ ++probe_free_irq: ++ free_irq(host->irq_mci, host); ++ ++probe_iounmap: ++ iounmap(host->base); ++ ++probe_free_mem_region: ++ release_mem_region(host->mem->start, resource_size(host->mem)); ++ ++probe_free_host: ++ mmc_free_host(host->mmc); ++ ++probe_out: ++ return ret; ++} ++ ++static int __devexit akmci_remove(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc; ++ struct akmci_host *host; ++ ++ mmc = platform_get_drvdata(pdev); ++ host = mmc_priv(mmc); ++ ++ mmc_remove_host(mmc); ++ ++ akmci_cpufreq_deregister(host); ++ ++ if(host->detect_mode == AKMCI_DETECT_MODE_AD) ++ addetect_unregister_client(&host->detect_nb); ++ ++ clk_disable(host->clk); ++ clk_put(host->clk); ++ ++ free_irq(host->irq_cd, host); ++ free_irq(host->irq_mci, host); ++ ++ iounmap(host->base); ++ release_mem_region(host->mem->start, resource_size(host->mem)); ++ ++ mmc_free_host(host->mmc); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int akmci_suspend(struct device *dev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); ++ ++ return mmc_suspend_host(mmc); ++} ++ ++static int akmci_resume(struct device *dev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); ++ ++ return mmc_resume_host(mmc); ++} ++ ++static struct dev_pm_ops akmci_pm = { ++ .suspend = akmci_suspend, ++ .resume = akmci_resume ++}; ++ ++#define akmci_pm_ops &akmci_pm ++#else ++#define akmci_pm_ops NULL ++#endif ++ ++struct platform_device_id ak_mci_ids[] ={ ++ {.name = "ak_mci", .driver_data = MCI_MODE_MMC_SD,}, ++ {.name = "ak_sdio", .driver_data = MCI_MODE_SDIO,}, ++}; ++ ++static struct platform_driver akmci_driver = { ++ .probe = akmci_probe, ++ .remove = __devexit_p(akmci_remove), ++ .id_table = ak_mci_ids, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .pm = akmci_pm_ops, ++ }, ++ ++}; ++ ++static int __init akmci_init(void) ++{ ++ printk("AK MCI Driver (c) 2010 ANYKA\n"); ++ return platform_driver_register(&akmci_driver); ++} ++ ++static void __exit akmci_exit(void) ++{ ++ return platform_driver_unregister(&akmci_driver); ++} ++ ++ ++module_init(akmci_init); ++module_exit(akmci_exit); ++ ++MODULE_DESCRIPTION("Anyka MCI Interface driver"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Anyka"); ++ +diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c +index 1fe0ca9c..4b970ade 100644 +--- a/drivers/mmc/host/sdhci.c ++++ b/drivers/mmc/host/sdhci.c +@@ -680,11 +680,8 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) + break; + } + +- if (count >= 0xF) { +- pr_warning("%s: Too large timeout requested for CMD%d!\n", +- mmc_hostname(host->mmc), cmd->opcode); ++ if (count >= 0xF) + count = 0xE; +- } + + return count; + } +diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile +index f9013542..0dd5e1a7 100644 +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -5,6 +5,7 @@ + # Core functionality. + obj-$(CONFIG_MTD) += mtd.o + mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o ++mtd-y += akfha_char.o + + obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o + obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o +diff --git a/drivers/mtd/akfha_char.c b/drivers/mtd/akfha_char.c +new file mode 100755 +index 00000000..0c426a59 +--- /dev/null ++++ b/drivers/mtd/akfha_char.c +@@ -0,0 +1,546 @@ ++/** ++ * @filename akfha_char.c ++ * @brief AK fha char device driver ++ * Copyright (C) 2010 Anyka (Guangzhou) Software Technology Co., LTD ++ * @author zhangshenglin ++ * @date 2012-12-7 ++ * @version 1.0 ++ * @ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_MTD_NAND_ANYKA ++#include ++#include ++#include ++#endif ++ ++#include ++#include ++#include ++ ++ ++#define FHA_CHAR_MAJOR 168 ++#define AK_FHA_UPDATE_BOOT_BEGIN 0xb1 ++#define AK_FHA_UPDATE_BOOT 0xb2 ++#define AK_FHA_UPDATE_BIN_BEGIN 0xb3 ++#define AK_FHA_UPDATE_BIN 0xb4 ++#define MAX_BUF_LEN 64*1024 ++extern int ak_fha_init_for_update(int n); ++extern T_U32 ak_fha_read_callback(T_U32 chip_num, T_U32 page_num, T_U8 *data, ++ T_U32 data_len, T_U8 *oob, T_U32 oob_len, T_U32 eDataType); ++ ++typedef struct ++{ ++ long filelen; ++ char filename[256];//the bin name in flash ++}T_BinInfo; ++ ++typedef struct ++{ ++ long buflen; ++ char buff[MAX_BUF_LEN]; ++ long ddrparcnt; ++ unsigned int ddrpar[64][2]; ++}T_BufInfo; ++static T_BufInfo* pBufInfo = NULL; ++ ++typedef struct ++{ ++ char Disk_Name; //盘符名 ++ char bOpenZone; // ++ char ProtectType; // ++ char ZoneType; // ++ unsigned int Size; ++ unsigned int EnlargeSize; // set this value if enlarge capacity,otherwise set 0 ++ unsigned int HideStartBlock; //hide disk start ++ unsigned int FSType; ++ unsigned int resv[1]; ++}__attribute__((packed)) T_PARTION_INFO; ++ ++#define MAX_PARTS_COUNT 26 ++static unsigned char* spipart_info = NULL; ++#define SPI_PAGESIZE 256 ++static int akfha_char_open(struct inode *inode, struct file *filp); ++static int akfha_char_close(struct inode *inode, struct file *filp); ++static long akfha_char_ioctl(/*struct inode *inode, */struct file *filp, unsigned int cmd, unsigned long arg); ++ ++static struct akfha_char_dev ++{ ++ struct cdev c_dev; ++}fha_c_dev; ++ ++static int fha_c_major = FHA_CHAR_MAJOR; ++ ++static const struct file_operations akfha_char_fops = ++{ ++ .owner = THIS_MODULE, ++ .open = akfha_char_open, ++ .release = akfha_char_close, ++ .unlocked_ioctl = akfha_char_ioctl, ++}; ++ ++static int akfha_char_open(struct inode *inode, struct file *filp) ++{ ++ pBufInfo = kmalloc(sizeof(T_BufInfo), GFP_KERNEL); ++ if(pBufInfo == NULL) ++ { ++ printk("malloc buf error\n"); ++ return -1; ++ } ++ filp->private_data = &fha_c_dev; ++ ak_fha_init_for_update(1); ++ return 0; ++} ++ ++static int akfha_char_close(struct inode *inode, struct file *filp) ++{ ++ if(pBufInfo) ++ { ++ kfree(pBufInfo); ++ pBufInfo = NULL; ++ } ++ if(spipart_info != NULL) ++ { ++ if(AK_FALSE == FHA_set_fs_part(spipart_info, 256)) ++ { ++ printk("FHA_set_fs_part error\n"); ++ } ++ kfree(spipart_info); ++ spipart_info = NULL; ++ } ++ ++ FHA_close(); ++ return 0; ++} ++ ++static int fhachar_UpdateBootBegin(unsigned long arg) ++{ ++ return 0; ++} ++static int fhachar_UpdateBoot(unsigned long arg) ++{ ++ int ret = 0; ++ //int size; ++ //unsigned long partition_count = 0; ++ struct partitions* parts = NULL; ++ unsigned long *ori_mbyte = NULL; ++ unsigned char *part_info = NULL; ++ //int i = 0; ++ ++ T_U8* page0 = NULL; ++ T_U8* pData = NULL; ++ ++ T_U8 ecc[4]; ++ T_U8 pagedata[512]; ++ long newBootLen; ++ int pagesize; ++ ++#ifdef CONFIG_MTD_NAND_ANYKA ++ /* get the old part_info */ ++ size = sizeof(partition_count) + sizeof(*parts) * MAX_PARTS_COUNT + ++ sizeof(*ori_mbyte) * MAX_PARTS_COUNT; ++ part_info = kmalloc(size, GFP_KERNEL); ++ if (part_info == NULL) { ++ printk("malloc part_info error\n"); ++ ret = -1; ++ goto go_out; ++ } ++ ++ if (AK_FALSE == FHA_get_fs_part(part_info, size)){ ++ ret = -1; ++ goto go_out; ++ } ++ ++ partition_count = *(unsigned long *)(part_info); ++ ++ parts = kmalloc(partition_count * sizeof(*parts), GFP_KERNEL); ++ ori_mbyte = kmalloc(partition_count * sizeof(*ori_mbyte), GFP_KERNEL); ++ ++ memcpy(parts, part_info + sizeof(partition_count), partition_count * sizeof(*parts)); ++ memcpy(ori_mbyte, part_info + sizeof(partition_count) + partition_count * sizeof(*parts), ++ partition_count * sizeof(*ori_mbyte)); ++ /* end get the old part_info */ ++#endif ++ ++ if(copy_from_user(pBufInfo, (T_BufInfo*)arg, sizeof(T_BufInfo)) != 0) ++ { ++ ret = -1; ++ goto go_out; ++ } ++ ++ memset(pagedata, 0, 512); ++ ++#ifdef CONFIG_MTD_NAND_ANYKA ++ ak_fha_read_callback(0, 0, pagedata, 512, ecc, 4, FHA_DATA_BOOT); ++ ++ pagesize = pagedata[0xc] * 512;//BOOT页大小 ++#else ++ pagesize = SPI_PAGESIZE;//pagedata[0xc] * 512;//BOOT页大小 ++#endif ++ page0 = (T_U8*)kmalloc(pagesize, GFP_KERNEL); ++ if(page0 == NULL) ++ { ++ ret = -1; ++ goto go_out; ++ } ++ memset(page0, 0, pagesize); ++ ++#ifdef CONFIG_MTD_NAND_ANYKA ++ ak_fha_read_callback(0, 0, page0, pagesize, ecc, 4, FHA_DATA_BOOT); ++ ++ page0[0xd] = pBufInfo->buflen / pagesize + 2;//修改nandboot所占的页数 ++#else ++ ak_fha_read_callback(0, 0, page0, 1, ecc, 4, FHA_DATA_BOOT); ++ ak_fha_read_callback(0, 1, pagedata, 1, ecc, 4, FHA_DATA_BOOT); ++ *((unsigned int*)(page0+0x0c)) = pBufInfo->buflen; ++#endif ++ //ddr param ++ if(pBufInfo->ddrparcnt > 0 && pBufInfo->ddrparcnt < 53) ++ { ++#ifdef CONFIG_MTD_NAND_ANYKA ++ for(i = 0x2c; i < 472 - sizeof(T_NAND_PHY_INFO) - 4; i ++) ++ { ++ if(page0[i] == 'N' && page0[i + 1] == 'A' && page0[i + 2] == 'N' && page0[i + 3] == 'D') ++ { ++ printk("NAND found\n"); ++ break; ++ } ++ } ++ ++ if(i == 472 - sizeof(T_NAND_PHY_INFO) - 4) ++ { ++ ret = -1; ++ goto go_out; ++ } ++ ++ memmove(page0 + 0x2c + pBufInfo->ddrparcnt * 8, page0 + i, sizeof(T_NAND_PHY_INFO)+ 4); ++ ++ memcpy(page0 + 0x2c, pBufInfo->ddrpar, pBufInfo->ddrparcnt * sizeof(unsigned int) * 2); ++#endif ++ } ++ newBootLen = pBufInfo->buflen; ++ if(newBootLen < 0) ++ { ++ ret = -1; ++ goto go_out; ++ } ++ ++#ifdef CONFIG_MTD_NAND_ANYKA ++ newBootLen += pagesize; ++#endif ++ //printk("New Boot len:%ld\n", newBootLen); ++ ++ pData = (T_U8*)kmalloc(newBootLen, GFP_KERNEL); ++ if(pData == NULL) ++ { ++ printk("malloc error\n"); ++ ret = -1; ++ goto go_out; ++ } ++ ++#ifndef CONFIG_MTD_NAND_ANYKA ++ memcpy(pData, pBufInfo->buff, pBufInfo->buflen); ++#endif ++ memcpy(pData, page0, pagesize); ++#ifndef CONFIG_MTD_NAND_ANYKA ++ memcpy(pData+pagesize, pagedata, pagesize); ++#endif ++ ++//ddr param ++ if(pBufInfo->ddrparcnt > 0 && pBufInfo->ddrparcnt < 53) ++ { ++#ifndef CONFIG_MTD_NAND_ANYKA ++ memcpy(pData + 0x18, pBufInfo->ddrpar, pBufInfo->ddrparcnt * sizeof(unsigned int) * 2); ++#endif ++ } ++ ++#ifdef CONFIG_MTD_NAND_ANYKA ++ memcpy(pData+pagesize, pBufInfo->buff, pBufInfo->buflen); ++ ++ ak_fha_read_callback(0, 1, pagedata, 512, ecc, 4, FHA_DATA_BOOT); ++ memcpy(pData + pagesize, pagedata, 208); ++#endif ++#ifdef CONFIG_MTD_NAND_ANYKA ++ if (FHA_FAIL == FHA_set_fs_part(part_info, sizeof(int) ++ + partition_count * sizeof(parts[0]) + partition_count * sizeof(ori_mbyte[0]))) ++ { ++ ret = -1; ++ goto go_out; ++ } ++#endif ++ if(FHA_write_boot_begin(newBootLen) == AK_TRUE) ++ { ++ printk("write boot begin ok\n"); ++ } ++ else ++ { ++ printk("write boot begin error\n"); ++ ret = -1; ++ goto go_out; ++ } ++ ++ if(FHA_write_boot(pData, newBootLen) == AK_TRUE) ++ { ++ printk("write boot ok\n"); ++ } ++ else ++ { ++ printk("write boot error\n"); ++ ret = -1; ++ goto go_out; ++ } ++ ++go_out: ++ if(page0) ++ { ++ kfree(page0); ++ page0 = NULL; ++ } ++ if(pData) ++ { ++ kfree(pData); ++ pData = NULL; ++ } ++ if(parts) ++ { ++ kfree(parts); ++ parts = NULL; ++ } ++ ++ if(part_info) ++ { ++ kfree(part_info); ++ part_info = NULL; ++ } ++ if(ori_mbyte) ++ { ++ kfree(ori_mbyte); ++ ori_mbyte = NULL; ++ } ++ ++ printk("*********************end to update nandboot:ret=%d\n", ret); ++ return ret; ++} ++static int fhachar_UpdateBinBegin(unsigned long arg) ++{ ++ T_BinInfo binInfo; ++ T_FHA_BIN_PARAM binParam; ++ memset(&binParam, 0, sizeof(binParam)); ++ printk("e00000\n"); ++ if(copy_from_user(&binInfo, (T_BinInfo*)arg, sizeof(T_BinInfo)) != 0) ++ { ++ printk("e11111\n"); ++ return -1; ++ } ++ strcpy(binParam.file_name, binInfo.filename); ++ if(FHA_read_bin_begin(&binParam) == FHA_FAIL) ++ { ++ printk("e22222\n"); ++ return -1; ++ } ++ ++ binParam.data_length = binInfo.filelen; ++ if(FHA_write_bin_begin(&binParam) == FHA_FAIL) ++ { ++ printk("e33333\n"); ++ return -1; ++ } ++ printk("e444444\n"); ++ return 0; ++} ++ ++static int fhachar_UpdateBin(unsigned long arg) ++{ ++ if(copy_from_user(pBufInfo, (T_BufInfo*)arg, sizeof(T_BufInfo)) != 0) ++ { ++ return -1; ++ } ++ ++#ifndef CONFIG_MTD_NAND_ANYKA ++ if(spipart_info == NULL) ++ { ++ spipart_info = kmalloc(SPI_PAGESIZE, GFP_KERNEL); ++ if(AK_FALSE == FHA_get_fs_part(spipart_info, SPI_PAGESIZE)) ++ { ++ printk("fha get fs part error\n"); ++ } ++ } ++#endif ++ if(FHA_write_bin(pBufInfo->buff, pBufInfo->buflen) != FHA_SUCCESS) ++ { ++ return -1; ++ } ++ return 0; ++} ++/** ++ * @brief :Command Control interface for burntool . ++ * ++ * @author zhangshenglin ++ * @date 2012-12-07 ++ * @param inode[in] nand char device inode. ++ * @param filp[in] nand char file id. ++ * @param cmd[in] burntool command. ++ * @param arg[in] parameter from user. ++ * @return int ++ * @retval 1: fail 0: success ++ */ ++static long akfha_char_ioctl(/*struct inode *inode, */struct file *filp, ++ unsigned int cmd, unsigned long arg) ++{ ++ int ret = 0; ++ switch(cmd) ++ { ++ case AK_FHA_UPDATE_BOOT_BEGIN: ++ { ++ ret = fhachar_UpdateBootBegin(arg); ++ break; ++ } ++ case AK_FHA_UPDATE_BOOT: ++ { ++ ret = fhachar_UpdateBoot(arg); ++ break; ++ } ++ case AK_FHA_UPDATE_BIN_BEGIN: ++ { ++ ret = fhachar_UpdateBinBegin(arg); ++ break; ++ } ++ case AK_FHA_UPDATE_BIN: ++ { ++ ret = fhachar_UpdateBin(arg); ++ break; ++ } ++ default: ++ { ++ return -EINVAL; ++ } ++ ++ } ++ return ret; ++} ++ ++static struct class *fha_class; ++ ++static void fha_setup_cdev(void) ++{ ++ int err = 0; ++ dev_t devno = MKDEV(fha_c_major, 0); ++ ++ cdev_init(&(fha_c_dev.c_dev), &akfha_char_fops); ++ fha_c_dev.c_dev.owner = THIS_MODULE; ++ fha_c_dev.c_dev.ops = &akfha_char_fops; ++ err = cdev_add(&(fha_c_dev.c_dev), devno, 1); ++ if(err) ++ { ++ printk(KERN_NOTICE "Error %d adding anyka akfha char dev\n", err); ++ } ++ ++ //automatic mknod device node ++ fha_class = class_create(THIS_MODULE, "akfha_class"); ++ device_create(fha_class, NULL, devno, &fha_c_dev, "akfha_char"); ++} ++static int akfha_char_probe(struct platform_device *pdev) ++{ ++ int result = 0; ++ //int existent_chips; ++ //int nr_sets; ++ dev_t devno; ++ ++#ifdef CONFIG_MTD_NAND_ANYKA ++ struct ak_platform_nand_multice *plat = pdev->dev.platform_data; ++ struct ak_nand_set *sets; ++ sets = (plat != NULL) ? plat->sets : NULL; ++ nr_sets = (plat != NULL) ? plat->nr_sets : 1; ++ ak_nand_clock(AK_TRUE); ++#ifndef CONFIG_MTD_DOWNLOAD_MODE ++ existent_chips = ak_nand_CE_set(sets->max_chips, sets->nr_chips, &sets->ce2_gpio, &sets->ce3_gpio); ++ if (!existent_chips) { ++ printk("Can not find one chip\n"); ++ return -1; ++ } ++ ++ if(FHA_FAIL == ak_fha_init(sets->nr_chips)) ++ { ++ printk(KERN_INFO "%s, %d init fha lib error return!!!\n", __func__, __LINE__); ++ return -1; ++ } ++#endif ++#endif ++ devno = MKDEV(fha_c_major, 0); ++ if(fha_c_major) ++ { ++ result = register_chrdev_region(devno, 1, "anyka fha char dev"); ++ } ++ else ++ { ++ result = alloc_chrdev_region(&devno, 0, 1, "anyka fha char dev"); ++ } ++ if(result < 0) ++ { ++ return result; ++ } ++ fha_setup_cdev(); ++ ++ printk(KERN_INFO "akfha Char Device Initialize Successed!\n"); ++ return 0; ++} ++static int akfha_char_remove(struct platform_device *pdev) ++{ ++ dev_t devno = MKDEV(fha_c_major, 0); ++ ++ //destroy device node ++ device_destroy(fha_class, devno); ++ class_destroy(fha_class); ++ ++ //delete char device ++ cdev_del(&(fha_c_dev.c_dev)); ++ unregister_chrdev_region(devno, 1); ++ ++ return 0; ++} ++ ++/* device driver for platform bus bits */ ++static struct platform_driver akfha_char_driver = { ++ .probe = akfha_char_probe, ++ .remove = akfha_char_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ak-fhachar", ++ .pm = NULL, ++ }, ++}; ++static int __init akfha_char_init(void) ++{ ++ printk("*********akfha_char init\n"); ++ return platform_driver_register(&akfha_char_driver); ++} ++ ++static void __exit akfha_char_exit(void) ++{ ++ platform_driver_unregister(&akfha_char_driver); ++ printk("*******akfha_char_exit"); ++} ++ ++subsys_initcall(akfha_char_init); ++module_exit(akfha_char_exit); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("ZhangShenglin"); ++MODULE_DESCRIPTION("Direct character-device access to fha devices"); ++ +diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig +index 4cdb2af7..de263fbe 100644 +--- a/drivers/mtd/devices/Kconfig ++++ b/drivers/mtd/devices/Kconfig +@@ -334,4 +334,15 @@ config MTD_DOCPROBE_55AA + LinuxBIOS or if you need to recover a DiskOnChip Millennium on which + you have managed to wipe the first block. + ++config MTD_AK_SPIFLASH ++ tristate "Support Anyka SPI Flash chips(Most chips)" ++ depends on SPI_MASTER ++ help ++ Anyka driver enables access to most SPI flash chips, used for ++ program and data storage. ++ ++ Set up your spi devices with the right board-specific platform data, ++ if you want to specify device partitioning or to use a device which ++ doesn't support the JEDEC ID instruction. ++ + endmenu +diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile +index a4dd1d82..748ba408 100644 +--- a/drivers/mtd/devices/Makefile ++++ b/drivers/mtd/devices/Makefile +@@ -19,5 +19,7 @@ obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o + obj-$(CONFIG_MTD_M25P80) += m25p80.o + obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o + obj-$(CONFIG_MTD_SST25L) += sst25l.o ++obj-$(CONFIG_MTD_AK_SPIFLASH) += ak_spiflash.o ++ ++CFLAGS_docg3.o += -I$(src) + +-CFLAGS_docg3.o += -I$(src) +\ No newline at end of file +diff --git a/drivers/mtd/devices/ak_spiflash.c b/drivers/mtd/devices/ak_spiflash.c +new file mode 100644 +index 00000000..ce0a53c7 +--- /dev/null ++++ b/drivers/mtd/devices/ak_spiflash.c +@@ -0,0 +1,1795 @@ ++ /** ++ * @file /driver/mtd/devices/ak_SPIFlash.c ++ * @brief SPI Flash driver for Anyka AK37 platform. ++ * Copyright C 2012 Anyka CO.,LTD ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * @author She Shaohua ++ * @date 2012-03-23 ++ * @note 2011-03-20 created ++ * @note 2011-03-23 Debug OK. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++//#define SPIFLASH_DEBUG ++#undef PDEBUG ++#ifdef SPIFLASH_DEBUG ++#define PDEBUG(fmt, args...) printk( KERN_INFO fmt,## args) ++#define DEBUG(n, args...) printk(KERN_INFO args) ++#else ++#define PDEBUG(fmt, args...) ++#define DEBUG(n, args...) ++#endif ++ ++#define FLASH_BUF_SIZE (32*1024*1024) ++#define FLASH_PAGESIZE 256 ++ ++#define SPI_FLASH_READ 1 ++#define SPI_FLASH_WRITE 2 ++ ++#define CONFIG_SPIFLASH_USE_FAST_READ 1 ++ ++/*mtd layer allocate memory use for 'vmalloc' interface, need to convert.*/ ++//#define SPIFLASH_USE_MTD_BLOCK_LAYER ++ ++#define OPCODE_WREN 0x06 /* Write Enable */ ++#define OPCODE_WRDI 0x04 /* Write Disable */ ++#define OPCODE_RDSR1 0x05 /* Read Status Register1 */ ++#define OPCODE_RDSR2 0x35 /* Read Status Register2 */ ++#define OPCODE_WRSR 0x01 /* Write Status Register */ ++ ++#define OPCODE_NORM_READ 0x03 /* Read Data Bytes */ ++#define OPCODE_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ ++#define OPCODE_FAST_D_READ 0x3b /* Read Data Bytes at Dual output */ ++#define OPCODE_FAST_Q_READ 0x6b /* Read Data Bytes at Quad output */ ++#define OPCODE_FAST_D_IO 0xbb /* Read Data Bytes at Dual i/o */ ++#define OPCODE_FAST_Q_IO 0xeb /* Read Data Bytes at Quad i/o */ ++ ++#define OPCODE_PP 0x02 /* Page Program */ ++#define OPCODE_PP_DUAL 0x12 /* Dual Page Program*/ ++#define OPCODE_PP_QUAD 0x32 /* Quad Page Program*/ ++#define OPCODE_2IO_PP 0x18 /* 2I/O Page Program (tmp)*/ ++#define OPCODE_4IO_PP 0x38 /* 4I/O Page Program*/ ++ ++#define OPCODE_BE_4K 0x20 /* Sector (4K) Erase */ ++#define OPCODE_BE_32K 0x52 /* Block (32K) Erase */ ++#define OPCODE_BE_64K 0xd8 /* Block (64K) Erase */ ++#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ ++#define OPCODE_CHIP_ERASE 0xc7 /* Chip Erase */ ++#define OPCODE_RDID 0x9f /* Read JEDEC ID */ ++#define OPCODE_DP 0xb9 /* Deep Power-down */ ++#define OPCODE_RES 0xab /* Release from DP, and Read Signature */ ++ ++ ++#define SPI_STATUS_REG1 1 ++#define SPI_STATUS_REG2 2 ++ ++ ++/* Used for SST flashes only. */ ++#define OPCODE_BP 0x02 /* Byte program */ ++#define OPCODE_WRDI 0x04 /* Write disable */ ++#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ ++ ++ ++/* Define max times to check status register before we give up. */ ++#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* 40s max chip erase */ ++ ++#define CMD_SIZE (1) ++#define ADDR_SIZE (3) ++#define CMD_ADDR_SIZE (CMD_SIZE + ADDR_SIZE) ++#define MAX_DUMMY_SIZE (4) ++ ++#define MTD_PART_NAME_LEN (4) ++ ++#ifdef CONFIG_SPIFLASH_USE_FAST_READ ++#define OPCODE_READ OPCODE_FAST_READ ++#define FAST_READ_DUMMY_BYTE 1 ++#else ++#define OPCODE_READ OPCODE_NORM_READ ++#define FAST_READ_DUMMY_BYTE 0 ++#endif ++ ++#define ALIGN_DOWN(a, b) (((a) / (b)) * (b)) ++ ++/****************************************************************************/ ++struct partitions ++{ ++ char name[MTD_PART_NAME_LEN]; ++ unsigned long long size; ++ unsigned long long offset; ++ unsigned int mask_flags; ++}__attribute__((packed)); ++ ++typedef struct ++{ ++ T_U32 BinPageStart; /*bin data start addr*/ ++ T_U32 PageSize; /*spi page size*/ ++ T_U32 PagesPerBlock;/*page per block*/ ++ T_U32 BinInfoStart; ++ T_U32 FSPartStart; ++} ++T_SPI_BURN_INIT_INFO; ++ ++ ++/* ++ * SPI device driver setup and teardown ++ */ ++struct flash_info { ++ char *name; ++ ++ /* JEDEC id zero means "no ID" (most older chips); otherwise it has ++ * a high byte of zero plus three data bytes: the manufacturer id, ++ * then a two byte device id. ++ */ ++ u32 jedec_id; ++ u16 ext_id; ++ ++ /* The size listed here is what works with OPCODE_SE, which isn't ++ * necessarily called a "sector" by the vendor. ++ */ ++ unsigned sector_size; ++ u16 n_sectors; ++ ++ /** ++ * chip character bits: ++ * bit 0: under_protect flag, the serial flash under protection or not when power on ++ * bit 1: fast read flag, the serial flash support fast read or not(command 0Bh) ++ * bit 2: AAI flag, the serial flash support auto address increment word programming ++ * bit 3: support dual write or no ++ * bit 4: support dual read or no ++ * bit 5: support quad write or no ++ * bit 6: support quad read or no ++ * bit 7: the second status command (35h) flag,if use 4-wire(quad) mode,the bit must be is enable ++ */ ++ u16 flags; ++#define SFLAG_UNDER_PROTECT (1<<0) ++#define SFLAG_FAST_READ (1<<1) ++#define SFLAG_AAAI (1<<2) ++#define SFLAG_COM_STATUS2 (1<<3) ++ ++#define SFLAG_DUAL_IO_READ (1<<4) ++#define SFLAG_DUAL_READ (1<<5) ++#define SFLAG_QUAD_IO_READ (1<<6) ++#define SFLAG_QUAD_READ (1<<7) ++ ++#define SFLAG_DUAL_IO_WRITE (1<<8) ++#define SFLAG_DUAL_WRITE (1<<9) ++#define SFLAG_QUAD_IO_WRITE (1<<10) ++#define SFLAG_QUAD_WRITE (1<<11) ++ ++#define SFLAG_SECT_4K (1<<12) ++ ++}; ++ ++/** ++ *because of some spi flash is difference of status register difinition. ++ *this structure use mapping the status reg function and corresponding. ++*/ ++struct flash_status_reg ++{ ++ u32 jedec_id; ++ u16 ext_id; ++ unsigned b_wip:4; /*write in progress*/ ++ unsigned b_wel:4; /*wrute ebabke latch*/ ++ unsigned b_bp0:4; /*block protected 0*/ ++ unsigned b_bp1:4; /*block protected 1*/ ++ unsigned b_bp2:4; /*block protected 2*/ ++ unsigned b_bp3:4; /*block protected 3*/ ++ unsigned b_bp4:4; /*block protected 4*/ ++ unsigned b_srp0:4; /*status register protect 0*/ ++ ++ unsigned b_srp1:4; /*status register protect 1*/ ++ unsigned b_qe:4; /*quad enable*/ ++ unsigned b_lb:4; /*write protect control and status to the security reg.*/ ++/* ++ unsigned b_reserved0:4; ++ unsigned b_reserved1:4; ++ unsigned b_reserved2:4; ++*/ ++ unsigned b_cmp:4; /*conjunction bp0-bp4 bit*/ ++ unsigned b_sus:4; /*exec an erase/program suspend command*/ ++}; ++ ++struct ak_spiflash { ++ struct spi_device *spi; ++ struct mutex lock; ++ struct flash_info info; ++ struct mtd_info mtd; ++ unsigned partitioned:1; ++ ++ u8 bus_width; ++ unsigned char *buf; ++ u8 command[CMD_ADDR_SIZE + MAX_DUMMY_SIZE]; ++ u8 dummy_len; ++ ++ u8 erase_opcode; ++ u8 tx_opcode; ++ u8 rx_opcode; ++ u8 txd_bus_width; ++ u8 rxd_bus_width; ++ ++ u8 txa_bus_width; ++ u8 rxa_bus_width; ++ struct flash_status_reg stat_reg; ++}; ++ ++static struct mtd_info *ak_mtd_info; ++ ++ ++static inline struct ak_spiflash *mtd_to_spiflash(struct mtd_info *mtd) ++{ ++ return container_of(mtd, struct ak_spiflash, mtd); ++} ++ ++#ifdef SPIFLASH_USE_MTD_BLOCK_LAYER ++/** ++* @brief: because of the _read() function call by mtd block layer, the buffer be ++* allocate by vmalloc() in mtd layer, spi driver layer may use this buffer that ++* intents of use for DMA transfer, so, add this function to transition buffer. ++* call this function at before real read/write data. ++* ++* @author lixinhai ++* @date 2013-04-10 ++* @param[in] flash spiflash handle. ++* @param[in] buffer. ++* @param[in] buffer len ++* @param[in] read/write ++* @retval return the transition buffer ++*/ ++static void *flash_buf_bounce_pre(struct ak_spiflash *flash, ++ void *buf, u32 len, int dir) ++{ ++ if(!is_vmalloc_addr(buf)) { ++ return buf; ++ } ++ ++ if(dir == SPI_FLASH_WRITE) { ++ memcpy(flash->buf, buf, len); ++ } ++ return flash->buf; ++} ++ ++/** ++* @brief: because of the _read() function call by mtd block layer, the buffer be ++* allocate by vmalloc() in mtd layer, spi driver layer may use this buffer that ++* intents of use for DMA transfer, so, add this function to transition buffer. ++* call this function at after real read/write data ++* ++* @author lixinhai ++* @date 2013-04-10 ++* @param[in] flash spiflash handle. ++* @param[in] buffer. ++* @param[in] buffer len ++* @param[in] read/write ++* @retval return the transition buffer ++*/ ++static void flash_buf_bounce_post(struct ak_spiflash *flash, ++ void *buf, u32 len, int dir) ++{ ++ if(!is_vmalloc_addr(buf)) { ++ return; ++ } ++ ++ if(dir == SPI_FLASH_READ) { ++ memcpy(buf, flash->buf, len); ++ } ++} ++#else ++static inline void *flash_buf_bounce_pre(struct ak_spiflash *flash, ++ void *buf, u32 len, int dir) ++{ ++ return buf; ++} ++ ++static inline void flash_buf_bounce_post(struct ak_spiflash *flash, ++ void *buf, u32 len, int dir) ++{ ++} ++ ++#endif ++ ++/* ++ * Internal helper functions ++ */ ++ ++/** ++* @brief Read the status register. ++* ++* returning its value in the location ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] spiflash handle. ++* @return int Return the status register value. ++*/ ++static int read_sr(struct ak_spiflash *flash) ++{ ++ ssize_t retval; ++ u8 code; ++ u16 status; ++ u8 status1, status2; ++ ++ code = OPCODE_RDSR1; ++ ++ if((retval = spi_write_then_read(flash->spi, &code, 1, &status1, 1))<0) ++ return retval; ++ ++ if(flash->info.flags & SFLAG_COM_STATUS2){ ++ code = OPCODE_RDSR2; ++ if((retval = spi_write_then_read(flash->spi, &code, 1, &status2, 1))<0) ++ return retval; ++ ++ status = (status1 | (status2 << 8)); ++ } else ++ status = status1 & 0xff; ++ ++ return status; ++} ++ ++ ++/** ++* @brief Write status register ++* ++* Write status register 1 byte. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] flash spiflash handle. ++* @param[in] val register value to be write. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a negative error code if failed ++*/ ++static int write_sr(struct ak_spiflash *flash, u16 val) ++{ ++ int wr_cnt; ++ ++ flash->command[0] = OPCODE_WRSR; ++ flash->command[1] = val & 0xff; ++ flash->command[2] = (val>>8) &0xff; ++ ++ if (flash->info.flags & SFLAG_COM_STATUS2) { ++ wr_cnt = 3; ++ } else { ++ wr_cnt = 2; ++ } ++ ++ return spi_write(flash->spi, flash->command, wr_cnt); ++} ++ ++ ++/** ++* @brief Set write enable latch. ++* ++* Set write enable latch with Write Enable command. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] flash spiflash handle. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a negative error code if failed ++*/ ++static inline int write_enable(struct ak_spiflash *flash) ++{ ++ u8 code = OPCODE_WREN; ++ ++ return spi_write_then_read(flash->spi, &code, 1, NULL, 0); ++} ++ ++ ++/** ++* @brief Set write disble ++* ++* Set write disble instruction to the chip. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] flash spiflash handle. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a negative error code if failed ++*/ ++static inline int write_disable(struct ak_spiflash *flash) ++{ ++ u8 code = OPCODE_WRDI; ++ ++ return spi_write_then_read(flash->spi, &code, 1, NULL, 0); ++} ++ ++/** ++* @brief Wait for SPI flash ready. ++* ++* Service routine to read status register until ready, or timeout occurs. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] flash spiflash handle. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a non-zero error code if failed ++*/ ++static int wait_till_ready(struct ak_spiflash *flash) ++{ ++ unsigned long deadline; ++ int sr; ++ struct flash_status_reg *fsr = &flash->stat_reg; ++ ++ deadline = jiffies + MAX_READY_WAIT_JIFFIES; ++ ++ do { ++ if ((sr = read_sr(flash)) < 0) ++ break; ++ else if (!(sr & (1<b_wip))) ++ return 0; ++ ++ cond_resched(); ++ ++ } while (!time_after_eq(jiffies, deadline)); ++ ++ return 1; ++} ++ ++static int quad_mode_enable(struct ak_spiflash *flash) ++{ ++ int ret; ++ u16 regval; ++ struct flash_status_reg *fsr = &flash->stat_reg; ++ ++ ret = wait_till_ready(flash); ++ if (ret) ++ return -EBUSY; ++ ++ write_enable(flash); ++ ++ regval = read_sr(flash); ++ regval |= 1<b_qe; ++ write_sr(flash, regval); ++ ++ write_disable(flash); ++ return 0; ++} ++ ++static int quad_mode_disable(struct ak_spiflash *flash) ++{ ++ int ret; ++ u16 regval; ++ struct flash_status_reg *fsr = &flash->stat_reg; ++ ++ ret = wait_till_ready(flash); ++ if (ret) ++ return -EBUSY; ++ ++ write_enable(flash); ++ ++ regval = read_sr(flash); ++ regval &= ~(1<b_qe); ++ write_sr(flash, regval); ++ ++ write_disable(flash); ++ return 0; ++} ++ ++/** ++* @brief Erase chip ++* ++* Erase the whole flash memory. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] flash spiflash handle. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a non-zero error code if failed ++*/ ++static int erase_chip(struct ak_spiflash *flash) ++{ ++ DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %lldKiB\n", ++ dev_name(&flash->spi->dev), __func__, ++ (long long)(flash->mtd.size >> 10)); ++ ++ /* Wait until finished previous write command. */ ++ if (wait_till_ready(flash)) ++ return -EBUSY; ++ ++ /* Send write enable, then erase commands. */ ++ write_enable(flash); ++ ++ /* Set up command buffer. */ ++ flash->command[0] = OPCODE_CHIP_ERASE; ++ ++ spi_write(flash->spi, flash->command, 1); ++ ++ return 0; ++} ++ ++ ++ ++/** ++* @brief Erase sector ++* ++* Erase a sector specialed by user. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] flash spiflash handle. ++* @param[in] offset which is any address within the sector which should be erased. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a non-zero error code if failed ++*/ ++static int erase_sector(struct ak_spiflash *flash, u32 offset) ++{ ++ DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n", ++ dev_name(&flash->spi->dev), __func__, ++ flash->mtd.erasesize / 1024, offset); ++ ++ /* Wait until finished previous write command. */ ++ if (wait_till_ready(flash)) ++ return -EBUSY; ++ ++ /* Send write enable, then erase commands. */ ++ write_enable(flash); ++ ++ /* Set up command buffer. */ ++ flash->command[0] = flash->erase_opcode; ++ flash->command[1] = offset >> 16; ++ flash->command[2] = offset >> 8; ++ flash->command[3] = offset; ++ ++ spi_write(flash->spi, flash->command, CMD_ADDR_SIZE); ++ ++ return 0; ++} ++ ++/****************************************************************************/ ++ ++/* ++ * MTD implementation ++ */ ++ ++ ++/** ++* @brief MTD Erase ++* ++* Erase an address range on the flash chip. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] mtd mtd info handle. ++* @param[in] instr erase info. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a non-zero error code if failed ++*/ ++static int ak_spiflash_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ struct ak_spiflash *flash = mtd_to_spiflash(mtd); ++ u32 addr,len; ++ uint32_t rem; ++ ++ PDEBUG("ak_spiflash_erase\n"); ++ //printk(": instr->len=%lld, mtd->erasesize=%ld, addr=%lld\n", instr->len,mtd->erasesize,(long long)instr->addr); ++ ++ DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%llx, len %lld\n", ++ dev_name(&flash->spi->dev), __func__, "at", ++ (long long)instr->addr, (long long)instr->len); ++ ++ /* sanity checks */ ++ if (instr->addr + instr->len > mtd->size) ++ { ++ printk(KERN_ERR "ak_spiflash_erase:instr->addr[0x%llx] + instr->len[%lld] > mtd->size[%lld]\n", ++ instr->addr, instr->len, mtd->size ); ++ return -EINVAL; ++ } ++ div_u64_rem(instr->len, mtd->erasesize, &rem); ++ if (rem != 0) ++ { ++ printk(KERN_ERR "ak_spiflash_erase:rem!=0 [%u]\n", rem ); ++ return -EINVAL; ++ } ++ ++ addr = instr->addr; ++ len = instr->len; ++ ++ mutex_lock(&flash->lock); ++ ++ /* whole-chip erase? */ ++ if (len == mtd->size) { ++ if (erase_chip(flash)) { ++ instr->state = MTD_ERASE_FAILED; ++ mutex_unlock(&flash->lock); ++ return -EIO; ++ } ++ ++ /* REVISIT in some cases we could speed up erasing large regions ++ * by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up ++ * to use "small sector erase", but that's not always optimal. ++ */ ++ ++ /* "sector"-at-a-time erase */ ++ } else { ++ while (len) { ++ if (erase_sector(flash, addr)) { ++ instr->state = MTD_ERASE_FAILED; ++ mutex_unlock(&flash->lock); ++ return -EIO; ++ } ++ ++ addr += mtd->erasesize; ++ len -= mtd->erasesize; ++ } ++ } ++ ++ mutex_unlock(&flash->lock); ++ ++ instr->state = MTD_ERASE_DONE; ++ mtd_erase_callback(instr); ++ ++ return 0; ++} ++ ++ ++static int init_spiflash_rw_info(struct ak_spiflash *flash) ++{ ++ /**default param.*/ ++ flash->rx_opcode = OPCODE_READ; ++ flash->rxd_bus_width = XFER_1DATAWIRE; ++ flash->rxa_bus_width = XFER_1DATAWIRE; ++ flash->tx_opcode = OPCODE_PP; ++ flash->txd_bus_width = XFER_1DATAWIRE; ++ flash->txa_bus_width = XFER_1DATAWIRE; ++ flash->dummy_len = 1; ++ ++ if(flash->bus_width & FLASH_BUS_WIDTH_2WIRE){ ++ if(flash->info.flags & SFLAG_DUAL_READ) { ++ flash->rx_opcode = OPCODE_FAST_D_READ; ++ flash->rxd_bus_width = XFER_2DATAWIRE; ++ flash->rxa_bus_width = XFER_1DATAWIRE; ++ flash->dummy_len = 1; ++ } else if (flash->info.flags & SFLAG_DUAL_IO_READ) { ++ flash->rx_opcode = OPCODE_FAST_D_IO; ++ flash->rxd_bus_width = XFER_2DATAWIRE; ++ flash->rxa_bus_width = XFER_2DATAWIRE; ++ flash->dummy_len = 1; ++ } ++ ++ if(flash->info.flags & SFLAG_DUAL_WRITE) { ++ flash->tx_opcode = OPCODE_PP_DUAL; ++ flash->txd_bus_width = XFER_2DATAWIRE; ++ flash->txa_bus_width = XFER_1DATAWIRE; ++ } else if(flash->info.flags & SFLAG_DUAL_IO_WRITE) { ++ flash->tx_opcode = OPCODE_2IO_PP; ++ flash->txd_bus_width = XFER_2DATAWIRE; ++ flash->txa_bus_width = XFER_2DATAWIRE; ++ } ++ } ++ ++ if(flash->bus_width & FLASH_BUS_WIDTH_4WIRE){ ++ if(flash->info.flags & SFLAG_QUAD_READ) { ++ flash->rx_opcode = OPCODE_FAST_Q_READ; ++ flash->rxd_bus_width = XFER_4DATAWIRE; ++ flash->rxa_bus_width = XFER_1DATAWIRE; ++ flash->dummy_len = 1; ++ }else if(flash->info.flags & SFLAG_QUAD_IO_READ){ ++ flash->rx_opcode = OPCODE_FAST_Q_IO; ++ flash->rxd_bus_width = XFER_4DATAWIRE; ++ flash->rxa_bus_width = XFER_4DATAWIRE; ++ flash->dummy_len = 3; ++ } ++ ++ if(flash->info.flags & SFLAG_QUAD_WRITE) { ++ flash->tx_opcode = OPCODE_PP_QUAD; ++ flash->txd_bus_width = XFER_4DATAWIRE; ++ flash->txa_bus_width = XFER_1DATAWIRE; ++ }else if(flash->info.flags & SFLAG_QUAD_IO_WRITE) { ++ flash->tx_opcode = OPCODE_4IO_PP; ++ flash->txd_bus_width = XFER_4DATAWIRE; ++ flash->txa_bus_width = XFER_4DATAWIRE; ++ } ++ ++ } ++ return 0; ++} ++ ++static int ak_spiflash_cfg_quad_mode(struct ak_spiflash *flash) ++{ ++ int ret = 0; ++ ++ if((flash->bus_width & FLASH_BUS_WIDTH_4WIRE) && ++ (flash->info.flags & (SFLAG_QUAD_WRITE|SFLAG_QUAD_IO_WRITE| ++ SFLAG_DUAL_READ|SFLAG_DUAL_IO_READ))) { ++ ret = quad_mode_enable(flash); ++ if(ret) ++ flash->bus_width &= ~FLASH_BUS_WIDTH_4WIRE; ++ } ++ else ++ quad_mode_disable(flash); ++ ++ return ret; ++} ++ ++ ++#define FILL_CMD(c, val) do{c[0] = (val);}while(0) ++#define FILL_ADDR(c, val) do{ \ ++ c[CMD_SIZE] = (val) >> 16; \ ++ c[CMD_SIZE+1] = (val) >> 8; \ ++ c[CMD_SIZE+2] = (val); \ ++ }while(0) ++ ++#define FILL_DUMMY_DATA(c, val) do{ \ ++ c[CMD_ADDR_SIZE] = val >> 16; \ ++ c[CMD_ADDR_SIZE+1] = 0; \ ++ c[CMD_ADDR_SIZE+2] = 0; \ ++ c[CMD_ADDR_SIZE+3] = 0; \ ++ }while(0) ++ ++ ++static int spiflash_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ struct ak_spiflash *flash = mtd_to_spiflash(mtd); ++ struct spi_transfer t[3]; ++ struct spi_message m; ++ void *bounce_buf; ++ ++ spi_message_init(&m); ++ memset(t, 0, (sizeof t)); ++ ++ mutex_lock(&flash->lock); ++ ++ bounce_buf = flash_buf_bounce_pre(flash, buf, len, SPI_FLASH_READ); ++ ++ t[0].tx_buf = flash->command; ++ t[0].len = CMD_SIZE; ++ spi_message_add_tail(&t[0], &m); ++ ++ t[1].tx_buf = &flash->command[CMD_SIZE]; ++ t[1].len = ADDR_SIZE + flash->dummy_len; ++ t[1].xfer_mode = flash->rxa_bus_width; ++ spi_message_add_tail(&t[1], &m); ++ ++ t[2].rx_buf = bounce_buf; ++ t[2].len = len; ++ t[2].cs_change = 1; ++ t[2].xfer_mode = flash->rxd_bus_width; ++ ++ spi_message_add_tail(&t[2], &m); ++ ++ /* Byte count starts at zero. */ ++ if (retlen) ++ *retlen = 0; ++ ++ /* Wait till previous write/erase is done. */ ++ if (wait_till_ready(flash)) { ++ /* REVISIT status return?? */ ++ mutex_unlock(&flash->lock); ++ return -EBUSY; ++ } ++ ++ /* Set up the write data buffer. */ ++ FILL_CMD(flash->command, flash->rx_opcode); ++ FILL_ADDR(flash->command, from); ++ FILL_DUMMY_DATA(flash->command, 0x00); ++ ++ spi_sync(flash->spi, &m); ++ ++ *retlen = m.actual_length - CMD_ADDR_SIZE - flash->dummy_len; ++ ++ flash_buf_bounce_post(flash, buf, len, SPI_FLASH_READ); ++ ++ mutex_unlock(&flash->lock); ++ ++ return 0; ++} ++ ++ ++static int ak_spiflash_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ int ret = 0; ++ size_t rlen = 0; ++ u32 xfer_len; ++ u32 offset = 0; ++ u32 count = len; ++ ++ while(count > 0) { ++ xfer_len = (count > FLASH_BUF_SIZE) ? FLASH_BUF_SIZE : count; ++ ++ if(xfer_len > FLASH_PAGESIZE) ++ xfer_len = ALIGN_DOWN(xfer_len, FLASH_PAGESIZE); ++ ++ ret = spiflash_read(mtd, from + offset, xfer_len, &rlen, buf + offset); ++ if(unlikely(ret)) { ++ ret = -EBUSY; ++ goto out; ++ } ++ ++ *retlen += rlen; ++ count -= rlen; ++ offset += rlen; ++ } ++out: ++ return ret; ++} ++ ++ ++/** ++* @brief MTD write ++* ++* Write an address range to the flash chip. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] mtd mtd info handle. ++* @param[in] to write start address. ++* @param[in] len write length. ++* @param[out] retlen write length at actually. ++* @param[out] buf the pointer to write data. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a non-zero error code if failed ++*/ ++static int ak_spiflash_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct ak_spiflash *flash = mtd_to_spiflash(mtd); ++ u32 page_offset, page_size; ++ struct spi_transfer t[3]; ++ struct spi_message m; ++ void *bounce_buf; ++ ++ DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", ++ dev_name(&flash->spi->dev), __func__, "to", ++ (u32)to, len); ++ ++ if (retlen) ++ *retlen = 0; ++ ++ /* sanity checks */ ++ if (!len) ++ return(0); ++ ++ if (to + len > mtd->size) ++ return -EINVAL; ++ ++ spi_message_init(&m); ++ memset(t, 0, (sizeof t)); ++ ++ mutex_lock(&flash->lock); ++ bounce_buf = flash_buf_bounce_pre(flash, (void*)buf, len, SPI_FLASH_WRITE); ++ ++ t[0].tx_buf = flash->command; ++ t[0].len = CMD_SIZE; ++ spi_message_add_tail(&t[0], &m); ++ ++ t[1].tx_buf = &flash->command[CMD_SIZE]; ++ t[1].len = ADDR_SIZE; ++ t[1].xfer_mode = flash->txa_bus_width; ++ spi_message_add_tail(&t[1], &m); ++ ++ t[2].tx_buf = bounce_buf; ++ t[2].cs_change = 1; ++ t[2].xfer_mode = flash->txd_bus_width; ++ ++ spi_message_add_tail(&t[2], &m); ++ ++ //memcpy(flash->buf, buf, len); ++ ++ /* Wait until finished previous write command. */ ++ if (wait_till_ready(flash)) { ++ mutex_unlock(&flash->lock); ++ return 1; ++ } ++ ++ write_enable(flash); ++ ++ /* Set up the opcode in the write buffer. */ ++ FILL_CMD(flash->command, flash->tx_opcode); ++ FILL_ADDR(flash->command, to); ++ ++ /* what page do we start with? */ ++ page_offset = to % FLASH_PAGESIZE; ++ ++ /* do all the bytes fit onto one page? */ ++ if (page_offset + len <= FLASH_PAGESIZE) { ++ t[2].len = len; ++ ++ spi_sync(flash->spi, &m); ++ ++ *retlen = m.actual_length - CMD_ADDR_SIZE; ++ } else { ++ u32 i; ++ ++ /* the size of data remaining on the first page */ ++ page_size = FLASH_PAGESIZE - page_offset; ++ ++ t[2].len = page_size; ++ spi_sync(flash->spi, &m); ++ ++ *retlen = m.actual_length - CMD_ADDR_SIZE; ++ ++ /* write everything in PAGESIZE chunks */ ++ for (i = page_size; i < len; i += page_size) { ++ page_size = len - i; ++ if (page_size > FLASH_PAGESIZE) ++ page_size = FLASH_PAGESIZE; ++ ++ /* write the next page to flash */ ++ FILL_ADDR(flash->command, to+i); ++ ++ t[2].tx_buf = buf + i; ++ t[2].len = page_size; ++ ++ wait_till_ready(flash); ++ ++ write_enable(flash); ++ ++ spi_sync(flash->spi, &m); ++ ++ if (retlen) ++ *retlen += m.actual_length - CMD_ADDR_SIZE; ++ } ++ } ++ ++ PDEBUG("ak_spiflash_write: retlen=%ld\n", *retlen); ++ flash_buf_bounce_post(flash, (void*)buf, len, SPI_FLASH_WRITE); ++ ++ mutex_unlock(&flash->lock); ++ ++ return 0; ++} ++ ++/** ++* @brief MTD get device ID ++* ++* get the device ID of the spi flash chip. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] mtd mtd info handle. ++* @return int return device ID of the spi flash chip. ++*/ ++static int ak_spiflash_get_devid(struct mtd_info *mtd) ++{ ++ struct ak_spiflash *flash = mtd_to_spiflash(mtd); ++ int ret; ++ u8 code = OPCODE_RDID; ++ u8 id[5]; ++ u32 jedec; ++ ++ /* Wait until finished previous write command. */ ++ if (wait_till_ready(flash)) ++ return -EBUSY; ++ ++ /* JEDEC also defines an optional "extended device information" ++ * string for after vendor-specific data, after the three bytes ++ * we use here. Supporting some chips might require using it. ++ */ ++ ret = spi_write_then_read(flash->spi, &code, 1, id, 3); ++ if (ret < 0) { ++ DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d ak_spiflash_get_devid\n", ++ dev_name(&flash->spi->dev), ret); ++ return AK_FALSE; ++ } ++ ++ jedec = id[0] | (id[1]<<8) | (id[2]<<16); ++ printk("spi flash ID: 0x%08x\n", jedec); ++ ++ return jedec; ++} ++ ++ ++ /** ++ * @brief MTD write only for SST spi flash. ++ * ++ * Write an address range to the flash chip. ++ * @author SheShaohua ++ * @date 2012-03-20 ++ * @param[in] mtd mtd info handle. ++ * @param[in] to write start address. ++ * @param[in] len write length. ++ * @param[out] retlen write length at actually. ++ * @param[out] buf the pointer to write data. ++ * @return int return write success or failed ++ * @retval returns zero on success ++ * @retval return a non-zero error code if failed ++ */ ++static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct ak_spiflash *flash = mtd_to_spiflash(mtd); ++ struct spi_transfer t[2]; ++ struct spi_message m; ++ size_t actual; ++ int cmd_sz, ret; ++ ++ if (retlen) ++ *retlen = 0; ++ ++ /* sanity checks */ ++ if (!len) ++ return 0; ++ ++ if (to + len > flash->mtd.size) ++ return -EINVAL; ++ ++ spi_message_init(&m); ++ memset(t, 0, (sizeof t)); ++ ++ t[0].tx_buf = flash->command; ++ t[0].len = CMD_ADDR_SIZE; ++ spi_message_add_tail(&t[0], &m); ++ ++ t[1].tx_buf = buf; ++ spi_message_add_tail(&t[1], &m); ++ ++ mutex_lock(&flash->lock); ++ ++ /* Wait until finished previous write command. */ ++ ret = wait_till_ready(flash); ++ if (ret) ++ goto time_out; ++ ++ write_enable(flash); ++ ++ actual = to % 2; ++ /* Start write from odd address. */ ++ if (actual) { ++ flash->command[0] = OPCODE_BP; ++ flash->command[1] = to >> 16; ++ flash->command[2] = to >> 8; ++ flash->command[3] = to; ++ ++ /* write one byte. */ ++ t[1].len = 1; ++ spi_sync(flash->spi, &m); ++ ret = wait_till_ready(flash); ++ if (ret) ++ goto time_out; ++ *retlen += m.actual_length - CMD_ADDR_SIZE; ++ } ++ to += actual; ++ ++ flash->command[0] = OPCODE_AAI_WP; ++ flash->command[1] = to >> 16; ++ flash->command[2] = to >> 8; ++ flash->command[3] = to; ++ ++ /* Write out most of the data here. */ ++ cmd_sz = CMD_ADDR_SIZE; ++ for (; actual < len - 1; actual += 2) { ++ t[0].len = cmd_sz; ++ /* write two bytes. */ ++ t[1].len = 2; ++ t[1].tx_buf = buf + actual; ++ ++ spi_sync(flash->spi, &m); ++ ret = wait_till_ready(flash); ++ if (ret) ++ goto time_out; ++ *retlen += m.actual_length - cmd_sz; ++ cmd_sz = 1; ++ to += 2; ++ } ++ write_disable(flash); ++ ret = wait_till_ready(flash); ++ if (ret) ++ goto time_out; ++ ++ /* Write out trailing byte if it exists. */ ++ if (actual != len) { ++ write_enable(flash); ++ flash->command[0] = OPCODE_BP; ++ flash->command[1] = to >> 16; ++ flash->command[2] = to >> 8; ++ flash->command[3] = to; ++ t[0].len = CMD_ADDR_SIZE; ++ t[1].len = 1; ++ t[1].tx_buf = buf + actual; ++ ++ spi_sync(flash->spi, &m); ++ ret = wait_till_ready(flash); ++ if (ret) ++ goto time_out; ++ *retlen += m.actual_length - CMD_ADDR_SIZE; ++ write_disable(flash); ++ } ++ ++time_out: ++ mutex_unlock(&flash->lock); ++ return ret; ++} ++ ++/****************************************************************************/ ++ ++static struct flash_status_reg __devinitdata status_reg_list[] = { ++ /*spiflash mx25l12805d*/ ++ { ++ .jedec_id = 0xc22018, .ext_id = 0, ++ .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, ++ .b_bp2 = 4, .b_bp3 = 5, .b_qe = 6, .b_srp0 = 7, ++ }, ++ /*normal status reg define*/ ++ { ++ .jedec_id = 0, .ext_id = 0, ++ .b_wip = 0, .b_wel = 1, .b_bp0 = 2, .b_bp1 = 3, ++ .b_bp2 = 4, .b_bp3 = 5, .b_bp4 = 6, .b_srp0 = 7, ++ ++ .b_srp1 = 8,.b_qe = 9, .b_lb = 10, .b_cmp = 14, ++ .b_sus = 15, ++ }, ++}; ++ ++ ++ ++/* NOTE: double check command sets and memory organization when you add ++ * more flash chips. This current list focusses on newer chips, which ++ * have been converging on command sets which including JEDEC ID. ++ */ ++static struct flash_info __devinitdata ak_spiflash_supportlist [] = { ++ ++ /* Atmel -- some are (confusingly) marketed as "DataFlash" */ ++ { "at25fs010", 0x1f6601, 0, 32 * 1024, 4, SFLAG_SECT_4K, }, ++ { "at25fs040", 0x1f6604, 0, 64 * 1024, 8, SFLAG_SECT_4K, }, ++ ++ { "at25df041a", 0x1f4401, 0, 64 * 1024, 8, SFLAG_SECT_4K, }, ++ { "at25df641", 0x1f4800, 0, 64 * 1024, 128, SFLAG_SECT_4K, }, ++ ++ { "at26f004", 0x1f0400, 0, 64 * 1024, 8, SFLAG_SECT_4K, }, ++ { "at26df081a", 0x1f4501, 0, 64 * 1024, 16, SFLAG_SECT_4K, }, ++ { "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SFLAG_SECT_4K, }, ++ { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SFLAG_SECT_4K, }, ++ ++ /* Macronix */ ++ { "mx25l3205d", 0xc22016, 0, 64 * 1024, 64, SFLAG_SECT_4K | SFLAG_DUAL_READ}, ++ { "mx25l6405d", 0xc22017, 0, 64 * 1024, 128, SFLAG_SECT_4K | SFLAG_DUAL_READ}, ++ { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, SFLAG_SECT_4K | SFLAG_DUAL_IO_READ | SFLAG_QUAD_IO_READ | SFLAG_QUAD_IO_WRITE}, ++ { "mx25l12855e", 0xc22618, 0, 64 * 1024, 256, SFLAG_SECT_4K | SFLAG_DUAL_READ}, ++ ++ /* Spansion -- single (large) sector size only, at least ++ * for the chips listed here (without boot sectors). ++ */ ++ { "s25sl004a", 0x010212, 0, 64 * 1024, 8, }, ++ { "s25sl008a", 0x010213, 0, 64 * 1024, 16, }, ++ { "s25sl016a", 0x010214, 0, 64 * 1024, 32, }, ++ { "s25sl032a", 0x010215, 0, 64 * 1024, 64, }, ++ { "s25sl064a", 0x010216, 0, 64 * 1024, 128, }, ++ { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, }, ++ { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, }, ++ { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, }, ++ { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, }, ++ ++ /* SST -- large erase sizes are "overlays", "sectors" are 4K */ ++ { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SFLAG_SECT_4K, }, ++ { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SFLAG_SECT_4K, }, ++ { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SFLAG_SECT_4K, }, ++ { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SFLAG_SECT_4K, }, ++ { "sst25wf512", 0xbf2501, 0, 64 * 1024, 1, SFLAG_SECT_4K, }, ++ { "sst25wf010", 0xbf2502, 0, 64 * 1024, 2, SFLAG_SECT_4K, }, ++ { "sst25wf020", 0xbf2503, 0, 64 * 1024, 4, SFLAG_SECT_4K, }, ++ { "sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SFLAG_SECT_4K, }, ++ ++ /* ST Microelectronics -- newer production may have feature updates */ ++ { "m25p05", 0x202010, 0, 32 * 1024, 2, }, ++ { "m25p10", 0x202011, 0, 32 * 1024, 4, }, ++ { "m25p20", 0x202012, 0, 64 * 1024, 4, }, ++ { "m25p40", 0x202013, 0, 64 * 1024, 8, }, ++ { "m25p80", 0, 0, 64 * 1024, 16, }, ++ { "m25p16", 0x202015, 0, 64 * 1024, 32, }, ++ { "m25p32", 0x202016, 0, 64 * 1024, 64, }, ++ { "m25p64", 0x202017, 0, 64 * 1024, 128, }, ++ { "m25p128", 0x202018, 0, 256 * 1024, 64, }, ++ ++ { "m45pe10", 0x204011, 0, 64 * 1024, 2, }, ++ { "m45pe80", 0x204014, 0, 64 * 1024, 16, }, ++ { "m45pe16", 0x204015, 0, 64 * 1024, 32, }, ++ ++ { "m25pe80", 0x208014, 0, 64 * 1024, 16, }, ++ { "m25pe16", 0x208015, 0, 64 * 1024, 32, SFLAG_SECT_4K, }, ++ ++ /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ ++ { "w25x10", 0xef3011, 0, 64 * 1024, 2, SFLAG_SECT_4K, }, ++ { "w25x20", 0xef3012, 0, 64 * 1024, 4, SFLAG_SECT_4K, }, ++ { "w25x40", 0xef3013, 0, 64 * 1024, 8, SFLAG_SECT_4K, }, ++ { "w25x80", 0xef3014, 0, 64 * 1024, 16, SFLAG_SECT_4K, }, ++ { "w25x16", 0xef3015, 0, 64 * 1024, 32, SFLAG_SECT_4K, }, ++ { "w25x32", 0xef3016, 0, 64 * 1024, 64, SFLAG_SECT_4K, }, ++ { "w25x64", 0xef3017, 0, 64 * 1024, 128, SFLAG_SECT_4K, }, ++ ++ { "w25q32", 0xef4016, 0, 32 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE, }, ++ { "w25q64", 0xef4017, 0, 64 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE, }, ++ { "w25q128", 0xef4018, 0, 64 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_QUAD_WRITE, }, ++ ++ /* GigaDevice -- w25x "blocks" are 64K, "sectors" are 4KiB */ ++ { "gd25q64", 0xc84017, 0, 64 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE , }, ++ { "gd25q128", 0xc84018, 0, 64 * 1024, 128, SFLAG_SECT_4K|SFLAG_COM_STATUS2|SFLAG_DUAL_READ|SFLAG_QUAD_READ|SFLAG_DUAL_IO_READ|SFLAG_QUAD_IO_READ|SFLAG_QUAD_WRITE ,}, ++}; ++ ++T_U32 ak_fha_erase_callback(T_U32 chip_num, T_U32 startpage) ++{ ++ struct erase_info einfo; ++ memset(&einfo, 0, sizeof(struct erase_info)); ++ einfo.addr = startpage * FLASH_PAGESIZE; ++ einfo.len = ak_mtd_info->erasesize; ++ einfo.mtd = ak_mtd_info; ++ ++ if(ak_spiflash_erase(ak_mtd_info, &einfo) == 0) ++ { ++ return FHA_SUCCESS; ++ } ++ else ++ { ++ printk("***erase failed\n"); ++ return FHA_FAIL; ++ } ++} ++ ++T_U32 ak_fha_write_callback(T_U32 chip_num, T_U32 page_num, const T_U8 *data, ++ T_U32 data_len, T_U8 *oob, T_U32 oob_len, T_U32 eDataType) ++{ ++ int ret; ++ ssize_t retlen; ++ loff_t to = page_num * FLASH_PAGESIZE; ++ ret = ak_spiflash_write(ak_mtd_info, to, data_len * FLASH_PAGESIZE, &retlen, data); ++ if(ret) ++ { ++ printk("%s:%d\n", __func__, __LINE__); ++ return FHA_FAIL; ++ } ++ return FHA_SUCCESS; ++} ++ ++T_U32 ak_fha_read_callback(T_U32 chip_num, T_U32 page_num, T_U8 *data, ++ T_U32 data_len, T_U8 *oob, T_U32 oob_len, T_U32 eDataType) ++{ ++ int ret; ++ ssize_t retlen; ++ loff_t from = page_num * FLASH_PAGESIZE; ++ ++ ret = ak_spiflash_read(ak_mtd_info, from, data_len * FLASH_PAGESIZE, &retlen, data); ++ if (ret) { ++ printk("%s:%d\n", __func__, __LINE__); ++ return FHA_FAIL; ++ } ++ ++ return FHA_SUCCESS; ++} ++ ++static T_VOID *fha_ram_alloc(T_U32 size) ++{ ++ return kmalloc(size, GFP_KERNEL); ++} ++ ++static T_VOID *fha_ram_free(void *point) ++{ ++ kfree(point); ++ return NULL; ++} ++ ++static T_S32 fha_print(T_pCSTR fmt, ...) ++{ ++ va_list args; ++ int r; ++ ++ va_start(args, fmt); ++ vprintk("FHA:",args); ++ r = vprintk(fmt, args); ++ va_end(args); ++ ++ return r; ++} ++ ++int ak_fha_init(void) ++{ ++ int ret = 0; ++ T_PFHA_INIT_INFO pInit_info = NULL; ++ T_PFHA_LIB_CALLBACK pCallback = NULL; ++ T_SPI_BURN_INIT_INFO spi_info; ++ ++ pInit_info = kmalloc(sizeof(T_FHA_INIT_INFO), GFP_KERNEL); ++ if (!pInit_info) { ++ printk("allocate memory for pInit_info failed\n"); ++ return -ENOMEM; ++ } ++ ++ pInit_info->nChipCnt = 0; ++ pInit_info->nBlockStep = 1; ++ pInit_info->eAKChip = FHA_CHIP_SET_TYPE; ++ pInit_info->ePlatform = PLAT_LINUX; ++ pInit_info->eMedium = MEDIUM_SPIFLASH; ++ pInit_info->eMode = MODE_UPDATE; ++ ++ pCallback = kmalloc(sizeof(T_FHA_LIB_CALLBACK), GFP_KERNEL); ++ if (!pCallback) { ++ printk("allocate memory for pCallback failed\n"); ++ ret = -ENOMEM; ++ goto err_out; ++ } ++ ++ pCallback->Erase = ak_fha_erase_callback; ++ pCallback->Write = (FHA_Write)ak_fha_write_callback; ++ pCallback->Read = (FHA_Read)ak_fha_read_callback; ++ pCallback->RamAlloc = fha_ram_alloc; ++ pCallback->RamFree = fha_ram_free; ++ pCallback->MemCmp = (FHA_MemCmp)memcmp; ++ pCallback->MemSet = (FHA_MemSet)memset; ++ pCallback->MemCpy = (FHA_MemCpy)memcpy; ++ pCallback->Printf = (FHA_Printf)fha_print; ++ ++ /* Yea, PagePerBlock=16 in producer_all, ++ * when SPI flash didn`t suport 4k sector erase, ++ * all will dead, Why can be forbear of this big BUG ? */ ++ spi_info.BinPageStart = 64; ++ spi_info.PageSize = 256; ++ spi_info.PagesPerBlock = ak_mtd_info->erasesize / 256; ++ ++ ret = FHA_mount(pInit_info, pCallback, &spi_info); ++ if (ret == FHA_FAIL) { ++ printk("FHA_mount failed\n"); ++ ret = -EINVAL; ++ } else { ++ ret = 0; ++ } ++ ++ kfree(pCallback); ++err_out: ++ kfree(pInit_info); ++ return ret; ++} ++ ++int ak_fha_init_for_update(int n) ++{ ++ int ret = 0; ++ T_PFHA_INIT_INFO pInit_info = NULL; ++ T_PFHA_LIB_CALLBACK pCallback = NULL; ++ T_SPI_BURN_INIT_INFO spi_info; ++ ++ pInit_info = kmalloc(sizeof(T_FHA_INIT_INFO), GFP_KERNEL); ++ if (!pInit_info) { ++ printk("allocate memory for pInit_info failed\n"); ++ return -ENOMEM; ++ } ++ ++ pInit_info->nChipCnt = 0; ++ pInit_info->nBlockStep = 1; ++ pInit_info->eAKChip = FHA_CHIP_SET_TYPE; ++ pInit_info->ePlatform = PLAT_LINUX; ++ pInit_info->eMedium = MEDIUM_SPIFLASH; ++ pInit_info->eMode = MODE_NEWBURN; ++ ++ pCallback = kmalloc(sizeof(T_FHA_LIB_CALLBACK), GFP_KERNEL); ++ if (!pCallback) { ++ printk("allocate memory for pCallback failed\n"); ++ ret = -ENOMEM; ++ goto err_out; ++ } ++ ++ pCallback->Erase = ak_fha_erase_callback; ++ pCallback->Write = (FHA_Write)ak_fha_write_callback; ++ pCallback->Read = (FHA_Read)ak_fha_read_callback; ++ pCallback->RamAlloc = fha_ram_alloc; ++ pCallback->RamFree = fha_ram_free; ++ pCallback->MemCmp = (FHA_MemCmp)memcmp; ++ pCallback->MemSet = (FHA_MemSet)memset; ++ pCallback->MemCpy = (FHA_MemCpy)memcpy; ++ pCallback->Printf = (FHA_Printf)fha_print; ++ ++ /* Yea, PagePerBlock=16 in producer_all, ++ * when SPI flash didn`t suport 4k sector erase, ++ * all will dead, Why can be forbear of this big BUG ? */ ++ spi_info.BinPageStart = 64; ++ spi_info.PageSize = 256; ++ spi_info.PagesPerBlock = ak_mtd_info->erasesize / 256; ++ ++ ret = FHA_burn_init(pInit_info, pCallback, &spi_info); ++ if (ret == FHA_FAIL) { ++ printk("FHA_mount failed\n"); ++ ret = -EINVAL; ++ } else { ++ ret = 0; ++ } ++ ++ kfree(pCallback); ++err_out: ++ kfree(pInit_info); ++ return ret; ++} ++ ++static int ak_mount_partitions(struct spi_device *spi) ++{ ++ int i, ret; ++ unsigned long nr_parts; ++ unsigned char *buf; ++ struct partitions *parts = NULL; ++ struct mtd_partition *mtd_part; ++ struct mtd_part_parser_data ppdata; ++ ++ ret = ak_fha_init(); ++ if (ret) { ++ printk("Init FHA lib failed\n"); ++ goto err_out; ++ } ++ ++ buf = kzalloc(FLASH_PAGESIZE, GFP_KERNEL); ++ if (!buf) { ++ printk("allocate memory for page buffer failed\n"); ++ ret = -ENOMEM; ++ goto err_out; ++ } ++ ++ ret = FHA_get_fs_part(buf, FLASH_PAGESIZE); ++ if (ret == FHA_FAIL) { ++ printk("get partition info failed\n"); ++ ret = !ret; ++ goto no_parts; ++ } ++ ++ nr_parts = *(unsigned long *)buf; ++ /* if no partiton to mount, the buf will be all 0xFF but not constant. ++ * So, it is not safe here. */ ++ printk("nr_parts=0x%lx\n", nr_parts); ++ if (nr_parts <= 0 || nr_parts > 5) { ++ printk("partition count invalid\n"); ++ ret = -EINVAL; ++ goto no_parts; ++ } ++ ++ mtd_part = kzalloc(sizeof(struct mtd_partition) * nr_parts, GFP_KERNEL); ++ if (!mtd_part) { ++ printk("allocate memory for mtd_partition failed\n"); ++ ret = -ENOMEM; ++ goto no_parts; ++ } ++ ++ parts = (struct partitions *)(&buf[sizeof(unsigned long)]); ++ for (i = 0; i < nr_parts; i++) { ++ mtd_part[i].name = kzalloc(MTD_PART_NAME_LEN, GFP_KERNEL); ++ memcpy(mtd_part[i].name, parts[i].name, MTD_PART_NAME_LEN); ++ mtd_part[i].size = parts[i].size; ++ mtd_part[i].offset = parts[i].offset; ++ mtd_part[i].mask_flags = parts[i].mask_flags; ++ printk("mtd_part[%d]:\nname = %s\nsize = 0x%llx\noffset = 0x%llx\nmask_flags = 0x%x\n\n", ++ i, mtd_part[i].name, mtd_part[i].size, mtd_part[i].offset, mtd_part[i].mask_flags); ++ } ++ ppdata.of_node = spi->dev.of_node; ++ ++ ret = mtd_device_parse_register(ak_mtd_info, NULL, &ppdata, ++ (const struct mtd_partition *)mtd_part, nr_parts); ++ if (ret) { ++ printk("add mtd partition failed\n"); ++ goto no_parts; ++ } ++ ++no_parts: ++ kfree(buf); ++err_out: ++ return ret; ++} ++ ++/** ++* @brief jedec probe ++* ++* Read the device ID and identify that it was supported or not. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] mtd spi device handle. ++* @return int return the device info. ++*/ ++static struct flash_info *__devinit jedec_probe(struct spi_device *spi) ++{ ++ int tmp; ++ u8 code = OPCODE_RDID; ++ u8 id[5]; ++ u32 jedec; ++ u16 ext_jedec = 0; ++ struct flash_info *info; ++ ++ /* JEDEC also defines an optional "extended device information" ++ * string for after vendor-specific data, after the three bytes ++ * we use here. Supporting some chips might require using it. ++ */ ++ tmp = spi_write_then_read(spi, &code, 1, id, 3); ++ if (tmp < 0) { ++ DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", ++ dev_name(&spi->dev), tmp); ++ return NULL; ++ } ++ jedec = id[0]; ++ jedec = jedec << 8; ++ jedec |= id[1]; ++ jedec = jedec << 8; ++ jedec |= id[2]; ++ ++ printk("akspi flash ID: 0x%08x\n", jedec); ++ ++ //ext_jedec = id[3] << 8 | id[4]; ++ for (tmp = 0, info = ak_spiflash_supportlist; ++ tmp < ARRAY_SIZE(ak_spiflash_supportlist); ++ tmp++, info++) { ++ if (info->jedec_id == jedec) { ++ if (info->ext_id != 0 && info->ext_id != ext_jedec) ++ continue; ++ return info; ++ } ++ } ++ dev_err(&spi->dev, "jedec_probe() unrecognized JEDEC id %06x\n", jedec); ++ return NULL; ++} ++ ++static int ak_spiflash_init_stat_reg(struct ak_spiflash *flash) ++{ ++ int i; ++ struct flash_status_reg *sr; ++ struct flash_info *info = &flash->info; ++ ++ for(i=0, sr=status_reg_list; ijedec_id == info->jedec_id) { ++ if (info->ext_id != 0 && info->ext_id != sr->ext_id) ++ continue; ++ flash->stat_reg = *sr; ++ return 0; ++ } ++ } ++ ++ flash->stat_reg = status_reg_list[i-1]; ++ return 0; ++} ++ ++ ++ ++/** ++* @brief spi flash probe ++* ++* Initial the spi flash device driver to kernel. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] mtd spi device handle. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a non-zero error code if failed ++*/ ++static int __devinit ak_spiflash_probe(struct spi_device *spi) ++{ ++ struct flash_platform_data *data; ++ struct ak_spiflash *flash; ++ struct flash_info *info; ++ unsigned i, ret = 0; ++ ++ printk("ak spiflash probe enter.\n"); ++ /* Platform data helps sort out which chip type we have, as ++ * well as how this board partitions it. If we don't have ++ * a chip ID, try the JEDEC id commands; they'll work for most ++ * newer chips, even if we don't recognize the particular chip. ++ */ ++ data = spi->dev.platform_data; ++ if (data && data->type) { ++ for (i = 0, info = ak_spiflash_supportlist; ++ i < ARRAY_SIZE(ak_spiflash_supportlist); ++ i++, info++) { ++ if (strcmp(data->type, info->name) == 0) ++ break; ++ } ++ ++ /* unrecognized chip? */ ++ if (i == ARRAY_SIZE(ak_spiflash_supportlist)) { ++ DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n", ++ dev_name(&spi->dev), data->type); ++ info = NULL; ++ ++ /* recognized; is that chip really what's there? */ ++ } else if (info->jedec_id) { ++ struct flash_info *chip = jedec_probe(spi); ++ ++ if (!chip || chip != info) { ++ dev_warn(&spi->dev, "found %s, expected %s\n", ++ chip ? chip->name : "UNKNOWN", ++ info->name); ++ info = NULL; ++ } ++ } ++ } else ++ info = jedec_probe(spi); ++ ++ if (!info) ++ return -ENODEV; ++ ++ flash = kzalloc(sizeof *flash, GFP_KERNEL); ++ if (!flash) ++ return -ENOMEM; ++ ++#ifdef SPIFLASH_USE_MTD_BLOCK_LAYER ++ /*pre-allocation buffer use for spi flash data transfer.*/ ++ flash->buf = kzalloc(FLASH_BUF_SIZE, GFP_KERNEL); ++ if (!flash->buf) { ++ printk("Allocate buf for spi page failed\n"); ++ kfree(flash); ++ return -ENOMEM; ++ } ++#endif ++ ++ ak_mtd_info = &flash->mtd; ++ ++ flash->spi = spi; ++ flash->info = *info; ++ mutex_init(&flash->lock); ++ dev_set_drvdata(&spi->dev, flash); ++ ++ flash->bus_width = data->bus_width; ++ ++ /* ++ * Atmel serial flash tend to power up ++ * with the software protection bits set ++ */ ++ ++ if (info->jedec_id >> 16 == 0x1f) { ++ write_enable(flash); ++ write_sr(flash, 0); ++ } ++ ++ if (data && data->name) ++ flash->mtd.name = data->name; ++ else ++ flash->mtd.name = dev_name(&spi->dev); ++ ++ flash->mtd.type = MTD_NORFLASH; ++ flash->mtd.writesize = FLASH_PAGESIZE; ++ flash->mtd.flags = MTD_WRITEABLE; ++ flash->mtd.size = info->sector_size * info->n_sectors; ++ flash->mtd._erase = ak_spiflash_erase; ++ flash->mtd._read = ak_spiflash_read; ++ flash->mtd.get_device_id = ak_spiflash_get_devid; ++ printk("%s, info->sector_size = %d, info->n_sectors = %d\n", info->name, info->sector_size, info->n_sectors); ++ //printk("flash->mtd.size = %x, %ld\n", flash->mtd.size, flash->mtd.size); ++ ++ /* sst flash chips use AAI word program */ ++ if (info->jedec_id >> 16 == 0xbf) ++ flash->mtd._write = sst_write; ++ else ++ flash->mtd._write = ak_spiflash_write; ++ ++ /* prefer "small sector" erase if possible */ ++ if (info->flags & SFLAG_SECT_4K) { ++ flash->erase_opcode = OPCODE_BE_4K; ++ flash->mtd.erasesize = 4096; ++ } else { ++ flash->erase_opcode = OPCODE_SE; ++ flash->mtd.erasesize = info->sector_size; ++ } ++ ++ ak_spiflash_init_stat_reg(flash); ++ ak_spiflash_cfg_quad_mode(flash); ++ init_spiflash_rw_info(flash); ++ ++ flash->mtd.dev.parent = &spi->dev; ++ ++ dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name, ++ (long long)flash->mtd.size >> 10); ++ ++ DEBUG(MTD_DEBUG_LEVEL0, ++ "mtd .name = %s, .size = 0x%llx (%lldMiB) " ++ ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", ++ flash->mtd.name, ++ (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), ++ flash->mtd.erasesize, flash->mtd.erasesize / 1024, ++ flash->mtd.numeraseregions); ++ ++ if (flash->mtd.numeraseregions) ++ for (i = 0; i < flash->mtd.numeraseregions; i++) ++ DEBUG(MTD_DEBUG_LEVEL0, ++ "mtd.eraseregions[%d] = { .offset = 0x%llx, " ++ ".erasesize = 0x%.8x (%uKiB), " ++ ".numblocks = %d }\n", ++ i, (long long)flash->mtd.eraseregions[i].offset, ++ flash->mtd.eraseregions[i].erasesize, ++ flash->mtd.eraseregions[i].erasesize / 1024, ++ flash->mtd.eraseregions[i].numblocks); ++ ++ ++ /* partitions should match sector boundaries; and it may be good to ++ * use readonly partitions for writeprotected sectors (BP2..BP0). ++ */ ++ ret = mtd_device_parse_register(ak_mtd_info, NULL, NULL, NULL, 0); ++ if (ret) { ++ printk("Add root MTD device failed\n"); ++ kfree(flash->buf); ++ kfree(flash); ++ return -EINVAL; ++ } ++ ret = ak_mount_partitions(spi); ++ if (ret) ++ printk("Add MTD partitions failed\n"); ++ ++ printk("Init AK SPI Flash finish.\n"); ++ ++ return 0; ++} ++ ++/** ++* @brief spi flash remove ++* ++* Remove the spi flash device driver from kernel. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] mtd spi device handle. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a non-zero error code if failed ++*/ ++static int __devexit ak_spiflash_remove(struct spi_device *spi) ++{ ++ struct ak_spiflash *flash = dev_get_drvdata(&spi->dev); ++ int status; ++ ++ status = mtd_device_unregister(&flash->mtd); ++ ++ if (status == 0) { ++ kfree(flash->buf); ++ kfree(flash); ++ } ++ return 0; ++} ++ ++ ++static struct spi_driver ak_spiflash_driver = { ++ .driver = { ++ .name = "ak-spiflash", ++ .bus = &spi_bus_type, ++ .owner = THIS_MODULE, ++ }, ++ .probe = ak_spiflash_probe, ++ .remove = __devexit_p(ak_spiflash_remove), ++ ++ /* REVISIT: many of these chips have deep power-down modes, which ++ * should clearly be entered on suspend() to minimize power use. ++ * And also when they're otherwise idle... ++ */ ++}; ++ ++/** ++* @brief spi flash device init ++* ++* Moudle initial. ++* @author SheShaohua ++* @date 2012-03-20 ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a non-zero error code if failed ++*/ ++static int __init ak_spiflash_init(void) ++{ ++ printk("Start to init Anyka SPI Flash...\n"); ++ return spi_register_driver(&ak_spiflash_driver); ++} ++ ++ ++/** ++* @brief spi flash device exit ++* ++* Moudle exit. ++* @author SheShaohua ++* @date 2012-03-20 ++* @return None ++*/ ++static void __exit ak_spiflash_exit(void) ++{ ++ spi_unregister_driver(&ak_spiflash_driver); ++} ++ ++ ++module_init(ak_spiflash_init); ++module_exit(ak_spiflash_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("She Shaohua"); ++MODULE_DESCRIPTION("MTD SPI driver for Anyka spiflash chips"); +diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c +index f2f482be..4efca411 100644 +--- a/drivers/mtd/mtdchar.c ++++ b/drivers/mtd/mtdchar.c +@@ -35,11 +35,50 @@ + #include + #include + #include +- ++#include + #include + + static DEFINE_MUTEX(mtd_mutex); + ++/* Add ioctl data structure for spi flash burn tool. ++*/ ++#define FLASH_PAGESIZE 256 ++#define FLASH_ERASESIZE 4096 ++ ++#define AK_SPIFLASH_PHY_ERASE 0x80 ++#define AK_SPIFLASH_PHY_READ 0x81 ++#define AK_SPIFLASH_PHY_WRITE 0x82 ++#define AK_SPIFLASH_GET_CHIP_ID 0x83 ++#define AK_SPIFLASH_READ_BYTES 0x84 ++ ++#define LEFT_ALIGN(a,b) ((a/b)*b) ++#define RIGHT_ALIGN(a, b) (((a+b-1)/b)*b) ++ ++struct rw_para ++{ ++ T_U32 chip_num; ++ T_U32 page_num; ++ T_U8 *data; ++ T_U32 data_len; ++ T_U8 *oob; //not used in spi flash, only reserve for nand. ++ T_U32 oob_len; //not used in spi flash, only reserve for nand. ++ T_U32 eDataType; ++}; ++ ++/* erase block info */ ++struct erase_para ++{ ++ T_U32 chip_num; //which chip the block is in ++ T_U32 startpage; // the block's first page number ++}; ++ ++struct get_id_para ++{ ++ T_U32 chip_num; ++ T_U32 *spiflash_id; ++}; ++ ++ + /* + * Data structure to hold the pointer to the mtd device as well + * as mode information of various use cases. +@@ -620,6 +659,236 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, + return ret; + } + ++ ++/** ++* @brief MTD char earse ++* ++* Erase a sector specified by input paramerter. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] mtd mtd info handle. ++* @param[in] arg erase info. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a non-zero error code if failed ++*/ ++static int erase_SPIFlash_page(struct mtd_info *mtd, unsigned long arg) ++{ ++ struct erase_info instr_info; ++ struct erase_para e_para; ++ unsigned long len; ++ int ret; ++ ++ ret = copy_from_user(&e_para, (struct erase_para *)arg, sizeof(struct erase_para)); ++ if(ret) ++ return -EFAULT; ++ //printk("Erase sector: %ld \n", e_para.startpage/16); ++ ++ memset(&instr_info, 0, sizeof(struct erase_info)); ++ instr_info.addr = e_para.startpage * FLASH_PAGESIZE; ++ instr_info.len = len = FLASH_ERASESIZE; ++ instr_info.mtd = mtd; ++ ++ //printk("Addr: %lld, len: %lld, start to erase...\n", instr_info.addr, instr_info.len); ++ ++ mtd_erase(mtd, &instr_info); ++ ++ return 0; ++} ++ ++ ++static int read_spiflash_bytes(struct mtd_info *mtd, unsigned long arg) ++{ ++ struct rw_para r_para; ++ unsigned long baseaddr, len, retlen; ++ unsigned char *buf; ++ int ret; ++ ++ ret = copy_from_user(&r_para, (struct rw_para *)arg, sizeof(struct rw_para)); ++ if(ret) ++ return -EFAULT; ++ ++ baseaddr = r_para.page_num; ++ len = r_para.data_len; ++ buf = (unsigned char *)kmalloc(len, GFP_KERNEL); ++ if( buf == NULL ) ++ { ++ printk(KERN_ERR "%s, kmalloc buf fail!\n", __func__); ++ return -1; ++ } ++ ++ mtd_read(mtd, baseaddr, len, (size_t*)&retlen, buf); ++ ++ ret = copy_to_user(((struct rw_para *)arg)->data, buf, retlen); ++ if(ret) { ++ ret = -EFAULT; ++ goto rd_fail; ++ } ++ ++rd_fail: ++ kfree(buf); ++ ++ return 0; ++} ++ ++ ++/** ++* @brief MTD char read ++* ++* Read data from spi flash. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] mtd mtd info handle. ++* @param[in] arg read address and length info. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a non-zero error code if failed ++*/ ++static int read_SPIFlash_page(struct mtd_info *mtd, unsigned long arg) ++{ ++ struct rw_para r_para; ++ unsigned long baseaddr, addr, len, retlen, pageCount; ++ unsigned char *buf; ++ int ret; ++ ++ ret = copy_from_user(&r_para, (struct rw_para *)arg, sizeof(struct rw_para)); ++ if(ret) ++ return -EFAULT; ++ ++ //printk("Read page: %ld, len: %ld\n", r_para.page_num, r_para.data_len); ++ ++ baseaddr = r_para.page_num * FLASH_PAGESIZE; ++ pageCount = r_para.data_len; ++ len = r_para.data_len * FLASH_PAGESIZE; ++ buf = (unsigned char *)kmalloc(len, GFP_KERNEL); ++ if( buf == NULL ) ++ { ++ printk(KERN_ERR "%s, kmalloc buf fail!\n", __func__); ++ return -1; ++ } ++ ++ addr = baseaddr; ++ mtd_read(mtd, addr, len, (size_t*)&retlen, buf); ++ ++ ret = copy_to_user(((struct rw_para *)arg)->data, buf, retlen); ++ if(ret) { ++ ret = -EFAULT; ++ goto rd_fail; ++ } ++ ++rd_fail: ++ kfree(buf); ++ ++ return 0; ++} ++ ++ ++/** ++* @brief MTD char write ++* ++* write data to spi flash. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] mtd mtd info handle. ++* @param[in] arg write related info. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a non-zero error code if failed ++*/ ++static int write_SPIFlash_page(struct mtd_info *mtd, unsigned long arg) ++{ ++ struct rw_para w_para; ++ unsigned long baseAddr, addr, total_len, len, remainCount, retlen, pageCount; ++ unsigned char *buff, *pbuf; ++ u32 i; ++ int ret; ++ ++ ret = copy_from_user(&w_para, (struct rw_para *)arg, sizeof(struct rw_para)); ++ if(ret) ++ return -EFAULT; ++ ++// printk("Write page: %ld ~ %ld\n", w_para.page_num, (w_para.page_num + w_para.data_len - 1) ); ++ ++ baseAddr = w_para.page_num * FLASH_PAGESIZE; ++ pageCount = w_para.data_len; ++ total_len = w_para.data_len * FLASH_PAGESIZE; ++ buff = (unsigned char *)kmalloc(total_len, GFP_KERNEL); ++ if( buff == NULL ) ++ { ++ printk(KERN_ERR "%s, kmalloc buf fail!\n", __func__); ++ return -1; ++ } ++ ++ ret = copy_from_user(buff, w_para.data, total_len); ++ if(ret) { ++ ret = -EFAULT; ++ goto wr_fail; ++ } ++ ++ ++ remainCount = total_len; ++ addr = baseAddr; ++ pbuf = buff; ++ for(i=0; i FLASH_PAGESIZE ) ++ { ++ len = FLASH_PAGESIZE; ++ } ++ else ++ { ++ len = remainCount; ++ } ++ ++ addr = baseAddr + (i * FLASH_PAGESIZE); ++ pbuf = buff + (i * FLASH_PAGESIZE); ++ mtd_write(mtd, addr, len, (size_t*)&retlen, pbuf); ++ remainCount -= FLASH_PAGESIZE; ++ } ++ ++wr_fail: ++ kfree(buff); ++ ++ return 0; ++} ++ ++ ++/** ++* @brief Get Device ID ++* ++* Get Device ID of spi flash. ++* @author SheShaohua ++* @date 2012-03-20 ++* @param[in] mtd mtd info handle. ++* @param[out] arg return device ID. ++* @return int return write success or failed ++* @retval returns zero on success ++* @retval return a non-zero error code if failed ++*/ ++static int get_SPIFlash_ID(struct mtd_info *mtd, unsigned long arg) ++{ ++ int ret; ++ u32 spi_id = 0; ++ ++ if(mtd->get_device_id) ++ { ++ spi_id = mtd->get_device_id(mtd); ++ if( spi_id == AK_FALSE) ++ { ++ printk(KERN_ERR "%s, get_device_id fail!\n", __func__); ++ return -1; ++ } ++ } ++ ++ ret = copy_to_user(((struct get_id_para *)arg)->spiflash_id, &spi_id, sizeof(u32)); ++ if(ret) ++ return -EFAULT; ++ ++ printk("get_SPIFlash_ID: 0x%08x\n", spi_id); ++ return 0; ++} ++ ++ + static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) + { + struct mtd_file_info *mfi = file->private_data; +@@ -641,7 +910,51 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) + return -EFAULT; + } + +- switch (cmd) { ++ switch (cmd) { ++ case AK_SPIFLASH_PHY_READ: ++ ret = read_SPIFlash_page( mtd, arg ); ++ if( ret != 0 ) ++ { ++ printk(KERN_ERR "AK_SPIFLASH_PHY_READ failed:ret=%d\n", ret); ++ return -EFAULT; ++ } ++ break; ++ ++ case AK_SPIFLASH_PHY_WRITE: ++ ret = write_SPIFlash_page( mtd, arg ); ++ if( ret != 0 ) ++ { ++ printk(KERN_ERR "AK_SPIFLASH_PHY_WRITE failed:ret=%d\n", ret); ++ return -EFAULT; ++ } ++ break; ++ ++ case AK_SPIFLASH_PHY_ERASE: ++ ret = erase_SPIFlash_page( mtd, arg ); ++ if( ret != 0 ) ++ { ++ printk(KERN_ERR "AK_SPIFLASH_PHY_ERASE failed:ret=%d\n", ret); ++ return -EFAULT; ++ } ++ break; ++ ++ case AK_SPIFLASH_GET_CHIP_ID: ++ ret = get_SPIFlash_ID( mtd, arg ); ++ if( ret != 0 ) ++ { ++ printk(KERN_ERR "AK_SPIFLASH_GET_CHIP_ID failed:ret=%d\n", ret); ++ return -EFAULT; ++ } ++ break; ++ case AK_SPIFLASH_READ_BYTES: ++ ret = read_spiflash_bytes(mtd, arg); ++ if( ret != 0 ) ++ { ++ printk(KERN_ERR "AK_SPIFLASH_READ_BYTES failed:ret=%d\n", ret); ++ return -EFAULT; ++ } ++ break; ++ + case MEMGETREGIONCOUNT: + if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) + return -EFAULT; +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index 7d17ceca..884f99ca 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -1,3 +1,10 @@ ++config MTD_NAND_IDS ++ tristate "Include chip ids for known NAND devices." ++ depends on MTD ++ help ++ Useful for NAND drivers that do not use the NAND subsystem but ++ still like to take advantage of the known chip information. ++ + config MTD_NAND_ECC + tristate + +@@ -115,9 +122,6 @@ config MTD_NAND_OMAP2 + Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4 + platforms. + +-config MTD_NAND_IDS +- tristate +- + config MTD_NAND_RICOH + tristate "Ricoh xD card reader" + default n +diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig +index c63a64cb..a8d24dde 100644 +--- a/drivers/net/ethernet/Kconfig ++++ b/drivers/net/ethernet/Kconfig +@@ -176,5 +176,5 @@ source "drivers/net/ethernet/tundra/Kconfig" + source "drivers/net/ethernet/via/Kconfig" + source "drivers/net/ethernet/xilinx/Kconfig" + source "drivers/net/ethernet/xircom/Kconfig" +- ++source "drivers/net/ethernet/ak-ethernet/Kconfig" + endif # ETHERNET +diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile +index 9676a510..2c46e882 100644 +--- a/drivers/net/ethernet/Makefile ++++ b/drivers/net/ethernet/Makefile +@@ -75,3 +75,4 @@ obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/ + obj-$(CONFIG_NET_VENDOR_VIA) += via/ + obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/ + obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/ ++obj-$(CONFIG_AK_ETHERNET) += ak-ethernet/ +diff --git a/drivers/net/ethernet/ak-ethernet/Ethernethw.h b/drivers/net/ethernet/ak-ethernet/Ethernethw.h +new file mode 100755 +index 00000000..2fa00197 +--- /dev/null ++++ b/drivers/net/ethernet/ak-ethernet/Ethernethw.h +@@ -0,0 +1,731 @@ ++#ifndef _MAC_REG_DEFINE_H_ ++#define _MAC_REG_DEFINE_H_ ++#define MAC_REG_BASE 0x60000000 ++ ++#define REG_MASTER_CTRL ( 0x1400) /* WORD reg */ ++ #define MASTER_CTRL_MAC_SOFT_RST_OFF 0 /* hw sc */ ++ #define MASTER_CTRL_MAC_SOFT_RST_BITS 1 ++ #define MASTER_CTRL_PCIE_SOFT_RST_OFF 1 /* hw sc */ ++ #define MASTER_CTRL_PCIE_SOFT_RST_BITS 1 ++ #define MASTER_CTRL_PCIE_TEST_MOD_OFF 2 ++ #define MASTER_CTRL_PCIE_TEST_MOD_BITS 2 ++ #define MASTER_CTRL_BERT_START_OFF 4 ++ #define MASTER_CTRL_BERT_START_BITS 1 ++ #define MASTER_CTRL_OOB_DIS_OFF 6 ++ #define MASTER_CTRL_OOB_DIS_BITS 1 ++ #define MASTER_CTRL_SA_TIMER_EN_OFF 7 ++ #define MASTER_CTRL_SA_TIMER_EN_BITS 1 ++ #define MASTER_CTRL_MANUAL_TIME_EN_OFF 8 ++ #define MASTER_CTRL_MANUAL_TIMER_EN_BITS 1 ++ #define MASTER_CTRL_MANUAL_INT_OFF 9 ++ #define MASTER_CTRL_MANUAL_INT_BITS 1 ++ #define MASTER_CTRL_IRQ_MODRT_EN_OFF 10 /* tx if 2 timer */ ++ #define MASTER_CTRL_IRQ_MODRT_EN_BITS 1 ++ #define MASTER_CTRL_IRQ_MODRT_RXEN_OFF 11 /* rx if 2 timer */ ++ #define MASTER_CTRL_IRQ_MODRT_RXEN_BITS 1 ++ #define MASTER_CTRL_PCLK_SEL_DIS_OFF 12 /* ps may set it */ ++ #define MASTER_CTRL_PCLK_SEL_DIS_BITS 1 ++ #define MASTER_CTRL_CLKSW_MODE_OFF 13 ++ #define MASTER_CTRL_CLKSW_MODE_BITS 1 ++ #define MASTER_CTRL_INT_RCLR_EN_OFF 14 ++ #define MASTER_CTRL_INT_CLCR_EN_BITS 1 ++ #define MASTER_CTRL_OTP_SEL_OFF 31 ++ #define MASTER_CTRL_OTP_SEL_BITS 1 ++ ++#define REG_DEV_REV_NUM (0x1402) /* BYTE reg */ ++#define REG_DEV_ID_NUM (0x1403) /* BYTE reg */ ++ ++#define REG_MANUAL_TIMER_INIT (0x1404) /* DWORD reg */ ++ ++#define REG_IRQ_MODRT_INIT (0x1408) /* WORD reg */ ++#define REG_IRQ_MODRT_RX_INIT (0x140A) /* WORD reg */ ++ ++#define REG_PHY_CTRL (0x140C) /* DWORD reg */ ++ #define PHY_CTRL_EXIT_RST_OFF 0 ++ #define PHY_CTRL_EXIT_RST_BITS 1 ++ #define PHY_CTRL_RTL_MOD_OFF 1 ++ #define PHY_CTRL_RTL_MOD_BITS 1 ++ #define PHY_CTRL_LED_MOD_OFF 2 ++ #define PHY_CTRL_LED_MOD_BITS 1 ++ #define PHY_CTRL_ANEG_NOW_OFF 3 ++ #define PHY_CTRL_ANEG_NOW_BITS 1 ++ #define PHY_CTRL_REV_ANEG_OFF 4 ++ #define PHY_CTRL_REV_ANEG_BITS 1 ++ #define PHY_CTRL_GATE25M_EN_OFF 5 ++ #define PHY_CTRL_GATE25M_EN_BITS 1 ++ #define PHY_CTRL_LPW_EXIT_OFF 6 ++ #define PHY_CTRL_LPW_EXIT_BITS 1 ++ #define PHY_CTRL_PHY_IDDQ_OFF 7 ++ #define PHY_CTRL_PHY_IDDQ_BITS 1 ++ #define PHY_CTRL_PHY_IDDQ_DIS_OFF 8 ++ #define PHY_CTRL_PHY_IDDQ_DIS_BITS 1 ++ #define PHY_CTRL_GIGA_DIS_OFF 9 ++ #define PHY_CTRL_GIGA_DIS_BITS 1 ++ #define PHY_CTRL_HIB_EN_HW_OFF 10 ++ #define PHY_CTRL_HIB_EN_HW_BITS 1 ++ #define PHY_CTRL_HIB_PULSE_HW_OFF 11 ++ #define PHY_CTRL_HIB_PULSE_HW_BITS 1 ++ #define PHY_CTRL_SEL_ANA_RST_OFF 12 ++ #define PHY_CTRL_SEL_ANA_RST_BITS 1 ++ #define PHY_CTRL_PHY_PLL_ON_OFF 13 ++ #define PHY_CTRL_PHY_PLL_ON_BITS 1 ++ #define PHY_CTRL_POWERDOWN_HW_OFF 14 ++ #define PHY_CTRL_POWERDOWN_HW_BITS 1 ++ #define PHY_CTRL_PHY_PLL_BYPASS_OFF 15 ++ #define PHY_CTRL_PHY_PLL_BYPASS_BITS 1 ++ ++#ifdef EN_HIB ++ #define PHY_CTRL_DEFAULT (\ ++ FLAG(PHY_CTRL_SEL_ANA_RST_OFF) |\ ++ FLAG(PHY_CTRL_HIB_EN_HW_OFF) |\ ++ FLAG(PHY_CTRL_HIB_PULSE_HW_OFF) ) ++#else ++ #define PHY_CTRL_DEFAULT (\ ++ FLAG(PHY_CTRL_SEL_ANA_RST_OFF) |\ ++ FLAG(PHY_CTRL_HIB_PULSE_HW_OFF) ) ++#endif/*EN_HIB*/ ++ ++ #define PHY_CTRL_POWER_SAVING (\ ++ FLAG(PHY_CTRL_SEL_ANA_RST_OFF) |\ ++ FLAG(PHY_CTRL_HIB_EN_HW_OFF) |\ ++ FLAG(PHY_CTRL_HIB_PULSE_HW_OFF) |\ ++ FLAG(PHY_CTRL_POWERDOWN_HW_OFF) |\ ++ FLAG(PHY_CTRL_PHY_IDDQ_OFF) ) ++ ++ ++#define REG_IDLE_STATUS (0x1410) /* BYTE reg */ ++ #define IDLE_STATUS_RXMAC_OFF 0 ++ #define IDLE_STATUS_RXMAC_BITS 1 ++ #define IDLE_STATUS_TXMAC_OFF 1 ++ #define IDLE_STATUS_TXMAC_BITS 1 ++ #define IDLE_STATUS_RXQ_OFF 2 ++ #define IDLE_STATUS_RXQ_BITS 1 ++ #define IDLE_STATUS_TXQ_OFF 3 ++ #define IDLE_STATUS_TXQ_BITS 1 ++ ++#define REG_MDIO_CTRL (0x1414) /* DWORD reg */ ++ #define MDIO_CTRL_DATA(val) (val&0xFFFF) ++ #define MDIO_CTRL_REG_ADDR(val) ((val&0x1f) << 16) ++ #define MDIO_CTRL_REG_ADDR_BITS 5 ++ #define MDIO_CTRL_WRITE (0<<21) ++ #define MDIO_CTRL_READ (1<<21)/*read:0, write:1*/ ++ #define MDIO_CTRL_RW_OFF 21 ++ #define MDIO_CTRL_RW_BITS 1 ++ #define MDIO_CTRL_SUP_PREAMBLE (1<<22) ++ #define MDIO_CTRL_SUP_PREAMBLE_BITS 1 ++ #define MDIO_CTRL_START (1<<23) ++ #define MDIO_CTRL_START_BITS 1 /* sc */ ++ #define MDIO_CTRL_CLK_SEL_OFF 24 ++ #define MDIO_CTRL_CLK_SEL_BITS 3 ++ #define MDIO_CTRL_BUSY_OFF 27 ++ #define MDIO_CTRL_BUSY_BITS 1 ++ #define MDIO_CTRL_AP_EN_OFF 28 ++ #define MDIO_CTRL_AP_EN_BITS 1 ++ #define MDIO_CLK_25_4 0 ++ #define MDIO_CLK_25_6 2 ++ #define MDIO_CLK_25_8 3 ++ #define MDIO_CLK_25_10 4 ++ #define MDIO_CLK_25_14 5 ++ #define MDIO_CLK_25_20 6 ++ #define MDIO_CLK_25_28 7 ++ #define MDIO_MAX_AC_TIMER 100 /* 1 ms */ ++ #define MDIO_CTRL_POST_READ_OFF 29 ++ #define MDIO_CTRL_POST_READ_BITS 1 ++ #define MDIO_CTRL_MODE_OFF 30 ++ #define MDIO_CTRL_MODE_BITS 1 ++ #define MDIO_CTRL_MODE_OLD 0 ++ #define MDIO_CTRL_MODE_EXTENSION 1 ++ ++ ++#define REG_PHY_STATUS (0x1418) /* DWORD reg */ ++ #define PHY_STATUS_PHY_STATUS_OFF 0 ++ #define PHY_STATUS_PHY_STATUS_BITS 16 ++ #define PHY_STATUS_OE_PWSTRP_OFF 16 ++ #define PHY_STATUS_OE_PWSTRP_BITS 11 ++ #define PHY_STATUS_LPW_STATUS_OFF 31 ++ #define PHY_STATUS_LPW_STATUS_BITS 1 ++ ++#define REG_BIST0_CTRL (0x141C) /* DWORD reg */ ++ #define BIST0_CTRL_NOW_OFF 0 ++ #define BIST0_CTRL_NOW_BITS 1 ++ #define BIST0_CTRL_SRAM_FAIL_OFF 1 ++ #define BIST0_CTRL_SRAM_FAIL_BITS 1 ++ #define BIST0_CTRL_FUSE_FLAG_OFF 2 ++ #define BIST0_CTRL_FUSE_FLAG_BITS 1 ++ #define BIST0_CTRL_FUSE_PAT_OFF 4 ++ #define BIST0_CTRL_FUSE_PAT_BITS 3 ++ #define BIST0_CTRL_FUSE_STEP_OFF 8 ++ #define BIST0_CTRL_FUSE_SETP_BITS 4 ++ #define BIST0_CTRL_FUSE_ROW_OFF 12 ++ #define BIST0_CTRL_FUSE_ROW_BITS 12 ++ #define BIST0_CTRL_FUSE_COL_OFF 24 ++ #define BIST0_CTRL_FUSE_COL_BITS 6 ++ ++#define REG_BIST1_CTRL (0x1420) /* DWORD reg */ ++ #define BIST1_CTRL_NOW_OFF 0 ++ #define BIST1_CTRL_NOW_BITS 1 ++ #define BIST1_CTRL_SRAM_FAIL_OFF 1 ++ #define BIST1_CTRL_SRAM_FAIL_BITS 1 ++ #define BIST1_CTRL_FUSE_FLAG_OFF 2 ++ #define BIST1_CTRL_FUSE_FLAG_BITS 1 ++ #define BIST1_CTRL_FUSE_PAT_OFF 4 ++ #define BIST1_CTRL_FUSE_PAT_BITS 3 ++ #define BIST1_CTRL_FUSE_STEP_OFF 8 ++ #define BIST1_CTRL_FUSE_SETP_BITS 4 ++ #define BIST1_CTRL_FUSE_ROW_OFF 12 ++ #define BIST1_CTRL_FUSE_ROW_BITS 7 ++ #define BIST1_CTRL_FUSE_COL_OFF 24 ++ #define BIST1_CTRL_FUSE_COL_BITS 5 ++ ++#define REG_SERDES_CTRL_STS (0x1424) ++#define SERDES_CTRL_STS_SELFB_PLL_SEL_OFF 14 ++#define SERDES_CTRL_STS_SELFB_PLL_SEL_BITS 2 ++#define SERDES_OVCLK_18_25 0 ++#define SERDES_OVCLK_12_18 1 ++#define SERDES_OVCLK_0_4 2 ++#define SERDES_OVCLK_4_12 3 ++#define SERDES_MAC_CLK_SLOWDOWN_OFF 17 ++#define SERDES_MAC_CLK_SLOWDOWN_BITS 1 ++#define SERDES_PHY_CLK_SLOWDOWN_OFF 18 ++#define SERDES_PHY_CLK_SLOWDOWN_BITS 1 ++ ++ ++#define REG_LED_CTRL (0x1428) /* DWORD reg */ ++ #define LED_CTRL_DC_CTRL_OFF 0 ++ #define LED_CTRL_DC_CTRL_BITS 2 ++ #define LED_CTRL_D3_MODE_CTRL_OFF 2 ++ #define LED_CTRL_D3_MODE_CTRL_BITS 2 ++ #define LED_CTRL_0_PAT_MAP_OFF 4 ++ #define LED_CTRL_0_PAT_MAP_BITS 2 ++ #define LED_CTRL_1_PAT_MAP_OFF 6 ++ #define LED_CTRL_1_PAT_MAP_BITS 2 ++ #define LED_CTRL_2_PAT_MAP_OFF 8 ++ #define LED_CTRL_2_PAT_MAP_BITS 2 ++ ++#define REG_LED_PAT0 (0x142C) /* WORD reg */ ++#define REG_LED_PAT1 (0x142E) /* WORD reg */ ++#define REG_LED_PAT2 (0x1430) /* WORD reg */ ++ ++#define REG_SYS_ALIVE (0x1434) ++ #define SYS_ALIVE_FLAG_OFF 0 ++ #define SYS_ALIVE_FLAG_BITS 1 ++ ++#define REG_LPI_TD (0x143C) ++ ++#define REG_LPI_CTRL (0x1440) ++ #define LPI_CTRL_EN_OFF 0 ++ #define LPI_CTRL_EN_BITS 1 ++ #define LPI_CTRL_CTRL_OFF 1 ++ #define LPI_CTRL_CTRL_BITS 1 ++ #define LPI_CTRL_GMII_OFF 2 ++ #define LPI_CTRL_GMII_BITS 1 ++ #define LPI_CTRL_CHK_STATE_OFF 3 ++ #define LPI_CTRL_CHK_STATE_BITS 1 ++ #define LPI_CTRL_CHK_RX_OFF 4 ++ #define LPI_CTRL_CHK_RX_BITS 1 ++ ++#define REG_LPI_TW (0x1444) ++ ++ ++#define REG_MDIO_EXT_CTRL (0x1448) ++ #define MDIO_EXT_CTRL_REG_ADDR_OFF 0 ++ #define MDIO_EXT_CTRL_REG_ADDR_BITS 16 ++ #define MDIO_EXT_CTRL_DEVADDR_OFF 16 ++ #define MDIO_EXT_CTRL_DEVADDR_BITS 5 ++ #define MDIO_EXT_CTRL_PTADDR_OFF 21 ++ #define MDIO_EXT_CTRL_PTADDR_BITS 5 ++ ++#define REG_MAC_CTRL (0x1480) /* DWORD reg */ ++ #define MAC_CTRL_TXEN_OFF 0 ++ #define MAC_CTRL_TXEN_BITS 1 ++ #define MAC_CTRL_RXEN_OFF 1 ++ #define MAC_CTRL_RXEN_BITS 1 ++ #define MAC_CTRL_TXFC_OFF 2 ++ #define MAC_CTRL_TXFC_BITS 1 ++ #define MAC_CTRL_RXFC_OFF 3 ++ #define MAC_CTRL_RXFC_BITS 1 ++ #define MAC_CTRL_LOOPBACK_OFF 4 ++ #define MAC_CTRL_LOOPBACK_BITS 1 ++ #define MAC_CTRL_FULLD_OFF 5 ++ #define MAC_CTRL_FULLD_BITS 1 ++ #define MAC_CTRL_CRCE_OFF 6 ++ #define MAC_CTRL_CRCE_BITS 1 ++ #define MAC_CTRL_PCRCE_OFF 7 ++ #define MAC_CTRL_PCRCE_BITS 1 ++ #define MAC_CTRL_FLCHK_OFF 8 ++ #define MAC_CTRL_FLCHK_BITS 1 ++ #define MAC_CTRL_HUGEN_OFF 9 ++ #define MAC_CTRL_HUGEN_BITS 1 ++ #define MAC_CTRL_PRLEN_OFF 10 ++ #define MAC_CTRL_PRLEN_BITS 4 ++ #define MAC_CTRL_PRLEN_DEF 7 ++ #define MAC_CTRL_VLAN_STRIP_OFF 14 ++ #define MAC_CTRL_VLAN_STRIP_BITS 1 ++ #define MAC_CTRL_PROM_MODE_OFF 15 ++ #define MAC_CTRL_PROM_MODE_BITS 1 ++ #define MAC_CTRL_TPAUSE_OFF 16 ++ #define MAC_CTRL_TPAUSE_BITS 1 ++ #define MAC_CTRL_SSTCT_OFF 17 ++ #define MAC_CTRL_SSTCT_BITS 1 ++ #define MAC_CTRL_SRTFN_OFF 18 ++ #define MAC_CTRL_SRTFN_BITS 1 ++ #define MAC_CTRL_SIMR_OFF 19 ++ #define MAC_CTRL_SIMR_BITS 1 ++ #define MAC_CTRL_SPEED_OFF 20 ++ #define MAC_CTRL_SPEED_BITS 2 ++ #define MAC_CTRL_SPEED_10_100 1 ++ #define MAC_CTRL_SPEED_1000 2 ++ #define MAC_CTRL_MBOF_OFF 22 ++ #define MAC_CTRL_MBOF_BITS 1 ++ #define MAC_CTRL_HUGE_OFF 23 ++ #define MAC_CTRL_HUGE_BITS 1 ++ #define MAC_CTRL_RX_XSUM_EN_OFF 24 ++ #define MAC_CTRL_RX_XSUM_EN_BITS 1 ++ #define MAC_CTRL_MUTI_ALL_OFF 25 ++ #define MAC_CTRL_MUTI_ALL_BITS 1 ++ #define MAC_CTRL_BROAD_EN_OFF 26 ++ #define MAC_CTRL_BROAD_EN_BITS 1 ++ #define MAC_CTRL_DEBUG_MODE_OFF 27 ++ #define MAC_CTRL_DEBUG_MODE_BITS 1 ++ #define MAC_CTRL_SINGLE_PAUSE_OFF 28 ++ #define MAC_CTRL_SINGLE_PAUSE_BITS 1 ++ #define MAC_CTRL_HASH_ALG_MODE_OFF 29 ++ #define MAC_CTRL_HASH_ALG_MODE_BITS 1 ++ #define MAC_CTRL_HASH_ALG_CRC32 1 ++ #define MAC_CTRL_HASH_ALG_CRC16 0 ++ #define MAC_CTRL_SPEED_MODE_OFF 30 ++ #define MAC_CTRL_SPEED_MODE_BITS 1 ++ #define MAC_CTRL_SPEED_MODE_PHY 0 ++ #define MAC_CTRL_SPEED_MODE_SW 1 ++ ++ ++#define REG_MAC_STA_ADDR (0x1488) /* QWORD reg */ ++/* [1488]=0x749dc320, [148c]=0x00000013, mac-addr:00-13-74-9d-c3-20 */ ++ ++#define REG_RX_HASH_TABLE (0x1490) /* QWORD reg */ ++ ++#define REG_MTU (0x149C) /* WORD reg */ ++ ++#define REG_WOL_CTRL (0x14A0) /* DWORD reg */ ++ #define WOL_CTRL_PATTERN_EN_OFF 0 ++ #define WOL_CTRL_PATTERN_EN_BITS 1 ++ #define WOL_CTRL_PATTERN_PME_EN_OFF 1 ++ #define WOL_CTRL_PATTERN_PME_EN_BITS 1 ++ #define WOL_CTRL_MAGIC_EN_OFF 2 ++ #define WOL_CTRL_MAGIC_EN_BITS 1 ++ #define WOL_CTRL_MAGIC_PME_EN_OFF 3 ++ #define WOL_CTRL_MAGIC_PME_EN_BITS 1 ++ #define WOL_CTRL_LINKCHG_EN_OFF 4 ++ #define WOL_CTRL_LINKCHG_EN_BITS 1 ++ #define WOL_CTRL_LINKCHG_PME_EN_OFF 5 ++ #define WOL_CTRL_LINKCHG_PME_EN_BITS 1 ++ #define WOL_CTRL_PATTERN_ST_OFF 8 ++ #define WOL_CTRL_PATTERN_ST_BITS 1 ++ #define WOL_CTRL_MAGIC_ST_OFF 9 ++ #define WOL_CTRL_MAGIC_ST_BITS 1 ++ #define WOL_CTRL_LINKCHG_ST_OFF 10 ++ #define WOL_CTRL_LINKCHG_ST_BITS 1 ++ #define WOL_CTRL_CLK_SWH_EN_OFF 15 ++ #define WOL_CTRL_CLK_SWH_EN_BITS 1 ++ #define WOL_CTRL_PT0_EN_OFF 16 ++ #define WOL_CTRL_PT0_EN_BITS 1 ++ #define WOL_CTRL_PT1_EN_OFF 17 ++ #define WOL_CTRL_PT1_EN_BITS 1 ++ #define WOL_CTRL_PT2_EN_OFF 18 ++ #define WOL_CTRL_PT2_EN_BITS 1 ++ #define WOL_CTRL_PT3_EN_OFF 19 ++ #define WOL_CTRL_PT3_EN_BITS 1 ++ #define WOL_CTRL_PT4_EN_OFF 20 ++ #define WOL_CTRL_PT4_EN_BITS 1 ++ #define WOL_CTRL_PT5_EN_OFF 21 ++ #define WOL_CTRL_PT5_EN_BITS 1 ++ #define WOL_CTRL_PT6_EN_OFF 22 ++ #define WOL_CTRL_PT6_EN_BITS 1 ++ #define WOL_CTRL_PT0_MATCH_OFF 24 ++ #define WOL_CTRL_PT0_MATCH_BITS 1 ++ #define WOL_CTRL_PT1_MATCH_OFF 25 ++ #define WOL_CTRL_PT1_MATCH_BITS 1 ++ #define WOL_CTRL_PT2_MATCH_OFF 26 ++ #define WOL_CTRL_PT2_MATCH_BITS 1 ++ #define WOL_CTRL_PT3_MATCH_OFF 27 ++ #define WOL_CTRL_PT3_MATCH_BITS 1 ++ #define WOL_CTRL_PT4_MATCH_OFF 28 ++ #define WOL_CTRL_PT4_MATCH_BITS 1 ++ #define WOL_CTRL_PT5_MATCH_OFF 29 ++ #define WOL_CTRL_PT5_MATCH_BITS 1 ++ #define WOL_CTRL_PT6_MATCH_OFF 30 ++ #define WOL_CTRL_PT6_MATCH_BITS 1 ++ ++#define REG_WOL_PT0_LEN (0x14A4) /* BYTE reg */ ++#define REG_WOL_PT1_LEN 0x14A5 /* BYTE reg */ ++#define REG_WOL_PT2_LEN 0x14A6 /* BYTE reg */ ++#define REG_WOL_PT3_LEN 0x14A7 /* BYTE reg */ ++#define REG_WOL_PT4_LEN 0x14A8 /* BYTE reg */ ++#define REG_WOL_PT5_LEN 0x14A9 /* BYTE reg */ ++#define REG_WOL_PT6_LEN 0x14AA /* BYTE reg */ ++ ++#define REG_SRAM_RFD0 (0x1500) /* DWORD reg */ ++ #define SRAM_RFD0_HDRADDR_OFF 0 ++ #define SRAM_RFD0_HDRADDR_BITS 12 ++ #define SRAM_RFD0_TALADDR_OFF 16 ++ #define SRAM_RFD0_TALADDR_BITS 12 ++ ++#define REG_SRAM_RFD1 (0x1504) /* DWORD reg */ ++ #define SRAM_RFD1_HDRADDR_OFF 0 ++ #define SRAM_RFD1_HDRADDR_BITS 12 ++ #define SRAM_RFD1_TALADDR_OFF 16 ++ #define SRAM_RFD1_TALADDR_BITS 12 ++ ++#define REG_SRAM_RFD2 (0x1508) /* DWORD reg */ ++ #define SRAM_RFD2_HDRADDR_OFF 0 ++ #define SRAM_RFD2_HDRADDR_BITS 12 ++ #define SRAM_RFD2_TALADDR_OFF 16 ++ #define SRAM_RFD2_TALADDR_BITS 12 ++ ++#define REG_SRAM_RFD3 (0x150C) /* DWORD reg */ ++ #define SRAM_RFD3_HDRADDR_OFF 0 ++ #define SRAM_RFD3_HDRADDR_BITS 12 ++ #define SRAM_RFD3_TALADDR_OFF 16 ++ #define SRAM_RFD3_TALADDR_BITS 12 ++ ++#define REG_SRAM_RFD_NICLEN (0x1510) /* DWORD reg */ ++ ++#define REG_SRAM_TRD 0x1518 /* DWORD reg */ ++ #define SRAM_TRD_HDRADDR_OFF 0 ++ #define SRAM_TRD_HDRADDR_BITS 12 ++ #define SRAM_TRD_TALADDR_OFF 16 ++ #define SRAM_TRD_TALADDR_BITS 12 ++ ++#define REG_SRAM_TRD_NICLEN (0x151C) /* DWORD reg */ ++ ++#define REG_SRAM_RXF (0x1520) /* DWORD reg */ ++ #define SRAM_RXF_HDRADDR_OFF 0 ++ #define SRAM_RXF_HDRADDR_BITS 12 ++ #define SRAM_RXF_TALADDR_OFF 16 ++ #define SRAM_RXF_TALADDR_BITS 12 ++ ++#define REG_SRAM_RXF_NICLEN (0x1524) /* DWORD reg */ ++ ++#define REG_SRAM_TXF (0x1528) /* DWORD reg */ ++ #define SRAM_TXF_HDRADDR_OFF 0 ++ #define SRAM_TXF_HDRADDR_BITS 12 ++ #define SRAM_TXF_TALADDR_OFF 16 ++ #define SRAM_TXF_TALADDR_BITS 12 ++ ++#define REG_SRAM_TXF_NICLEN (0x152C) /* DWORD reg */ ++ ++#define REG_SRAM_TCP_HDRADDR (0x1530) /* WORD reg */ ++ ++#define REG_SRAM_PAT_HDRADDR (0x1532) /* WORD reg */ ++ ++#define REG_SRAM_LOAD_PTR (0x1534) /* BYTE reg, sc */ ++ #define SRAM_LOAD_PTR_OFF 0 ++ #define SRAM_LOAD_PTR_BITS 1 ++ ++ ++#define REG_RX_BASE_ADDR_HI (0x1540) /* DWORD reg */ ++ ++#define REG_TX_BASE_ADDR_HI (0x1544) /* DWORD reg */ ++ ++#define REG_SMB_BASE_ADDR_HI (0x1548) /* DWORD reg */ ++#define REG_SMB_BASE_ADDR_LO (0x154C) /* DWORD reg */ ++ ++#define REG_RFD0_HDRADDR_LO (0x1550) /* DWORD reg */ ++#define REG_RFD1_HDRADDR_LO (0x1554) /* DWORD reg */ ++#define REG_RFD2_HDRADDR_LO (0x1558) /* DWORD reg */ ++#define REG_RFD3_HDRADDR_LO (0x155C) /* DWORD reg */ ++#define REG_RFD_RING_SIZE (0x1560) /* DWORD reg */ ++#define REG_RFD_BUFFER_SIZE (0x1564) /* DWORD reg */ ++ ++#define REG_RRD0_HDRADDR_LO (0x1568) /* DWORD reg */ ++#define REG_RRD1_HDRADDR_LO (0x156C) /* DWORD reg */ ++#define REG_RRD2_HDRADDR_LO (0x1570) /* DWORD reg */ ++#define REG_RRD3_HDRADDR_LO (0x1574) /* DWORD reg */ ++#define REG_RRD_RING_SIZE (0x1578) /* DWORD reg */ ++ ++#define REG_HTPD_HDRADDR_LO (0x157C) /* DWORD reg */ ++#define REG_NTPD_HDRADDR_LO (0x1580) /* DWORD reg */ ++#define REG_TPD_RING_SIZE (0x1584) /* DWORD reg */ ++ ++#define REG_CMB_BASE_ADDR_LO (0x1588) /* DWORD reg */ ++ ++#define REG_RSS_KEY0 (0x14B0) /* DWORD reg */ ++#define REG_RSS_KEY1 (0x14B4) /* DWORD reg */ ++#define REG_RSS_KEY2 (0x14B8) /* DWORD reg */ ++#define REG_RSS_KEY3 (0x14BC) /* DWORD reg */ ++#define REG_RSS_KEY4 (0x14C0) /* DWORD reg */ ++#define REG_RSS_KEY5 (0x14C4) /* DWORD reg */ ++#define REG_RSS_KEY6 (0x14C8) /* DWORD reg */ ++#define REG_RSS_KEY7 (0x14CC) /* DWORD reg */ ++#define REG_RSS_KEY8 (0x14D0) /* DWORD reg */ ++#define REG_RSS_KEY9 (0x14D4) /* DWORD reg */ ++ ++#define REG_RSS_IDT_TABLE0 (0x14E0) /* DWORD reg */ ++#define REG_RSS_IDT_TABLE1 (0x14E4) /* DWORD reg */ ++#define REG_RSS_IDT_TABLE2 (0x14E8) /* DWORD reg */ ++#define REG_RSS_IDT_TABLE3 (0x14EC) /* DWORD reg */ ++#define REG_RSS_IDT_TABLE4 (0x14F0) /* DWORD reg */ ++#define REG_RSS_IDT_TABLE5 (0x14F4) /* DWORD reg */ ++#define REG_RSS_IDT_TABLE6 (0x14F8) /* DWORD reg */ ++#define REG_RSS_IDT_TABLE7 (0x14FC) /* DWORD reg */ ++ ++#define REG_RSS_HASH_VAL (0x15B0) /* DWORD reg */ ++#define REG_RSS_HASH_FLAG (0x15B4) /* DOWRD reg */ ++ ++#define REG_RSS_BASE_CPU_NUMBER (0x15B8) /* DWORD reg */ ++ ++#define REG_TXQ_CTRL (0x1590) /* DWORD reg */ ++ #define TXQ_CTRL_NUM_TPD_BURST_OFF 0 ++ #define TXQ_CTRL_NUM_TPD_BURST_BITS 4 ++ #define TXQ_CTRL_NUM_TPD_BURST_DEF 5 ++ #define TXQ_CTRL_IP_OPT_SP_OFF 4 ++ #define TXQ_CTRL_IP_OPT_SP_BITS 1 ++ #define TXQ_CTRL_EN_OFF 5 ++ #define TXQ_CTRL_EN_BITS 1 ++ #define TXQ_CTRL_MODE_OFF 6 ++ #define TXQ_CTRL_MODE_BITS 1 ++ #define TXQ_CTRL_MODE_ENH 1 ++ #define TXQ_CTRL_EN_SNAP_LSO_OFF 7 ++ #define TXQ_CTRL_EN_SNAP_LSO_BITS 1 ++ #define TXQ_CTRL_NUM_TXF_BURST_OFF 16 ++ #define TXQ_CTRL_NUM_TXF_BURST_BITS 16 ++ ++ ++#define REG_TXQ_JUMBO_TSO_THRESHOLD (0x1594) /* DWORD reg */ ++ ++#define REG_TXQ_TXF_BURST_L1 (0x1598) /* DWORD reg */ ++ #define TXQ_TXF_BURST_L1_LWM_OFF 0 ++ #define TXQ_TXF_BURST_L1_LWM_BITS 12 ++ #define TXQ_TXF_BURST_L1_HWM_OFF 16 ++ #define TXQ_TXF_BURST_L1_HWM_BITS 12 ++ #define TXQ_TXF_BURST_L1_EN_OFF 31 ++ #define TXQ_TXF_BURST_L1_EN_BITS 1 ++ ++ ++#define REG_THRUPUT_MON_CTRL (0x159C) /* DWORD reg */ ++ #define THRUPUT_MON_CTRL_RATE_OFF 0 ++ #define THRUPUT_MON_CTRL_RATE_BITS 2 ++ ++ #define THRUPUT_MON_CTRL_EN_OFF 7 ++ #define THRUPUT_MON_CTRL_EN_BITS 1 ++ ++#define REG_RXQ_CTRL (0x15A0) /* DWORD reg */ ++ #define RXQ_CTRL_ASPM_THRUPUT_LIM_OFF 0 ++ #define RXQ_CTRL_ASPM_THRUPUT_LIM_BITS 2 ++ #define RXQ_CTRL_ASPM_THRUPUT_LIM_NO 0 ++ #define RXQ_CTRL_ASPM_THRUPUT_LIM_1MB 1 ++ #define RXQ_CTRL_ASPM_THRUPUT_LIM_10MB 2 ++ #define RXQ_CTRL_ASPM_THRUPUT_LIM_100MB 3 ++ #define RXQ_CTRL_Q1_EN_OFF 4 ++ #define RXQ_CTRL_Q1_EN_BITS 1 ++ #define RXQ_CTRL_Q2_EN_OFF 5 ++ #define RXQ_CTRL_Q2_EN_BITS 1 ++ #define RXQ_CTRL_Q3_EN_OFF 6 ++ #define RXQ_CTRL_Q3_EN_BITS 1 ++ #define RXQ_CTRL_IPV6_XSUM_EN_OFF 7 ++ #define RXQ_CTRL_IPV6_XSUM_EN_BITS 1 ++ #define RXQ_CTRL_RSS_HASH_BITS_OFF 8 ++ #define RXQ_CTRL_RSS_HASH_BITS_BITS 8 ++ #define RXQ_CTRL_RSS_HASH_TYPE_IPV4_OFF 16 ++ #define RXQ_CTRL_RSS_HASH_TYPE_IPV4_TCP_OFF 17 ++ #define RXQ_CTRL_RSS_HASH_TYPE_IPV6_OFF 18 ++ #define RXQ_CTRL_RSS_HASH_TYPE_IPV6_TCP_OFF 19 ++ #define RXQ_CTRL_NUM_RFD_PREF_OFF 20 ++ #define RXQ_CTRL_NUM_RFD_PREF_BITS 6 ++ #define RXQ_CTRL_NUM_RFD_PREF_DEF 8 ++ #define RXQ_CTRL_RSS_MODE_OFF 26 ++ #define RXQ_CTRL_RSS_MODE_BITS 2 ++ #define RXQ_CTRL_RSS_MODE_DIS 0 ++ #define RXQ_CTRL_RSS_MODE_SQSI 1 ++ #define RXQ_CTRL_RSS_MODE_MQSI 2 ++ #define RXQ_CTRL_RSS_MODE_MQMI 3 ++ #define RXQ_CTRL_NIP_QUEUE_SEL_OFF 28 ++ #define RXQ_CTRL_NIP_QUEUE_SEL_BITS 1 ++ #define RXQ_CTRL_RSS_HASH_EN_OFF 29 ++ #define RXQ_CTRL_RSS_HASH_EN_BITS 1 ++ #define RXQ_CTRL_CUT_THRU_OFF 30 ++ #define RXQ_CTRL_CUT_THRU_BITS 1 ++ #define RXQ_CTRL_EN_OFF 31 ++ #define RXQ_CTRL_EN_BITS 1 ++ ++#define REG_RFD_PREF_CTRL (0x15A4) ++ #define RFD_PREF_CTRL_UP_TH_OFF 0 ++ #define RFD_PREF_CTRL_UP_TH_BITS 6 ++ #define RFD_PREF_CTRL_UP_TH_DEF 16 ++ #define RFD_PREF_CTRL_LOW_TH_OFF 6 ++ #define RFD_PREF_CTRL_LOW_TH_BITS 6 ++ #define RFD_PREF_CTRL_LOW_TH_DEF 8 ++ ++ ++#define REG_FC_RXF_HI (0x15A8) /* WORD reg */ ++#define REG_FC_RXF_LO 0x15AA /* WORD reg */ ++ ++#define REG_RXD_CTRL (0x15AC) /* DWORD reg */ ++ #define RXD_CTRL_THRESHOLD_OFF 0 ++ #define RXD_CTRL_THRESHOLD_BITS 12 ++ #define RXD_CTRL_TIMER_OFF 16 ++ #define RXD_CTRL_TIMER_BITS 16 ++ ++ ++#define REG_DMA_CTRL (0x15C0) /* DWORD reg */ ++ #define DMA_CTRL_ORDER_MODE_OFF 0 ++ #define DMA_CTRL_ORDER_MODE_BITS 3 ++ #define DMA_CTRL_ORDER_MODE_IN 1 ++ #define DMA_CTRL_ORDER_MODE_ENH 2 ++ #define DMA_CTRL_ORDER_MODE_OUT 4 ++ #define DMA_CTRL_RCB_VAL_OFF 3 ++ #define DMA_CTRL_RCB_VAL_BITS 1 ++ #define DMA_CTRL_REGRDBLEN_OFF 4 ++ #define DMA_CTRL_REGRDBLEN_BITS 3 ++ #define DMA_CTRL_REGWRBLEN_OFF 7 ++ #define DMA_CTRL_REGWRBLEN_BITS 3 ++ #define DMA_CTRL_DMAR_REQ_PRI_OFF 10 ++ #define DMA_CTRL_DMAR_REQ_PRI_BITS 1 ++ #define DMA_CTRL_DMAR_DLY_CNT_OFF 11 ++ #define DMA_CTRL_DMAR_DLY_CNT_BITS 5 ++ #define DMA_CTRL_DMAR_DLY_CNT_DEF 15 ++ #define DMA_CTRL_DMAW_DLY_CNT_OFF 16 ++ #define DMA_CTRL_DMAW_DLY_CNT_BITS 4 ++ #define DMA_CTRL_DMAW_DLY_CNT_DEF 4 ++ #define DMA_CTRL_CMB_EN_OFF 20 ++ #define DMA_CTRL_CMB_EN_BITS 1 ++ #define DMA_CTRL_SMB_DMA_SP_OFF 21 /* enable SMB DMA */ ++ #define DMA_CTRL_SMB_DMA_SP_BITS 1 ++ #define DMA_CTRL_CMB_NOW_OFF 22 ++ #define DMA_CTRL_CMB_NOW_BITS 1 ++ #define DMA_CTRL_SMB_DIS_OFF 24 ++ #define DMA_CTRL_SMB_DIS_BITS 1 ++ #define DMA_CTRL_SMB_NOW_OFF 31 ++ #define DMA_CTRL_SMB_NOW_BITS 1 ++ ++#define REG_SMB_DIS (0x15C3) /* BYTE reg */ ++ ++#define REG_SMB_TIMER (0x15C4 ) /* DWORD reg */ ++ ++#define REG_CMB_TPD_THRESHOLD (0x15C8) /* WORD reg */ ++#define REG_CMB_TIMER (0x15CC) /* WORD reg */ ++ ++#define REG_RFD0_PROD_INDEX (0x15E0) /* WORD reg */ ++#define REG_RFD1_PROD_INDEX (0x15E4) /* WORD reg */ ++#define REG_RFD2_PROD_INDEX (0x15E8) /* WORD reg */ ++#define REG_RFD3_PROD_INDEX (0x15EC) /* WORD reg */ ++ ++#define REG_HTPD_PROD_INDEX (0x15F0) /* WORD reg */ ++#define REG_NTPD_PROD_INDEX (0x15F2) /* WORD reg */ ++#define REG_HTPD_CONS_INDEX (0x15F4) /* WORD reg, ro */ ++#define REG_NTPD_CONS_INDEX (0x15F6) /* WORD reg, ro */ ++ ++#define REG_RFD0_CONS_INDEX (0x15F8) /* WORD reg, ro */ ++#define REG_RFD1_CONS_INDEX (0x15FA) /* WORD reg, ro */ ++#define REG_RFD2_CONS_INDEX (0x15FC) /* WORD reg, ro */ ++#define REG_RFD3_CONS_INDEX (0x15FE) /* WORD reg, ro */ ++ ++ ++#define REG_ISR (0x1600) /* DWORD reg */ ++ #define ISR_SMB_OFF 0 ++ ++ #define ISR_TIMER_OFF 1 ++ ++ #define ISR_SW_MANUAL_OFF 2 ++ #define ISR_SW_MANUAL_BITS 1 ++ #define ISR_RXF_OV_OFF (1<<3) ++ #define ISR_RXF_OV_BITS 1 ++ #define ISR_RFD0_UR_OFF (1<<4) ++ #define ISR_RFD0_UR_BITS 1 ++ #define ISR_RFD1_UR_OFF 5 ++ #define ISR_RFD1_UR_BITS 1 ++ #define ISR_RFD2_UR_OFF 6 ++ #define ISR_RFD2_UR_BITS 1 ++ #define ISR_RFD3_UR_OFF 7 ++ #define ISR_RFD3_UR_BITS 1 ++ #define ISR_TXF_UR_OFF 8 ++ #define ISR_TXF_UR_BITS 1 ++ #define ISR_DMAR_OFF (1<<9) ++ #define ISR_DMAR_BITS 1 ++ #define ISR_DMAW_OFF (1<<10) ++ #define ISR_DMAW_BITS 1 ++ #define ISR_TX_CREDIT_OFF 11 ++ #define ISR_TX_CREDIT_BITS 1 ++ #define ISR_GPHY_OFF (1<<12) ++ #define ISR_GPHY_BITS 1 ++ #define ISR_GPHY_LPW_OFF (1<<13) ++ #define ISR_GPHY_LPW_BITS 1 ++ #define ISR_TXQ_OFF (1<<14) ++ #define ISR_TXQ_BITS 1 ++ #define ISR_TX_PKT_OFF (1<<15) ++ #define ISR_TX_PKT_BITS 1 ++ #define ISR_RX0_PKT_OFF (1<<16) ++ #define ISR_RX0_PKT_BITS 1 ++ #define ISR_RX1_PKT_OFF 17 ++ #define ISR_RX1_PKT_BITS 1 ++ #define ISR_RX2_PKT_OFF 18 ++ #define ISR_RX2_PKT_BITS 1 ++ #define ISR_RX3_PKT_OFF 19 ++ #define ISR_RX3_PKT_BITS 1 ++ #define ISR_MAC_RX_OFF 20 ++ #define ISR_MAC_RX_BITS 1 ++ #define ISR_MAC_TX_OFF 21 ++ #define ISR_MAC_TX_BITS 1 ++ #define ISR_PCIE_UR_OFF 22 ++ #define ISR_PCIE_UR_BITS 1 ++ #define ISR_PCIE_FERR_OFF 23 ++ #define ISR_PCIE_FERR_BITS 1 ++ #define ISR_PCIE_NFERR_OFF 24 ++ #define ISR_PCIE_NFERR_BITS 1 ++ #define ISR_PCIE_CERR_OFF 25 ++ #define ISR_PCIE_CERR_BITS 1 ++ #define ISR_PCIE_LINKDOWN_OFF 26 ++ #define ISR_PCIE_LINKDOWN_BITS 1 ++ #define ISR_DIS_OFF 31 ++ #define ISR_DIS_BITS 1 ++ ++#define REG_IMR (0x1604) /* DWORD reg */ ++ ++ ++#define INT_FATAL_MASK (\ ++ FLAG(ISR_DMAR_OFF) |\ ++ FLAG(ISR_DMAW_OFF) |\ ++ FLAG(ISR_PCIE_FERR_OFF) |\ ++ FLAG(ISR_PCIE_LINKDOWN_OFF) ) ++ ++ ++#define INT_TX_MASK (\ ++ FLAG(ISR_MAC_TX_OFF) |\ ++ FLAG(ISR_TX_PKT_OFF) |\ ++ FLAG(ISR_TXF_UR_OFF) ) ++ ++#define INT_RX_MASK (\ ++ FLAG(ISR_RXF_OV_OFF) |\ ++ /* FLAG(ISR_RFD0_UR_OFF) |*/\ ++ FLAG(ISR_RFD1_UR_OFF) |\ ++ FLAG(ISR_RFD2_UR_OFF) |\ ++ FLAG(ISR_RFD3_UR_OFF) |\ ++ FLAG(ISR_RX0_PKT_OFF) |\ ++ FLAG(ISR_RX1_PKT_OFF) |\ ++ FLAG(ISR_RX2_PKT_OFF) |\ ++ FLAG(ISR_RX3_PKT_OFF) |\ ++ FLAG(ISR_MAC_RX_OFF) ) ++ ++#define INT_MASK (\ ++ INT_RX_MASK |\ ++ INT_TX_MASK |\ ++ INT_FATAL_MASK |\ ++ FLAG(ISR_SMB_OFF) |\ ++ FLAG(ISR_SW_MANUAL_OFF) |\ ++ FLAG(ISR_GPHY_OFF) |\ ++ FLAG(ISR_GPHY_LPW_OFF) ) ++ ++ ++ ++ ++#define REG_INT_RETRIG_TIMER (0x1608) /* WORD reg */ ++ ++#define REG_HDS_CTRL (0x160C) /* DWORD reg */ ++ #define HDS_CTRL_EN_OFF 0 ++ #define HDS_CTRL_EN_BITS 1 ++ #define HDS_CTRL_BACKFILLSIZE_OFF 8 ++ #define HDS_CTRL_BACKFILLSIZE_BITS 12 ++ #define HDS_CTRL_MAX_HDRSIZE_OFF 20 ++ #define HDS_CTRL_MAX_HDRSIZE_BITS 12 ++ ++#endif +diff --git a/drivers/net/ethernet/ak-ethernet/Kconfig b/drivers/net/ethernet/ak-ethernet/Kconfig +new file mode 100755 +index 00000000..84962124 +--- /dev/null ++++ b/drivers/net/ethernet/ak-ethernet/Kconfig +@@ -0,0 +1,6 @@ ++config AK_ETHERNET ++ tristate "Anyka Ethernet support" ++ depends on ARCH_AK39 ++ help ++ Anyka Ethernet device support ++ +diff --git a/drivers/net/ethernet/ak-ethernet/Makefile b/drivers/net/ethernet/ak-ethernet/Makefile +new file mode 100755 +index 00000000..f6c1f519 +--- /dev/null ++++ b/drivers/net/ethernet/ak-ethernet/Makefile +@@ -0,0 +1,5 @@ ++ ++ ++obj-$(CONFIG_AK_ETHERNET) += ak_ethernet.o ++ ++ +diff --git a/drivers/net/ethernet/ak-ethernet/ak_ethernet.c b/drivers/net/ethernet/ak-ethernet/ak_ethernet.c +new file mode 100755 +index 00000000..9244f71b +--- /dev/null ++++ b/drivers/net/ethernet/ak-ethernet/ak_ethernet.c +@@ -0,0 +1,1657 @@ ++/* ++ * Anyka MAC Fast Ethernet driver for Linux. ++ * Features ++ * Copyright (C) 2010 ANYKA ++ * AUTHOR Tang Anyang ++ * AUTHOR Zhang Jingyuan ++ * 10-11-01 09:08:08 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++//#include ++#include ++#include ++ ++#define MACNAME "ak98_mac" ++#define DRV_VERSION "1.0" ++#define TPD_RING_SIZE 0x50 ++#define RFD_RING_SIZE 0x50 ++#define RRD_RING_SIZE 0x50 ++#define MAC_FILE_NAME "MACADDR" ++#define CTOI(c) (isdigit(c) ? (c - '0') : (c - 'A' + 10)) ++ ++#include "eth_ops.h" ++#include "Ethernethw.h" ++#include "phyhw.h" ++ ++#if 0 ++#define dbg(fmt, arg...) printk( "%s(%d): " fmt "\n", __func__, __LINE__, ##arg) ++#else ++#define dbg(fmt, arg...) {} ++#endif ++ ++/* rrd format */ ++typedef struct _RrdDescr_s { ++ ++ unsigned short xsum; /* */ ++ ++ unsigned short nor :4 ; /* number of RFD */ ++ unsigned short si :12 ; /* start index of rfd-ring */ ++ ++ unsigned short hash; /* rss(MSFT) hash value */ ++ ++ unsigned short hash1; ++ ++ unsigned short vidh :4 ; /* vlan-id high part */ ++ unsigned short cfi :1 ; /* vlan-cfi */ ++ unsigned short pri :3 ; /* vlan-priority */ ++ unsigned short vidl :8 ; /* vlan-id low part */ ++ unsigned char hdr_len; /* Header Length of Header-Data Split. unsigned short unit */ ++ unsigned char hds_typ :2 ; /* Header-Data Split Type, ++ 00:no split, ++ 01:split at upper layer protocol header ++ 10:split at upper layer payload */ ++ unsigned char rss_cpu :2 ; /* CPU number used by RSS */ ++ unsigned char hash_t6 :1 ; /* TCP(IPv6) flag for RSS hash algrithm */ ++ unsigned char hash_i6 :1 ; /* IPv6 flag for RSS hash algrithm */ ++ unsigned char hash_t4 :1 ; /* TCP(IPv4) flag for RSS hash algrithm */ ++ unsigned char hash_i4 :1 ; /* IPv4 flag for RSS hash algrithm */ ++ ++ unsigned short frm_len :14 ; /* frame length of the packet */ ++ unsigned short l4f :1 ; /* L4(TCP/UDP) checksum failed */ ++ unsigned short ipf :1 ; /* IP checksum failed */ ++ unsigned short vtag :1 ; /* vlan tag */ ++ unsigned short pid :3 ; /* protocol id, ++ 000: non-ip packet ++ 001: ipv4(only) ++ 011: tcp/ipv4 ++ 101: udp/ipv4 ++ 010: tcp/ipv6 ++ 100: udp/ipv6 ++ 110: ipv6(only) */ ++ unsigned short res :1 ; /* received error summary */ ++ unsigned short crc :1 ; /* crc error */ ++ unsigned short fae :1 ; /* frame alignment error */ ++ unsigned short trunc :1 ; /* truncated packet, larger than MTU */ ++ unsigned short runt :1 ; /* runt packet */ ++ unsigned short icmp :1 ; /* incomplete packet, due to insufficient rx-descriptor */ ++ unsigned short bar :1 ; /* broadcast address received */ ++ unsigned short mar :1 ; /* multicast address received */ ++ unsigned short typ :1 ; /* type of packet (ethernet_ii(1) or snap(0)) */ ++ unsigned short resv1 :2 ; /* reserved, must be 0 */ ++ unsigned short updt :1 ; /* update by hardware. after hw fulfill the buffer, this bit ++ should be 1 */ ++} RrdDescr_t, *PRrdDescr_t; ++ ++unsigned char *pMacBase = NULL; ++//unsigned char *pSystemBase; ++unsigned char *psysbase; ++unsigned long g_tpdconsumerindex = 0; ++unsigned long g_rfdconsumerindex = 0; ++unsigned long g_rrdconsumerindex = 0; ++bool g_update = false; ++ ++//unsigned long rfdaddress = 0; ++unsigned long tpdaddress = 0; /* physical address for tpd */ ++unsigned long tpdaddressVa = 0; /* virtual address for tpd */ ++void *tpdbufaddressVa = NULL; /* virtual address for tpd buffer */ ++dma_addr_t tpdbufaddressPa = 0; /* physical address for tpd buffer */ ++unsigned long rrdaddressVa = 0; /* virtual address for rrd */ ++ ++void *rfd_sequenceva = NULL; /* virtual address for rfd */ ++dma_addr_t rfd_sequence; /* physical address for rfd */ ++void *RingbufVa = NULL; /* virtual address for ring buf */ ++dma_addr_t RingbufPa; /* physical address for ring buf */ ++ ++void *rfdbaseva = NULL; ++dma_addr_t rfdbasepa; ++ ++static void ak_mac_hash_table(struct net_device *ndev); ++ ++/* close the 2x when cpu clk is bigger than 340 */ ++unsigned long _2xswitchflag = 0; ++#if 0 ++static inline void cpu_clk_2x_switch(void) ++{ ++ ++ if (ak98_get_cpu_clk() / MHz > 340) ++ { ++ printk("CPU Core > 340 MHz"); ++ _2xswitchflag = 1; ++ } ++ else { ++ printk("CPU Core <= 340 MHz"); ++ _2xswitchflag = 0; ++ } ++} ++ ++static inline void close_2x(void) ++{ ++ if (_2xswitchflag) { ++ REG32(psysbase + 0x04) &= ~(0x1 << 15); ++ } ++} ++static inline void open_2x(void) ++{ ++ if (_2xswitchflag) { ++ REG32(psysbase + 0x04) |= (0x1 << 15); ++ } ++} ++#endif ++ ++ ++/* Structure/enum declaration ------------------------------- */ ++typedef struct mac_info { ++ void __iomem *io_addr; /* Register I/O base address */ ++ u16 irq; /* IRQ */ ++ ++ u16 tx_pkt_cnt; ++ u16 queue_pkt_len; ++ u16 queue_start_addr; ++ u16 queue_ip_summed; ++ u16 dbug_cnt; ++ u8 io_mode; /* 0:word, 2:byte */ ++ u8 phy_addr; ++ u8 imr_all; ++ ++ unsigned int flags; ++ unsigned int in_suspend :1; ++ ++ void (*inblk)(void __iomem *port, void *data, int length); ++ void (*outblk)(void __iomem *port, void *data, int length); ++ void (*dumpblk)(void __iomem *port, int length); ++ ++ struct device *dev; /* parent device */ ++ ++ struct resource *addr_res; /* resources found */ ++ struct resource *addr_req; /* resources requested */ ++ struct resource *irq_res; ++ ++ struct mutex addr_lock; /* phy and eeprom access lock */ ++ ++ struct delayed_work phy_poll; ++ struct net_device *ndev; ++ ++ spinlock_t lock; ++ ++ u32 msg_enable; ++ ++ int rx_csum; ++ int can_csum; ++ int ip_summed; ++ int phy_id; ++ struct clk *clk; ++ ++ struct work_struct link_chg_task; ++} mac_info_t; ++ ++void MacDelay(unsigned long us) ++{ ++ unsigned long i =0; ++ for (i=0; i< 10*us*1000; i++) ++ ; ++} ++ ++/** * @brief Read Phy Register ++* Read Phy Register from MII Interface ++* @author Tang Anyang ++* @date 2010-11-16 ++* @param unsigned long RegAddr: Phy Register address ++* @retval unsigned long: the value of Phy Register. ++*/ ++unsigned long MIIRead(unsigned long RegAddr) ++{ ++ unsigned int Val; ++ unsigned short Index; ++ unsigned short phyVal; ++ unsigned int macbug; ++ ++ Val = ++ MDIO_CTRL_REG_ADDR(RegAddr) | ++ MDIO_CTRL_START| ++ MDIO_CTRL_READ; ++ ++ REG32(pMacBase + REG_MDIO_CTRL)= Val; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ for (Index=0; Index gpio_init(&pdata->phy_rst_gpio); ++ mdelay(10); ++ /* second set gpio as input and be in powersave */ ++ ak_gpio_cfgpin(pdata->phy_rst_gpio.pin, !pdata->phy_rst_gpio.dir); ++ mdelay(1); ++} ++ ++static int mac_init_hw(struct ak_mac_data *pdata) ++{ ++ ak_group_config(ePIN_AS_MAC); ++ ++ if (pdata != NULL) { ++ /* init mac power on */ ++ if (pdata->pwr_gpio.pin > 0) ++ pdata->gpio_init(&pdata->pwr_gpio); ++ ++ /* init the gpio for phy */ ++ if (pdata->phy_rst_gpio.pin > 0) ++ mac_phy_reset(pdata); ++ } ++ return 0; ++} ++ ++/* init the mac and the phy */ ++bool init_hw(struct net_device *ndev) ++{ ++ unsigned long Val = 0; ++ unsigned long IntModerate = 100;//500000/5000; ++ ++ unsigned int mac_addL; ++ unsigned int mac_addH; ++ ++ unsigned long rrdaddress; ++ unsigned int macbug; ++ ++ struct mac_info *db = netdev_priv(ndev); ++ ++ // initial mac hw support ++ mac_init_hw(db->dev->platform_data); ++ ++ g_tpdconsumerindex = 0; ++ g_rfdconsumerindex = 0; ++ g_rrdconsumerindex = 0; ++ Val = REG32(pMacBase + 0x140c); ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ Val |= (5<< 19); ++ REG32(pMacBase + 0x140c)= Val; ++ ++ ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++#if 0 ++ MIIWrite(MII_BMCR,0x8000); ++ ++ mdelay(50); ++ ++ dbg("PHYSID1:0x%lx, PHYSID2:0x%lx", MIIRead(MII_PHYSID1), MIIRead(MII_PHYSID2)); ++ ++ ++ ++ while(MIIRead(MII_BMCR)&0x8000) ++ { ++ dbg("BMCR:0x%lx", MIIRead(MII_BMCR)); ++ } ++#endif ++ mdelay(200); ++ dbg("BMCR:0x%lx", MIIRead(MII_BMCR)); ++ dbg("PHYSID1:0x%lx, PHYSID2:0x%lx\r\n", MIIRead(MII_PHYSID1), MIIRead(MII_PHYSID2)); ++ dbg("PHYSID1:0x%lx, PHYSID2:0x%lx\r\n", MIIRead(MII_PHYSID1), MIIRead(MII_PHYSID2)); ++ dbg("PHYSID1:0x%lx, PHYSID2:0x%lx\r\n", MIIRead(MII_PHYSID1), MIIRead(MII_PHYSID2)); ++ dbg("PHYSID1:0x%lx, PHYSID2:0x%lx\r\n", MIIRead(MII_PHYSID1), MIIRead(MII_PHYSID2)); ++ dbg("PHYSID1:0x%lx, PHYSID2:0x%lx\r\n", MIIRead(MII_PHYSID1), MIIRead(MII_PHYSID2)); ++ ++ db->phy_id = MIIRead(MII_PHYSID1); ++ printk("===PHY ID:0x%x===\r\n", db->phy_id); ++ if(db->phy_id == 0x22) ++ { ++ MIIWrite(MII_BMCR, MIIRead(MII_BMCR) | 0x1000); ++ } ++ else ++ { ++ MIIWrite(MII_BMCR, MIIRead(MII_BMCR) | 0x1100); ++ ++ } ++ dbg("BMCR:0x%lx", MIIRead(MII_BMCR)); ++ dbg("GIGA_PSSR:0x%lx", MIIRead(MII_GIGA_PSSR)); ++ ++ /* set mac-address */ ++ mac_addL = ndev->dev_addr[5] | (ndev->dev_addr[4] << 8) ++ | (ndev->dev_addr[3] << 16) | (ndev->dev_addr[2] << 24); ++ mac_addH = ndev->dev_addr[1] | (ndev->dev_addr[0] << 8); ++ ++ REG32(pMacBase + REG_MAC_STA_ADDR)= mac_addL; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_MAC_STA_ADDR+4)= mac_addH; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ // clear the Multicast HASH table ++ REG32(pMacBase + REG_RX_HASH_TABLE) = 0x00; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_RX_HASH_TABLE+4)= 0x00; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ // clear any WOL setting/status / ++ Val = REG32(pMacBase + REG_WOL_CTRL); ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_WOL_CTRL)=0x00; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ tpdaddressVa = (unsigned long)RingbufVa; ++ // tx/rx/smb Ring BaseMem ++ REG32(pMacBase + REG_NTPD_HDRADDR_LO) = RingbufPa;//NTPD_HDRADDR_LO ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_HTPD_HDRADDR_LO)= RingbufPa;//HTPD_HDRADDR_LO ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_TX_BASE_ADDR_HI) = 0x00;//TX_BASE_ADDR_HI ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_TPD_RING_SIZE)= TPD_RING_SIZE;//TPD_RING_SIZE ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ REG32(pMacBase + REG_RX_BASE_ADDR_HI)= 0x00;//RX_BASE_ADDR_HI ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ REG32(pMacBase + REG_RFD0_HDRADDR_LO) = rfd_sequence;//RFD0_HDRADDR_LO ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ REG32(pMacBase + REG_RFD1_HDRADDR_LO)= 0x00; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_RFD2_HDRADDR_LO)= 0x00; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_RFD3_HDRADDR_LO)= 0x00; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_RFD_RING_SIZE)= RFD_RING_SIZE;//RFD_RING_SIZE ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_RFD_BUFFER_SIZE)= 0x600;//RFD_BUFFER_SIZE ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ rrdaddress = RingbufPa + TPD_RING_SIZE * 16; ++ rrdaddressVa = (unsigned long)RingbufVa + TPD_RING_SIZE * 16; ++ ++ REG32(pMacBase + REG_RRD0_HDRADDR_LO)= rrdaddress; // RRD0_HDRADDR_LO ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_RRD1_HDRADDR_LO)= 0x00; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_RRD2_HDRADDR_LO)= 0x00; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_RRD3_HDRADDR_LO)= 0x00; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_RRD_RING_SIZE)= RRD_RING_SIZE;//REG_RRD_RING_SIZE ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ ++ REG32(pMacBase + REG_TXQ_TXF_BURST_L1)= 0;// TX watermark, to enter l1 state. ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_RXD_CTRL)= 0; // RXD threshold. ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ // load all base/mem ptr ++ REG32(pMacBase + REG_SRAM_LOAD_PTR)= FLAG(SRAM_LOAD_PTR_OFF); ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ // set Interrupt Moderator Timer (max interrupt per sec) ++ // we use seperate time for rx/tx ++ REG16(pMacBase + REG_IRQ_MODRT_INIT)= IntModerate * 2; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG16(pMacBase + REG_IRQ_MODRT_RX_INIT)= IntModerate; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ // set Interrupt Clear Timer ++ // HW will enable self to assert interrupt event to system after ++ // waiting x-time for software to notify it accept interrupt. ++ ++ REG32(pMacBase + REG_INT_RETRIG_TIMER)= 10000;// 20ms ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ // Enable Read-Clear Interrupt Mechanism ++ Val = FLAG(MASTER_CTRL_INT_RCLR_EN_OFF); ++ BIT_SET(Val, MASTER_CTRL_SA_TIMER_EN_OFF); ++ REG32(pMacBase + REG_MASTER_CTRL)= Val; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ REG32(pMacBase + REG_FC_RXF_HI)= 0x03300400; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_TXQ_JUMBO_TSO_THRESHOLD)= 0xbf; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ // set MTU ++ REG32(pMacBase + REG_MTU)= 1540; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ // set DMA ++ //mac_set_reg(REG_DMA_CTRL, 0x347ed1);//DMA Engine Control ++ //mac_set_reg(REG_DMA_CTRL, 0x47C20);//DMA Engine Control ++ //REG32(pMacBase + REG_DMA_CTRL)= 0x47C10; ++ REG32(pMacBase + REG_DMA_CTRL)= 0x47C14; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ // set TXQ ++ REG32(pMacBase + REG_TXQ_CTRL)= 0x01000025; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ // set RXQ ++ //mac_set_reg(0x600015a0, 0xc08f10f0); ++ REG32(pMacBase + REG_RXQ_CTRL)= 0xC0800000; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ // rfd producer index ++ REG32(pMacBase + REG_RFD0_PROD_INDEX)= RFD_RING_SIZE - 1; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ //set MAC control ++ //REG32(pMacBase + REG_MAC_CTRL)= 0x0e10dcef;//MAC control register ++ REG32(pMacBase + REG_MAC_CTRL)= 0x06105cef;//MAC control register ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ REG32(pMacBase + REG_IMR)= 0x1d608;//Interrupt Mask ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ if(db->phy_id == 0x22) ++ { ++ MIIWrite(0x1B, 0x0500); ++ MIIWrite(0x1F, 0x8300); ++ } ++ else ++ { ++ MIIWrite(MII_IER, 0x0c00); ++ } ++ ++ ak_mac_hash_table(ndev); ++ ++ return true; ++} ++ ++/** * @brief Initialize Mac ++* Initialize MAC and PHY ++* @author Tang Anyang ++* @date 2010-11-16 ++* @param unsigned char * MacAddress: ++*/ ++bool MacInit(struct net_device *ndev) ++{ ++ //struct mac_info *db; ++ volatile unsigned long count; ++ ++ ++ int i=0; ++ //db = netdev_priv(ndev); ++ //close_2x(); ++ ++ ++ ++ //to enable the 25MHz oscillator ++ //TODO: Need to be changed to new clock API ++ REG32(psysbase + 0x74) &= ~(3 << 2); ++ REG32(psysbase + 0x74) |= (1 << 2); ++ REG32(psysbase + 0x80) |= (1 << 2); ++ //REG32(psysbase + 0x14) |= (1 << 18); ++ //REG32(psysbase + 0x1c) |= (1 << 13); ++ REG32(psysbase + 0x14) |= (1 << 16|1 << 18); ++ REG32(psysbase + 0x1c) &= ~(1 << 13); ++ for(i=0;i<6;i++) ++ { ++ REG32(psysbase + 0x14) |= (1 << 20); ++ REG32(psysbase + 0x14) &= ~(1 << 20); ++ } ++/* ++ ++ reg_value = REG32(psysbase + 0x18); ++ //x0800,0014Ĵġ18λΪ1opclkѡopclk dividerʱӶMAC_CLK, 25MHz ++ ++ //÷Ƶϵ ++ //reg_value &= (0x3F); ++ reg_value = (0x17); ++ REG32(psysbase + 0x18)= reg_value; ++ ++ //enable the adjustment of OPCLK divider. ++ REG32(psysbase + 0x18) |= (1 << 9); ++ //ʱӿ ++ // REG32(psysbase + 0x18) |= (1 << 8); ++ ++ REG32(psysbase + 0x18) |= (1 << 8); ++ ++ ++ REG32(psysbase + 0x14) &= ~(1 << 14); ++ ++ //unreset MAC module ++ count = 10000; ++ while(count--); ++ REG32(psysbase + 0x20) &= ~(1 << 13); ++ count = 10000; ++ */ ++ count = 10; ++ while(count--); ++ ++ //to enable the 25MHz oscillator ++ //REG32(psysbase + 0x14) |= (1 << 16); ++ ++ count = 10; ++ //reset MAC module ++ while(count--); ++ REG32(psysbase + 0x20) |= (1 << 13); ++ count = 10; ++ ++ while(count--); ++ ++ //unreset MAC module ++ REG32(psysbase + 0x20) &= ~(1 << 13); ++ count = 10; ++ ++ while(count--); ++ ++ if (false == InitEthernetMemory()) ++ return false; ++ ++ if (false == init_hw(ndev)) ++ return false; ++ ++// open_2x(); ++ ++ return true; ++} ++ ++void Macexit(struct net_device *ndev) ++{ ++ if (rfd_sequenceva) { ++ dma_free_coherent(NULL, RFD_RING_SIZE * 4, rfd_sequenceva, rfd_sequence); ++ rfd_sequenceva = NULL; ++ rfd_sequence = 0; ++ } ++ ++ if (tpdbufaddressVa) { ++ dma_free_coherent(NULL, TPD_RING_SIZE * 1520, tpdbufaddressVa, tpdbufaddressPa); ++ tpdbufaddressVa = NULL; ++ tpdbufaddressPa = 0; ++ } ++ ++ if (rfdbaseva) { ++ dma_free_coherent(NULL, RFD_RING_SIZE * 1520, rfdbaseva, rfdbasepa); ++ rfdbaseva = NULL; ++ rfdbasepa = 0; ++ } ++ ++ if (RingbufVa) { ++ dma_free_coherent(NULL, TPD_RING_SIZE * 16 + RRD_RING_SIZE * 16, RingbufVa, RingbufPa); ++ RingbufVa = NULL; ++ RingbufPa = 0; ++ } ++} ++ ++unsigned long GetPacketCount(void) ++{ ++ unsigned long ulNewRfdConsIdx = 0; ++ unsigned int macbug; ++ ++ ulNewRfdConsIdx = REG32(pMacBase + REG_RFD0_CONS_INDEX); ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ dbg("ulNewRfdConsIdx:0x%lx, g_rfdconsumerindex:%lx", ulNewRfdConsIdx, g_rfdconsumerindex); ++ ++ return ulNewRfdConsIdx - g_rfdconsumerindex; ++} ++ ++long ReceivePacket(struct net_device *ndev) ++{ ++ PRrdDescr_t prrd; ++ short length; ++ static unsigned long g_wait_count = 0; ++ unsigned char *sendbuffer; ++ struct sk_buff *skb; ++ unsigned int macbug; ++ ++ //l2cache_invalidate(); ++ ++ prrd = (PRrdDescr_t)(rrdaddressVa + g_rfdconsumerindex*16); ++ length = prrd->frm_len; ++ dbg("g_rfdconsumerindex:%lx, length:%x, updt:%d", ++ g_rfdconsumerindex, length, prrd->updt); ++ ++ if (length == 0) ++ { ++ if (prrd->nor == 0) ++ { ++ if (g_update) ++ { ++ g_rrdconsumerindex = g_rfdconsumerindex; ++ g_update = false; ++ } ++ } ++ length = 0; ++ ++ g_rfdconsumerindex++; ++ g_rfdconsumerindex %= RFD_RING_SIZE; ++ ++ return length; ++ } ++ ++ if (prrd->updt == 0) ++ ++ { ++ g_wait_count++; ++ ++ if (g_wait_count < 5) ++ { ++ length = -1; ++ ++ return length; ++ } ++ } ++ g_wait_count = 0; ++ prrd->updt = 0; ++ prrd->frm_len = 0; ++ ++ sendbuffer = (unsigned char *)(rfdbaseva + (g_rfdconsumerindex)*1520); ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ skb = dev_alloc_skb(length + 2); ++ skb_reserve(skb, 2); ++ skb->dev = ndev; ++ memcpy(skb_put(skb, length), sendbuffer, length); ++ skb->protocol = eth_type_trans(skb, ndev); ++ ++ netif_rx(skb); ++ ++ ndev->last_rx = jiffies; ++ ndev->stats.rx_packets++; ++ ndev->stats.rx_bytes += length; ++ ++ REG32(pMacBase + REG_RFD0_PROD_INDEX) = g_rfdconsumerindex; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ g_rfdconsumerindex++; ++ g_rfdconsumerindex %= RFD_RING_SIZE; ++ ++ return length; ++} ++ ++void SendPacket(unsigned char *sendbuffer, unsigned long length) ++{ ++ int tpdvalue = 0x80000000; ++ unsigned char *RingbufVa; ++ unsigned long tpdbufv; ++ unsigned int macbug; ++ ++ RingbufVa = (unsigned char *)(tpdbufaddressVa+g_tpdconsumerindex*1520); ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ memcpy(RingbufVa, sendbuffer, length); ++ ++ tpdbufv = tpdaddressVa + g_tpdconsumerindex*16; ++ ++ REG32(tpdbufv)= (unsigned long)0x3aa00000+length; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ dbg("Send 0x%lx", length); ++ ++ tpdbufv += 4; ++ REG32(tpdbufv)= tpdvalue; ++ ++ tpdbufv += 4; ++ ++ //close_2x(); ++ REG32(tpdbufv)= tpdbufaddressPa + g_tpdconsumerindex*1520; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ tpdbufv += 4; ++ ++ g_tpdconsumerindex++; ++ ++ g_tpdconsumerindex %= TPD_RING_SIZE; ++ REG32(pMacBase + REG_HTPD_PROD_INDEX) = g_tpdconsumerindex; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ //open_2x(); ++ ++ return; ++} ++ ++/* ak_mac_release_board ++ * ++ * release a board, and any mapped resources ++ */ ++ ++static void ++ak_mac_release_board(struct platform_device *pdev, struct mac_info *db) ++{ ++ /* unmap our resources */ ++ ++ iounmap(db->io_addr); ++ ++ /* release the resources */ ++ ++ release_resource(db->addr_req); ++ kfree(db->addr_req); ++} ++ ++/* ++ * Set AK98 MAC address ++ */ ++static int set_mac_address(struct net_device *ndev, void *p) ++{ ++ struct sockaddr *addr = p; ++ unsigned int macbug; ++ ++ if (netif_running(ndev)) ++ return -EBUSY; ++ if (!is_valid_ether_addr(addr->sa_data)) ++ return -EADDRNOTAVAIL; ++ ++ memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); ++ ++ /* set the Ethernet address */ ++ REG32(pMacBase + REG_MAC_STA_ADDR) = ndev->dev_addr[0] | (ndev->dev_addr[1] << 8) ++ | (ndev->dev_addr[2] << 16) | (ndev->dev_addr[3] << 24); ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_MAC_STA_ADDR + 4) = ndev->dev_addr[4] | (ndev->dev_addr[5] << 8); ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ return 0; ++} ++ ++/* ++ * atl1c_hash_mc_addr ++ * purpose ++ * set hash value for a multicast address ++ * hash calcu processing : ++ * 1. calcu 32bit CRC for multicast address ++ * 2. reverse crc with MSB to LSB ++ */ ++u32 ak_mac_hash_mc_addr(u8 *mc_addr) ++{ ++ u32 crc32; ++ u32 value = 0; ++ int i; ++ ++ crc32 = ether_crc_le(6, mc_addr); ++ for (i = 0; i < 32; i++) ++ value |= (((crc32 >> i) & 1) << (31 - i)); ++ ++ return value; ++} ++ ++/* ++ * Sets the bit in the multicast table corresponding to the hash value. ++ * hw - Struct containing variables accessed by shared code ++ * hash_value - Multicast address hash value ++ */ ++void ak_mac_hash_set(u32 hash_value) ++{ ++ u32 hash_bit, hash_reg; ++ u32 mta; ++ unsigned int macbug; ++ ++ /* ++ * The HASH Table is a register array of 2 32-bit registers. ++ * It is treated like an array of 64 bits. We want to set ++ * bit BitArray[hash_value]. So we figure out what register ++ * the bit is in, read it, OR in the new bit, then write ++ * back the new value. The register is determined by the ++ * upper bit of the hash value and the bit within that ++ * register are determined by the lower 5 bits of the value. ++ */ ++ hash_reg = (hash_value >> 31) & 0x1; ++ hash_bit = (hash_value >> 26) & 0x1F; ++ ++ if (hash_reg == 0) ++ mta = REG32(pMacBase + REG_RX_HASH_TABLE); ++ else ++ mta = REG32(pMacBase + REG_RX_HASH_TABLE + 4); ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ mta |= (1 << hash_bit); ++ if (hash_reg == 0) ++ REG32(pMacBase + REG_RX_HASH_TABLE) = mta; ++ else ++ REG32(pMacBase + REG_RX_HASH_TABLE + 4) = mta; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++} ++ ++/* ++ * Set AK98 MAC multicast address ++ */ ++ #if 1 ++static void ++ak_mac_hash_table(struct net_device *ndev) ++{ ++// struct dev_mc_list *mc_ptr; ++ u32 mac_ctrl_data; ++// u32 hash_value; ++ unsigned int macbug; ++ ++ ++ /* Check for Promiscuous and All Multicast modes */ ++ mac_ctrl_data = REG32(pMacBase + REG_MAC_CTRL); ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ if (ndev->flags & IFF_PROMISC) { ++ mac_ctrl_data |= FLAG(MAC_CTRL_PROM_MODE_OFF); ++ } else if (ndev->flags & IFF_ALLMULTI) { ++ mac_ctrl_data |= FLAG(MAC_CTRL_MUTI_ALL_OFF); ++ mac_ctrl_data &= ~(FLAG(MAC_CTRL_PROM_MODE_OFF)); ++ } else { ++ mac_ctrl_data &= ~(FLAG(MAC_CTRL_MUTI_ALL_OFF) | FLAG(MAC_CTRL_PROM_MODE_OFF)); ++ } ++ ++ REG32(pMacBase + REG_MAC_CTRL) = mac_ctrl_data; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ /* clear the old settings from the multicast hash table */ ++ REG32(pMacBase + REG_RX_HASH_TABLE) = 0; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ REG32(pMacBase + REG_RX_HASH_TABLE + 4) = 0;; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ /* comoute mc addresses' hash value ,and put it into hash table */ ++ //for (mc_ptr = ndev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) { ++ // hash_value = ak_mac_hash_mc_addr(mc_ptr->dmi_addr); ++ // ak_mac_hash_set(hash_value);// ++ //} ++} ++#endif ++/* Our watchdog timed out. Called by the networking layer */ ++static void ak_mac_timeout(struct net_device *ndev) ++{ ++ /* Initialize AK98 MAC */ ++ if (MacInit(ndev) == false) { ++ printk("Mac reset fail\n"); ++ return; ++ } ++ ++ /* wake up the send queue */ ++ netif_wake_queue(ndev); ++} ++ ++/* ++ * Hardware start transmission. ++ * Send a packet to media from the upper layer. ++ */ ++static int ++ak_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev) ++{ ++ unsigned long flags; ++ int len; ++ char *data, shortpkt[ETH_ZLEN]; ++ mac_info_t *db = netdev_priv(ndev); ++ ++ /* keep the upload from being interrupted, since we ++ * ask the chip to start transmitting before the ++ * whole packet has been completely uploaded. */ ++ spin_lock_irqsave(&db->lock, flags); ++ data = skb->data; ++ len = skb->len; ++ if (len < ETH_ZLEN) { ++ memset(shortpkt, 0, ETH_ZLEN); ++ memcpy(shortpkt, skb->data, skb->len); ++ len = ETH_ZLEN; ++ data = shortpkt; ++ } ++ netif_stop_queue(ndev); ++ ++ SendPacket(data, len); ++ spin_unlock_irqrestore(&db->lock, flags); ++ ndev->stats.tx_bytes += skb->len; ++ ndev->trans_start = jiffies; ++ dev_kfree_skb(skb); ++ ++ return NETDEV_TX_OK; ++} ++ ++int receive(struct net_device *ndev) ++{ ++ int length = 0; ++ ++ g_rfdconsumerindex = g_rrdconsumerindex; ++ g_update = true; ++ ++ while (GetPacketCount()) ++ { ++ length = ReceivePacket(ndev); ++ if (length == 0) ++ break; ++ else if (length == -1) ++ continue; ++ } ++ if(g_update) ++ g_rrdconsumerindex = g_rfdconsumerindex; ++ ++ return length; ++} ++ ++/* ++ * check whether the mac is connect, ++ * if connect, judge the full-duplex or half-duplex ++ * and set mac control register ++ */ ++int ak_mac_check_link(mac_info_t *db) ++{ ++ unsigned long status; ++ unsigned long ctrl_reg; ++ unsigned int macbug; ++ ++ ctrl_reg = REG32(pMacBase + REG_MAC_CTRL); ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ status = MIIRead(MII_BMSR); ++ status = MIIRead(MII_BMSR); ++ dbg("==BMSR: 0x%lx===", status); ++ ++ if(status == 0xFFFF) ++ { ++ netif_carrier_off(db->ndev); ++ return 0; ++ } ++ ++ if ((status & BMSR_LINK_STATUS) == 0) { ++ netif_carrier_off(db->ndev); ++ printk("%s: link down\n", db->ndev->name); ++ } else { ++ ++ if ((status & 0x20) == 0) { ++ printk("auto negotiation is not resolved\n"); ++ return -1; ++ } ++ else if (status & 0x4000) { ++ ++ printk("%s: link up, full duplex, 100Mb\n", db->ndev->name); ++ } ++ else if(status & 0x2000) ++ { ++ ++ printk("%s: link up, half duplex, 100Mb\n", db->ndev->name); ++ } ++ else if(status & 0x1000) ++ { ++ ++ printk("%s: link up, full duplex, 10Mb\n", db->ndev->name); ++ } ++ else ++ { ++ ++ printk("%s: link up, half duplex, 10Mb\n", db->ndev->name); ++ } ++ ++ ++ ++ netif_carrier_on(db->ndev); ++ ++ ++ ++ } ++ return 0; ++} ++ ++void ak_mac_link_chg_task(struct work_struct *work) ++{ ++ mac_info_t *db = container_of(work, struct mac_info, link_chg_task); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&db->lock, flags); ++ ak_mac_check_link(db); ++ spin_unlock_irqrestore(&db->lock, flags); ++} ++ ++static irqreturn_t ak_mac_interrupt(int irq, void *dev_id) ++{ ++ struct net_device *ndev = dev_id; ++ mac_info_t *db = netdev_priv(ndev); ++ unsigned long flags; ++ unsigned long IntStatus = 0; ++ unsigned int macbug; ++ /* holders of db->lock must always block IRQs */ ++ spin_lock_irqsave(&db->lock, flags); ++ ++ //close_2x(); ++ IntStatus = REG32(pMacBase + REG_ISR); ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ ++ dbg("IntStatus %lx", IntStatus); ++ ++ if(IntStatus & ISR_GPHY_OFF) { ++ if(db->phy_id==0x22) ++ { ++ MIIRead(0x1B); ++ } ++ else ++ { ++ MIIRead(MII_ISR); ++ } ++ //if(dwPHYIntStatus & (ISR_LINK_DOWN | ISR_LINK_UP)) { ++ schedule_work(&db->link_chg_task); ++ //} ++ REG32(pMacBase + REG_ISR) = ISR_GPHY_OFF; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ } ++ if(IntStatus & ISR_GPHY_LPW_OFF) { ++ unsigned long dwPHYIntStatus = MIIRead(MII_ISR); ++ if(dwPHYIntStatus & ISR_LINK_UP) { ++ //SetNetLinkStatus(TRUE); ++ netif_carrier_on(ndev); ++ dbg("%s: low power state link up", ndev->name); ++ } ++ if(dwPHYIntStatus & ISR_LINK_DOWN) { ++ //SetNetLinkStatus(FALSE); ++ netif_carrier_off(ndev); ++ dbg("%s: low power state link down", ndev->name); ++ } ++ } ++ ++ if(IntStatus & ISR_TX_PKT_OFF) { ++ REG32(pMacBase + REG_ISR) = ISR_TX_PKT_OFF; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ dbg("send complete!%lx", g_tpdconsumerindex); ++ ndev->stats.tx_packets++; ++ netif_wake_queue(ndev); ++ } ++ if(IntStatus & ISR_RX0_PKT_OFF) { ++ receive(ndev); ++ REG32(pMacBase + REG_ISR) = ISR_RX0_PKT_OFF; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ } ++ if(IntStatus & ISR_RXF_OV_OFF) { ++ receive(ndev); ++ REG32(pMacBase + REG_ISR) = ISR_RXF_OV_OFF; ++ macbug = REG32(AK_VA_L2CTRL+0xFF0); ++ } ++ if(IntStatus & ISR_DMAW_OFF) ++ { ++ printk("DMAW operation timeout"); ++ init_hw(ndev); ++ netif_wake_queue(ndev); ++ } ++ if(IntStatus & ISR_DMAR_OFF) ++ { ++ printk("DMAR operation timeout"); ++ init_hw(ndev); ++ netif_wake_queue(ndev); ++ } ++ if(IntStatus & ISR_TXQ_OFF) ++ { ++ printk("TXQ operation timeout"); ++ init_hw(ndev); ++ netif_wake_queue(ndev); ++ } ++ ++ //open_2x(); ++ ++ spin_unlock_irqrestore(&db->lock, flags); ++ ++ return IRQ_HANDLED; ++} ++ ++#ifdef CONFIG_NET_POLL_CONTROLLER ++/* ++ *Used by netconsole ++ */ ++static void ak_mac_poll_controller(struct net_device *ndev) ++{ ++ disable_irq(ndev->irq); ++ ak_mac_interrupt(ndev->irq, ndev); ++ enable_irq(ndev->irq); ++} ++#endif ++ ++/* ++ * Open the interface. ++ * The interface is opened whenever "ifconfig" actives it. ++ */ ++static int ++ak_mac_open(struct net_device *ndev) ++{ ++ mac_info_t *db = netdev_priv(ndev); ++ ++ /* enable clk */ ++// clk_enable(db->clk); ++ ++ /* Initialize AK98 MAC */ ++ if (MacInit(ndev) == false) { ++ printk("mac init fail\n"); ++ return -ENOMEM; ++ } else ++ printk("mac init success!\n"); ++ ++ if (request_irq(ndev->irq, &ak_mac_interrupt, 0, ndev->name, ndev)) ++ return -EAGAIN; ++ ++ schedule_work(&db->link_chg_task); ++ ++ netif_start_queue(ndev); ++ ++ return 0; ++} ++ ++/* ++ * Stop the interface. ++ * The interface is stopped when it is brought. ++ */ ++static int ++ak_mac_stop(struct net_device *ndev) ++{ ++// mac_info_t *db = netdev_priv(ndev); ++ ++ netif_stop_queue(ndev); ++ netif_carrier_off(ndev); ++ ++ /* free interrupt */ ++ free_irq(ndev->irq, ndev); ++ ++ /* reset mac and close phy */ ++ //close_2x(); ++ MacRest(); ++ MIIWrite(MII_BMCR, MIIRead(MII_BMCR) | 0x800); ++ //open_2x(); ++ ++ /* close clk */ ++ //clk_disable(db->clk); ++ ++ return 0; ++} ++ ++/* Get the current statistics. This may be called with the card open or ++ closed. */ ++static struct net_device_stats * ++ak_mac_get_stats(struct net_device *ndev) ++{ ++ //struct mac_info_t *db = netdev_priv(ndev); ++ //unsigned long flags; ++ ++ //spin_lock_irqsave(&lp->lock, flags); ++ /* Update the statistics from the device registers. */ ++ //db->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6); ++ //db->stats.collisions += (readreg(dev, PP_TxCol) >> 6); ++ //spin_unlock_irqrestore(&lp->lock, flags); ++ ++ return &ndev->stats; ++} ++ ++static const struct net_device_ops ak_mac_netdev_ops = { ++ .ndo_open = ak_mac_open, ++ .ndo_stop = ak_mac_stop, ++ .ndo_start_xmit = ak_mac_start_xmit, ++ .ndo_tx_timeout = ak_mac_timeout, ++ .ndo_set_rx_mode = ak_mac_hash_table, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_set_mac_address = set_mac_address, ++#ifdef CONFIG_NET_POLL_CONTROLLER ++ .ndo_poll_controller = ak_mac_poll_controller, ++#endif ++ .ndo_get_stats = ak_mac_get_stats, ++}; ++ ++static int get_mac_addr(struct ak_mac_data *pdata) ++{ ++ int i; ++ unsigned char mac_addr[32] = {0}; ++ unsigned char file_len[4] = {0}; ++ ++ if (FHA_asa_read_file(MAC_FILE_NAME, file_len, 4) == AK_FALSE) { ++ goto out; ++ } ++ ++ if (FHA_asa_read_file(MAC_FILE_NAME, mac_addr, *(unsigned long*)file_len + 4) == AK_FALSE) { ++ goto out; ++ } ++ ++ for (i = 0; i < MAC_ADDR_STRING_LEN; i++) { ++ if ((i % 3 != 2)) { ++ mac_addr[i + 4] = toupper(mac_addr[i + 4]); ++ if (!(isdigit(mac_addr[i + 4]) || (mac_addr[i + 4] <= 'F' && mac_addr[i + 4] >= 'A'))) ++ goto out; ++ } ++ else if (mac_addr[i + 4] != ':') ++ goto out; ++ } ++ ++ for (i = 0; i < MAC_ADDR_LEN; i++) ++ pdata->dev_addr[i] = CTOI(mac_addr[i * 3 + 4]) * 16 + CTOI(mac_addr[i * 3 + 5]); ++ ++ return 0; ++out: ++ ++ printk("Failed to read MAC addres in medium storage, use default mac\n"); ++ return -1; ++} ++ ++ ++/* ++ * Search AK98 MAC, allocate space and register it ++ */ ++static int __devinit ak_mac_probe(struct platform_device *pdev) ++{ ++ struct ak_mac_data *pdata = pdev->dev.platform_data; ++ struct mac_info *db; /* Point a board information structure */ ++ struct net_device *ndev; ++ int ret = 0; ++ int iosize; ++ ++ //cpu_clk_2x_switch(); ++ ++ /* Init network device */ ++ ndev = alloc_etherdev(sizeof(struct mac_info)); ++ if (!ndev) { ++ dev_err(&pdev->dev, "could not allocate device.\n"); ++ return -ENOMEM; ++ } ++ ++ SET_NETDEV_DEV(ndev, &pdev->dev); ++ ++ dev_dbg(&pdev->dev, "ak_mac_probe()\n"); ++ ++ /* setup board info structure */ ++ db = netdev_priv(ndev); ++ ++ db->dev = &pdev->dev; ++ db->ndev = ndev; ++ ++ /* get the register and irq resource */ ++ db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ ++ if (db->addr_res == NULL || db->irq_res == NULL) { ++ dev_err(db->dev, "insufficient resources\n"); ++ ret = -ENOENT; ++ goto out; ++ } ++ ++ iosize = resource_size(db->addr_res); ++ db->addr_req = request_mem_region(db->addr_res->start, iosize, ++ pdev->name); ++ ++ if (db->addr_req == NULL) { ++ dev_err(db->dev, "cannot claim address reg area\n"); ++ ret = -EIO; ++ goto out; ++ } ++ ++ db->io_addr = ioremap(db->addr_res->start, iosize); ++ ++ if (db->io_addr == NULL) { ++ dev_err(db->dev, "failed to ioremap address reg\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++#if 0 ++ /* get mac clock */ ++ db->clk = clk_get(db->dev, "mac_clk"); ++ if (IS_ERR(db->clk)) { ++ dbg("clocks missing"); ++ ret = -ENODEV; ++ goto out; ++ } ++ spin_lock_init(&db->lock); ++#endif ++ /* fill in parameters for net-dev structure */ ++ ndev->base_addr = (unsigned long)db->io_addr; ++ ndev->irq = db->irq_res->start; ++ ++ /* driver system function */ ++ ether_setup(ndev); ++ ++ ndev->netdev_ops = &ak_mac_netdev_ops; ++ ndev->watchdog_timeo = msecs_to_jiffies(5000); ++ ++ /* get Ethernet address from flash area */ ++ if ((get_mac_addr(pdata) < 0)||(!is_valid_ether_addr(pdata->dev_addr))) { ++ ++ dev_warn(db->dev, "%s, Invalid Ethernet address. " ++ "Generate software assigned\n\trandom Ethernet address.\n", ndev->name); ++ ++ /* Generate software assigned random Ethernet address */ ++ random_ether_addr(pdata->dev_addr); ++ if (!is_valid_ether_addr(pdata->dev_addr)) ++ dev_warn(db->dev, "%s: Invalid Ethernet address. Please " ++ "set using ifconfig\n", ndev->name); ++ } ++ ++ memcpy(ndev->dev_addr, pdata->dev_addr, 6); ++ ++ pMacBase = db->io_addr; ++ ++ psysbase = AK_VA_SYSCTRL; ++ if(psysbase == NULL) ++ { ++ dbg("sysbase alloc error!"); ++ } ++ ++ platform_set_drvdata(pdev, ndev); ++ INIT_WORK(&db->link_chg_task, ak_mac_link_chg_task); ++ ret = register_netdev(ndev); ++ ++ if (ret == 0) ++ printk(KERN_INFO "%s: ak98_mac at %p IRQ %d MAC: %pM\n", ++ ndev->name, db->io_addr, ndev->irq, ndev->dev_addr); ++ ++ return 0; ++out: ++ dev_err(db->dev, "not found (%d).\n", ret); ++ ++ ak_mac_release_board(pdev, db); ++ free_netdev(ndev); ++ ++ return ret; ++} ++ ++static int ++ak_mac_drv_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ mac_info_t *db = netdev_priv(ndev); ++ ++ if (ndev) { ++ db = netdev_priv(ndev); ++ db->in_suspend = 1; ++ ++ if (netif_running(ndev)) { ++ netif_device_detach(ndev); ++ ++ //close_2x(); ++ MacRest(); ++ MIIWrite(MII_BMCR, MIIRead(MII_BMCR) | 0x800); ++ //MIIWrite(0x29, MIIRead(0x29) | 0x8000); ++ // open_2x(); ++ ++ // clk_disable(db->clk); ++ } ++ } ++ return 0; ++} ++ ++static int ++ak_mac_drv_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ mac_info_t *db = netdev_priv(ndev); ++ ++ if (ndev) { ++ if (netif_running(ndev)) { ++ //clk_enable(db->clk); ++ ++ /* Initialize AK98 MAC */ ++ if (MacInit(ndev) == false) { ++ printk("mac init fail\n"); ++ return -ENOMEM; ++ } else ++ printk("mac init success!\n"); ++ ++ schedule_work(&db->link_chg_task); ++ netif_device_attach(ndev); ++ } ++ } ++ return 0; ++} ++ ++static struct dev_pm_ops ak_mac_drv_pm_ops = { ++ .suspend = ak_mac_drv_suspend, ++ .resume = ak_mac_drv_resume, ++}; ++ ++static int __devexit ak_mac_drv_remove(struct platform_device *pdev) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct ak_mac_data *pdata = pdev->dev.platform_data; ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ unregister_netdev(ndev); ++ ak_mac_release_board(pdev, (mac_info_t *) netdev_priv(ndev)); ++ Macexit(ndev); ++ free_netdev(ndev); /* free device structure */ ++ ++ //mac power off ++ if (pdata != NULL) ++ ak_gpio_setpin(pdata->pwr_gpio.pin, !pdata->pwr_gpio.value); ++ ++ dev_dbg(&pdev->dev, "released and freed device\n"); ++ return 0; ++} ++ ++static struct platform_driver ak_mac_driver = { ++ .driver = { ++ .name = "ak_ethernet", ++ .owner = THIS_MODULE, ++ .pm = &ak_mac_drv_pm_ops, ++ }, ++ .probe = ak_mac_probe, ++ .remove = __devexit_p(ak_mac_drv_remove), ++}; ++ ++static int __init ak_mac_init(void) ++{ ++ printk(KERN_INFO "%s Ethernet Driver, V%s\n", MACNAME, DRV_VERSION); ++ ++ return platform_driver_register(&ak_mac_driver); ++} ++ ++static void __exit ak_mac_cleanup(void) ++{ ++ platform_driver_unregister(&ak_mac_driver); ++} ++ ++module_init(ak_mac_init); ++module_exit(ak_mac_cleanup); ++ ++MODULE_AUTHOR("Tang Anyang, Zhang Jingyuan (C) ANYKA"); ++MODULE_DESCRIPTION("Anyka MAC driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:ak-ethernet"); +diff --git a/drivers/net/ethernet/ak-ethernet/eth_ops.h b/drivers/net/ethernet/ak-ethernet/eth_ops.h +new file mode 100644 +index 00000000..de632b34 +--- /dev/null ++++ b/drivers/net/ethernet/ak-ethernet/eth_ops.h +@@ -0,0 +1,8 @@ ++#ifndef _ETHERNET_OPERATION_H_ ++#define _ETHERNET_OPERATION_H_ ++ ++#define FLAG(_off) ((unsigned int)1 << (_off)) ++#define BIT_SET(_val, _off) ( (_val) |= FLAG(_off) ) //set the bit ++#define BIT_CLEAR(_val, _off) ( (_val) &= ~FLAG(_off) ) // clear the bit ++#define BIT_TEST(_val, _off) (0 != ( (_val) & FLAG(_off) ) ) //test the bit ++#endif +diff --git a/drivers/net/ethernet/ak-ethernet/phyhw.h b/drivers/net/ethernet/ak-ethernet/phyhw.h +new file mode 100755 +index 00000000..a4caf42c +--- /dev/null ++++ b/drivers/net/ethernet/ak-ethernet/phyhw.h +@@ -0,0 +1,140 @@ ++#ifndef _ANYKA_PHY_REG_H_ ++#define _ANYKA_PHY_REG_H_ ++/********************* PHY regs definition ***************************/ ++#define LC_10H 0x01 ++#define LC_10F 0x02 ++#define LC_100H 0x04 ++#define LC_100F 0x08 ++#define LC_1000F 0x10 ++#define LC_ALL (LC_10H|LC_10F|LC_100H|LC_100F|LC_1000F) ++ ++/* PHY Control Register */ ++#define MII_BMCR 0x00 ++ #define BMCR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ ++ #define BMCR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ ++ #define BMCR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ ++ #define BMCR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ ++ #define BMCR_ISOLATE 0x0400 /* Isolate PHY from MII */ ++ #define BMCR_POWER_DOWN 0x0800 /* Power down */ ++ #define BMCR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ ++ #define BMCR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ ++ #define BMCR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ ++ #define BMCR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ ++ #define BMCR_SPEED_MASK 0x2040 ++ #define BMCR_SPEED_1000 0x0040 ++ #define BMCR_SPEED_100 0x2000 ++ #define BMCR_SPEED_10 0x0000 ++ ++/* PHY Status Register */ ++#define MII_BMSR 0x01 ++ #define BMMSR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ ++ #define BMSR_JABBER_DETECT 0x0002 /* Jabber Detected */ ++ #define BMSR_LINK_STATUS 0x0004 /* Link Status 1 = link */ ++ #define BMSR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ ++ #define BMSR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ ++ #define BMSR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ ++ #define BMSR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ ++ #define BMSR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ ++ #define BMSR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ ++ #define BMSR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ ++ #define BMSR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ ++ #define BMSR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ ++ #define BMSR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ ++ #define BMMII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ ++ #define BMMII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ ++ ++#define MII_PHYSID1 0x02 ++#define MII_PHYSID2 0x03 ++#define L1D_MPW_PHYID1 0xD01C /* V7 */ ++#define L1D_MPW_PHYID2 0xD01D /* V1-V6 */ ++#define L1D_MPW_PHYID3 0xD01E /* V8 */ ++ ++ ++/* Autoneg Advertisement Register */ ++#define MII_ADVERTISE 0x04 ++ #define ADVERTISE_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ ++ #define ADVERTISE_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ ++ #define ADVERTISE_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ ++ #define ADVERTISE_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ ++ #define ADVERTISE_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ ++ #define ADVERTISE_100T4_CAPS 0x0200 /* 100T4 Capable */ ++ #define ADVERTISE_PAUSE 0x0400 /* Pause operation desired */ ++ #define ADVERTISE_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ ++ #define ADVERTISE_REMOTE_FAULT 0x2000 /* Remote Fault detected */ ++ #define ADVERTISE_NEXT_PAGE 0x8000 /* Next Page ability supported */ ++ #define ADVERTISE_SPEED_MASK 0x01E0 ++ #define ADVERTISE_DEFAULT_CAP 0x0DE0 ++ ++/* Link partner ability register */ ++#define MII_LPA 0x05 ++ #define LPA_SLCT 0x001 /* Same as advertise selector */ ++ #define LPA_10HALF 0x002 /* Can do 10mbps half-duplex */ ++ #define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ ++ #define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ ++ #define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ ++ #define LPA_100BASE4 0x0200 /* 100BASE-T4 */ ++ #define LPA_PAUSE 0x0400 /* PAUSE */ ++ #define LPA_ASYPAUSE 0x0800 /* Asymmetrical PAUSE */ ++ #define LPA_RFAULT 0x2000 /* Link partner faulted */ ++ #define LPA_LPACK 0x4000 /* Link partner acked us */ ++ #define LPA_NPAGE 0x8000 /* Next page bit */ ++ ++/* 1000BASE-T Control Register */ ++#define MII_GIGA_CR 0x09 ++ #define GIGA_CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ ++ #define GIGA_CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ ++ #define GIGA_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ ++ /* 0=DTE device */ ++ #define GIGA_CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ ++ /* 0=Configure PHY as Slave */ ++ #define GIGA_CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ ++ /* 0=Automatic Master/Slave config */ ++ #define GIGA_CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ ++ #define GIGA_CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ ++ #define GIGA_CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ ++ #define GIGA_CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ ++ #define GIGA_CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ ++ #define GIGA_CR_1000T_SPEED_MASK 0x0300 ++ #define GIGA_CR_1000T_DEFAULT_CAP 0x0300 ++ ++/* 1000BASE-T Status Register */ ++#define MII_GIGA_SR 0x0A ++ ++/* PHY Specific Status Register */ ++#define MII_GIGA_PSSR 0x11 ++ #define GIGA_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ ++ #define GIGA_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ ++ #define GIGA_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ ++ #define GIGA_PSSR_10MBS 0x0000 /* 00=10Mbs */ ++ #define GIGA_PSSR_100MBS 0x4000 /* 01=100Mbs */ ++ #define GIGA_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ ++ ++/* PHY Interrupt Enable Register */ ++#define MII_IER 0x12 ++ #define IER_LINK_UP 0x0400 ++ #define IER_LINK_DOWN 0x0800 ++ ++/* PHY Interrupt Status Register */ ++#define MII_ISR 0x13 ++ #define ISR_LINK_UP 0x0400 ++ #define ISR_LINK_DOWN 0x0800 ++ ++/* Cable-Detect-Test Control Register */ ++#define MII_CDTC 0x16 ++ #define CDTC_EN 1 /* sc */ ++ #define CDTC_PAIR_OFFSET 8 ++ #define CDTC_PAIR_MASK 3 ++ ++ ++/* Cable-Detect-Test Status Register */ ++#define MII_CDTS 0x1C ++ #define CDTS_STATUS_OFFSET 8 ++ #define CDTS_STATUS_MASK 3 ++ #define CDTS_STATUS_NORMAL 0 ++ #define CDTS_STATUS_SHORT 1 ++ #define CDTS_STATUS_OPEN 2 ++ #define CDTS_STATUS_INVALID 3 ++ ++#define MII_DBG_ADDR 0x1D ++#define MII_DBG_DATA 0x1E ++#endif +\ No newline at end of file +diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig +index 872df3ef..7936ae4c 100644 +--- a/drivers/net/ppp/Kconfig ++++ b/drivers/net/ppp/Kconfig +@@ -148,6 +148,23 @@ config PPPOL2TP + used by ISPs and enterprises to tunnel PPP traffic over UDP + tunnels. L2TP is replacing PPTP for VPN uses. + ++config PPPOLAC ++ tristate "PPP on L2TP Access Concentrator" ++ depends on PPP && INET ++ help ++ L2TP (RFC 2661) is a tunneling protocol widely used in virtual private ++ networks. This driver handles L2TP data packets between a UDP socket ++ and a PPP channel, but only permits one session per socket. Thus it is ++ fairly simple and suited for clients. ++ ++config PPPOPNS ++ tristate "PPP on PPTP Network Server" ++ depends on PPP && INET ++ help ++ PPTP (RFC 2637) is a tunneling protocol widely used in virtual private ++ networks. This driver handles PPTP data packets between a RAW socket ++ and a PPP channel. It is fairly simple and easy to use. ++ + config PPP_ASYNC + tristate "PPP support for async serial ports" + depends on PPP +diff --git a/drivers/net/ppp/Makefile b/drivers/net/ppp/Makefile +index a6b6297b..d283d03c 100644 +--- a/drivers/net/ppp/Makefile ++++ b/drivers/net/ppp/Makefile +@@ -11,3 +11,5 @@ obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o + obj-$(CONFIG_PPPOE) += pppox.o pppoe.o + obj-$(CONFIG_PPPOL2TP) += pppox.o + obj-$(CONFIG_PPTP) += pppox.o pptp.o ++obj-$(CONFIG_PPPOLAC) += pppox.o pppolac.o ++obj-$(CONFIG_PPPOPNS) += pppox.o pppopns.o +diff --git a/drivers/net/ppp/pppolac.c b/drivers/net/ppp/pppolac.c +new file mode 100644 +index 00000000..a5d3d634 +--- /dev/null ++++ b/drivers/net/ppp/pppolac.c +@@ -0,0 +1,449 @@ ++/* drivers/net/pppolac.c ++ * ++ * Driver for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661) ++ * ++ * Copyright (C) 2009 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++/* This driver handles L2TP data packets between a UDP socket and a PPP channel. ++ * The socket must keep connected, and only one session per socket is permitted. ++ * Sequencing of outgoing packets is controlled by LNS. Incoming packets with ++ * sequences are reordered within a sliding window of one second. Currently ++ * reordering only happens when a packet is received. It is done for simplicity ++ * since no additional locks or threads are required. This driver only works on ++ * IPv4 due to the lack of UDP encapsulation support in IPv6. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define L2TP_CONTROL_BIT 0x80 ++#define L2TP_LENGTH_BIT 0x40 ++#define L2TP_SEQUENCE_BIT 0x08 ++#define L2TP_OFFSET_BIT 0x02 ++#define L2TP_VERSION 0x02 ++#define L2TP_VERSION_MASK 0x0F ++ ++#define PPP_ADDR 0xFF ++#define PPP_CTRL 0x03 ++ ++union unaligned { ++ __u32 u32; ++} __attribute__((packed)); ++ ++static inline union unaligned *unaligned(void *ptr) ++{ ++ return (union unaligned *)ptr; ++} ++ ++struct meta { ++ __u32 sequence; ++ __u32 timestamp; ++}; ++ ++static inline struct meta *skb_meta(struct sk_buff *skb) ++{ ++ return (struct meta *)skb->cb; ++} ++ ++/******************************************************************************/ ++ ++static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) ++{ ++ struct sock *sk = (struct sock *)sk_udp->sk_user_data; ++ struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac; ++ struct meta *meta = skb_meta(skb); ++ __u32 now = jiffies; ++ __u8 bits; ++ __u8 *ptr; ++ ++ /* Drop the packet if L2TP header is missing. */ ++ if (skb->len < sizeof(struct udphdr) + 6) ++ goto drop; ++ ++ /* Put it back if it is a control packet. */ ++ if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_BIT) ++ return opt->backlog_rcv(sk_udp, skb); ++ ++ /* Skip UDP header. */ ++ skb_pull(skb, sizeof(struct udphdr)); ++ ++ /* Check the version. */ ++ if ((skb->data[1] & L2TP_VERSION_MASK) != L2TP_VERSION) ++ goto drop; ++ bits = skb->data[0]; ++ ptr = &skb->data[2]; ++ ++ /* Check the length if it is present. */ ++ if (bits & L2TP_LENGTH_BIT) { ++ if ((ptr[0] << 8 | ptr[1]) != skb->len) ++ goto drop; ++ ptr += 2; ++ } ++ ++ /* Skip all fields including optional ones. */ ++ if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_BIT ? 4 : 0) + ++ (bits & L2TP_LENGTH_BIT ? 2 : 0) + ++ (bits & L2TP_OFFSET_BIT ? 2 : 0))) ++ goto drop; ++ ++ /* Skip the offset padding if it is present. */ ++ if (bits & L2TP_OFFSET_BIT && ++ !skb_pull(skb, skb->data[-2] << 8 | skb->data[-1])) ++ goto drop; ++ ++ /* Check the tunnel and the session. */ ++ if (unaligned(ptr)->u32 != opt->local) ++ goto drop; ++ ++ /* Check the sequence if it is present. */ ++ if (bits & L2TP_SEQUENCE_BIT) { ++ meta->sequence = ptr[4] << 8 | ptr[5]; ++ if ((__s16)(meta->sequence - opt->recv_sequence) < 0) ++ goto drop; ++ } ++ ++ /* Skip PPP address and control if they are present. */ ++ if (skb->len >= 2 && skb->data[0] == PPP_ADDR && ++ skb->data[1] == PPP_CTRL) ++ skb_pull(skb, 2); ++ ++ /* Fix PPP protocol if it is compressed. */ ++ if (skb->len >= 1 && skb->data[0] & 1) ++ skb_push(skb, 1)[0] = 0; ++ ++ /* Drop the packet if PPP protocol is missing. */ ++ if (skb->len < 2) ++ goto drop; ++ ++ /* Perform reordering if sequencing is enabled. */ ++ atomic_set(&opt->sequencing, bits & L2TP_SEQUENCE_BIT); ++ if (bits & L2TP_SEQUENCE_BIT) { ++ struct sk_buff *skb1; ++ ++ /* Insert the packet into receive queue in order. */ ++ skb_set_owner_r(skb, sk); ++ skb_queue_walk(&sk->sk_receive_queue, skb1) { ++ struct meta *meta1 = skb_meta(skb1); ++ __s16 order = meta->sequence - meta1->sequence; ++ if (order == 0) ++ goto drop; ++ if (order < 0) { ++ meta->timestamp = meta1->timestamp; ++ skb_insert(skb1, skb, &sk->sk_receive_queue); ++ skb = NULL; ++ break; ++ } ++ } ++ if (skb) { ++ meta->timestamp = now; ++ skb_queue_tail(&sk->sk_receive_queue, skb); ++ } ++ ++ /* Remove packets from receive queue as long as ++ * 1. the receive buffer is full, ++ * 2. they are queued longer than one second, or ++ * 3. there are no missing packets before them. */ ++ skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) { ++ meta = skb_meta(skb); ++ if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && ++ now - meta->timestamp < HZ && ++ meta->sequence != opt->recv_sequence) ++ break; ++ skb_unlink(skb, &sk->sk_receive_queue); ++ opt->recv_sequence = (__u16)(meta->sequence + 1); ++ skb_orphan(skb); ++ ppp_input(&pppox_sk(sk)->chan, skb); ++ } ++ return NET_RX_SUCCESS; ++ } ++ ++ /* Flush receive queue if sequencing is disabled. */ ++ skb_queue_purge(&sk->sk_receive_queue); ++ skb_orphan(skb); ++ ppp_input(&pppox_sk(sk)->chan, skb); ++ return NET_RX_SUCCESS; ++drop: ++ kfree_skb(skb); ++ return NET_RX_DROP; ++} ++ ++static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb) ++{ ++ sock_hold(sk_udp); ++ sk_receive_skb(sk_udp, skb, 0); ++ return 0; ++} ++ ++static struct sk_buff_head delivery_queue; ++ ++static void pppolac_xmit_core(struct work_struct *delivery_work) ++{ ++ mm_segment_t old_fs = get_fs(); ++ struct sk_buff *skb; ++ ++ set_fs(KERNEL_DS); ++ while ((skb = skb_dequeue(&delivery_queue))) { ++ struct sock *sk_udp = skb->sk; ++ struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len}; ++ struct msghdr msg = { ++ .msg_iov = (struct iovec *)&iov, ++ .msg_iovlen = 1, ++ .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, ++ }; ++ sk_udp->sk_prot->sendmsg(NULL, sk_udp, &msg, skb->len); ++ kfree_skb(skb); ++ } ++ set_fs(old_fs); ++} ++ ++static DECLARE_WORK(delivery_work, pppolac_xmit_core); ++ ++static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb) ++{ ++ struct sock *sk_udp = (struct sock *)chan->private; ++ struct pppolac_opt *opt = &pppox_sk(sk_udp->sk_user_data)->proto.lac; ++ ++ /* Install PPP address and control. */ ++ skb_push(skb, 2); ++ skb->data[0] = PPP_ADDR; ++ skb->data[1] = PPP_CTRL; ++ ++ /* Install L2TP header. */ ++ if (atomic_read(&opt->sequencing)) { ++ skb_push(skb, 10); ++ skb->data[0] = L2TP_SEQUENCE_BIT; ++ skb->data[6] = opt->xmit_sequence >> 8; ++ skb->data[7] = opt->xmit_sequence; ++ skb->data[8] = 0; ++ skb->data[9] = 0; ++ opt->xmit_sequence++; ++ } else { ++ skb_push(skb, 6); ++ skb->data[0] = 0; ++ } ++ skb->data[1] = L2TP_VERSION; ++ unaligned(&skb->data[2])->u32 = opt->remote; ++ ++ /* Now send the packet via the delivery queue. */ ++ skb_set_owner_w(skb, sk_udp); ++ skb_queue_tail(&delivery_queue, skb); ++ schedule_work(&delivery_work); ++ return 1; ++} ++ ++/******************************************************************************/ ++ ++static struct ppp_channel_ops pppolac_channel_ops = { ++ .start_xmit = pppolac_xmit, ++}; ++ ++static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr, ++ int addrlen, int flags) ++{ ++ struct sock *sk = sock->sk; ++ struct pppox_sock *po = pppox_sk(sk); ++ struct sockaddr_pppolac *addr = (struct sockaddr_pppolac *)useraddr; ++ struct socket *sock_udp = NULL; ++ struct sock *sk_udp; ++ int error; ++ ++ if (addrlen != sizeof(struct sockaddr_pppolac) || ++ !addr->local.tunnel || !addr->local.session || ++ !addr->remote.tunnel || !addr->remote.session) { ++ return -EINVAL; ++ } ++ ++ lock_sock(sk); ++ error = -EALREADY; ++ if (sk->sk_state != PPPOX_NONE) ++ goto out; ++ ++ sock_udp = sockfd_lookup(addr->udp_socket, &error); ++ if (!sock_udp) ++ goto out; ++ sk_udp = sock_udp->sk; ++ lock_sock(sk_udp); ++ ++ /* Remove this check when IPv6 supports UDP encapsulation. */ ++ error = -EAFNOSUPPORT; ++ if (sk_udp->sk_family != AF_INET) ++ goto out; ++ error = -EPROTONOSUPPORT; ++ if (sk_udp->sk_protocol != IPPROTO_UDP) ++ goto out; ++ error = -EDESTADDRREQ; ++ if (sk_udp->sk_state != TCP_ESTABLISHED) ++ goto out; ++ error = -EBUSY; ++ if (udp_sk(sk_udp)->encap_type || sk_udp->sk_user_data) ++ goto out; ++ if (!sk_udp->sk_bound_dev_if) { ++ struct dst_entry *dst = sk_dst_get(sk_udp); ++ error = -ENODEV; ++ if (!dst) ++ goto out; ++ sk_udp->sk_bound_dev_if = dst->dev->ifindex; ++ dst_release(dst); ++ } ++ ++ po->chan.hdrlen = 12; ++ po->chan.private = sk_udp; ++ po->chan.ops = &pppolac_channel_ops; ++ po->chan.mtu = PPP_MRU - 80; ++ po->proto.lac.local = unaligned(&addr->local)->u32; ++ po->proto.lac.remote = unaligned(&addr->remote)->u32; ++ atomic_set(&po->proto.lac.sequencing, 1); ++ po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv; ++ ++ error = ppp_register_channel(&po->chan); ++ if (error) ++ goto out; ++ ++ sk->sk_state = PPPOX_CONNECTED; ++ udp_sk(sk_udp)->encap_type = UDP_ENCAP_L2TPINUDP; ++ udp_sk(sk_udp)->encap_rcv = pppolac_recv; ++ sk_udp->sk_backlog_rcv = pppolac_recv_core; ++ sk_udp->sk_user_data = sk; ++out: ++ if (sock_udp) { ++ release_sock(sk_udp); ++ if (error) ++ sockfd_put(sock_udp); ++ } ++ release_sock(sk); ++ return error; ++} ++ ++static int pppolac_release(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ if (!sk) ++ return 0; ++ ++ lock_sock(sk); ++ if (sock_flag(sk, SOCK_DEAD)) { ++ release_sock(sk); ++ return -EBADF; ++ } ++ ++ if (sk->sk_state != PPPOX_NONE) { ++ struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private; ++ lock_sock(sk_udp); ++ skb_queue_purge(&sk->sk_receive_queue); ++ pppox_unbind_sock(sk); ++ udp_sk(sk_udp)->encap_type = 0; ++ udp_sk(sk_udp)->encap_rcv = NULL; ++ sk_udp->sk_backlog_rcv = pppox_sk(sk)->proto.lac.backlog_rcv; ++ sk_udp->sk_user_data = NULL; ++ release_sock(sk_udp); ++ sockfd_put(sk_udp->sk_socket); ++ } ++ ++ sock_orphan(sk); ++ sock->sk = NULL; ++ release_sock(sk); ++ sock_put(sk); ++ return 0; ++} ++ ++/******************************************************************************/ ++ ++static struct proto pppolac_proto = { ++ .name = "PPPOLAC", ++ .owner = THIS_MODULE, ++ .obj_size = sizeof(struct pppox_sock), ++}; ++ ++static struct proto_ops pppolac_proto_ops = { ++ .family = PF_PPPOX, ++ .owner = THIS_MODULE, ++ .release = pppolac_release, ++ .bind = sock_no_bind, ++ .connect = pppolac_connect, ++ .socketpair = sock_no_socketpair, ++ .accept = sock_no_accept, ++ .getname = sock_no_getname, ++ .poll = sock_no_poll, ++ .ioctl = pppox_ioctl, ++ .listen = sock_no_listen, ++ .shutdown = sock_no_shutdown, ++ .setsockopt = sock_no_setsockopt, ++ .getsockopt = sock_no_getsockopt, ++ .sendmsg = sock_no_sendmsg, ++ .recvmsg = sock_no_recvmsg, ++ .mmap = sock_no_mmap, ++}; ++ ++static int pppolac_create(struct net *net, struct socket *sock) ++{ ++ struct sock *sk; ++ ++ sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppolac_proto); ++ if (!sk) ++ return -ENOMEM; ++ ++ sock_init_data(sock, sk); ++ sock->state = SS_UNCONNECTED; ++ sock->ops = &pppolac_proto_ops; ++ sk->sk_protocol = PX_PROTO_OLAC; ++ sk->sk_state = PPPOX_NONE; ++ return 0; ++} ++ ++/******************************************************************************/ ++ ++static struct pppox_proto pppolac_pppox_proto = { ++ .create = pppolac_create, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init pppolac_init(void) ++{ ++ int error; ++ ++ error = proto_register(&pppolac_proto, 0); ++ if (error) ++ return error; ++ ++ error = register_pppox_proto(PX_PROTO_OLAC, &pppolac_pppox_proto); ++ if (error) ++ proto_unregister(&pppolac_proto); ++ else ++ skb_queue_head_init(&delivery_queue); ++ return error; ++} ++ ++static void __exit pppolac_exit(void) ++{ ++ unregister_pppox_proto(PX_PROTO_OLAC); ++ proto_unregister(&pppolac_proto); ++} ++ ++module_init(pppolac_init); ++module_exit(pppolac_exit); ++ ++MODULE_DESCRIPTION("PPP on L2TP Access Concentrator (PPPoLAC)"); ++MODULE_AUTHOR("Chia-chi Yeh "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c +new file mode 100644 +index 00000000..6016d29c +--- /dev/null ++++ b/drivers/net/ppp/pppopns.c +@@ -0,0 +1,428 @@ ++/* drivers/net/pppopns.c ++ * ++ * Driver for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) ++ * ++ * Copyright (C) 2009 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++/* This driver handles PPTP data packets between a RAW socket and a PPP channel. ++ * The socket is created in the kernel space and connected to the same address ++ * of the control socket. Outgoing packets are always sent with sequences but ++ * without acknowledgements. Incoming packets with sequences are reordered ++ * within a sliding window of one second. Currently reordering only happens when ++ * a packet is received. It is done for simplicity since no additional locks or ++ * threads are required. This driver should work on both IPv4 and IPv6. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define GRE_HEADER_SIZE 8 ++ ++#define PPTP_GRE_BITS htons(0x2001) ++#define PPTP_GRE_BITS_MASK htons(0xEF7F) ++#define PPTP_GRE_SEQ_BIT htons(0x1000) ++#define PPTP_GRE_ACK_BIT htons(0x0080) ++#define PPTP_GRE_TYPE htons(0x880B) ++ ++#define PPP_ADDR 0xFF ++#define PPP_CTRL 0x03 ++ ++struct header { ++ __u16 bits; ++ __u16 type; ++ __u16 length; ++ __u16 call; ++ __u32 sequence; ++} __attribute__((packed)); ++ ++struct meta { ++ __u32 sequence; ++ __u32 timestamp; ++}; ++ ++static inline struct meta *skb_meta(struct sk_buff *skb) ++{ ++ return (struct meta *)skb->cb; ++} ++ ++/******************************************************************************/ ++ ++static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) ++{ ++ struct sock *sk = (struct sock *)sk_raw->sk_user_data; ++ struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns; ++ struct meta *meta = skb_meta(skb); ++ __u32 now = jiffies; ++ struct header *hdr; ++ ++ /* Skip transport header */ ++ skb_pull(skb, skb_transport_header(skb) - skb->data); ++ ++ /* Drop the packet if GRE header is missing. */ ++ if (skb->len < GRE_HEADER_SIZE) ++ goto drop; ++ hdr = (struct header *)skb->data; ++ ++ /* Check the header. */ ++ if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || ++ (hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS) ++ goto drop; ++ ++ /* Skip all fields including optional ones. */ ++ if (!skb_pull(skb, GRE_HEADER_SIZE + ++ (hdr->bits & PPTP_GRE_SEQ_BIT ? 4 : 0) + ++ (hdr->bits & PPTP_GRE_ACK_BIT ? 4 : 0))) ++ goto drop; ++ ++ /* Check the length. */ ++ if (skb->len != ntohs(hdr->length)) ++ goto drop; ++ ++ /* Check the sequence if it is present. */ ++ if (hdr->bits & PPTP_GRE_SEQ_BIT) { ++ meta->sequence = ntohl(hdr->sequence); ++ if ((__s32)(meta->sequence - opt->recv_sequence) < 0) ++ goto drop; ++ } ++ ++ /* Skip PPP address and control if they are present. */ ++ if (skb->len >= 2 && skb->data[0] == PPP_ADDR && ++ skb->data[1] == PPP_CTRL) ++ skb_pull(skb, 2); ++ ++ /* Fix PPP protocol if it is compressed. */ ++ if (skb->len >= 1 && skb->data[0] & 1) ++ skb_push(skb, 1)[0] = 0; ++ ++ /* Drop the packet if PPP protocol is missing. */ ++ if (skb->len < 2) ++ goto drop; ++ ++ /* Perform reordering if sequencing is enabled. */ ++ if (hdr->bits & PPTP_GRE_SEQ_BIT) { ++ struct sk_buff *skb1; ++ ++ /* Insert the packet into receive queue in order. */ ++ skb_set_owner_r(skb, sk); ++ skb_queue_walk(&sk->sk_receive_queue, skb1) { ++ struct meta *meta1 = skb_meta(skb1); ++ __s32 order = meta->sequence - meta1->sequence; ++ if (order == 0) ++ goto drop; ++ if (order < 0) { ++ meta->timestamp = meta1->timestamp; ++ skb_insert(skb1, skb, &sk->sk_receive_queue); ++ skb = NULL; ++ break; ++ } ++ } ++ if (skb) { ++ meta->timestamp = now; ++ skb_queue_tail(&sk->sk_receive_queue, skb); ++ } ++ ++ /* Remove packets from receive queue as long as ++ * 1. the receive buffer is full, ++ * 2. they are queued longer than one second, or ++ * 3. there are no missing packets before them. */ ++ skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) { ++ meta = skb_meta(skb); ++ if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && ++ now - meta->timestamp < HZ && ++ meta->sequence != opt->recv_sequence) ++ break; ++ skb_unlink(skb, &sk->sk_receive_queue); ++ opt->recv_sequence = meta->sequence + 1; ++ skb_orphan(skb); ++ ppp_input(&pppox_sk(sk)->chan, skb); ++ } ++ return NET_RX_SUCCESS; ++ } ++ ++ /* Flush receive queue if sequencing is disabled. */ ++ skb_queue_purge(&sk->sk_receive_queue); ++ skb_orphan(skb); ++ ppp_input(&pppox_sk(sk)->chan, skb); ++ return NET_RX_SUCCESS; ++drop: ++ kfree_skb(skb); ++ return NET_RX_DROP; ++} ++ ++static void pppopns_recv(struct sock *sk_raw, int length) ++{ ++ struct sk_buff *skb; ++ while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) { ++ sock_hold(sk_raw); ++ sk_receive_skb(sk_raw, skb, 0); ++ } ++} ++ ++static struct sk_buff_head delivery_queue; ++ ++static void pppopns_xmit_core(struct work_struct *delivery_work) ++{ ++ mm_segment_t old_fs = get_fs(); ++ struct sk_buff *skb; ++ ++ set_fs(KERNEL_DS); ++ while ((skb = skb_dequeue(&delivery_queue))) { ++ struct sock *sk_raw = skb->sk; ++ struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len}; ++ struct msghdr msg = { ++ .msg_iov = (struct iovec *)&iov, ++ .msg_iovlen = 1, ++ .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, ++ }; ++ sk_raw->sk_prot->sendmsg(NULL, sk_raw, &msg, skb->len); ++ kfree_skb(skb); ++ } ++ set_fs(old_fs); ++} ++ ++static DECLARE_WORK(delivery_work, pppopns_xmit_core); ++ ++static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) ++{ ++ struct sock *sk_raw = (struct sock *)chan->private; ++ struct pppopns_opt *opt = &pppox_sk(sk_raw->sk_user_data)->proto.pns; ++ struct header *hdr; ++ __u16 length; ++ ++ /* Install PPP address and control. */ ++ skb_push(skb, 2); ++ skb->data[0] = PPP_ADDR; ++ skb->data[1] = PPP_CTRL; ++ length = skb->len; ++ ++ /* Install PPTP GRE header. */ ++ hdr = (struct header *)skb_push(skb, 12); ++ hdr->bits = PPTP_GRE_BITS | PPTP_GRE_SEQ_BIT; ++ hdr->type = PPTP_GRE_TYPE; ++ hdr->length = htons(length); ++ hdr->call = opt->remote; ++ hdr->sequence = htonl(opt->xmit_sequence); ++ opt->xmit_sequence++; ++ ++ /* Now send the packet via the delivery queue. */ ++ skb_set_owner_w(skb, sk_raw); ++ skb_queue_tail(&delivery_queue, skb); ++ schedule_work(&delivery_work); ++ return 1; ++} ++ ++/******************************************************************************/ ++ ++static struct ppp_channel_ops pppopns_channel_ops = { ++ .start_xmit = pppopns_xmit, ++}; ++ ++static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, ++ int addrlen, int flags) ++{ ++ struct sock *sk = sock->sk; ++ struct pppox_sock *po = pppox_sk(sk); ++ struct sockaddr_pppopns *addr = (struct sockaddr_pppopns *)useraddr; ++ struct sockaddr_storage ss; ++ struct socket *sock_tcp = NULL; ++ struct socket *sock_raw = NULL; ++ struct sock *sk_tcp; ++ struct sock *sk_raw; ++ int error; ++ ++ if (addrlen != sizeof(struct sockaddr_pppopns)) ++ return -EINVAL; ++ ++ lock_sock(sk); ++ error = -EALREADY; ++ if (sk->sk_state != PPPOX_NONE) ++ goto out; ++ ++ sock_tcp = sockfd_lookup(addr->tcp_socket, &error); ++ if (!sock_tcp) ++ goto out; ++ sk_tcp = sock_tcp->sk; ++ error = -EPROTONOSUPPORT; ++ if (sk_tcp->sk_protocol != IPPROTO_TCP) ++ goto out; ++ addrlen = sizeof(struct sockaddr_storage); ++ error = kernel_getpeername(sock_tcp, (struct sockaddr *)&ss, &addrlen); ++ if (error) ++ goto out; ++ if (!sk_tcp->sk_bound_dev_if) { ++ struct dst_entry *dst = sk_dst_get(sk_tcp); ++ error = -ENODEV; ++ if (!dst) ++ goto out; ++ sk_tcp->sk_bound_dev_if = dst->dev->ifindex; ++ dst_release(dst); ++ } ++ ++ error = sock_create(ss.ss_family, SOCK_RAW, IPPROTO_GRE, &sock_raw); ++ if (error) ++ goto out; ++ sk_raw = sock_raw->sk; ++ sk_raw->sk_bound_dev_if = sk_tcp->sk_bound_dev_if; ++ error = kernel_connect(sock_raw, (struct sockaddr *)&ss, addrlen, 0); ++ if (error) ++ goto out; ++ ++ po->chan.hdrlen = 14; ++ po->chan.private = sk_raw; ++ po->chan.ops = &pppopns_channel_ops; ++ po->chan.mtu = PPP_MRU - 80; ++ po->proto.pns.local = addr->local; ++ po->proto.pns.remote = addr->remote; ++ po->proto.pns.data_ready = sk_raw->sk_data_ready; ++ po->proto.pns.backlog_rcv = sk_raw->sk_backlog_rcv; ++ ++ error = ppp_register_channel(&po->chan); ++ if (error) ++ goto out; ++ ++ sk->sk_state = PPPOX_CONNECTED; ++ lock_sock(sk_raw); ++ sk_raw->sk_data_ready = pppopns_recv; ++ sk_raw->sk_backlog_rcv = pppopns_recv_core; ++ sk_raw->sk_user_data = sk; ++ release_sock(sk_raw); ++out: ++ if (sock_tcp) ++ sockfd_put(sock_tcp); ++ if (error && sock_raw) ++ sock_release(sock_raw); ++ release_sock(sk); ++ return error; ++} ++ ++static int pppopns_release(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ if (!sk) ++ return 0; ++ ++ lock_sock(sk); ++ if (sock_flag(sk, SOCK_DEAD)) { ++ release_sock(sk); ++ return -EBADF; ++ } ++ ++ if (sk->sk_state != PPPOX_NONE) { ++ struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private; ++ lock_sock(sk_raw); ++ skb_queue_purge(&sk->sk_receive_queue); ++ pppox_unbind_sock(sk); ++ sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready; ++ sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv; ++ sk_raw->sk_user_data = NULL; ++ release_sock(sk_raw); ++ sock_release(sk_raw->sk_socket); ++ } ++ ++ sock_orphan(sk); ++ sock->sk = NULL; ++ release_sock(sk); ++ sock_put(sk); ++ return 0; ++} ++ ++/******************************************************************************/ ++ ++static struct proto pppopns_proto = { ++ .name = "PPPOPNS", ++ .owner = THIS_MODULE, ++ .obj_size = sizeof(struct pppox_sock), ++}; ++ ++static struct proto_ops pppopns_proto_ops = { ++ .family = PF_PPPOX, ++ .owner = THIS_MODULE, ++ .release = pppopns_release, ++ .bind = sock_no_bind, ++ .connect = pppopns_connect, ++ .socketpair = sock_no_socketpair, ++ .accept = sock_no_accept, ++ .getname = sock_no_getname, ++ .poll = sock_no_poll, ++ .ioctl = pppox_ioctl, ++ .listen = sock_no_listen, ++ .shutdown = sock_no_shutdown, ++ .setsockopt = sock_no_setsockopt, ++ .getsockopt = sock_no_getsockopt, ++ .sendmsg = sock_no_sendmsg, ++ .recvmsg = sock_no_recvmsg, ++ .mmap = sock_no_mmap, ++}; ++ ++static int pppopns_create(struct net *net, struct socket *sock) ++{ ++ struct sock *sk; ++ ++ sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppopns_proto); ++ if (!sk) ++ return -ENOMEM; ++ ++ sock_init_data(sock, sk); ++ sock->state = SS_UNCONNECTED; ++ sock->ops = &pppopns_proto_ops; ++ sk->sk_protocol = PX_PROTO_OPNS; ++ sk->sk_state = PPPOX_NONE; ++ return 0; ++} ++ ++/******************************************************************************/ ++ ++static struct pppox_proto pppopns_pppox_proto = { ++ .create = pppopns_create, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init pppopns_init(void) ++{ ++ int error; ++ ++ error = proto_register(&pppopns_proto, 0); ++ if (error) ++ return error; ++ ++ error = register_pppox_proto(PX_PROTO_OPNS, &pppopns_pppox_proto); ++ if (error) ++ proto_unregister(&pppopns_proto); ++ else ++ skb_queue_head_init(&delivery_queue); ++ return error; ++} ++ ++static void __exit pppopns_exit(void) ++{ ++ unregister_pppox_proto(PX_PROTO_OPNS); ++ proto_unregister(&pppopns_proto); ++} ++ ++module_init(pppopns_init); ++module_exit(pppopns_exit); ++ ++MODULE_DESCRIPTION("PPP on PPTP Network Server (PPPoPNS)"); ++MODULE_AUTHOR("Chia-chi Yeh "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/net/tun.c b/drivers/net/tun.c +index 147b6284..279d860e 100644 +--- a/drivers/net/tun.c ++++ b/drivers/net/tun.c +@@ -1254,6 +1254,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, + int vnet_hdr_sz; + int ret; + ++#ifdef CONFIG_ANDROID_PARANOID_NETWORK ++ if (cmd != TUNGETIFF && !capable(CAP_NET_ADMIN)) { ++ return -EPERM; ++ } ++#endif ++ + if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) { + if (copy_from_user(&ifr, argp, ifreq_len)) + return -EFAULT; +diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig +index abd3b71c..d9252ff7 100644 +--- a/drivers/net/wireless/Kconfig ++++ b/drivers/net/wireless/Kconfig +@@ -268,9 +268,15 @@ config MWL8K + To compile this driver as a module, choose M here: the module + will be called mwl8k. If unsure, say N. + ++config WIFI_CONTROL_FUNC ++ bool "Enable WiFi control function abstraction" ++ help ++ Enables Power/Reset/Carddetect function abstraction ++ + source "drivers/net/wireless/ath/Kconfig" + source "drivers/net/wireless/b43/Kconfig" + source "drivers/net/wireless/b43legacy/Kconfig" ++source "drivers/net/wireless/bcmdhd/Kconfig" + source "drivers/net/wireless/brcm80211/Kconfig" + source "drivers/net/wireless/hostap/Kconfig" + source "drivers/net/wireless/ipw2x00/Kconfig" +diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile +index 98db7619..8db066f3 100644 +--- a/drivers/net/wireless/Makefile ++++ b/drivers/net/wireless/Makefile +@@ -59,5 +59,7 @@ obj-$(CONFIG_IWM) += iwmc3200wifi/ + + obj-$(CONFIG_MWIFIEX) += mwifiex/ + ++obj-$(CONFIG_BCMDHD) += bcmdhd/ ++ + obj-$(CONFIG_BRCMFMAC) += brcm80211/ + obj-$(CONFIG_BRCMSMAC) += brcm80211/ +diff --git a/drivers/net/wireless/bcmdhd/Kconfig b/drivers/net/wireless/bcmdhd/Kconfig +new file mode 100644 +index 00000000..231ae187 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/Kconfig +@@ -0,0 +1,47 @@ ++config BCMDHD ++ tristate "Broadcom 4329/30 wireless cards support" ++ depends on MMC ++ ---help--- ++ This module adds support for wireless adapters based on ++ Broadcom 4329/30 chipset. ++ ++ This driver uses the kernel's wireless extensions subsystem. ++ ++ If you choose to build a module, it'll be called dhd. Say M if ++ unsure. ++ ++config BCMDHD_FW_PATH ++ depends on BCMDHD ++ string "Firmware path" ++ default "/system/etc/firmware/fw_bcmdhd.bin" ++ ---help--- ++ Path to the firmware file. ++ ++config BCMDHD_NVRAM_PATH ++ depends on BCMDHD ++ string "NVRAM path" ++ default "/system/etc/wifi/bcmdhd.cal" ++ ---help--- ++ Path to the calibration file. ++ ++config BCMDHD_WEXT ++ bool "Enable WEXT support" ++ depends on BCMDHD && CFG80211 = n ++ select WIRELESS_EXT ++ select WEXT_PRIV ++ help ++ Enables WEXT support ++ ++config DHD_USE_STATIC_BUF ++ bool "Enable memory preallocation" ++ depends on BCMDHD ++ default n ++ ---help--- ++ Use memory preallocated in platform ++ ++config DHD_USE_SCHED_SCAN ++ bool "Use CFG80211 sched scan" ++ depends on BCMDHD && CFG80211 ++ default n ++ ---help--- ++ Use CFG80211 sched scan +diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile +new file mode 100644 +index 00000000..85149537 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/Makefile +@@ -0,0 +1,45 @@ ++# bcmdhd for 43241 ++# ++ ++DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER \ ++ -DBCMDONGLEHOST -DUNRELEASEDCHIP -DBCMDMA32 -DBCMFILEIMAGE \ ++ -DDHDTHREAD -DDHD_DEBUG -DSDTEST -DBDC -DTOE \ ++ -DDHD_BCMEVENTS -DSHOW_EVENTS -DPROP_TXSTATUS -DBCMDBG \ ++ -DCUSTOMER_HW2 -DOOB_INTR_ONLY -DHW_OOB \ ++ -DMMC_SDIO_ABORT -DBCMSDIO -DBCMLXSDMMC -DBCMPLATFORM_BUS -DWLP2P \ ++ -DWIFI_ACT_FRAME -DARP_OFFLOAD_SUPPORT \ ++ -DKEEP_ALIVE -DGET_CUSTOM_MAC_ENABLE -DPKT_FILTER_SUPPORT \ ++ -DEMBEDDED_PLATFORM -DENABLE_INSMOD_NO_FW_LOAD -DPNO_SUPPORT \ ++ -DDHD_USE_IDLECOUNT -DSET_RANDOM_MAC_SOFTAP -DROAM_ENABLE -DVSDB \ ++ -DWL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST -DSDIO_CRC_ERROR_FIX \ ++ -DESCAN_RESULT_PATCH -DHT40_GO -DPASS_ARP_PACKET -DSUPPORT_PM2_ONLY \ ++ -DDHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT \ ++ -DCUSTOM_SDIO_F2_BLKSIZE=128 \ ++ -Idrivers/net/wireless/bcmdhd -Idrivers/net/wireless/bcmdhd/include ++ ++DHDOFILES = aiutils.o bcmsdh_sdmmc_linux.o dhd_linux.o siutils.o bcmutils.o \ ++ dhd_linux_sched.o dhd_sdio.o bcmwifi_channels.o bcmevent.o hndpmu.o \ ++ bcmsdh.o dhd_cdc.o bcmsdh_linux.o dhd_common.o linux_osl.o \ ++ bcmsdh_sdmmc.o dhd_custom_gpio.o sbutils.o wldev_common.o wl_android.o ++ ++obj-$(CONFIG_BCMDHD) += bcmdhd.o ++bcmdhd-objs += $(DHDOFILES) ++ifneq ($(CONFIG_WIRELESS_EXT),) ++bcmdhd-objs += wl_iw.o ++DHDCFLAGS += -DSOFTAP -DWL_WIRELESS_EXT -DUSE_IW ++endif ++ifneq ($(CONFIG_CFG80211),) ++bcmdhd-objs += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o dhd_cfg80211.o ++DHDCFLAGS += -DWL_CFG80211 -DWL_CFG80211_STA_EVENT -DWL_ENABLE_P2P_IF ++DHDCFLAGS += -DCUSTOM_ROAM_TRIGGER_SETTING=-65 ++DHDCFLAGS += -DCUSTOM_ROAM_DELTA_SETTING=15 ++DHDCFLAGS += -DCUSTOM_KEEP_ALIVE_SETTING=28000 ++DHDCFLAGS += -DCUSTOM_PNO_EVENT_LOCK_xTIME=7 ++endif ++ifneq ($(CONFIG_DHD_USE_SCHED_SCAN),) ++DHDCFLAGS += -DWL_SCHED_SCAN ++endif ++EXTRA_CFLAGS = $(DHDCFLAGS) ++ifeq ($(CONFIG_BCMDHD),m) ++EXTRA_LDFLAGS += --strip-debug ++endif +diff --git a/drivers/net/wireless/bcmdhd/aiutils.c b/drivers/net/wireless/bcmdhd/aiutils.c +new file mode 100644 +index 00000000..3ca17259 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/aiutils.c +@@ -0,0 +1,846 @@ ++/* ++ * Misc utility routines for accessing chip-specific features ++ * of the SiliconBackplane-based Broadcom chips. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: aiutils.c 347614 2012-07-27 10:24:51Z $ ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "siutils_priv.h" ++ ++#define BCM47162_DMP() (0) ++#define BCM5357_DMP() (0) ++#define remap_coreid(sih, coreid) (coreid) ++#define remap_corerev(sih, corerev) (corerev) ++ ++ ++ ++static uint32 ++get_erom_ent(si_t *sih, uint32 **eromptr, uint32 mask, uint32 match) ++{ ++ uint32 ent; ++ uint inv = 0, nom = 0; ++ ++ while (TRUE) { ++ ent = R_REG(si_osh(sih), *eromptr); ++ (*eromptr)++; ++ ++ if (mask == 0) ++ break; ++ ++ if ((ent & ER_VALID) == 0) { ++ inv++; ++ continue; ++ } ++ ++ if (ent == (ER_END | ER_VALID)) ++ break; ++ ++ if ((ent & mask) == match) ++ break; ++ ++ nom++; ++ } ++ ++ SI_VMSG(("%s: Returning ent 0x%08x\n", __FUNCTION__, ent)); ++ if (inv + nom) { ++ SI_VMSG((" after %d invalid and %d non-matching entries\n", inv, nom)); ++ } ++ return ent; ++} ++ ++static uint32 ++get_asd(si_t *sih, uint32 **eromptr, uint sp, uint ad, uint st, uint32 *addrl, uint32 *addrh, ++ uint32 *sizel, uint32 *sizeh) ++{ ++ uint32 asd, sz, szd; ++ ++ asd = get_erom_ent(sih, eromptr, ER_VALID, ER_VALID); ++ if (((asd & ER_TAG1) != ER_ADD) || ++ (((asd & AD_SP_MASK) >> AD_SP_SHIFT) != sp) || ++ ((asd & AD_ST_MASK) != st)) { ++ ++ (*eromptr)--; ++ return 0; ++ } ++ *addrl = asd & AD_ADDR_MASK; ++ if (asd & AD_AG32) ++ *addrh = get_erom_ent(sih, eromptr, 0, 0); ++ else ++ *addrh = 0; ++ *sizeh = 0; ++ sz = asd & AD_SZ_MASK; ++ if (sz == AD_SZ_SZD) { ++ szd = get_erom_ent(sih, eromptr, 0, 0); ++ *sizel = szd & SD_SZ_MASK; ++ if (szd & SD_SG32) ++ *sizeh = get_erom_ent(sih, eromptr, 0, 0); ++ } else ++ *sizel = AD_SZ_BASE << (sz >> AD_SZ_SHIFT); ++ ++ SI_VMSG((" SP %d, ad %d: st = %d, 0x%08x_0x%08x @ 0x%08x_0x%08x\n", ++ sp, ad, st, *sizeh, *sizel, *addrh, *addrl)); ++ ++ return asd; ++} ++ ++static void ++ai_hwfixup(si_info_t *sii) ++{ ++} ++ ++ ++ ++void ++ai_scan(si_t *sih, void *regs, uint devid) ++{ ++ si_info_t *sii = SI_INFO(sih); ++ chipcregs_t *cc = (chipcregs_t *)regs; ++ uint32 erombase, *eromptr, *eromlim; ++ ++ erombase = R_REG(sii->osh, &cc->eromptr); ++ ++ switch (BUSTYPE(sih->bustype)) { ++ case SI_BUS: ++ eromptr = (uint32 *)REG_MAP(erombase, SI_CORE_SIZE); ++ break; ++ ++ case PCI_BUS: ++ ++ sii->curwrap = (void *)((uintptr)regs + SI_CORE_SIZE); ++ ++ ++ OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, erombase); ++ eromptr = regs; ++ break; ++ ++ case SPI_BUS: ++ case SDIO_BUS: ++ eromptr = (uint32 *)(uintptr)erombase; ++ break; ++ ++ case PCMCIA_BUS: ++ default: ++ SI_ERROR(("Don't know how to do AXI enumertion on bus %d\n", sih->bustype)); ++ ASSERT(0); ++ return; ++ } ++ eromlim = eromptr + (ER_REMAPCONTROL / sizeof(uint32)); ++ ++ SI_VMSG(("ai_scan: regs = 0x%p, erombase = 0x%08x, eromptr = 0x%p, eromlim = 0x%p\n", ++ regs, erombase, eromptr, eromlim)); ++ while (eromptr < eromlim) { ++ uint32 cia, cib, cid, mfg, crev, nmw, nsw, nmp, nsp; ++ uint32 mpd, asd, addrl, addrh, sizel, sizeh; ++ uint i, j, idx; ++ bool br; ++ ++ br = FALSE; ++ ++ ++ cia = get_erom_ent(sih, &eromptr, ER_TAG, ER_CI); ++ if (cia == (ER_END | ER_VALID)) { ++ SI_VMSG(("Found END of erom after %d cores\n", sii->numcores)); ++ ai_hwfixup(sii); ++ return; ++ } ++ ++ cib = get_erom_ent(sih, &eromptr, 0, 0); ++ ++ if ((cib & ER_TAG) != ER_CI) { ++ SI_ERROR(("CIA not followed by CIB\n")); ++ goto error; ++ } ++ ++ cid = (cia & CIA_CID_MASK) >> CIA_CID_SHIFT; ++ mfg = (cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT; ++ crev = (cib & CIB_REV_MASK) >> CIB_REV_SHIFT; ++ nmw = (cib & CIB_NMW_MASK) >> CIB_NMW_SHIFT; ++ nsw = (cib & CIB_NSW_MASK) >> CIB_NSW_SHIFT; ++ nmp = (cib & CIB_NMP_MASK) >> CIB_NMP_SHIFT; ++ nsp = (cib & CIB_NSP_MASK) >> CIB_NSP_SHIFT; ++ ++#ifdef BCMDBG_SI ++ SI_VMSG(("Found component 0x%04x/0x%04x rev %d at erom addr 0x%p, with nmw = %d, " ++ "nsw = %d, nmp = %d & nsp = %d\n", ++ mfg, cid, crev, eromptr - 1, nmw, nsw, nmp, nsp)); ++#else ++ BCM_REFERENCE(crev); ++#endif ++ ++ if (((mfg == MFGID_ARM) && (cid == DEF_AI_COMP)) || (nsp == 0)) ++ continue; ++ if ((nmw + nsw == 0)) { ++ ++ if (cid == OOB_ROUTER_CORE_ID) { ++ asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE, ++ &addrl, &addrh, &sizel, &sizeh); ++ if (asd != 0) { ++ sii->oob_router = addrl; ++ } ++ } ++ if (cid != GMAC_COMMON_4706_CORE_ID) ++ continue; ++ } ++ ++ idx = sii->numcores; ++ ++ sii->cia[idx] = cia; ++ sii->cib[idx] = cib; ++ sii->coreid[idx] = remap_coreid(sih, cid); ++ ++ for (i = 0; i < nmp; i++) { ++ mpd = get_erom_ent(sih, &eromptr, ER_VALID, ER_VALID); ++ if ((mpd & ER_TAG) != ER_MP) { ++ SI_ERROR(("Not enough MP entries for component 0x%x\n", cid)); ++ goto error; ++ } ++ SI_VMSG((" Master port %d, mp: %d id: %d\n", i, ++ (mpd & MPD_MP_MASK) >> MPD_MP_SHIFT, ++ (mpd & MPD_MUI_MASK) >> MPD_MUI_SHIFT)); ++ } ++ ++ ++ asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE, &addrl, &addrh, &sizel, &sizeh); ++ if (asd == 0) { ++ do { ++ ++ asd = get_asd(sih, &eromptr, 0, 0, AD_ST_BRIDGE, &addrl, &addrh, ++ &sizel, &sizeh); ++ if (asd != 0) ++ br = TRUE; ++ else { ++ if (br == TRUE) { ++ break; ++ } ++ else if ((addrh != 0) || (sizeh != 0) || ++ (sizel != SI_CORE_SIZE)) { ++ SI_ERROR(("addrh = 0x%x\t sizeh = 0x%x\t size1 =" ++ "0x%x\n", addrh, sizeh, sizel)); ++ SI_ERROR(("First Slave ASD for" ++ "core 0x%04x malformed " ++ "(0x%08x)\n", cid, asd)); ++ goto error; ++ } ++ } ++ } while (1); ++ } ++ sii->coresba[idx] = addrl; ++ sii->coresba_size[idx] = sizel; ++ ++ j = 1; ++ do { ++ asd = get_asd(sih, &eromptr, 0, j, AD_ST_SLAVE, &addrl, &addrh, ++ &sizel, &sizeh); ++ if ((asd != 0) && (j == 1) && (sizel == SI_CORE_SIZE)) { ++ sii->coresba2[idx] = addrl; ++ sii->coresba2_size[idx] = sizel; ++ } ++ j++; ++ } while (asd != 0); ++ ++ ++ for (i = 1; i < nsp; i++) { ++ j = 0; ++ do { ++ asd = get_asd(sih, &eromptr, i, j, AD_ST_SLAVE, &addrl, &addrh, ++ &sizel, &sizeh); ++ ++ if (asd == 0) ++ break; ++ j++; ++ } while (1); ++ if (j == 0) { ++ SI_ERROR((" SP %d has no address descriptors\n", i)); ++ goto error; ++ } ++ } ++ ++ ++ for (i = 0; i < nmw; i++) { ++ asd = get_asd(sih, &eromptr, i, 0, AD_ST_MWRAP, &addrl, &addrh, ++ &sizel, &sizeh); ++ if (asd == 0) { ++ SI_ERROR(("Missing descriptor for MW %d\n", i)); ++ goto error; ++ } ++ if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) { ++ SI_ERROR(("Master wrapper %d is not 4KB\n", i)); ++ goto error; ++ } ++ if (i == 0) ++ sii->wrapba[idx] = addrl; ++ } ++ ++ ++ for (i = 0; i < nsw; i++) { ++ uint fwp = (nsp == 1) ? 0 : 1; ++ asd = get_asd(sih, &eromptr, fwp + i, 0, AD_ST_SWRAP, &addrl, &addrh, ++ &sizel, &sizeh); ++ if (asd == 0) { ++ SI_ERROR(("Missing descriptor for SW %d\n", i)); ++ goto error; ++ } ++ if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) { ++ SI_ERROR(("Slave wrapper %d is not 4KB\n", i)); ++ goto error; ++ } ++ if ((nmw == 0) && (i == 0)) ++ sii->wrapba[idx] = addrl; ++ } ++ ++ ++ ++ if (br) ++ continue; ++ ++ ++ sii->numcores++; ++ } ++ ++ SI_ERROR(("Reached end of erom without finding END")); ++ ++error: ++ sii->numcores = 0; ++ return; ++} ++ ++ ++void * ++ai_setcoreidx(si_t *sih, uint coreidx) ++{ ++ si_info_t *sii = SI_INFO(sih); ++ uint32 addr, wrap; ++ void *regs; ++ ++ if (coreidx >= MIN(sii->numcores, SI_MAXCORES)) ++ return (NULL); ++ ++ addr = sii->coresba[coreidx]; ++ wrap = sii->wrapba[coreidx]; ++ ++ ++ ASSERT((sii->intrsenabled_fn == NULL) || !(*(sii)->intrsenabled_fn)((sii)->intr_arg)); ++ ++ switch (BUSTYPE(sih->bustype)) { ++ case SI_BUS: ++ ++ if (!sii->regs[coreidx]) { ++ sii->regs[coreidx] = REG_MAP(addr, SI_CORE_SIZE); ++ ASSERT(GOODREGS(sii->regs[coreidx])); ++ } ++ sii->curmap = regs = sii->regs[coreidx]; ++ if (!sii->wrappers[coreidx]) { ++ sii->wrappers[coreidx] = REG_MAP(wrap, SI_CORE_SIZE); ++ ASSERT(GOODREGS(sii->wrappers[coreidx])); ++ } ++ sii->curwrap = sii->wrappers[coreidx]; ++ break; ++ ++ ++ case SPI_BUS: ++ case SDIO_BUS: ++ sii->curmap = regs = (void *)((uintptr)addr); ++ sii->curwrap = (void *)((uintptr)wrap); ++ break; ++ ++ case PCMCIA_BUS: ++ default: ++ ASSERT(0); ++ regs = NULL; ++ break; ++ } ++ ++ sii->curmap = regs; ++ sii->curidx = coreidx; ++ ++ return regs; ++} ++ ++void ++ai_coreaddrspaceX(si_t *sih, uint asidx, uint32 *addr, uint32 *size) ++{ ++ si_info_t *sii = SI_INFO(sih); ++ chipcregs_t *cc = NULL; ++ uint32 erombase, *eromptr, *eromlim; ++ uint i, j, cidx; ++ uint32 cia, cib, nmp, nsp; ++ uint32 asd, addrl, addrh, sizel, sizeh; ++ ++ for (i = 0; i < sii->numcores; i++) { ++ if (sii->coreid[i] == CC_CORE_ID) { ++ cc = (chipcregs_t *)sii->regs[i]; ++ break; ++ } ++ } ++ if (cc == NULL) ++ goto error; ++ ++ erombase = R_REG(sii->osh, &cc->eromptr); ++ eromptr = (uint32 *)REG_MAP(erombase, SI_CORE_SIZE); ++ eromlim = eromptr + (ER_REMAPCONTROL / sizeof(uint32)); ++ ++ cidx = sii->curidx; ++ cia = sii->cia[cidx]; ++ cib = sii->cib[cidx]; ++ ++ nmp = (cib & CIB_NMP_MASK) >> CIB_NMP_SHIFT; ++ nsp = (cib & CIB_NSP_MASK) >> CIB_NSP_SHIFT; ++ ++ ++ while (eromptr < eromlim) { ++ if ((get_erom_ent(sih, &eromptr, ER_TAG, ER_CI) == cia) && ++ (get_erom_ent(sih, &eromptr, 0, 0) == cib)) { ++ break; ++ } ++ } ++ ++ ++ for (i = 0; i < nmp; i++) ++ get_erom_ent(sih, &eromptr, ER_VALID, ER_VALID); ++ ++ ++ asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE, &addrl, &addrh, &sizel, &sizeh); ++ if (asd == 0) { ++ ++ asd = get_asd(sih, &eromptr, 0, 0, AD_ST_BRIDGE, &addrl, &addrh, ++ &sizel, &sizeh); ++ } ++ ++ j = 1; ++ do { ++ asd = get_asd(sih, &eromptr, 0, j, AD_ST_SLAVE, &addrl, &addrh, ++ &sizel, &sizeh); ++ j++; ++ } while (asd != 0); ++ ++ ++ for (i = 1; i < nsp; i++) { ++ j = 0; ++ do { ++ asd = get_asd(sih, &eromptr, i, j, AD_ST_SLAVE, &addrl, &addrh, ++ &sizel, &sizeh); ++ if (asd == 0) ++ break; ++ ++ if (!asidx--) { ++ *addr = addrl; ++ *size = sizel; ++ return; ++ } ++ j++; ++ } while (1); ++ ++ if (j == 0) { ++ SI_ERROR((" SP %d has no address descriptors\n", i)); ++ break; ++ } ++ } ++ ++error: ++ *size = 0; ++ return; ++} ++ ++ ++int ++ai_numaddrspaces(si_t *sih) ++{ ++ return 2; ++} ++ ++ ++uint32 ++ai_addrspace(si_t *sih, uint asidx) ++{ ++ si_info_t *sii; ++ uint cidx; ++ ++ sii = SI_INFO(sih); ++ cidx = sii->curidx; ++ ++ if (asidx == 0) ++ return sii->coresba[cidx]; ++ else if (asidx == 1) ++ return sii->coresba2[cidx]; ++ else { ++ SI_ERROR(("%s: Need to parse the erom again to find addr space %d\n", ++ __FUNCTION__, asidx)); ++ return 0; ++ } ++} ++ ++ ++uint32 ++ai_addrspacesize(si_t *sih, uint asidx) ++{ ++ si_info_t *sii; ++ uint cidx; ++ ++ sii = SI_INFO(sih); ++ cidx = sii->curidx; ++ ++ if (asidx == 0) ++ return sii->coresba_size[cidx]; ++ else if (asidx == 1) ++ return sii->coresba2_size[cidx]; ++ else { ++ SI_ERROR(("%s: Need to parse the erom again to find addr space %d\n", ++ __FUNCTION__, asidx)); ++ return 0; ++ } ++} ++ ++uint ++ai_flag(si_t *sih) ++{ ++ si_info_t *sii; ++ aidmp_t *ai; ++ ++ sii = SI_INFO(sih); ++ if (BCM47162_DMP()) { ++ SI_ERROR(("%s: Attempting to read MIPS DMP registers on 47162a0", __FUNCTION__)); ++ return sii->curidx; ++ } ++ if (BCM5357_DMP()) { ++ SI_ERROR(("%s: Attempting to read USB20H DMP registers on 5357b0\n", __FUNCTION__)); ++ return sii->curidx; ++ } ++ ai = sii->curwrap; ++ ++ return (R_REG(sii->osh, &ai->oobselouta30) & 0x1f); ++} ++ ++void ++ai_setint(si_t *sih, int siflag) ++{ ++} ++ ++uint ++ai_wrap_reg(si_t *sih, uint32 offset, uint32 mask, uint32 val) ++{ ++ si_info_t *sii = SI_INFO(sih); ++ uint32 *map = (uint32 *) sii->curwrap; ++ ++ if (mask || val) { ++ uint32 w = R_REG(sii->osh, map+(offset/4)); ++ w &= ~mask; ++ w |= val; ++ W_REG(sii->osh, map+(offset/4), val); ++ } ++ ++ return (R_REG(sii->osh, map+(offset/4))); ++} ++ ++uint ++ai_corevendor(si_t *sih) ++{ ++ si_info_t *sii; ++ uint32 cia; ++ ++ sii = SI_INFO(sih); ++ cia = sii->cia[sii->curidx]; ++ return ((cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT); ++} ++ ++uint ++ai_corerev(si_t *sih) ++{ ++ si_info_t *sii; ++ uint32 cib; ++ ++ sii = SI_INFO(sih); ++ cib = sii->cib[sii->curidx]; ++ return remap_corerev(sih, (cib & CIB_REV_MASK) >> CIB_REV_SHIFT); ++} ++ ++bool ++ai_iscoreup(si_t *sih) ++{ ++ si_info_t *sii; ++ aidmp_t *ai; ++ ++ sii = SI_INFO(sih); ++ ai = sii->curwrap; ++ ++ return (((R_REG(sii->osh, &ai->ioctrl) & (SICF_FGC | SICF_CLOCK_EN)) == SICF_CLOCK_EN) && ++ ((R_REG(sii->osh, &ai->resetctrl) & AIRC_RESET) == 0)); ++} ++ ++ ++uint ++ai_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) ++{ ++ uint origidx = 0; ++ uint32 *r = NULL; ++ uint w; ++ uint intr_val = 0; ++ bool fast = FALSE; ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ ++ ASSERT(GOODIDX(coreidx)); ++ ASSERT(regoff < SI_CORE_SIZE); ++ ASSERT((val & ~mask) == 0); ++ ++ if (coreidx >= SI_MAXCORES) ++ return 0; ++ ++ if (BUSTYPE(sih->bustype) == SI_BUS) { ++ ++ fast = TRUE; ++ ++ if (!sii->regs[coreidx]) { ++ sii->regs[coreidx] = REG_MAP(sii->coresba[coreidx], ++ SI_CORE_SIZE); ++ ASSERT(GOODREGS(sii->regs[coreidx])); ++ } ++ r = (uint32 *)((uchar *)sii->regs[coreidx] + regoff); ++ } else if (BUSTYPE(sih->bustype) == PCI_BUS) { ++ ++ ++ if ((sii->coreid[coreidx] == CC_CORE_ID) && SI_FAST(sii)) { ++ ++ ++ fast = TRUE; ++ r = (uint32 *)((char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff); ++ } else if (sii->pub.buscoreidx == coreidx) { ++ ++ fast = TRUE; ++ if (SI_FAST(sii)) ++ r = (uint32 *)((char *)sii->curmap + ++ PCI_16KB0_PCIREGS_OFFSET + regoff); ++ else ++ r = (uint32 *)((char *)sii->curmap + ++ ((regoff >= SBCONFIGOFF) ? ++ PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) + ++ regoff); ++ } ++ } ++ ++ if (!fast) { ++ INTR_OFF(sii, intr_val); ++ ++ ++ origidx = si_coreidx(&sii->pub); ++ ++ ++ r = (uint32*) ((uchar*) ai_setcoreidx(&sii->pub, coreidx) + regoff); ++ } ++ ASSERT(r != NULL); ++ ++ ++ if (mask || val) { ++ w = (R_REG(sii->osh, r) & ~mask) | val; ++ W_REG(sii->osh, r, w); ++ } ++ ++ ++ w = R_REG(sii->osh, r); ++ ++ if (!fast) { ++ ++ if (origidx != coreidx) ++ ai_setcoreidx(&sii->pub, origidx); ++ ++ INTR_RESTORE(sii, intr_val); ++ } ++ ++ return (w); ++} ++ ++void ++ai_core_disable(si_t *sih, uint32 bits) ++{ ++ si_info_t *sii; ++ volatile uint32 dummy; ++ uint32 status; ++ aidmp_t *ai; ++ ++ sii = SI_INFO(sih); ++ ++ ASSERT(GOODREGS(sii->curwrap)); ++ ai = sii->curwrap; ++ ++ ++ if (R_REG(sii->osh, &ai->resetctrl) & AIRC_RESET) ++ return; ++ ++ ++ SPINWAIT(((status = R_REG(sii->osh, &ai->resetstatus)) != 0), 300); ++ ++ ++ if (status != 0) { ++ ++ ++ SPINWAIT(((status = R_REG(sii->osh, &ai->resetstatus)) != 0), 10000); ++ ++ ++ } ++ ++ W_REG(sii->osh, &ai->ioctrl, bits); ++ dummy = R_REG(sii->osh, &ai->ioctrl); ++ BCM_REFERENCE(dummy); ++ OSL_DELAY(10); ++ ++ W_REG(sii->osh, &ai->resetctrl, AIRC_RESET); ++ dummy = R_REG(sii->osh, &ai->resetctrl); ++ BCM_REFERENCE(dummy); ++ OSL_DELAY(1); ++} ++ ++ ++void ++ai_core_reset(si_t *sih, uint32 bits, uint32 resetbits) ++{ ++ si_info_t *sii; ++ aidmp_t *ai; ++ volatile uint32 dummy; ++ ++ sii = SI_INFO(sih); ++ ASSERT(GOODREGS(sii->curwrap)); ++ ai = sii->curwrap; ++ ++ ++ ai_core_disable(sih, (bits | resetbits)); ++ ++ ++ W_REG(sii->osh, &ai->ioctrl, (bits | SICF_FGC | SICF_CLOCK_EN)); ++ dummy = R_REG(sii->osh, &ai->ioctrl); ++ BCM_REFERENCE(dummy); ++ ++ W_REG(sii->osh, &ai->resetctrl, 0); ++ dummy = R_REG(sii->osh, &ai->resetctrl); ++ BCM_REFERENCE(dummy); ++ OSL_DELAY(1); ++ ++ W_REG(sii->osh, &ai->ioctrl, (bits | SICF_CLOCK_EN)); ++ dummy = R_REG(sii->osh, &ai->ioctrl); ++ BCM_REFERENCE(dummy); ++ OSL_DELAY(1); ++} ++ ++void ++ai_core_cflags_wo(si_t *sih, uint32 mask, uint32 val) ++{ ++ si_info_t *sii; ++ aidmp_t *ai; ++ uint32 w; ++ ++ sii = SI_INFO(sih); ++ ++ if (BCM47162_DMP()) { ++ SI_ERROR(("%s: Accessing MIPS DMP register (ioctrl) on 47162a0", ++ __FUNCTION__)); ++ return; ++ } ++ if (BCM5357_DMP()) { ++ SI_ERROR(("%s: Accessing USB20H DMP register (ioctrl) on 5357\n", ++ __FUNCTION__)); ++ return; ++ } ++ ++ ASSERT(GOODREGS(sii->curwrap)); ++ ai = sii->curwrap; ++ ++ ASSERT((val & ~mask) == 0); ++ ++ if (mask || val) { ++ w = ((R_REG(sii->osh, &ai->ioctrl) & ~mask) | val); ++ W_REG(sii->osh, &ai->ioctrl, w); ++ } ++} ++ ++uint32 ++ai_core_cflags(si_t *sih, uint32 mask, uint32 val) ++{ ++ si_info_t *sii; ++ aidmp_t *ai; ++ uint32 w; ++ ++ sii = SI_INFO(sih); ++ if (BCM47162_DMP()) { ++ SI_ERROR(("%s: Accessing MIPS DMP register (ioctrl) on 47162a0", ++ __FUNCTION__)); ++ return 0; ++ } ++ if (BCM5357_DMP()) { ++ SI_ERROR(("%s: Accessing USB20H DMP register (ioctrl) on 5357\n", ++ __FUNCTION__)); ++ return 0; ++ } ++ ++ ASSERT(GOODREGS(sii->curwrap)); ++ ai = sii->curwrap; ++ ++ ASSERT((val & ~mask) == 0); ++ ++ if (mask || val) { ++ w = ((R_REG(sii->osh, &ai->ioctrl) & ~mask) | val); ++ W_REG(sii->osh, &ai->ioctrl, w); ++ } ++ ++ return R_REG(sii->osh, &ai->ioctrl); ++} ++ ++uint32 ++ai_core_sflags(si_t *sih, uint32 mask, uint32 val) ++{ ++ si_info_t *sii; ++ aidmp_t *ai; ++ uint32 w; ++ ++ sii = SI_INFO(sih); ++ if (BCM47162_DMP()) { ++ SI_ERROR(("%s: Accessing MIPS DMP register (iostatus) on 47162a0", ++ __FUNCTION__)); ++ return 0; ++ } ++ if (BCM5357_DMP()) { ++ SI_ERROR(("%s: Accessing USB20H DMP register (iostatus) on 5357\n", ++ __FUNCTION__)); ++ return 0; ++ } ++ ++ ASSERT(GOODREGS(sii->curwrap)); ++ ai = sii->curwrap; ++ ++ ASSERT((val & ~mask) == 0); ++ ASSERT((mask & ~SISF_CORE_BITS) == 0); ++ ++ if (mask || val) { ++ w = ((R_REG(sii->osh, &ai->iostatus) & ~mask) | val); ++ W_REG(sii->osh, &ai->iostatus, w); ++ } ++ ++ return R_REG(sii->osh, &ai->iostatus); ++} +diff --git a/drivers/net/wireless/bcmdhd/bcmevent.c b/drivers/net/wireless/bcmdhd/bcmevent.c +new file mode 100644 +index 00000000..35859488 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/bcmevent.c +@@ -0,0 +1,147 @@ ++/* ++ * bcmevent read-only data shared by kernel or app layers ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * $Id: bcmevent.c 370587 2012-11-22 09:32:38Z $ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#if WLC_E_LAST != 107 ++#error "You need to add an entry to bcmevent_names[] for the new event" ++#endif ++ ++const bcmevent_name_t bcmevent_names[] = { ++ { WLC_E_SET_SSID, "SET_SSID" }, ++ { WLC_E_JOIN, "JOIN" }, ++ { WLC_E_START, "START" }, ++ { WLC_E_AUTH, "AUTH" }, ++ { WLC_E_AUTH_IND, "AUTH_IND" }, ++ { WLC_E_DEAUTH, "DEAUTH" }, ++ { WLC_E_DEAUTH_IND, "DEAUTH_IND" }, ++ { WLC_E_ASSOC, "ASSOC" }, ++ { WLC_E_ASSOC_IND, "ASSOC_IND" }, ++ { WLC_E_REASSOC, "REASSOC" }, ++ { WLC_E_REASSOC_IND, "REASSOC_IND" }, ++ { WLC_E_DISASSOC, "DISASSOC" }, ++ { WLC_E_DISASSOC_IND, "DISASSOC_IND" }, ++ { WLC_E_QUIET_START, "START_QUIET" }, ++ { WLC_E_QUIET_END, "END_QUIET" }, ++ { WLC_E_BEACON_RX, "BEACON_RX" }, ++ { WLC_E_LINK, "LINK" }, ++ { WLC_E_MIC_ERROR, "MIC_ERROR" }, ++ { WLC_E_NDIS_LINK, "NDIS_LINK" }, ++ { WLC_E_ROAM, "ROAM" }, ++ { WLC_E_TXFAIL, "TXFAIL" }, ++ { WLC_E_PMKID_CACHE, "PMKID_CACHE" }, ++ { WLC_E_RETROGRADE_TSF, "RETROGRADE_TSF" }, ++ { WLC_E_PRUNE, "PRUNE" }, ++ { WLC_E_AUTOAUTH, "AUTOAUTH" }, ++ { WLC_E_EAPOL_MSG, "EAPOL_MSG" }, ++ { WLC_E_SCAN_COMPLETE, "SCAN_COMPLETE" }, ++ { WLC_E_ADDTS_IND, "ADDTS_IND" }, ++ { WLC_E_DELTS_IND, "DELTS_IND" }, ++ { WLC_E_BCNSENT_IND, "BCNSENT_IND" }, ++ { WLC_E_BCNRX_MSG, "BCNRX_MSG" }, ++ { WLC_E_BCNLOST_MSG, "BCNLOST_IND" }, ++ { WLC_E_ROAM_PREP, "ROAM_PREP" }, ++ { WLC_E_PFN_NET_FOUND, "PFNFOUND_IND" }, ++ { WLC_E_PFN_NET_LOST, "PFNLOST_IND" }, ++#if defined(IBSS_PEER_DISCOVERY_EVENT) ++ { WLC_E_IBSS_ASSOC, "IBSS_ASSOC" }, ++#endif /* defined(IBSS_PEER_DISCOVERY_EVENT) */ ++ { WLC_E_RADIO, "RADIO" }, ++ { WLC_E_PSM_WATCHDOG, "PSM_WATCHDOG" }, ++ { WLC_E_PROBREQ_MSG, "PROBE_REQ_MSG" }, ++ { WLC_E_SCAN_CONFIRM_IND, "SCAN_CONFIRM_IND" }, ++ { WLC_E_PSK_SUP, "PSK_SUP" }, ++ { WLC_E_COUNTRY_CODE_CHANGED, "CNTRYCODE_IND" }, ++ { WLC_E_EXCEEDED_MEDIUM_TIME, "EXCEEDED_MEDIUM_TIME" }, ++ { WLC_E_ICV_ERROR, "ICV_ERROR" }, ++ { WLC_E_UNICAST_DECODE_ERROR, "UNICAST_DECODE_ERROR" }, ++ { WLC_E_MULTICAST_DECODE_ERROR, "MULTICAST_DECODE_ERROR" }, ++ { WLC_E_TRACE, "TRACE" }, ++#ifdef WLBTAMP ++ { WLC_E_BTA_HCI_EVENT, "BTA_HCI_EVENT" }, ++#endif ++ { WLC_E_IF, "IF" }, ++#ifdef WLP2P ++ { WLC_E_P2P_DISC_LISTEN_COMPLETE, "WLC_E_P2P_DISC_LISTEN_COMPLETE" }, ++#endif ++ { WLC_E_RSSI, "RSSI" }, ++ { WLC_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE" }, ++ { WLC_E_EXTLOG_MSG, "EXTERNAL LOG MESSAGE" }, ++#ifdef WIFI_ACT_FRAME ++ { WLC_E_ACTION_FRAME, "ACTION_FRAME" }, ++ { WLC_E_ACTION_FRAME_RX, "ACTION_FRAME_RX" }, ++ { WLC_E_ACTION_FRAME_COMPLETE, "ACTION_FRAME_COMPLETE" }, ++#endif ++#if 0 && (NDISVER >= 0x0620) ++ { WLC_E_PRE_ASSOC_IND, "ASSOC_RECV" }, ++ { WLC_E_PRE_REASSOC_IND, "REASSOC_RECV" }, ++ { WLC_E_CHANNEL_ADOPTED, "CHANNEL_ADOPTED" }, ++ { WLC_E_AP_STARTED, "AP_STARTED" }, ++ { WLC_E_DFS_AP_STOP, "DFS_AP_STOP" }, ++ { WLC_E_DFS_AP_RESUME, "DFS_AP_RESUME" }, ++ { WLC_E_ASSOC_IND_NDIS, "ASSOC_IND_NDIS"}, ++ { WLC_E_REASSOC_IND_NDIS, "REASSOC_IND_NDIS"}, ++ { WLC_E_ACTION_FRAME_RX_NDIS, "WLC_E_ACTION_FRAME_RX_NDIS" }, ++ { WLC_E_AUTH_REQ, "WLC_E_AUTH_REQ" }, ++#endif ++ { WLC_E_ESCAN_RESULT, "WLC_E_ESCAN_RESULT" }, ++ { WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE, "WLC_E_AF_OFF_CHAN_COMPLETE" }, ++#ifdef WLP2P ++ { WLC_E_PROBRESP_MSG, "PROBE_RESP_MSG" }, ++ { WLC_E_P2P_PROBREQ_MSG, "P2P PROBE_REQ_MSG" }, ++#endif ++#ifdef PROP_TXSTATUS ++ { WLC_E_FIFO_CREDIT_MAP, "FIFO_CREDIT_MAP" }, ++#endif ++ { WLC_E_WAKE_EVENT, "WAKE_EVENT" }, ++ { WLC_E_DCS_REQUEST, "DCS_REQUEST" }, ++ { WLC_E_RM_COMPLETE, "RM_COMPLETE" }, ++#ifdef WLMEDIA_HTSF ++ { WLC_E_HTSFSYNC, "HTSF_SYNC_EVENT" }, ++#endif ++ { WLC_E_OVERLAY_REQ, "OVERLAY_REQ_EVENT" }, ++ { WLC_E_CSA_COMPLETE_IND, "WLC_E_CSA_COMPLETE_IND"}, ++ { WLC_E_EXCESS_PM_WAKE_EVENT, "EXCESS_PM_WAKE_EVENT" }, ++ { WLC_E_PFN_SCAN_NONE, "PFN_SCAN_NONE" }, ++ { WLC_E_PFN_SCAN_ALLGONE, "PFN_SCAN_ALLGONE" }, ++#ifdef SOFTAP ++ { WLC_E_GTK_PLUMBED, "GTK_PLUMBED" }, ++#endif ++ { WLC_E_ASSOC_REQ_IE, "ASSOC_REQ_IE" }, ++ { WLC_E_ASSOC_RESP_IE, "ASSOC_RESP_IE" }, ++ { WLC_E_ACTION_FRAME_RX_NDIS, "WLC_E_ACTION_FRAME_RX_NDIS" }, ++#ifdef WLTDLS ++ { WLC_E_TDLS_PEER_EVENT, "TDLS_PEER_EVENT" }, ++#endif /* WLTDLS */ ++ { WLC_E_SERVICE_FOUND, "SERVICE_FOUND" }, ++ { WLC_E_P2PO_ADD_DEVICE, "P2PO_DEV_FOUND" }, ++ { WLC_E_P2PO_DEL_DEVICE, "P2PO_DEV_LOST" }, ++}; ++ ++const int bcmevent_names_size = ARRAYSIZE(bcmevent_names); +diff --git a/drivers/net/wireless/bcmdhd/bcmsdh.c b/drivers/net/wireless/bcmdhd/bcmsdh.c +new file mode 100644 +index 00000000..b05e2956 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/bcmsdh.c +@@ -0,0 +1,756 @@ ++/* ++ * BCMSDH interface glue ++ * implement bcmsdh API for SDIOH driver ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmsdh.c 347614 2012-07-27 10:24:51Z $ ++ */ ++ ++/** ++ * @file bcmsdh.c ++ */ ++ ++/* ****************** BCMSDH Interface Functions *************************** */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include /* BRCM API for SDIO clients (such as wl, dhd) */ ++#include /* common SDIO/controller interface */ ++#include /* SDIO device core hardware definitions. */ ++ ++#include /* SDIO Device and Protocol Specs */ ++ ++#define SDIOH_API_ACCESS_RETRY_LIMIT 2 ++const uint bcmsdh_msglevel = BCMSDH_ERROR_VAL; ++ ++/** ++ * BCMSDH API context ++ */ ++struct bcmsdh_info ++{ ++ bool init_success; /* underlying driver successfully attached */ ++ void *sdioh; /* handler for sdioh */ ++ uint32 vendevid; /* Target Vendor and Device ID on SD bus */ ++ osl_t *osh; ++ bool regfail; /* Save status of last reg_read/reg_write call */ ++ uint32 sbwad; /* Save backplane window address */ ++}; ++/* local copy of bcm sd handler */ ++bcmsdh_info_t * l_bcmsdh = NULL; ++ ++#if defined(OOB_INTR_ONLY) && defined(HW_OOB) ++extern int ++sdioh_enable_hw_oob_intr(void *sdioh, bool enable); ++ ++void ++bcmsdh_enable_hw_oob_intr(bcmsdh_info_t *sdh, bool enable) ++{ ++ sdioh_enable_hw_oob_intr(sdh->sdioh, enable); ++} ++#endif ++ ++/* Attach BCMSDH layer to SDIO Host Controller Driver ++ * ++ * @param osh OSL Handle. ++ * @param cfghdl Configuration Handle. ++ * @param regsva Virtual address of controller registers. ++ * @param irq Interrupt number of SDIO controller. ++ * ++ * @return bcmsdh_info_t Handle to BCMSDH context. ++ */ ++bcmsdh_info_t * ++bcmsdh_attach(osl_t *osh, void *cfghdl, void **regsva, uint irq) ++{ ++ bcmsdh_info_t *bcmsdh; ++ ++ if ((bcmsdh = (bcmsdh_info_t *)MALLOC(osh, sizeof(bcmsdh_info_t))) == NULL) { ++ BCMSDH_ERROR(("bcmsdh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh))); ++ return NULL; ++ } ++ bzero((char *)bcmsdh, sizeof(bcmsdh_info_t)); ++ ++ /* save the handler locally */ ++ l_bcmsdh = bcmsdh; ++ ++ if (!(bcmsdh->sdioh = sdioh_attach(osh, cfghdl, irq))) { ++ bcmsdh_detach(osh, bcmsdh); ++ return NULL; ++ } ++ ++ bcmsdh->osh = osh; ++ bcmsdh->init_success = TRUE; ++ ++ *regsva = (uint32 *)SI_ENUM_BASE; ++ ++ /* Report the BAR, to fix if needed */ ++ bcmsdh->sbwad = SI_ENUM_BASE; ++ return bcmsdh; ++} ++ ++int ++bcmsdh_detach(osl_t *osh, void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ ++ if (bcmsdh != NULL) { ++ if (bcmsdh->sdioh) { ++ sdioh_detach(osh, bcmsdh->sdioh); ++ bcmsdh->sdioh = NULL; ++ } ++ MFREE(osh, bcmsdh, sizeof(bcmsdh_info_t)); ++ } ++ ++ l_bcmsdh = NULL; ++ return 0; ++} ++ ++int ++bcmsdh_iovar_op(void *sdh, const char *name, ++ void *params, int plen, void *arg, int len, bool set) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ return sdioh_iovar_op(bcmsdh->sdioh, name, params, plen, arg, len, set); ++} ++ ++bool ++bcmsdh_intr_query(void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ bool on; ++ ++ ASSERT(bcmsdh); ++ status = sdioh_interrupt_query(bcmsdh->sdioh, &on); ++ if (SDIOH_API_SUCCESS(status)) ++ return FALSE; ++ else ++ return on; ++} ++ ++int ++bcmsdh_intr_enable(void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ ASSERT(bcmsdh); ++ ++ status = sdioh_interrupt_set(bcmsdh->sdioh, TRUE); ++ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); ++} ++ ++int ++bcmsdh_intr_disable(void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ ASSERT(bcmsdh); ++ ++ status = sdioh_interrupt_set(bcmsdh->sdioh, FALSE); ++ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); ++} ++ ++int ++bcmsdh_intr_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ ASSERT(bcmsdh); ++ ++ status = sdioh_interrupt_register(bcmsdh->sdioh, fn, argh); ++ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); ++} ++ ++int ++bcmsdh_intr_dereg(void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ ASSERT(bcmsdh); ++ ++ status = sdioh_interrupt_deregister(bcmsdh->sdioh); ++ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); ++} ++ ++#if defined(DHD_DEBUG) ++bool ++bcmsdh_intr_pending(void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ ++ ASSERT(sdh); ++ return sdioh_interrupt_pending(bcmsdh->sdioh); ++} ++#endif ++ ++ ++int ++bcmsdh_devremove_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh) ++{ ++ ASSERT(sdh); ++ ++ /* don't support yet */ ++ return BCME_UNSUPPORTED; ++} ++ ++/** ++ * Read from SDIO Configuration Space ++ * @param sdh SDIO Host context. ++ * @param func_num Function number to read from. ++ * @param addr Address to read from. ++ * @param err Error return. ++ * @return value read from SDIO configuration space. ++ */ ++uint8 ++bcmsdh_cfg_read(void *sdh, uint fnc_num, uint32 addr, int *err) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++#ifdef SDIOH_API_ACCESS_RETRY_LIMIT ++ int32 retry = 0; ++#endif ++ uint8 data = 0; ++ ++ if (!bcmsdh) ++ bcmsdh = l_bcmsdh; ++ ++ ASSERT(bcmsdh->init_success); ++ ++#ifdef SDIOH_API_ACCESS_RETRY_LIMIT ++ do { ++ if (retry) /* wait for 1 ms till bus get settled down */ ++ OSL_DELAY(1000); ++#endif ++ status = sdioh_cfg_read(bcmsdh->sdioh, fnc_num, addr, (uint8 *)&data); ++#ifdef SDIOH_API_ACCESS_RETRY_LIMIT ++ } while (!SDIOH_API_SUCCESS(status) && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT)); ++#endif ++ if (err) ++ *err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR); ++ ++ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint8data = 0x%x\n", __FUNCTION__, ++ fnc_num, addr, data)); ++ ++ return data; ++} ++ ++void ++bcmsdh_cfg_write(void *sdh, uint fnc_num, uint32 addr, uint8 data, int *err) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++#ifdef SDIOH_API_ACCESS_RETRY_LIMIT ++ int32 retry = 0; ++#endif ++ ++ if (!bcmsdh) ++ bcmsdh = l_bcmsdh; ++ ++ ASSERT(bcmsdh->init_success); ++ ++#ifdef SDIOH_API_ACCESS_RETRY_LIMIT ++ do { ++ if (retry) /* wait for 1 ms till bus get settled down */ ++ OSL_DELAY(1000); ++#endif ++ status = sdioh_cfg_write(bcmsdh->sdioh, fnc_num, addr, (uint8 *)&data); ++#ifdef SDIOH_API_ACCESS_RETRY_LIMIT ++ } while (!SDIOH_API_SUCCESS(status) && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT)); ++#endif ++ if (err) ++ *err = SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR; ++ ++ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint8data = 0x%x\n", __FUNCTION__, ++ fnc_num, addr, data)); ++} ++ ++uint32 ++bcmsdh_cfg_read_word(void *sdh, uint fnc_num, uint32 addr, int *err) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ uint32 data = 0; ++ ++ if (!bcmsdh) ++ bcmsdh = l_bcmsdh; ++ ++ ASSERT(bcmsdh->init_success); ++ ++ status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_READ, fnc_num, ++ addr, &data, 4); ++ ++ if (err) ++ *err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR); ++ ++ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint32data = 0x%x\n", __FUNCTION__, ++ fnc_num, addr, data)); ++ ++ return data; ++} ++ ++void ++bcmsdh_cfg_write_word(void *sdh, uint fnc_num, uint32 addr, uint32 data, int *err) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ ++ if (!bcmsdh) ++ bcmsdh = l_bcmsdh; ++ ++ ASSERT(bcmsdh->init_success); ++ ++ status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_WRITE, fnc_num, ++ addr, &data, 4); ++ ++ if (err) ++ *err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR); ++ ++ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint32data = 0x%x\n", __FUNCTION__, fnc_num, ++ addr, data)); ++} ++ ++ ++int ++bcmsdh_cis_read(void *sdh, uint func, uint8 *cis, uint length) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ ++ uint8 *tmp_buf, *tmp_ptr; ++ uint8 *ptr; ++ bool ascii = func & ~0xf; ++ func &= 0x7; ++ ++ if (!bcmsdh) ++ bcmsdh = l_bcmsdh; ++ ++ ASSERT(bcmsdh->init_success); ++ ASSERT(cis); ++ ASSERT(length <= SBSDIO_CIS_SIZE_LIMIT); ++ ++ status = sdioh_cis_read(bcmsdh->sdioh, func, cis, length); ++ ++ if (ascii) { ++ /* Move binary bits to tmp and format them into the provided buffer. */ ++ if ((tmp_buf = (uint8 *)MALLOC(bcmsdh->osh, length)) == NULL) { ++ BCMSDH_ERROR(("%s: out of memory\n", __FUNCTION__)); ++ return BCME_NOMEM; ++ } ++ bcopy(cis, tmp_buf, length); ++ for (tmp_ptr = tmp_buf, ptr = cis; ptr < (cis + length - 4); tmp_ptr++) { ++ ptr += snprintf((char*)ptr, (cis + length - ptr - 4), ++ "%.2x ", *tmp_ptr & 0xff); ++ if ((((tmp_ptr - tmp_buf) + 1) & 0xf) == 0) ++ ptr += snprintf((char *)ptr, (cis + length - ptr -4), "\n"); ++ } ++ MFREE(bcmsdh->osh, tmp_buf, length); ++ } ++ ++ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); ++} ++ ++ ++int ++bcmsdhsdio_set_sbaddr_window(void *sdh, uint32 address, bool force_set) ++{ ++ int err = 0; ++ uint bar0 = address & ~SBSDIO_SB_OFT_ADDR_MASK; ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ ++ if (bar0 != bcmsdh->sbwad || force_set) { ++ bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, ++ (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err); ++ if (!err) ++ bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID, ++ (address >> 16) & SBSDIO_SBADDRMID_MASK, &err); ++ if (!err) ++ bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH, ++ (address >> 24) & SBSDIO_SBADDRHIGH_MASK, &err); ++ ++ if (!err) ++ bcmsdh->sbwad = bar0; ++ else ++ /* invalidate cached window var */ ++ bcmsdh->sbwad = 0; ++ ++ } ++ ++ return err; ++} ++ ++uint32 ++bcmsdh_reg_read(void *sdh, uint32 addr, uint size) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ uint32 word = 0; ++ ++ BCMSDH_INFO(("%s:fun = 1, addr = 0x%x, ", __FUNCTION__, addr)); ++ ++ if (!bcmsdh) ++ bcmsdh = l_bcmsdh; ++ ++ ASSERT(bcmsdh->init_success); ++ ++ if (bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE)) ++ return 0xFFFFFFFF; ++ ++ addr &= SBSDIO_SB_OFT_ADDR_MASK; ++ if (size == 4) ++ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; ++ ++ status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, ++ SDIOH_READ, SDIO_FUNC_1, addr, &word, size); ++ ++ bcmsdh->regfail = !(SDIOH_API_SUCCESS(status)); ++ ++ BCMSDH_INFO(("uint32data = 0x%x\n", word)); ++ ++ /* if ok, return appropriately masked word */ ++ if (SDIOH_API_SUCCESS(status)) { ++ switch (size) { ++ case sizeof(uint8): ++ return (word & 0xff); ++ case sizeof(uint16): ++ return (word & 0xffff); ++ case sizeof(uint32): ++ return word; ++ default: ++ bcmsdh->regfail = TRUE; ++ ++ } ++ } ++ ++ /* otherwise, bad sdio access or invalid size */ ++ BCMSDH_ERROR(("%s: error reading addr 0x%04x size %d\n", __FUNCTION__, addr, size)); ++ return 0xFFFFFFFF; ++} ++ ++uint32 ++bcmsdh_reg_write(void *sdh, uint32 addr, uint size, uint32 data) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ int err = 0; ++ ++ BCMSDH_INFO(("%s:fun = 1, addr = 0x%x, uint%ddata = 0x%x\n", ++ __FUNCTION__, addr, size*8, data)); ++ ++ if (!bcmsdh) ++ bcmsdh = l_bcmsdh; ++ ++ ASSERT(bcmsdh->init_success); ++ ++ if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE))) ++ return err; ++ ++ addr &= SBSDIO_SB_OFT_ADDR_MASK; ++ if (size == 4) ++ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; ++ status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_WRITE, SDIO_FUNC_1, ++ addr, &data, size); ++ bcmsdh->regfail = !(SDIOH_API_SUCCESS(status)); ++ ++ if (SDIOH_API_SUCCESS(status)) ++ return 0; ++ ++ BCMSDH_ERROR(("%s: error writing 0x%08x to addr 0x%04x size %d\n", ++ __FUNCTION__, data, addr, size)); ++ return 0xFFFFFFFF; ++} ++ ++bool ++bcmsdh_regfail(void *sdh) ++{ ++ return ((bcmsdh_info_t *)sdh)->regfail; ++} ++ ++int ++bcmsdh_recv_buf(void *sdh, uint32 addr, uint fn, uint flags, ++ uint8 *buf, uint nbytes, void *pkt, ++ bcmsdh_cmplt_fn_t complete_fn, void *handle) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ uint incr_fix; ++ uint width; ++ int err = 0; ++ ++ ASSERT(bcmsdh); ++ ASSERT(bcmsdh->init_success); ++ ++ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n", ++ __FUNCTION__, fn, addr, nbytes)); ++ ++ /* Async not implemented yet */ ++ ASSERT(!(flags & SDIO_REQ_ASYNC)); ++ if (flags & SDIO_REQ_ASYNC) ++ return BCME_UNSUPPORTED; ++ ++ if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE))) ++ return err; ++ ++ addr &= SBSDIO_SB_OFT_ADDR_MASK; ++ ++ incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; ++ width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; ++ if (width == 4) ++ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; ++ ++ status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, incr_fix, ++ SDIOH_READ, fn, addr, width, nbytes, buf, pkt); ++ ++ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR); ++} ++ ++int ++bcmsdh_send_buf(void *sdh, uint32 addr, uint fn, uint flags, ++ uint8 *buf, uint nbytes, void *pkt, ++ bcmsdh_cmplt_fn_t complete_fn, void *handle) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ uint incr_fix; ++ uint width; ++ int err = 0; ++ ++ ASSERT(bcmsdh); ++ ASSERT(bcmsdh->init_success); ++ ++ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n", ++ __FUNCTION__, fn, addr, nbytes)); ++ ++ /* Async not implemented yet */ ++ ASSERT(!(flags & SDIO_REQ_ASYNC)); ++ if (flags & SDIO_REQ_ASYNC) ++ return BCME_UNSUPPORTED; ++ ++ if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE))) ++ return err; ++ ++ addr &= SBSDIO_SB_OFT_ADDR_MASK; ++ ++ incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; ++ width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; ++ if (width == 4) ++ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; ++ ++ status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, incr_fix, ++ SDIOH_WRITE, fn, addr, width, nbytes, buf, pkt); ++ ++ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); ++} ++ ++int ++bcmsdh_rwdata(void *sdh, uint rw, uint32 addr, uint8 *buf, uint nbytes) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ ++ ASSERT(bcmsdh); ++ ASSERT(bcmsdh->init_success); ++ ASSERT((addr & SBSDIO_SBWINDOW_MASK) == 0); ++ ++ addr &= SBSDIO_SB_OFT_ADDR_MASK; ++ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; ++ ++ status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, SDIOH_DATA_INC, ++ (rw ? SDIOH_WRITE : SDIOH_READ), SDIO_FUNC_1, ++ addr, 4, nbytes, buf, NULL); ++ ++ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); ++} ++ ++int ++bcmsdh_abort(void *sdh, uint fn) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ ++ return sdioh_abort(bcmsdh->sdioh, fn); ++} ++ ++int ++bcmsdh_start(void *sdh, int stage) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ ++ return sdioh_start(bcmsdh->sdioh, stage); ++} ++ ++int ++bcmsdh_stop(void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ ++ return sdioh_stop(bcmsdh->sdioh); ++} ++ ++int ++bcmsdh_waitlockfree(void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ if (!bcmsdh) ++ bcmsdh = l_bcmsdh; ++ ++ return sdioh_waitlockfree(bcmsdh->sdioh); ++} ++ ++ ++int ++bcmsdh_query_device(void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ bcmsdh->vendevid = (VENDOR_BROADCOM << 16) | 0; ++ return (bcmsdh->vendevid); ++} ++ ++uint ++bcmsdh_query_iofnum(void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ ++ if (!bcmsdh) ++ bcmsdh = l_bcmsdh; ++ ++ return (sdioh_query_iofnum(bcmsdh->sdioh)); ++} ++ ++int ++bcmsdh_reset(bcmsdh_info_t *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ ++ return sdioh_sdio_reset(bcmsdh->sdioh); ++} ++ ++void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh) ++{ ++ ASSERT(sdh); ++ return sdh->sdioh; ++} ++ ++/* Function to pass device-status bits to DHD. */ ++uint32 ++bcmsdh_get_dstatus(void *sdh) ++{ ++ return 0; ++} ++uint32 ++bcmsdh_cur_sbwad(void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ ++ if (!bcmsdh) ++ bcmsdh = l_bcmsdh; ++ ++ return (bcmsdh->sbwad); ++} ++ ++void ++bcmsdh_chipinfo(void *sdh, uint32 chip, uint32 chiprev) ++{ ++ return; ++} ++ ++ ++int ++bcmsdh_sleep(void *sdh, bool enab) ++{ ++#ifdef SDIOH_SLEEP_ENABLED ++ bcmsdh_info_t *p = (bcmsdh_info_t *)sdh; ++ sdioh_info_t *sd = (sdioh_info_t *)(p->sdioh); ++ ++ return sdioh_sleep(sd, enab); ++#else ++ return BCME_UNSUPPORTED; ++#endif ++} ++ ++int ++bcmsdh_gpio_init(void *sdh) ++{ ++ bcmsdh_info_t *p = (bcmsdh_info_t *)sdh; ++ sdioh_info_t *sd = (sdioh_info_t *)(p->sdioh); ++ ++ return sdioh_gpio_init(sd); ++} ++ ++bool ++bcmsdh_gpioin(void *sdh, uint32 gpio) ++{ ++ bcmsdh_info_t *p = (bcmsdh_info_t *)sdh; ++ sdioh_info_t *sd = (sdioh_info_t *)(p->sdioh); ++ ++ return sdioh_gpioin(sd, gpio); ++} ++ ++int ++bcmsdh_gpioouten(void *sdh, uint32 gpio) ++{ ++ bcmsdh_info_t *p = (bcmsdh_info_t *)sdh; ++ sdioh_info_t *sd = (sdioh_info_t *)(p->sdioh); ++ ++ return sdioh_gpioouten(sd, gpio); ++} ++ ++int ++bcmsdh_gpioout(void *sdh, uint32 gpio, bool enab) ++{ ++ bcmsdh_info_t *p = (bcmsdh_info_t *)sdh; ++ sdioh_info_t *sd = (sdioh_info_t *)(p->sdioh); ++ ++ return sdioh_gpioout(sd, gpio, enab); ++} ++ ++#ifdef BCMSDIOH_TXGLOM ++void ++bcmsdh_glom_post(void *sdh, uint8 *frame, uint len) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ sdioh_glom_post(bcmsdh->sdioh, frame, len); ++} ++ ++void ++bcmsdh_glom_clear(void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ sdioh_glom_clear(bcmsdh->sdioh); ++} ++ ++uint ++bcmsdh_set_mode(void *sdh, uint mode) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ return (sdioh_set_mode(bcmsdh->sdioh, mode)); ++} ++ ++bool ++bcmsdh_glom_enabled(void) ++{ ++ return (sdioh_glom_enabled()); ++} ++#endif /* BCMSDIOH_TXGLOM */ +diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c +new file mode 100644 +index 00000000..96a126ec +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c +@@ -0,0 +1,750 @@ ++/* ++ * SDIO access interface for drivers - linux specific (pci only) ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmsdh_linux.c 347638 2012-07-27 11:39:03Z $ ++ */ ++ ++/** ++ * @file bcmsdh_linux.c ++ */ ++ ++#define __UNDEF_NO_VERSION__ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#if defined(OOB_INTR_ONLY) ++#include ++extern void dhdsdio_isr(void * args); ++#include ++#include ++#include ++#endif /* defined(OOB_INTR_ONLY) */ ++ ++ ++/** ++ * SDIO Host Controller info ++ */ ++typedef struct bcmsdh_hc bcmsdh_hc_t; ++ ++struct bcmsdh_hc { ++ bcmsdh_hc_t *next; ++#ifdef BCMPLATFORM_BUS ++ struct device *dev; /* platform device handle */ ++#else ++ struct pci_dev *dev; /* pci device handle */ ++#endif /* BCMPLATFORM_BUS */ ++ osl_t *osh; ++ void *regs; /* SDIO Host Controller address */ ++ bcmsdh_info_t *sdh; /* SDIO Host Controller handle */ ++ void *ch; ++ unsigned int oob_irq; ++ unsigned long oob_flags; /* OOB Host specifiction as edge and etc */ ++ bool oob_irq_registered; ++ bool oob_irq_enable_flag; ++#if defined(OOB_INTR_ONLY) ++ spinlock_t irq_lock; ++#endif /* defined(OOB_INTR_ONLY) */ ++}; ++static bcmsdh_hc_t *sdhcinfo = NULL; ++ ++/* driver info, initialized when bcmsdh_register is called */ ++static bcmsdh_driver_t drvinfo = {NULL, NULL}; ++ ++/* debugging macros */ ++#define SDLX_MSG(x) ++ ++/** ++ * Checks to see if vendor and device IDs match a supported SDIO Host Controller. ++ */ ++bool ++bcmsdh_chipmatch(uint16 vendor, uint16 device) ++{ ++ /* Add other vendors and devices as required */ ++ ++#ifdef BCMSDIOH_STD ++ /* Check for Arasan host controller */ ++ if (vendor == VENDOR_SI_IMAGE) { ++ return (TRUE); ++ } ++ /* Check for BRCM 27XX Standard host controller */ ++ if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM) { ++ return (TRUE); ++ } ++ /* Check for BRCM Standard host controller */ ++ if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) { ++ return (TRUE); ++ } ++ /* Check for TI PCIxx21 Standard host controller */ ++ if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI) { ++ return (TRUE); ++ } ++ if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI) { ++ return (TRUE); ++ } ++ /* Ricoh R5C822 Standard SDIO Host */ ++ if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH) { ++ return (TRUE); ++ } ++ /* JMicron Standard SDIO Host */ ++ if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON) { ++ return (TRUE); ++ } ++ ++#endif /* BCMSDIOH_STD */ ++#ifdef BCMSDIOH_SPI ++ /* This is the PciSpiHost. */ ++ if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) { ++ printf("Found PCI SPI Host Controller\n"); ++ return (TRUE); ++ } ++ ++#endif /* BCMSDIOH_SPI */ ++ ++ return (FALSE); ++} ++ ++#if defined(BCMPLATFORM_BUS) ++#if defined(BCMLXSDMMC) ++/* forward declarations */ ++int bcmsdh_probe(struct device *dev); ++int bcmsdh_remove(struct device *dev); ++ ++EXPORT_SYMBOL(bcmsdh_probe); ++EXPORT_SYMBOL(bcmsdh_remove); ++ ++#else ++/* forward declarations */ ++static int __devinit bcmsdh_probe(struct device *dev); ++static int __devexit bcmsdh_remove(struct device *dev); ++#endif /* defined(BCMLXSDMMC) */ ++ ++#if !defined(BCMLXSDMMC) ++static ++#endif /* !defined(BCMLXSDMMC) */ ++int bcmsdh_probe(struct device *dev) ++{ ++ osl_t *osh = NULL; ++ bcmsdh_hc_t *sdhc = NULL; ++ ulong regs = 0; ++ bcmsdh_info_t *sdh = NULL; ++#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS) ++ struct platform_device *pdev; ++ struct resource *r; ++#endif /* !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS) */ ++ int irq = 0; ++ uint32 vendevid; ++ unsigned long irq_flags = 0; ++ ++#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS) ++ pdev = to_platform_device(dev); ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ irq = platform_get_irq(pdev, 0); ++ if (!r || irq == NO_IRQ) ++ return -ENXIO; ++#endif /* !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS) */ ++ ++#if defined(OOB_INTR_ONLY) ++#ifdef HW_OOB ++ irq_flags = ++ IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; ++#else ++ irq_flags = IRQF_TRIGGER_FALLING; ++#endif /* HW_OOB */ ++ ++ /* Get customer specific OOB IRQ parametres: IRQ number as IRQ type */ ++ irq = dhd_customer_oob_irq_map(&irq_flags); ++ if (irq < 0) { ++ SDLX_MSG(("%s: Host irq is not defined\n", __FUNCTION__)); ++ return 1; ++ } ++#endif /* defined(OOB_INTR_ONLY) */ ++ /* allocate SDIO Host Controller state info */ ++ if (!(osh = osl_attach(dev, PCI_BUS, FALSE))) { ++ SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__)); ++ goto err; ++ } ++ if (!(sdhc = MALLOC(osh, sizeof(bcmsdh_hc_t)))) { ++ SDLX_MSG(("%s: out of memory, allocated %d bytes\n", ++ __FUNCTION__, ++ MALLOCED(osh))); ++ goto err; ++ } ++ bzero(sdhc, sizeof(bcmsdh_hc_t)); ++ sdhc->osh = osh; ++ ++ sdhc->dev = (void *)dev; ++ ++#if defined(BCMLXSDMMC) ++ if (!(sdh = bcmsdh_attach(osh, (void *)0, ++ (void **)®s, irq))) { ++ SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__)); ++ goto err; ++ } ++#else ++ if (!(sdh = bcmsdh_attach(osh, (void *)r->start, ++ (void **)®s, irq))) { ++ SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__)); ++ goto err; ++ } ++#endif /* defined(BCMLXSDMMC) */ ++ sdhc->sdh = sdh; ++ sdhc->oob_irq = irq; ++ sdhc->oob_flags = irq_flags; ++ sdhc->oob_irq_registered = FALSE; /* to make sure.. */ ++ sdhc->oob_irq_enable_flag = FALSE; ++#if defined(OOB_INTR_ONLY) ++ spin_lock_init(&sdhc->irq_lock); ++#endif /* defined(BCMLXSDMMC) */ ++ ++ /* chain SDIO Host Controller info together */ ++ sdhc->next = sdhcinfo; ++ sdhcinfo = sdhc; ++ ++ /* Read the vendor/device ID from the CIS */ ++ vendevid = bcmsdh_query_device(sdh); ++ /* try to attach to the target device */ ++ if (!(sdhc->ch = drvinfo.attach((vendevid >> 16), ++ (vendevid & 0xFFFF), 0, 0, 0, 0, ++ (void *)regs, NULL, sdh))) { ++ SDLX_MSG(("%s: device attach failed\n", __FUNCTION__)); ++ goto err; ++ } ++ ++ return 0; ++ ++ /* error handling */ ++err: ++ if (sdhc) { ++ if (sdhc->sdh) ++ bcmsdh_detach(sdhc->osh, sdhc->sdh); ++ MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); ++ } ++ if (osh) ++ osl_detach(osh); ++ return -ENODEV; ++} ++ ++#if !defined(BCMLXSDMMC) ++static ++#endif /* !defined(BCMLXSDMMC) */ ++int bcmsdh_remove(struct device *dev) ++{ ++ bcmsdh_hc_t *sdhc, *prev; ++ osl_t *osh; ++ ++ sdhc = sdhcinfo; ++ drvinfo.detach(sdhc->ch); ++ bcmsdh_detach(sdhc->osh, sdhc->sdh); ++ ++ /* find the SDIO Host Controller state for this pdev and take it out from the list */ ++ for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) { ++ if (sdhc->dev == (void *)dev) { ++ if (prev) ++ prev->next = sdhc->next; ++ else ++ sdhcinfo = NULL; ++ break; ++ } ++ prev = sdhc; ++ } ++ if (!sdhc) { ++ SDLX_MSG(("%s: failed\n", __FUNCTION__)); ++ return 0; ++ } ++ ++ /* release SDIO Host Controller info */ ++ osh = sdhc->osh; ++ MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); ++ osl_detach(osh); ++ ++#if !defined(BCMLXSDMMC) || defined(OOB_INTR_ONLY) ++ dev_set_drvdata(dev, NULL); ++#endif /* !defined(BCMLXSDMMC) || defined(OOB_INTR_ONLY) */ ++ ++ return 0; ++} ++ ++#else /* BCMPLATFORM_BUS */ ++ ++#if !defined(BCMLXSDMMC) ++/* forward declarations for PCI probe and remove functions. */ ++static int __devinit bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); ++static void __devexit bcmsdh_pci_remove(struct pci_dev *pdev); ++ ++/** ++ * pci id table ++ */ ++static struct pci_device_id bcmsdh_pci_devid[] __devinitdata = { ++ { vendor: PCI_ANY_ID, ++ device: PCI_ANY_ID, ++ subvendor: PCI_ANY_ID, ++ subdevice: PCI_ANY_ID, ++ class: 0, ++ class_mask: 0, ++ driver_data: 0, ++ }, ++ { 0, } ++}; ++MODULE_DEVICE_TABLE(pci, bcmsdh_pci_devid); ++ ++/** ++ * SDIO Host Controller pci driver info ++ */ ++static struct pci_driver bcmsdh_pci_driver = { ++ node: {}, ++ name: "bcmsdh", ++ id_table: bcmsdh_pci_devid, ++ probe: bcmsdh_pci_probe, ++ remove: bcmsdh_pci_remove, ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) ++ save_state: NULL, ++#endif ++ suspend: NULL, ++ resume: NULL, ++ }; ++ ++ ++extern uint sd_pci_slot; /* Force detection to a particular PCI */ ++ /* slot only . Allows for having multiple */ ++ /* WL devices at once in a PC */ ++ /* Only one instance of dhd will be */ ++ /* usable at a time */ ++ /* Upper word is bus number, */ ++ /* lower word is slot number */ ++ /* Default value of 0xffffffff turns this */ ++ /* off */ ++module_param(sd_pci_slot, uint, 0); ++ ++ ++/** ++ * Detect supported SDIO Host Controller and attach if found. ++ * ++ * Determine if the device described by pdev is a supported SDIO Host ++ * Controller. If so, attach to it and attach to the target device. ++ */ ++static int __devinit ++bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ++{ ++ osl_t *osh = NULL; ++ bcmsdh_hc_t *sdhc = NULL; ++ ulong regs; ++ bcmsdh_info_t *sdh = NULL; ++ int rc; ++ ++ if (sd_pci_slot != 0xFFFFffff) { ++ if (pdev->bus->number != (sd_pci_slot>>16) || ++ PCI_SLOT(pdev->devfn) != (sd_pci_slot&0xffff)) { ++ SDLX_MSG(("%s: %s: bus %X, slot %X, vend %X, dev %X\n", ++ __FUNCTION__, ++ bcmsdh_chipmatch(pdev->vendor, pdev->device) ++ ?"Found compatible SDIOHC" ++ :"Probing unknown device", ++ pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor, ++ pdev->device)); ++ return -ENODEV; ++ } ++ SDLX_MSG(("%s: %s: bus %X, slot %X, vendor %X, device %X (good PCI location)\n", ++ __FUNCTION__, ++ bcmsdh_chipmatch(pdev->vendor, pdev->device) ++ ?"Using compatible SDIOHC" ++ :"WARNING, forced use of unkown device", ++ pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor, pdev->device)); ++ } ++ ++ if ((pdev->vendor == VENDOR_TI) && ((pdev->device == PCIXX21_FLASHMEDIA_ID) || ++ (pdev->device == PCIXX21_FLASHMEDIA0_ID))) { ++ uint32 config_reg; ++ ++ SDLX_MSG(("%s: Disabling TI FlashMedia Controller.\n", __FUNCTION__)); ++ if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) { ++ SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__)); ++ goto err; ++ } ++ ++ config_reg = OSL_PCI_READ_CONFIG(osh, 0x4c, 4); ++ ++ /* ++ * Set MMC_SD_DIS bit in FlashMedia Controller. ++ * Disbling the SD/MMC Controller in the FlashMedia Controller ++ * allows the Standard SD Host Controller to take over control ++ * of the SD Slot. ++ */ ++ config_reg |= 0x02; ++ OSL_PCI_WRITE_CONFIG(osh, 0x4c, 4, config_reg); ++ osl_detach(osh); ++ } ++ /* match this pci device with what we support */ ++ /* we can't solely rely on this to believe it is our SDIO Host Controller! */ ++ if (!bcmsdh_chipmatch(pdev->vendor, pdev->device)) { ++ return -ENODEV; ++ } ++ ++ /* this is a pci device we might support */ ++ SDLX_MSG(("%s: Found possible SDIO Host Controller: bus %d slot %d func %d irq %d\n", ++ __FUNCTION__, ++ pdev->bus->number, PCI_SLOT(pdev->devfn), ++ PCI_FUNC(pdev->devfn), pdev->irq)); ++ ++ /* use bcmsdh_query_device() to get the vendor ID of the target device so ++ * it will eventually appear in the Broadcom string on the console ++ */ ++ ++ /* allocate SDIO Host Controller state info */ ++ if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) { ++ SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__)); ++ goto err; ++ } ++ if (!(sdhc = MALLOC(osh, sizeof(bcmsdh_hc_t)))) { ++ SDLX_MSG(("%s: out of memory, allocated %d bytes\n", ++ __FUNCTION__, ++ MALLOCED(osh))); ++ goto err; ++ } ++ bzero(sdhc, sizeof(bcmsdh_hc_t)); ++ sdhc->osh = osh; ++ ++ sdhc->dev = pdev; ++ ++ /* map to address where host can access */ ++ pci_set_master(pdev); ++ rc = pci_enable_device(pdev); ++ if (rc) { ++ SDLX_MSG(("%s: Cannot enable PCI device\n", __FUNCTION__)); ++ goto err; ++ } ++ if (!(sdh = bcmsdh_attach(osh, (void *)(uintptr)pci_resource_start(pdev, 0), ++ (void **)®s, pdev->irq))) { ++ SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__)); ++ goto err; ++ } ++ ++ sdhc->sdh = sdh; ++ ++ /* try to attach to the target device */ ++ if (!(sdhc->ch = drvinfo.attach(VENDOR_BROADCOM, /* pdev->vendor, */ ++ bcmsdh_query_device(sdh) & 0xFFFF, 0, 0, 0, 0, ++ (void *)regs, NULL, sdh))) { ++ SDLX_MSG(("%s: device attach failed\n", __FUNCTION__)); ++ goto err; ++ } ++ ++ /* chain SDIO Host Controller info together */ ++ sdhc->next = sdhcinfo; ++ sdhcinfo = sdhc; ++ ++ return 0; ++ ++ /* error handling */ ++err: ++ if (sdhc) { ++ if (sdhc->sdh) ++ bcmsdh_detach(sdhc->osh, sdhc->sdh); ++ MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); ++ } ++ if (osh) ++ osl_detach(osh); ++ return -ENODEV; ++} ++ ++ ++/** ++ * Detach from target devices and SDIO Host Controller ++ */ ++static void __devexit ++bcmsdh_pci_remove(struct pci_dev *pdev) ++{ ++ bcmsdh_hc_t *sdhc, *prev; ++ osl_t *osh; ++ ++ /* find the SDIO Host Controller state for this pdev and take it out from the list */ ++ for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) { ++ if (sdhc->dev == pdev) { ++ if (prev) ++ prev->next = sdhc->next; ++ else ++ sdhcinfo = NULL; ++ break; ++ } ++ prev = sdhc; ++ } ++ if (!sdhc) ++ return; ++ ++ drvinfo.detach(sdhc->ch); ++ ++ bcmsdh_detach(sdhc->osh, sdhc->sdh); ++ ++ /* release SDIO Host Controller info */ ++ osh = sdhc->osh; ++ MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); ++ osl_detach(osh); ++} ++#endif /* BCMLXSDMMC */ ++#endif /* BCMPLATFORM_BUS */ ++ ++extern int sdio_function_init(void); ++ ++extern int sdio_func_reg_notify(void* semaphore); ++extern void sdio_func_unreg_notify(void); ++ ++#if defined(BCMLXSDMMC) ++int bcmsdh_reg_sdio_notify(void* semaphore) ++{ ++ return sdio_func_reg_notify(semaphore); ++} ++ ++void bcmsdh_unreg_sdio_notify(void) ++{ ++ sdio_func_unreg_notify(); ++} ++#endif /* defined(BCMLXSDMMC) */ ++ ++int ++bcmsdh_register(bcmsdh_driver_t *driver) ++{ ++ int error = 0; ++ ++ drvinfo = *driver; ++ ++#if defined(BCMPLATFORM_BUS) ++ SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n")); ++ error = sdio_function_init(); ++ return error; ++#endif /* defined(BCMPLATFORM_BUS) */ ++ ++#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) ++ if (!(error = pci_module_init(&bcmsdh_pci_driver))) ++ return 0; ++#else ++ if (!(error = pci_register_driver(&bcmsdh_pci_driver))) ++ return 0; ++#endif ++ ++ SDLX_MSG(("%s: pci_module_init failed 0x%x\n", __FUNCTION__, error)); ++#endif /* BCMPLATFORM_BUS */ ++ ++ return error; ++} ++ ++extern void sdio_function_cleanup(void); ++ ++void ++bcmsdh_unregister(void) ++{ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) ++ if (bcmsdh_pci_driver.node.next) ++#endif ++ ++#if defined(BCMLXSDMMC) ++ sdio_function_cleanup(); ++#endif /* BCMLXSDMMC */ ++ ++#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) ++ pci_unregister_driver(&bcmsdh_pci_driver); ++#endif /* BCMPLATFORM_BUS */ ++} ++ ++#if defined(OOB_INTR_ONLY) ++void bcmsdh_oob_intr_set(bool enable) ++{ ++ static bool curstate = 1; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&sdhcinfo->irq_lock, flags); ++ if (curstate != enable) { ++ if (enable) ++ enable_irq(sdhcinfo->oob_irq); ++ else ++ disable_irq_nosync(sdhcinfo->oob_irq); ++ curstate = enable; ++ } ++ spin_unlock_irqrestore(&sdhcinfo->irq_lock, flags); ++} ++ ++static irqreturn_t wlan_oob_irq(int irq, void *dev_id) ++{ ++ dhd_pub_t *dhdp; ++ ++ dhdp = (dhd_pub_t *)dev_get_drvdata(sdhcinfo->dev); ++ ++ bcmsdh_oob_intr_set(0); ++ ++ if (dhdp == NULL) { ++ SDLX_MSG(("Out of band GPIO interrupt fired way too early\n")); ++ return IRQ_HANDLED; ++ } ++ ++ dhdsdio_isr((void *)dhdp->bus); ++ ++ return IRQ_HANDLED; ++} ++ ++int bcmsdh_register_oob_intr(void * dhdp) ++{ ++ int error = 0; ++ ++ SDLX_MSG(("%s Enter \n", __FUNCTION__)); ++ ++ /* IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; */ ++ ++ dev_set_drvdata(sdhcinfo->dev, dhdp); ++ ++ if (!sdhcinfo->oob_irq_registered) { ++ SDLX_MSG(("%s IRQ=%d Type=%X \n", __FUNCTION__, ++ (int)sdhcinfo->oob_irq, (int)sdhcinfo->oob_flags)); ++ /* Refer to customer Host IRQ docs about proper irqflags definition */ ++ error = request_irq(sdhcinfo->oob_irq, wlan_oob_irq, sdhcinfo->oob_flags, ++ "bcmsdh_sdmmc", NULL); ++ if (error) ++ return -ENODEV; ++ ++ error = enable_irq_wake(sdhcinfo->oob_irq); ++ if (error) ++ SDLX_MSG(("%s enable_irq_wake error=%d \n", __FUNCTION__, error)); ++ sdhcinfo->oob_irq_registered = TRUE; ++ sdhcinfo->oob_irq_enable_flag = TRUE; ++ } ++ ++ return 0; ++} ++ ++void bcmsdh_set_irq(int flag) ++{ ++ if (sdhcinfo->oob_irq_registered && sdhcinfo->oob_irq_enable_flag != flag) { ++ SDLX_MSG(("%s Flag = %d", __FUNCTION__, flag)); ++ sdhcinfo->oob_irq_enable_flag = flag; ++ if (flag) { ++ enable_irq(sdhcinfo->oob_irq); ++ enable_irq_wake(sdhcinfo->oob_irq); ++ } else { ++ disable_irq_wake(sdhcinfo->oob_irq); ++ disable_irq(sdhcinfo->oob_irq); ++ } ++ } ++} ++ ++void bcmsdh_unregister_oob_intr(void) ++{ ++ SDLX_MSG(("%s: Enter\n", __FUNCTION__)); ++ ++ if (sdhcinfo->oob_irq_registered == TRUE) { ++ bcmsdh_set_irq(FALSE); ++ free_irq(sdhcinfo->oob_irq, NULL); ++ sdhcinfo->oob_irq_registered = FALSE; ++ } ++} ++#endif /* defined(OOB_INTR_ONLY) */ ++ ++#if defined(BCMLXSDMMC) ++void *bcmsdh_get_drvdata(void) ++{ ++ if (!sdhcinfo) ++ return NULL; ++ return dev_get_drvdata(sdhcinfo->dev); ++} ++#endif ++ ++/* Module parameters specific to each host-controller driver */ ++ ++extern uint sd_msglevel; /* Debug message level */ ++module_param(sd_msglevel, uint, 0); ++ ++extern uint sd_power; /* 0 = SD Power OFF, 1 = SD Power ON. */ ++module_param(sd_power, uint, 0); ++ ++extern uint sd_clock; /* SD Clock Control, 0 = SD Clock OFF, 1 = SD Clock ON */ ++module_param(sd_clock, uint, 0); ++ ++extern uint sd_divisor; /* Divisor (-1 means external clock) */ ++module_param(sd_divisor, uint, 0); ++ ++extern uint sd_sdmode; /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */ ++module_param(sd_sdmode, uint, 0); ++ ++extern uint sd_hiok; /* Ok to use hi-speed mode */ ++module_param(sd_hiok, uint, 0); ++ ++extern uint sd_f2_blocksize; ++module_param(sd_f2_blocksize, int, 0); ++ ++#ifdef BCMSDIOH_STD ++extern int sd_uhsimode; ++module_param(sd_uhsimode, int, 0); ++#endif ++ ++#ifdef BCMSDIOH_TXGLOM ++extern uint sd_txglom; ++module_param(sd_txglom, uint, 0); ++#endif ++ ++#ifdef BCMSDH_MODULE ++EXPORT_SYMBOL(bcmsdh_attach); ++EXPORT_SYMBOL(bcmsdh_detach); ++EXPORT_SYMBOL(bcmsdh_intr_query); ++EXPORT_SYMBOL(bcmsdh_intr_enable); ++EXPORT_SYMBOL(bcmsdh_intr_disable); ++EXPORT_SYMBOL(bcmsdh_intr_reg); ++EXPORT_SYMBOL(bcmsdh_intr_dereg); ++ ++#if defined(DHD_DEBUG) ++EXPORT_SYMBOL(bcmsdh_intr_pending); ++#endif ++ ++EXPORT_SYMBOL(bcmsdh_devremove_reg); ++EXPORT_SYMBOL(bcmsdh_cfg_read); ++EXPORT_SYMBOL(bcmsdh_cfg_write); ++EXPORT_SYMBOL(bcmsdh_cis_read); ++EXPORT_SYMBOL(bcmsdh_reg_read); ++EXPORT_SYMBOL(bcmsdh_reg_write); ++EXPORT_SYMBOL(bcmsdh_regfail); ++EXPORT_SYMBOL(bcmsdh_send_buf); ++EXPORT_SYMBOL(bcmsdh_recv_buf); ++ ++EXPORT_SYMBOL(bcmsdh_rwdata); ++EXPORT_SYMBOL(bcmsdh_abort); ++EXPORT_SYMBOL(bcmsdh_query_device); ++EXPORT_SYMBOL(bcmsdh_query_iofnum); ++EXPORT_SYMBOL(bcmsdh_iovar_op); ++EXPORT_SYMBOL(bcmsdh_register); ++EXPORT_SYMBOL(bcmsdh_unregister); ++EXPORT_SYMBOL(bcmsdh_chipmatch); ++EXPORT_SYMBOL(bcmsdh_reset); ++EXPORT_SYMBOL(bcmsdh_waitlockfree); ++ ++EXPORT_SYMBOL(bcmsdh_get_dstatus); ++EXPORT_SYMBOL(bcmsdh_cfg_read_word); ++EXPORT_SYMBOL(bcmsdh_cfg_write_word); ++EXPORT_SYMBOL(bcmsdh_cur_sbwad); ++EXPORT_SYMBOL(bcmsdh_chipinfo); ++ ++#endif /* BCMSDH_MODULE */ +diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c +new file mode 100644 +index 00000000..046bd028 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c +@@ -0,0 +1,1504 @@ ++/* ++ * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmsdh_sdmmc.c 379078 2013-01-16 00:41:36Z $ ++ */ ++#include ++ ++#include ++#include ++#include ++#include ++#include /* SDIO Device and Protocol Specs */ ++#include /* Standard SDIO Host Controller Specification */ ++#include /* bcmsdh to/from specific controller APIs */ ++#include /* ioctl/iovars */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) ++#include ++extern volatile bool dhd_mmc_suspend; ++#endif ++#include "bcmsdh_sdmmc.h" ++ ++#ifndef BCMSDH_MODULE ++extern int sdio_function_init(void); ++extern void sdio_function_cleanup(void); ++#endif /* BCMSDH_MODULE */ ++ ++#if !defined(OOB_INTR_ONLY) ++static void IRQHandler(struct sdio_func *func); ++static void IRQHandlerF2(struct sdio_func *func); ++#endif /* !defined(OOB_INTR_ONLY) */ ++static int sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr); ++extern int sdio_reset_comm(struct mmc_card *card); ++ ++extern PBCMSDH_SDMMC_INSTANCE gInstance; ++ ++#define DEFAULT_SDIO_F2_BLKSIZE 512 ++#ifndef CUSTOM_SDIO_F2_BLKSIZE ++#define CUSTOM_SDIO_F2_BLKSIZE DEFAULT_SDIO_F2_BLKSIZE ++#endif ++ ++uint sd_sdmode = SDIOH_MODE_SD4; /* Use SD4 mode by default */ ++uint sd_f2_blocksize = CUSTOM_SDIO_F2_BLKSIZE; ++uint sd_divisor = 2; /* Default 48MHz/2 = 24MHz */ ++ ++uint sd_power = 1; /* Default to SD Slot powered ON */ ++uint sd_clock = 1; /* Default to SD Clock turned ON */ ++uint sd_hiok = FALSE; /* Don't use hi-speed mode by default */ ++uint sd_msglevel = 0x01; ++uint sd_use_dma = TRUE; ++DHD_PM_RESUME_WAIT_INIT(sdioh_request_byte_wait); ++DHD_PM_RESUME_WAIT_INIT(sdioh_request_word_wait); ++DHD_PM_RESUME_WAIT_INIT(sdioh_request_packet_wait); ++DHD_PM_RESUME_WAIT_INIT(sdioh_request_buffer_wait); ++ ++#define DMA_ALIGN_MASK 0x03 ++#define MMC_SDIO_ABORT_RETRY_LIMIT 5 ++ ++int sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data); ++ ++static int ++sdioh_sdmmc_card_enablefuncs(sdioh_info_t *sd) ++{ ++ int err_ret; ++ uint32 fbraddr; ++ uint8 func; ++ ++ sd_trace(("%s\n", __FUNCTION__)); ++ ++ /* Get the Card's common CIS address */ ++ sd->com_cis_ptr = sdioh_sdmmc_get_cisaddr(sd, SDIOD_CCCR_CISPTR_0); ++ sd->func_cis_ptr[0] = sd->com_cis_ptr; ++ sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr)); ++ ++ /* Get the Card's function CIS (for each function) */ ++ for (fbraddr = SDIOD_FBR_STARTADDR, func = 1; ++ func <= sd->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) { ++ sd->func_cis_ptr[func] = sdioh_sdmmc_get_cisaddr(sd, SDIOD_FBR_CISPTR_0 + fbraddr); ++ sd_info(("%s: Function %d CIS Ptr = 0x%x\n", ++ __FUNCTION__, func, sd->func_cis_ptr[func])); ++ } ++ ++ sd->func_cis_ptr[0] = sd->com_cis_ptr; ++ sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr)); ++ ++ /* Enable Function 1 */ ++ sdio_claim_host(gInstance->func[1]); ++ err_ret = sdio_enable_func(gInstance->func[1]); ++ sdio_release_host(gInstance->func[1]); ++ if (err_ret) { ++ sd_err(("bcmsdh_sdmmc: Failed to enable F1 Err: 0x%08x", err_ret)); ++ } ++ ++ return FALSE; ++} ++ ++/* ++ * Public entry points & extern's ++ */ ++extern sdioh_info_t * ++sdioh_attach(osl_t *osh, void *bar0, uint irq) ++{ ++ sdioh_info_t *sd; ++ int err_ret; ++ ++ sd_trace(("%s\n", __FUNCTION__)); ++ ++ if (gInstance == NULL) { ++ sd_err(("%s: SDIO Device not present\n", __FUNCTION__)); ++ return NULL; ++ } ++ ++ if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) { ++ sd_err(("sdioh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh))); ++ return NULL; ++ } ++ bzero((char *)sd, sizeof(sdioh_info_t)); ++ sd->osh = osh; ++ if (sdioh_sdmmc_osinit(sd) != 0) { ++ sd_err(("%s:sdioh_sdmmc_osinit() failed\n", __FUNCTION__)); ++ MFREE(sd->osh, sd, sizeof(sdioh_info_t)); ++ return NULL; ++ } ++ ++ sd->num_funcs = 2; ++ sd->sd_blockmode = TRUE; ++ sd->use_client_ints = TRUE; ++ sd->client_block_size[0] = 64; ++ sd->use_rxchain = FALSE; ++ ++ gInstance->sd = sd; ++ ++ /* Claim host controller */ ++ if (gInstance->func[1]) { ++ sdio_claim_host(gInstance->func[1]); ++ ++ sd->client_block_size[1] = 64; ++ err_ret = sdio_set_block_size(gInstance->func[1], 64); ++ if (err_ret) { ++ sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize\n")); ++ } ++ ++ /* Release host controller F1 */ ++ sdio_release_host(gInstance->func[1]); ++ } else { ++ sd_err(("%s:gInstance->func[1] is null\n", __FUNCTION__)); ++ MFREE(sd->osh, sd, sizeof(sdioh_info_t)); ++ return NULL; ++ } ++ ++ if (gInstance->func[2]) { ++ /* Claim host controller F2 */ ++ sdio_claim_host(gInstance->func[2]); ++ ++ sd->client_block_size[2] = sd_f2_blocksize; ++ err_ret = sdio_set_block_size(gInstance->func[2], sd_f2_blocksize); ++ if (err_ret) { ++ sd_err(("bcmsdh_sdmmc: Failed to set F2 blocksize to %d\n", ++ sd_f2_blocksize)); ++ } ++ ++ /* Release host controller F2 */ ++ sdio_release_host(gInstance->func[2]); ++ } else { ++ sd_err(("%s:gInstance->func[2] is null\n", __FUNCTION__)); ++ MFREE(sd->osh, sd, sizeof(sdioh_info_t)); ++ return NULL; ++ } ++ ++ sdioh_sdmmc_card_enablefuncs(sd); ++ ++ sd_trace(("%s: Done\n", __FUNCTION__)); ++ return sd; ++} ++ ++ ++extern SDIOH_API_RC ++sdioh_detach(osl_t *osh, sdioh_info_t *sd) ++{ ++ sd_trace(("%s\n", __FUNCTION__)); ++ ++ if (sd) { ++ ++ /* Disable Function 2 */ ++ sdio_claim_host(gInstance->func[2]); ++ sdio_disable_func(gInstance->func[2]); ++ sdio_release_host(gInstance->func[2]); ++ ++ /* Disable Function 1 */ ++ if (gInstance->func[1]) { ++ sdio_claim_host(gInstance->func[1]); ++ sdio_disable_func(gInstance->func[1]); ++ sdio_release_host(gInstance->func[1]); ++ } ++ ++ gInstance->func[1] = NULL; ++ gInstance->func[2] = NULL; ++ ++ /* deregister irq */ ++ sdioh_sdmmc_osfree(sd); ++ ++ MFREE(sd->osh, sd, sizeof(sdioh_info_t)); ++ } ++ return SDIOH_API_RC_SUCCESS; ++} ++ ++#if defined(OOB_INTR_ONLY) && defined(HW_OOB) ++ ++extern SDIOH_API_RC ++sdioh_enable_func_intr(void) ++{ ++ uint8 reg; ++ int err; ++ ++ if (gInstance->func[0]) { ++ sdio_claim_host(gInstance->func[0]); ++ ++ reg = sdio_readb(gInstance->func[0], SDIOD_CCCR_INTEN, &err); ++ if (err) { ++ sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err)); ++ sdio_release_host(gInstance->func[0]); ++ return SDIOH_API_RC_FAIL; ++ } ++ ++ /* Enable F1 and F2 interrupts, clear master enable */ ++ reg &= ~INTR_CTL_MASTER_EN; ++ reg |= (INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN); ++ sdio_writeb(gInstance->func[0], reg, SDIOD_CCCR_INTEN, &err); ++ sdio_release_host(gInstance->func[0]); ++ ++ if (err) { ++ sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err)); ++ return SDIOH_API_RC_FAIL; ++ } ++ } ++ ++ return SDIOH_API_RC_SUCCESS; ++} ++ ++extern SDIOH_API_RC ++sdioh_disable_func_intr(void) ++{ ++ uint8 reg; ++ int err; ++ ++ if (gInstance->func[0]) { ++ sdio_claim_host(gInstance->func[0]); ++ reg = sdio_readb(gInstance->func[0], SDIOD_CCCR_INTEN, &err); ++ if (err) { ++ sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err)); ++ sdio_release_host(gInstance->func[0]); ++ return SDIOH_API_RC_FAIL; ++ } ++ ++ reg &= ~(INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN); ++ /* Disable master interrupt with the last function interrupt */ ++ if (!(reg & 0xFE)) ++ reg = 0; ++ sdio_writeb(gInstance->func[0], reg, SDIOD_CCCR_INTEN, &err); ++ ++ sdio_release_host(gInstance->func[0]); ++ if (err) { ++ sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err)); ++ return SDIOH_API_RC_FAIL; ++ } ++ } ++ return SDIOH_API_RC_SUCCESS; ++} ++#endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */ ++ ++/* Configure callback to client when we recieve client interrupt */ ++extern SDIOH_API_RC ++sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh) ++{ ++ sd_trace(("%s: Entering\n", __FUNCTION__)); ++ if (fn == NULL) { ++ sd_err(("%s: interrupt handler is NULL, not registering\n", __FUNCTION__)); ++ return SDIOH_API_RC_FAIL; ++ } ++#if !defined(OOB_INTR_ONLY) ++ sd->intr_handler = fn; ++ sd->intr_handler_arg = argh; ++ sd->intr_handler_valid = TRUE; ++ ++ /* register and unmask irq */ ++ if (gInstance->func[2]) { ++ sdio_claim_host(gInstance->func[2]); ++ sdio_claim_irq(gInstance->func[2], IRQHandlerF2); ++ sdio_release_host(gInstance->func[2]); ++ } ++ ++ if (gInstance->func[1]) { ++ sdio_claim_host(gInstance->func[1]); ++ sdio_claim_irq(gInstance->func[1], IRQHandler); ++ sdio_release_host(gInstance->func[1]); ++ } ++#elif defined(HW_OOB) ++ sdioh_enable_func_intr(); ++#endif /* !defined(OOB_INTR_ONLY) */ ++ ++ return SDIOH_API_RC_SUCCESS; ++} ++ ++extern SDIOH_API_RC ++sdioh_interrupt_deregister(sdioh_info_t *sd) ++{ ++ sd_trace(("%s: Entering\n", __FUNCTION__)); ++ ++#if !defined(OOB_INTR_ONLY) ++ if (gInstance->func[1]) { ++ /* register and unmask irq */ ++ sdio_claim_host(gInstance->func[1]); ++ sdio_release_irq(gInstance->func[1]); ++ sdio_release_host(gInstance->func[1]); ++ } ++ ++ if (gInstance->func[2]) { ++ /* Claim host controller F2 */ ++ sdio_claim_host(gInstance->func[2]); ++ sdio_release_irq(gInstance->func[2]); ++ /* Release host controller F2 */ ++ sdio_release_host(gInstance->func[2]); ++ } ++ ++ sd->intr_handler_valid = FALSE; ++ sd->intr_handler = NULL; ++ sd->intr_handler_arg = NULL; ++#elif defined(HW_OOB) ++ sdioh_disable_func_intr(); ++#endif /* !defined(OOB_INTR_ONLY) */ ++ return SDIOH_API_RC_SUCCESS; ++} ++ ++extern SDIOH_API_RC ++sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff) ++{ ++ sd_trace(("%s: Entering\n", __FUNCTION__)); ++ *onoff = sd->client_intr_enabled; ++ return SDIOH_API_RC_SUCCESS; ++} ++ ++#if defined(DHD_DEBUG) ++extern bool ++sdioh_interrupt_pending(sdioh_info_t *sd) ++{ ++ return (0); ++} ++#endif ++ ++uint ++sdioh_query_iofnum(sdioh_info_t *sd) ++{ ++ return sd->num_funcs; ++} ++ ++/* IOVar table */ ++enum { ++ IOV_MSGLEVEL = 1, ++ IOV_BLOCKMODE, ++ IOV_BLOCKSIZE, ++ IOV_DMA, ++ IOV_USEINTS, ++ IOV_NUMINTS, ++ IOV_NUMLOCALINTS, ++ IOV_HOSTREG, ++ IOV_DEVREG, ++ IOV_DIVISOR, ++ IOV_SDMODE, ++ IOV_HISPEED, ++ IOV_HCIREGS, ++ IOV_POWER, ++ IOV_CLOCK, ++ IOV_RXCHAIN ++}; ++ ++const bcm_iovar_t sdioh_iovars[] = { ++ {"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 }, ++ {"sd_blockmode", IOV_BLOCKMODE, 0, IOVT_BOOL, 0 }, ++ {"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */ ++ {"sd_dma", IOV_DMA, 0, IOVT_BOOL, 0 }, ++ {"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0 }, ++ {"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0 }, ++ {"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32, 0 }, ++ {"sd_hostreg", IOV_HOSTREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, ++ {"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, ++ {"sd_divisor", IOV_DIVISOR, 0, IOVT_UINT32, 0 }, ++ {"sd_power", IOV_POWER, 0, IOVT_UINT32, 0 }, ++ {"sd_clock", IOV_CLOCK, 0, IOVT_UINT32, 0 }, ++ {"sd_mode", IOV_SDMODE, 0, IOVT_UINT32, 100}, ++ {"sd_highspeed", IOV_HISPEED, 0, IOVT_UINT32, 0 }, ++ {"sd_rxchain", IOV_RXCHAIN, 0, IOVT_BOOL, 0 }, ++ {NULL, 0, 0, 0, 0 } ++}; ++ ++int ++sdioh_iovar_op(sdioh_info_t *si, const char *name, ++ void *params, int plen, void *arg, int len, bool set) ++{ ++ const bcm_iovar_t *vi = NULL; ++ int bcmerror = 0; ++ int val_size; ++ int32 int_val = 0; ++ bool bool_val; ++ uint32 actionid; ++ ++ ASSERT(name); ++ ASSERT(len >= 0); ++ ++ /* Get must have return space; Set does not take qualifiers */ ++ ASSERT(set || (arg && len)); ++ ASSERT(!set || (!params && !plen)); ++ ++ sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name)); ++ ++ if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) { ++ bcmerror = BCME_UNSUPPORTED; ++ goto exit; ++ } ++ ++ if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0) ++ goto exit; ++ ++ /* Set up params so get and set can share the convenience variables */ ++ if (params == NULL) { ++ params = arg; ++ plen = len; ++ } ++ ++ if (vi->type == IOVT_VOID) ++ val_size = 0; ++ else if (vi->type == IOVT_BUFFER) ++ val_size = len; ++ else ++ val_size = sizeof(int); ++ ++ if (plen >= (int)sizeof(int_val)) ++ bcopy(params, &int_val, sizeof(int_val)); ++ ++ bool_val = (int_val != 0) ? TRUE : FALSE; ++ BCM_REFERENCE(bool_val); ++ ++ actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); ++ switch (actionid) { ++ case IOV_GVAL(IOV_MSGLEVEL): ++ int_val = (int32)sd_msglevel; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_MSGLEVEL): ++ sd_msglevel = int_val; ++ break; ++ ++ case IOV_GVAL(IOV_BLOCKMODE): ++ int_val = (int32)si->sd_blockmode; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_BLOCKMODE): ++ si->sd_blockmode = (bool)int_val; ++ /* Haven't figured out how to make non-block mode with DMA */ ++ break; ++ ++ case IOV_GVAL(IOV_BLOCKSIZE): ++ if ((uint32)int_val > si->num_funcs) { ++ bcmerror = BCME_BADARG; ++ break; ++ } ++ int_val = (int32)si->client_block_size[int_val]; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_BLOCKSIZE): ++ { ++ uint func = ((uint32)int_val >> 16); ++ uint blksize = (uint16)int_val; ++ uint maxsize; ++ ++ if (func > si->num_funcs) { ++ bcmerror = BCME_BADARG; ++ break; ++ } ++ ++ switch (func) { ++ case 0: maxsize = 32; break; ++ case 1: maxsize = BLOCK_SIZE_4318; break; ++ case 2: maxsize = BLOCK_SIZE_4328; break; ++ default: maxsize = 0; ++ } ++ if (blksize > maxsize) { ++ bcmerror = BCME_BADARG; ++ break; ++ } ++ if (!blksize) { ++ blksize = maxsize; ++ } ++ ++ /* Now set it */ ++ si->client_block_size[func] = blksize; ++ ++ break; ++ } ++ ++ case IOV_GVAL(IOV_RXCHAIN): ++ int_val = (int32)si->use_rxchain; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_GVAL(IOV_DMA): ++ int_val = (int32)si->sd_use_dma; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_DMA): ++ si->sd_use_dma = (bool)int_val; ++ break; ++ ++ case IOV_GVAL(IOV_USEINTS): ++ int_val = (int32)si->use_client_ints; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_USEINTS): ++ si->use_client_ints = (bool)int_val; ++ if (si->use_client_ints) ++ si->intmask |= CLIENT_INTR; ++ else ++ si->intmask &= ~CLIENT_INTR; ++ ++ break; ++ ++ case IOV_GVAL(IOV_DIVISOR): ++ int_val = (uint32)sd_divisor; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_DIVISOR): ++ sd_divisor = int_val; ++ break; ++ ++ case IOV_GVAL(IOV_POWER): ++ int_val = (uint32)sd_power; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_POWER): ++ sd_power = int_val; ++ break; ++ ++ case IOV_GVAL(IOV_CLOCK): ++ int_val = (uint32)sd_clock; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_CLOCK): ++ sd_clock = int_val; ++ break; ++ ++ case IOV_GVAL(IOV_SDMODE): ++ int_val = (uint32)sd_sdmode; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_SDMODE): ++ sd_sdmode = int_val; ++ break; ++ ++ case IOV_GVAL(IOV_HISPEED): ++ int_val = (uint32)sd_hiok; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_HISPEED): ++ sd_hiok = int_val; ++ break; ++ ++ case IOV_GVAL(IOV_NUMINTS): ++ int_val = (int32)si->intrcount; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_GVAL(IOV_NUMLOCALINTS): ++ int_val = (int32)0; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_GVAL(IOV_HOSTREG): ++ { ++ sdreg_t *sd_ptr = (sdreg_t *)params; ++ ++ if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) { ++ sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset)); ++ bcmerror = BCME_BADARG; ++ break; ++ } ++ ++ sd_trace(("%s: rreg%d at offset %d\n", __FUNCTION__, ++ (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32), ++ sd_ptr->offset)); ++ if (sd_ptr->offset & 1) ++ int_val = 8; /* sdioh_sdmmc_rreg8(si, sd_ptr->offset); */ ++ else if (sd_ptr->offset & 2) ++ int_val = 16; /* sdioh_sdmmc_rreg16(si, sd_ptr->offset); */ ++ else ++ int_val = 32; /* sdioh_sdmmc_rreg(si, sd_ptr->offset); */ ++ ++ bcopy(&int_val, arg, sizeof(int_val)); ++ break; ++ } ++ ++ case IOV_SVAL(IOV_HOSTREG): ++ { ++ sdreg_t *sd_ptr = (sdreg_t *)params; ++ ++ if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) { ++ sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset)); ++ bcmerror = BCME_BADARG; ++ break; ++ } ++ ++ sd_trace(("%s: wreg%d value 0x%08x at offset %d\n", __FUNCTION__, sd_ptr->value, ++ (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32), ++ sd_ptr->offset)); ++ break; ++ } ++ ++ case IOV_GVAL(IOV_DEVREG): ++ { ++ sdreg_t *sd_ptr = (sdreg_t *)params; ++ uint8 data = 0; ++ ++ if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) { ++ bcmerror = BCME_SDIO_ERROR; ++ break; ++ } ++ ++ int_val = (int)data; ++ bcopy(&int_val, arg, sizeof(int_val)); ++ break; ++ } ++ ++ case IOV_SVAL(IOV_DEVREG): ++ { ++ sdreg_t *sd_ptr = (sdreg_t *)params; ++ uint8 data = (uint8)sd_ptr->value; ++ ++ if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) { ++ bcmerror = BCME_SDIO_ERROR; ++ break; ++ } ++ break; ++ } ++ ++ default: ++ bcmerror = BCME_UNSUPPORTED; ++ break; ++ } ++exit: ++ ++ return bcmerror; ++} ++ ++#if defined(OOB_INTR_ONLY) && defined(HW_OOB) ++ ++SDIOH_API_RC ++sdioh_enable_hw_oob_intr(sdioh_info_t *sd, bool enable) ++{ ++ SDIOH_API_RC status; ++ uint8 data; ++ ++ if (enable) ++ data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE | SDIO_SEPINT_ACT_HI; ++ else ++ data = SDIO_SEPINT_ACT_HI; /* disable hw oob interrupt */ ++ ++ status = sdioh_request_byte(sd, SDIOH_WRITE, 0, SDIOD_CCCR_BRCM_SEPINT, &data); ++ return status; ++} ++#endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */ ++ ++extern SDIOH_API_RC ++sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data) ++{ ++ SDIOH_API_RC status; ++ /* No lock needed since sdioh_request_byte does locking */ ++ status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data); ++ return status; ++} ++ ++extern SDIOH_API_RC ++sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data) ++{ ++ /* No lock needed since sdioh_request_byte does locking */ ++ SDIOH_API_RC status; ++ status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data); ++ return status; ++} ++ ++static int ++sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr) ++{ ++ /* read 24 bits and return valid 17 bit addr */ ++ int i; ++ uint32 scratch, regdata; ++ uint8 *ptr = (uint8 *)&scratch; ++ for (i = 0; i < 3; i++) { ++ if ((sdioh_sdmmc_card_regread (sd, 0, regaddr, 1, ®data)) != SUCCESS) ++ sd_err(("%s: Can't read!\n", __FUNCTION__)); ++ ++ *ptr++ = (uint8) regdata; ++ regaddr++; ++ } ++ ++ /* Only the lower 17-bits are valid */ ++ scratch = ltoh32(scratch); ++ scratch &= 0x0001FFFF; ++ return (scratch); ++} ++ ++extern SDIOH_API_RC ++sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length) ++{ ++ uint32 count; ++ int offset; ++ uint32 foo; ++ uint8 *cis = cisd; ++ ++ sd_trace(("%s: Func = %d\n", __FUNCTION__, func)); ++ ++ if (!sd->func_cis_ptr[func]) { ++ bzero(cis, length); ++ sd_err(("%s: no func_cis_ptr[%d]\n", __FUNCTION__, func)); ++ return SDIOH_API_RC_FAIL; ++ } ++ ++ sd_err(("%s: func_cis_ptr[%d]=0x%04x\n", __FUNCTION__, func, sd->func_cis_ptr[func])); ++ ++ for (count = 0; count < length; count++) { ++ offset = sd->func_cis_ptr[func] + count; ++ if (sdioh_sdmmc_card_regread (sd, 0, offset, 1, &foo) < 0) { ++ sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__)); ++ return SDIOH_API_RC_FAIL; ++ } ++ ++ *cis = (uint8)(foo & 0xff); ++ cis++; ++ } ++ ++ return SDIOH_API_RC_SUCCESS; ++} ++ ++extern SDIOH_API_RC ++sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte) ++{ ++ int err_ret; ++#if defined(MMC_SDIO_ABORT) ++ int sdio_abort_retry = MMC_SDIO_ABORT_RETRY_LIMIT; ++#endif ++ sd_info(("%s: rw=%d, func=%d, addr=0x%05x\n", __FUNCTION__, rw, func, regaddr)); ++ ++ DHD_PM_RESUME_WAIT(sdioh_request_byte_wait); ++ DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); ++ if(rw) { /* CMD52 Write */ ++ if (func == 0) { ++ /* Can only directly write to some F0 registers. Handle F2 enable ++ * as a special case. ++ */ ++ if (regaddr == SDIOD_CCCR_IOEN) { ++ if (gInstance->func[2]) { ++ sdio_claim_host(gInstance->func[2]); ++ if (*byte & SDIO_FUNC_ENABLE_2) { ++ /* Enable Function 2 */ ++ err_ret = sdio_enable_func(gInstance->func[2]); ++ if (err_ret) { ++ sd_err(("bcmsdh_sdmmc: enable F2 failed:%d", ++ err_ret)); ++ } ++ } else { ++ /* Disable Function 2 */ ++ err_ret = sdio_disable_func(gInstance->func[2]); ++ if (err_ret) { ++ sd_err(("bcmsdh_sdmmc: Disab F2 failed:%d", ++ err_ret)); ++ } ++ } ++ sdio_release_host(gInstance->func[2]); ++ } ++ } ++#if defined(MMC_SDIO_ABORT) ++ /* to allow abort command through F1 */ ++ else if (regaddr == SDIOD_CCCR_IOABORT) { ++ while (sdio_abort_retry--) { ++ if (gInstance->func[func]) { ++ sdio_claim_host(gInstance->func[func]); ++ /* ++ * this sdio_f0_writeb() can be replaced with ++ * another api depending upon MMC driver change. ++ * As of this time, this is temporaray one ++ */ ++ sdio_writeb(gInstance->func[func], ++ *byte, regaddr, &err_ret); ++ sdio_release_host(gInstance->func[func]); ++ } ++ if (!err_ret) ++ break; ++ } ++ } ++#endif /* MMC_SDIO_ABORT */ ++ else if (regaddr < 0xF0) { ++ sd_err(("bcmsdh_sdmmc: F0 Wr:0x%02x: write disallowed\n", regaddr)); ++ } else { ++ /* Claim host controller, perform F0 write, and release */ ++ if (gInstance->func[func]) { ++ sdio_claim_host(gInstance->func[func]); ++ sdio_f0_writeb(gInstance->func[func], ++ *byte, regaddr, &err_ret); ++ sdio_release_host(gInstance->func[func]); ++ } ++ } ++ } else { ++ /* Claim host controller, perform Fn write, and release */ ++ if (gInstance->func[func]) { ++ sdio_claim_host(gInstance->func[func]); ++ sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret); ++ sdio_release_host(gInstance->func[func]); ++ } ++ } ++ } else { /* CMD52 Read */ ++ /* Claim host controller, perform Fn read, and release */ ++ if (gInstance->func[func]) { ++ sdio_claim_host(gInstance->func[func]); ++ if (func == 0) { ++ *byte = sdio_f0_readb(gInstance->func[func], regaddr, &err_ret); ++ } else { ++ *byte = sdio_readb(gInstance->func[func], regaddr, &err_ret); ++ } ++ sdio_release_host(gInstance->func[func]); ++ } ++ } ++ ++ if (err_ret) { ++ sd_err(("bcmsdh_sdmmc: Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n", ++ rw ? "Write" : "Read", func, regaddr, *byte, err_ret)); ++ } ++ ++ return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); ++} ++ ++extern SDIOH_API_RC ++sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr, ++ uint32 *word, uint nbytes) ++{ ++ int err_ret = SDIOH_API_RC_FAIL; ++#if defined(MMC_SDIO_ABORT) ++ int sdio_abort_retry = MMC_SDIO_ABORT_RETRY_LIMIT; ++#endif ++ ++ if (func == 0) { ++ sd_err(("%s: Only CMD52 allowed to F0.\n", __FUNCTION__)); ++ return SDIOH_API_RC_FAIL; ++ } ++ ++ sd_info(("%s: cmd_type=%d, rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", ++ __FUNCTION__, cmd_type, rw, func, addr, nbytes)); ++ ++ DHD_PM_RESUME_WAIT(sdioh_request_word_wait); ++ DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); ++ /* Claim host controller */ ++ sdio_claim_host(gInstance->func[func]); ++ ++ if(rw) { /* CMD53 Write */ ++ if (nbytes == 4) { ++ sdio_writel(gInstance->func[func], *word, addr, &err_ret); ++ } else if (nbytes == 2) { ++ sdio_writew(gInstance->func[func], (*word & 0xFFFF), addr, &err_ret); ++ } else { ++ sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes)); ++ } ++ } else { /* CMD52 Read */ ++ if (nbytes == 4) { ++ *word = sdio_readl(gInstance->func[func], addr, &err_ret); ++ } else if (nbytes == 2) { ++ *word = sdio_readw(gInstance->func[func], addr, &err_ret) & 0xFFFF; ++ } else { ++ sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes)); ++ } ++ } ++ ++ /* Release host controller */ ++ sdio_release_host(gInstance->func[func]); ++ ++ if (err_ret) { ++#if defined(MMC_SDIO_ABORT) ++ /* Any error on CMD53 transaction should abort that function using function 0. */ ++ while (sdio_abort_retry--) { ++ if (gInstance->func[0]) { ++ sdio_claim_host(gInstance->func[0]); ++ /* ++ * this sdio_f0_writeb() can be replaced with another api ++ * depending upon MMC driver change. ++ * As of this time, this is temporaray one ++ */ ++ sdio_writeb(gInstance->func[0], ++ func, SDIOD_CCCR_IOABORT, &err_ret); ++ sdio_release_host(gInstance->func[0]); ++ } ++ if (!err_ret) ++ break; ++ } ++ if (err_ret) ++#endif /* MMC_SDIO_ABORT */ ++ { ++ sd_err(("bcmsdh_sdmmc: Failed to %s word, Err: 0x%08x\n", ++ rw ? "Write" : "Read", err_ret)); ++ } ++ } ++ ++ return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); ++} ++ ++static SDIOH_API_RC ++sdioh_request_packet(sdioh_info_t *sd, uint fix_inc, uint write, uint func, ++ uint addr, void *pkt) ++{ ++ bool fifo = (fix_inc == SDIOH_DATA_FIX); ++ uint32 SGCount = 0; ++ int err_ret = 0; ++ void *pnext, *pprev; ++ uint ttl_len, dma_len, lft_len, xfred_len, pkt_len; ++ uint blk_num; ++ int blk_size; ++ struct mmc_request mmc_req; ++ struct mmc_command mmc_cmd; ++ struct mmc_data mmc_dat; ++ ++ sd_trace(("%s: Enter\n", __FUNCTION__)); ++ ++ ASSERT(pkt); ++ DHD_PM_RESUME_WAIT(sdioh_request_packet_wait); ++ DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); ++ ++ ttl_len = xfred_len = 0; ++ /* at least 4 bytes alignment of skb buff is guaranteed */ ++ for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) ++ ttl_len += PKTLEN(sd->osh, pnext); ++ ++ blk_size = sd->client_block_size[func]; ++ if (!sd->use_rxchain || ttl_len <= blk_size) { ++ blk_num = 0; ++ dma_len = 0; ++ } else { ++ blk_num = ttl_len / blk_size; ++ dma_len = blk_num * blk_size; ++ } ++ lft_len = ttl_len - dma_len; ++ ++ sd_trace(("%s: %s %dB to func%d:%08x, %d blks with DMA, %dB leftover\n", ++ __FUNCTION__, write ? "W" : "R", ++ ttl_len, func, addr, blk_num, lft_len)); ++ ++ if (0 != dma_len) { ++ memset(&mmc_req, 0, sizeof(struct mmc_request)); ++ memset(&mmc_cmd, 0, sizeof(struct mmc_command)); ++ memset(&mmc_dat, 0, sizeof(struct mmc_data)); ++ ++ /* Set up DMA descriptors */ ++ pprev = pkt; ++ for (pnext = pkt; ++ pnext && dma_len; ++ pnext = PKTNEXT(sd->osh, pnext)) { ++ pkt_len = PKTLEN(sd->osh, pnext); ++ ++ if (dma_len > pkt_len) ++ dma_len -= pkt_len; ++ else { ++ pkt_len = xfred_len = dma_len; ++ dma_len = 0; ++ pkt = pnext; ++ } ++ ++ sg_set_buf(&sd->sg_list[SGCount++], ++ (uint8*)PKTDATA(sd->osh, pnext), ++ pkt_len); ++ ++ if (SGCount >= SDIOH_SDMMC_MAX_SG_ENTRIES) { ++ sd_err(("%s: sg list entries exceed limit\n", ++ __FUNCTION__)); ++ return (SDIOH_API_RC_FAIL); ++ } ++ } ++ ++ mmc_dat.sg = sd->sg_list; ++ mmc_dat.sg_len = SGCount; ++ mmc_dat.blksz = blk_size; ++ mmc_dat.blocks = blk_num; ++ mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; ++ ++ mmc_cmd.opcode = 53; /* SD_IO_RW_EXTENDED */ ++ mmc_cmd.arg = write ? 1<<31 : 0; ++ mmc_cmd.arg |= (func & 0x7) << 28; ++ mmc_cmd.arg |= 1<<27; ++ mmc_cmd.arg |= fifo ? 0 : 1<<26; ++ mmc_cmd.arg |= (addr & 0x1FFFF) << 9; ++ mmc_cmd.arg |= blk_num & 0x1FF; ++ mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; ++ ++ mmc_req.cmd = &mmc_cmd; ++ mmc_req.data = &mmc_dat; ++ ++ sdio_claim_host(gInstance->func[func]); ++ mmc_set_data_timeout(&mmc_dat, gInstance->func[func]->card); ++ mmc_wait_for_req(gInstance->func[func]->card->host, &mmc_req); ++ sdio_release_host(gInstance->func[func]); ++ ++ err_ret = mmc_cmd.error? mmc_cmd.error : mmc_dat.error; ++ if (0 != err_ret) { ++ sd_err(("%s:CMD53 %s failed with code %d\n", ++ __FUNCTION__, ++ write ? "write" : "read", ++ err_ret)); ++ sd_err(("%s:Disabling rxchain and fire it with PIO\n", ++ __FUNCTION__)); ++ sd->use_rxchain = FALSE; ++ pkt = pprev; ++ lft_len = ttl_len; ++ } else if (!fifo) { ++ addr = addr + ttl_len - lft_len - dma_len; ++ } ++ } ++ ++ /* PIO mode */ ++ if (0 != lft_len) { ++ /* Claim host controller */ ++ sdio_claim_host(gInstance->func[func]); ++ for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) { ++ uint8 *buf = (uint8*)PKTDATA(sd->osh, pnext) + ++ xfred_len; ++ pkt_len = PKTLEN(sd->osh, pnext); ++ if (0 != xfred_len) { ++ pkt_len -= xfred_len; ++ xfred_len = 0; ++ } ++ ++ /* Align Patch ++ * read or small packet(ex:BDC header) skip 32 byte align ++ * otherwise, padding DHD_SDALIGN for performance ++ */ ++ if (write == 0 || pkt_len < 32) ++ pkt_len = (pkt_len + 3) & 0xFFFFFFFC; ++ else if (pkt_len % blk_size) ++ pkt_len += blk_size - (pkt_len % blk_size); ++ ++#ifdef CONFIG_MMC_MSM7X00A ++ if ((pkt_len % 64) == 32) { ++ sd_trace(("%s: Rounding up TX packet +=32\n", __FUNCTION__)); ++ pkt_len += 32; ++ } ++#endif /* CONFIG_MMC_MSM7X00A */ ++ ++ if ((write) && (!fifo)) ++ err_ret = sdio_memcpy_toio( ++ gInstance->func[func], ++ addr, buf, pkt_len); ++ else if (write) ++ err_ret = sdio_memcpy_toio( ++ gInstance->func[func], ++ addr, buf, pkt_len); ++ else if (fifo) ++ err_ret = sdio_readsb( ++ gInstance->func[func], ++ buf, addr, pkt_len); ++ else ++ err_ret = sdio_memcpy_fromio( ++ gInstance->func[func], ++ buf, addr, pkt_len); ++ ++ if (err_ret) ++ sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=%d\n", ++ __FUNCTION__, ++ (write) ? "TX" : "RX", ++ pnext, SGCount, addr, pkt_len, err_ret)); ++ else ++ sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n", ++ __FUNCTION__, ++ (write) ? "TX" : "RX", ++ pnext, SGCount, addr, pkt_len)); ++ ++ if (!fifo) ++ addr += pkt_len; ++ SGCount ++; ++ } ++ sdio_release_host(gInstance->func[func]); ++ } ++ ++ sd_trace(("%s: Exit\n", __FUNCTION__)); ++ return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); ++} ++ ++ ++/* ++ * This function takes a buffer or packet, and fixes everything up so that in the ++ * end, a DMA-able packet is created. ++ * ++ * A buffer does not have an associated packet pointer, and may or may not be aligned. ++ * A packet may consist of a single packet, or a packet chain. If it is a packet chain, ++ * then all the packets in the chain must be properly aligned. If the packet data is not ++ * aligned, then there may only be one packet, and in this case, it is copied to a new ++ * aligned packet. ++ * ++ */ ++extern SDIOH_API_RC ++sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, uint func, ++ uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt) ++{ ++ SDIOH_API_RC Status; ++ void *mypkt = NULL; ++ ++ sd_trace(("%s: Enter\n", __FUNCTION__)); ++ ++ DHD_PM_RESUME_WAIT(sdioh_request_buffer_wait); ++ DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); ++ /* Case 1: we don't have a packet. */ ++ if (pkt == NULL) { ++ sd_data(("%s: Creating new %s Packet, len=%d\n", ++ __FUNCTION__, write ? "TX" : "RX", buflen_u)); ++#ifdef CONFIG_DHD_USE_STATIC_BUF ++ if (!(mypkt = PKTGET_STATIC(sd->osh, buflen_u, write ? TRUE : FALSE))) { ++#else ++ if (!(mypkt = PKTGET(sd->osh, buflen_u, write ? TRUE : FALSE))) { ++#endif /* CONFIG_DHD_USE_STATIC_BUF */ ++ sd_err(("%s: PKTGET failed: len %d\n", ++ __FUNCTION__, buflen_u)); ++ return SDIOH_API_RC_FAIL; ++ } ++ ++ /* For a write, copy the buffer data into the packet. */ ++ if (write) { ++ bcopy(buffer, PKTDATA(sd->osh, mypkt), buflen_u); ++ } ++ ++ Status = sdioh_request_packet(sd, fix_inc, write, func, addr, mypkt); ++ ++ /* For a read, copy the packet data back to the buffer. */ ++ if (!write) { ++ bcopy(PKTDATA(sd->osh, mypkt), buffer, buflen_u); ++ } ++#ifdef CONFIG_DHD_USE_STATIC_BUF ++ PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE); ++#else ++ PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE); ++#endif /* CONFIG_DHD_USE_STATIC_BUF */ ++ } else if (((uint32)(PKTDATA(sd->osh, pkt)) & DMA_ALIGN_MASK) != 0) { ++ /* Case 2: We have a packet, but it is unaligned. */ ++ ++ /* In this case, we cannot have a chain. */ ++ ASSERT(PKTNEXT(sd->osh, pkt) == NULL); ++ ++ sd_data(("%s: Creating aligned %s Packet, len=%d\n", ++ __FUNCTION__, write ? "TX" : "RX", PKTLEN(sd->osh, pkt))); ++#ifdef CONFIG_DHD_USE_STATIC_BUF ++ if (!(mypkt = PKTGET_STATIC(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) { ++#else ++ if (!(mypkt = PKTGET(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) { ++#endif /* CONFIG_DHD_USE_STATIC_BUF */ ++ sd_err(("%s: PKTGET failed: len %d\n", ++ __FUNCTION__, PKTLEN(sd->osh, pkt))); ++ return SDIOH_API_RC_FAIL; ++ } ++ ++ /* For a write, copy the buffer data into the packet. */ ++ if (write) { ++ bcopy(PKTDATA(sd->osh, pkt), ++ PKTDATA(sd->osh, mypkt), ++ PKTLEN(sd->osh, pkt)); ++ } ++ ++ Status = sdioh_request_packet(sd, fix_inc, write, func, addr, mypkt); ++ ++ /* For a read, copy the packet data back to the buffer. */ ++ if (!write) { ++ bcopy(PKTDATA(sd->osh, mypkt), ++ PKTDATA(sd->osh, pkt), ++ PKTLEN(sd->osh, mypkt)); ++ } ++#ifdef CONFIG_DHD_USE_STATIC_BUF ++ PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE); ++#else ++ PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE); ++#endif /* CONFIG_DHD_USE_STATIC_BUF */ ++ } else { /* case 3: We have a packet and it is aligned. */ ++ sd_data(("%s: Aligned %s Packet, direct DMA\n", ++ __FUNCTION__, write ? "Tx" : "Rx")); ++ Status = sdioh_request_packet(sd, fix_inc, write, func, addr, pkt); ++ } ++ ++ return (Status); ++} ++ ++/* this function performs "abort" for both of host & device */ ++extern int ++sdioh_abort(sdioh_info_t *sd, uint func) ++{ ++#if defined(MMC_SDIO_ABORT) ++ char t_func = (char) func; ++#endif /* defined(MMC_SDIO_ABORT) */ ++ sd_trace(("%s: Enter\n", __FUNCTION__)); ++ ++#if defined(MMC_SDIO_ABORT) ++ /* issue abort cmd52 command through F1 */ ++ sdioh_request_byte(sd, SD_IO_OP_WRITE, SDIO_FUNC_0, SDIOD_CCCR_IOABORT, &t_func); ++#endif /* defined(MMC_SDIO_ABORT) */ ++ ++ sd_trace(("%s: Exit\n", __FUNCTION__)); ++ return SDIOH_API_RC_SUCCESS; ++} ++ ++/* Reset and re-initialize the device */ ++int sdioh_sdio_reset(sdioh_info_t *si) ++{ ++ sd_trace(("%s: Enter\n", __FUNCTION__)); ++ sd_trace(("%s: Exit\n", __FUNCTION__)); ++ return SDIOH_API_RC_SUCCESS; ++} ++ ++/* Disable device interrupt */ ++void ++sdioh_sdmmc_devintr_off(sdioh_info_t *sd) ++{ ++ sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints)); ++ sd->intmask &= ~CLIENT_INTR; ++} ++ ++/* Enable device interrupt */ ++void ++sdioh_sdmmc_devintr_on(sdioh_info_t *sd) ++{ ++ sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints)); ++ sd->intmask |= CLIENT_INTR; ++} ++ ++/* Read client card reg */ ++int ++sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data) ++{ ++ ++ if ((func == 0) || (regsize == 1)) { ++ uint8 temp = 0; ++ ++ sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp); ++ *data = temp; ++ *data &= 0xff; ++ sd_data(("%s: byte read data=0x%02x\n", ++ __FUNCTION__, *data)); ++ } else { ++ sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, data, regsize); ++ if (regsize == 2) ++ *data &= 0xffff; ++ ++ sd_data(("%s: word read data=0x%08x\n", ++ __FUNCTION__, *data)); ++ } ++ ++ return SUCCESS; ++} ++ ++#if !defined(OOB_INTR_ONLY) ++/* bcmsdh_sdmmc interrupt handler */ ++static void IRQHandler(struct sdio_func *func) ++{ ++ sdioh_info_t *sd; ++ ++ sd_trace(("bcmsdh_sdmmc: ***IRQHandler\n")); ++ sd = gInstance->sd; ++ ++ ASSERT(sd != NULL); ++ sdio_release_host(gInstance->func[0]); ++ ++ if (sd->use_client_ints) { ++ sd->intrcount++; ++ ASSERT(sd->intr_handler); ++ ASSERT(sd->intr_handler_arg); ++ (sd->intr_handler)(sd->intr_handler_arg); ++ } else { ++ sd_err(("bcmsdh_sdmmc: ***IRQHandler\n")); ++ ++ sd_err(("%s: Not ready for intr: enabled %d, handler %p\n", ++ __FUNCTION__, sd->client_intr_enabled, sd->intr_handler)); ++ } ++ ++ sdio_claim_host(gInstance->func[0]); ++} ++ ++/* bcmsdh_sdmmc interrupt handler for F2 (dummy handler) */ ++static void IRQHandlerF2(struct sdio_func *func) ++{ ++ sdioh_info_t *sd; ++ ++ sd_trace(("bcmsdh_sdmmc: ***IRQHandlerF2\n")); ++ ++ sd = gInstance->sd; ++ ++ ASSERT(sd != NULL); ++ BCM_REFERENCE(sd); ++} ++#endif /* !defined(OOB_INTR_ONLY) */ ++ ++#ifdef NOTUSED ++/* Write client card reg */ ++static int ++sdioh_sdmmc_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data) ++{ ++ ++ if ((func == 0) || (regsize == 1)) { ++ uint8 temp; ++ ++ temp = data & 0xff; ++ sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp); ++ sd_data(("%s: byte write data=0x%02x\n", ++ __FUNCTION__, data)); ++ } else { ++ if (regsize == 2) ++ data &= 0xffff; ++ ++ sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, &data, regsize); ++ ++ sd_data(("%s: word write data=0x%08x\n", ++ __FUNCTION__, data)); ++ } ++ ++ return SUCCESS; ++} ++#endif /* NOTUSED */ ++ ++int ++sdioh_start(sdioh_info_t *si, int stage) ++{ ++ int ret; ++ sdioh_info_t *sd = gInstance->sd; ++ ++ if (!sd) return (0); ++ ++ /* Need to do this stages as we can't enable the interrupt till ++ downloading of the firmware is complete, other wise polling ++ sdio access will come in way ++ */ ++ if (gInstance->func[0]) { ++ if (stage == 0) { ++ /* Since the power to the chip is killed, we will have ++ re enumerate the device again. Set the block size ++ and enable the fucntion 1 for in preparation for ++ downloading the code ++ */ ++ /* sdio_reset_comm() - has been fixed in latest kernel/msm.git for Linux ++ 2.6.27. The implementation prior to that is buggy, and needs broadcom's ++ patch for it ++ */ ++ if ((ret = sdio_reset_comm(gInstance->func[0]->card))) { ++ sd_err(("%s Failed, error = %d\n", __FUNCTION__, ret)); ++ return ret; ++ } ++ else { ++ sd->num_funcs = 2; ++ sd->sd_blockmode = TRUE; ++ sd->use_client_ints = TRUE; ++ sd->client_block_size[0] = 64; ++ ++ if (gInstance->func[1]) { ++ /* Claim host controller */ ++ sdio_claim_host(gInstance->func[1]); ++ ++ sd->client_block_size[1] = 64; ++ if (sdio_set_block_size(gInstance->func[1], 64)) { ++ sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize\n")); ++ } ++ ++ /* Release host controller F1 */ ++ sdio_release_host(gInstance->func[1]); ++ } ++ ++ if (gInstance->func[2]) { ++ /* Claim host controller F2 */ ++ sdio_claim_host(gInstance->func[2]); ++ ++ sd->client_block_size[2] = sd_f2_blocksize; ++ if (sdio_set_block_size(gInstance->func[2], ++ sd_f2_blocksize)) { ++ sd_err(("bcmsdh_sdmmc: Failed to set F2 " ++ "blocksize to %d\n", sd_f2_blocksize)); ++ } ++ ++ /* Release host controller F2 */ ++ sdio_release_host(gInstance->func[2]); ++ } ++ ++ sdioh_sdmmc_card_enablefuncs(sd); ++ } ++ } else { ++#if !defined(OOB_INTR_ONLY) ++ sdio_claim_host(gInstance->func[0]); ++ if (gInstance->func[2]) ++ sdio_claim_irq(gInstance->func[2], IRQHandlerF2); ++ if (gInstance->func[1]) ++ sdio_claim_irq(gInstance->func[1], IRQHandler); ++ sdio_release_host(gInstance->func[0]); ++#else /* defined(OOB_INTR_ONLY) */ ++#if defined(HW_OOB) ++ sdioh_enable_func_intr(); ++#endif ++ bcmsdh_oob_intr_set(TRUE); ++#endif /* !defined(OOB_INTR_ONLY) */ ++ } ++ } ++ else ++ sd_err(("%s Failed\n", __FUNCTION__)); ++ ++ return (0); ++} ++ ++int ++sdioh_stop(sdioh_info_t *si) ++{ ++ /* MSM7201A Android sdio stack has bug with interrupt ++ So internaly within SDIO stack they are polling ++ which cause issue when device is turned off. So ++ unregister interrupt with SDIO stack to stop the ++ polling ++ */ ++ if (gInstance->func[0]) { ++#if !defined(OOB_INTR_ONLY) ++ sdio_claim_host(gInstance->func[0]); ++ if (gInstance->func[1]) ++ sdio_release_irq(gInstance->func[1]); ++ if (gInstance->func[2]) ++ sdio_release_irq(gInstance->func[2]); ++ sdio_release_host(gInstance->func[0]); ++#else /* defined(OOB_INTR_ONLY) */ ++#if defined(HW_OOB) ++ sdioh_disable_func_intr(); ++#endif ++ bcmsdh_oob_intr_set(FALSE); ++#endif /* !defined(OOB_INTR_ONLY) */ ++ } ++ else ++ sd_err(("%s Failed\n", __FUNCTION__)); ++ return (0); ++} ++ ++int ++sdioh_waitlockfree(sdioh_info_t *sd) ++{ ++ return (1); ++} ++ ++ ++SDIOH_API_RC ++sdioh_gpioouten(sdioh_info_t *sd, uint32 gpio) ++{ ++ return SDIOH_API_RC_FAIL; ++} ++ ++SDIOH_API_RC ++sdioh_gpioout(sdioh_info_t *sd, uint32 gpio, bool enab) ++{ ++ return SDIOH_API_RC_FAIL; ++} ++ ++bool ++sdioh_gpioin(sdioh_info_t *sd, uint32 gpio) ++{ ++ return FALSE; ++} ++ ++SDIOH_API_RC ++sdioh_gpio_init(sdioh_info_t *sd) ++{ ++ return SDIOH_API_RC_FAIL; ++} +diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c +new file mode 100644 +index 00000000..e9136401 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c +@@ -0,0 +1,425 @@ ++/* ++ * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmsdh_sdmmc_linux.c 381548 2013-01-28 17:25:38Z $ ++ */ ++ ++#include ++#include ++#include /* SDIO Device and Protocol Specs */ ++#include /* bcmsdh to/from specific controller APIs */ ++#include /* to get msglevel bit values */ ++ ++#include /* request_irq() */ ++ ++#include ++#include ++#include ++#include ++ ++#if !defined(SDIO_VENDOR_ID_BROADCOM) ++#define SDIO_VENDOR_ID_BROADCOM 0x02d0 ++#endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */ ++ ++#define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000 ++ ++#if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) ++#define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB 0x0492 /* BCM94325SDGWB */ ++#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */ ++#if !defined(SDIO_DEVICE_ID_BROADCOM_4325) ++#define SDIO_DEVICE_ID_BROADCOM_4325 0x0493 ++#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */ ++#if !defined(SDIO_DEVICE_ID_BROADCOM_4329) ++#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 ++#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */ ++#if !defined(SDIO_DEVICE_ID_BROADCOM_4319) ++#define SDIO_DEVICE_ID_BROADCOM_4319 0x4319 ++#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4319) */ ++#if !defined(SDIO_DEVICE_ID_BROADCOM_4330) ++#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330 ++#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4330) */ ++#if !defined(SDIO_DEVICE_ID_BROADCOM_4334) ++#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 ++#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4334) */ ++#if !defined(SDIO_DEVICE_ID_BROADCOM_4324) ++#define SDIO_DEVICE_ID_BROADCOM_4324 0x4324 ++#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4324) */ ++#if !defined(SDIO_DEVICE_ID_BROADCOM_43239) ++#define SDIO_DEVICE_ID_BROADCOM_43239 43239 ++#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_43239) */ ++ ++ ++#include ++ ++#include ++ ++#ifdef WL_CFG80211 ++extern void wl_cfg80211_set_parent_dev(void *dev); ++#endif ++ ++extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd); ++extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd); ++extern int dhd_os_check_wakelock(void *dhdp); ++extern int dhd_os_check_if_up(void *dhdp); ++extern void *bcmsdh_get_drvdata(void); ++ ++int sdio_function_init(void); ++void sdio_function_cleanup(void); ++ ++#define DESCRIPTION "bcmsdh_sdmmc Driver" ++#define AUTHOR "Broadcom Corporation" ++ ++/* module param defaults */ ++static int clockoverride = 0; ++ ++module_param(clockoverride, int, 0644); ++MODULE_PARM_DESC(clockoverride, "SDIO card clock override"); ++ ++PBCMSDH_SDMMC_INSTANCE gInstance; ++ ++/* Maximum number of bcmsdh_sdmmc devices supported by driver */ ++#define BCMSDH_SDMMC_MAX_DEVICES 1 ++ ++extern int bcmsdh_probe(struct device *dev); ++extern int bcmsdh_remove(struct device *dev); ++extern volatile bool dhd_mmc_suspend; ++ ++static int bcmsdh_sdmmc_probe(struct sdio_func *func, ++ const struct sdio_device_id *id) ++{ ++ int ret = 0; ++ static struct sdio_func sdio_func_0; ++ ++ if (func) { ++ sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__)); ++ sd_trace(("sdio_bcmsdh: func->class=%x\n", func->class)); ++ sd_trace(("sdio_vendor: 0x%04x\n", func->vendor)); ++ sd_trace(("sdio_device: 0x%04x\n", func->device)); ++ sd_trace(("Function#: 0x%04x\n", func->num)); ++ ++ if (func->num == 1) { ++ sdio_func_0.num = 0; ++ sdio_func_0.card = func->card; ++ gInstance->func[0] = &sdio_func_0; ++ if(func->device == 0x4) { /* 4318 */ ++ gInstance->func[2] = NULL; ++ sd_trace(("NIC found, calling bcmsdh_probe...\n")); ++ ret = bcmsdh_probe(&func->dev); ++ } ++ } ++ ++ gInstance->func[func->num] = func; ++ ++ if (func->num == 2) { ++ #ifdef WL_CFG80211 ++ wl_cfg80211_set_parent_dev(&func->dev); ++ #endif ++ sd_trace(("F2 found, calling bcmsdh_probe...\n")); ++ ret = bcmsdh_probe(&func->dev); ++ if (ret < 0 && gInstance) ++ gInstance->func[2] = NULL; ++ } ++ } else { ++ ret = -ENODEV; ++ } ++ ++ return ret; ++} ++ ++static void bcmsdh_sdmmc_remove(struct sdio_func *func) ++{ ++ if (func) { ++ sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__)); ++ sd_info(("sdio_bcmsdh: func->class=%x\n", func->class)); ++ sd_info(("sdio_vendor: 0x%04x\n", func->vendor)); ++ sd_info(("sdio_device: 0x%04x\n", func->device)); ++ sd_info(("Function#: 0x%04x\n", func->num)); ++ ++ if (gInstance->func[2]) { ++ sd_trace(("F2 found, calling bcmsdh_remove...\n")); ++ bcmsdh_remove(&func->dev); ++ gInstance->func[2] = NULL; ++ } ++ if (func->num == 1) { ++ sdio_claim_host(func); ++ sdio_disable_func(func); ++ sdio_release_host(func); ++ gInstance->func[1] = NULL; ++ } ++ } ++} ++ ++/* devices we support, null terminated */ ++static const struct sdio_device_id bcmsdh_sdmmc_ids[] = { ++ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) }, ++ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) }, ++ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) }, ++ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) }, ++ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) }, ++ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330) }, ++ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334) }, ++ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4324) }, ++ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43239) }, ++ { SDIO_DEVICE_CLASS(SDIO_CLASS_NONE) }, ++ { /* end: all zeroes */ }, ++}; ++ ++MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids); ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) ++static int bcmsdh_sdmmc_suspend(struct device *pdev) ++{ ++ struct sdio_func *func = dev_to_sdio_func(pdev); ++ mmc_pm_flag_t sdio_flags; ++ int ret; ++ ++ if (func->num != 2) ++ return 0; ++ ++ sd_trace_hw4(("%s Enter\n", __FUNCTION__)); ++ ++ if (dhd_os_check_wakelock(bcmsdh_get_drvdata())) ++ return -EBUSY; ++ ++ sdio_flags = sdio_get_host_pm_caps(func); ++ ++ if (!(sdio_flags & MMC_PM_KEEP_POWER)) { ++ sd_err(("%s: can't keep power while host is suspended\n", __FUNCTION__)); ++ return -EINVAL; ++ } ++ ++ /* keep power while host suspended */ ++ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); ++ if (ret) { ++ sd_err(("%s: error while trying to keep power\n", __FUNCTION__)); ++ return ret; ++ } ++ ++#if defined(OOB_INTR_ONLY) ++ bcmsdh_oob_intr_set(0); ++#endif /* defined(OOB_INTR_ONLY) */ ++ dhd_mmc_suspend = TRUE; ++ smp_mb(); ++ ++ return 0; ++} ++ ++static int bcmsdh_sdmmc_resume(struct device *pdev) ++{ ++#if defined(OOB_INTR_ONLY) ++ struct sdio_func *func = dev_to_sdio_func(pdev); ++#endif /* defined(OOB_INTR_ONLY) */ ++ sd_trace_hw4(("%s Enter\n", __FUNCTION__)); ++ ++ dhd_mmc_suspend = FALSE; ++#if defined(OOB_INTR_ONLY) ++ if ((func->num == 2) && dhd_os_check_if_up(bcmsdh_get_drvdata())) ++ bcmsdh_oob_intr_set(1); ++#endif /* (OOB_INTR_ONLY) */ ++ smp_mb(); ++ return 0; ++} ++ ++static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops = { ++ .suspend = bcmsdh_sdmmc_suspend, ++ .resume = bcmsdh_sdmmc_resume, ++}; ++#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ ++ ++#if defined(BCMLXSDMMC) ++static struct semaphore *notify_semaphore = NULL; ++ ++static int dummy_probe(struct sdio_func *func, ++ const struct sdio_device_id *id) ++{ ++ if (notify_semaphore) ++ up(notify_semaphore); ++ return 0; ++} ++ ++static void dummy_remove(struct sdio_func *func) ++{ ++} ++ ++static struct sdio_driver dummy_sdmmc_driver = { ++ .probe = dummy_probe, ++ .remove = dummy_remove, ++ .name = "dummy_sdmmc", ++ .id_table = bcmsdh_sdmmc_ids, ++ }; ++ ++int sdio_func_reg_notify(void* semaphore) ++{ ++ notify_semaphore = semaphore; ++ return sdio_register_driver(&dummy_sdmmc_driver); ++} ++ ++void sdio_func_unreg_notify(void) ++{ ++ sdio_unregister_driver(&dummy_sdmmc_driver); ++} ++ ++#endif /* defined(BCMLXSDMMC) */ ++ ++static struct sdio_driver bcmsdh_sdmmc_driver = { ++ .probe = bcmsdh_sdmmc_probe, ++ .remove = bcmsdh_sdmmc_remove, ++ .name = "bcmsdh_sdmmc", ++ .id_table = bcmsdh_sdmmc_ids, ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) ++ .drv = { ++ .pm = &bcmsdh_sdmmc_pm_ops, ++ }, ++#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ ++ }; ++ ++struct sdos_info { ++ sdioh_info_t *sd; ++ spinlock_t lock; ++}; ++ ++ ++int ++sdioh_sdmmc_osinit(sdioh_info_t *sd) ++{ ++ struct sdos_info *sdos; ++ ++ if (!sd) ++ return BCME_BADARG; ++ ++ sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info)); ++ sd->sdos_info = (void*)sdos; ++ if (sdos == NULL) ++ return BCME_NOMEM; ++ ++ sdos->sd = sd; ++ spin_lock_init(&sdos->lock); ++ return BCME_OK; ++} ++ ++void ++sdioh_sdmmc_osfree(sdioh_info_t *sd) ++{ ++ struct sdos_info *sdos; ++ ASSERT(sd && sd->sdos_info); ++ ++ sdos = (struct sdos_info *)sd->sdos_info; ++ MFREE(sd->osh, sdos, sizeof(struct sdos_info)); ++} ++ ++/* Interrupt enable/disable */ ++SDIOH_API_RC ++sdioh_interrupt_set(sdioh_info_t *sd, bool enable) ++{ ++ ulong flags; ++ struct sdos_info *sdos; ++ ++ if (!sd) ++ return BCME_BADARG; ++ ++ sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling")); ++ ++ sdos = (struct sdos_info *)sd->sdos_info; ++ ASSERT(sdos); ++ ++#if !defined(OOB_INTR_ONLY) ++ if (enable && !(sd->intr_handler && sd->intr_handler_arg)) { ++ sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__)); ++ return SDIOH_API_RC_FAIL; ++ } ++#endif /* !defined(OOB_INTR_ONLY) */ ++ ++ /* Ensure atomicity for enable/disable calls */ ++ spin_lock_irqsave(&sdos->lock, flags); ++ ++ sd->client_intr_enabled = enable; ++ if (enable) { ++ sdioh_sdmmc_devintr_on(sd); ++ } else { ++ sdioh_sdmmc_devintr_off(sd); ++ } ++ ++ spin_unlock_irqrestore(&sdos->lock, flags); ++ ++ return SDIOH_API_RC_SUCCESS; ++} ++ ++ ++#ifdef BCMSDH_MODULE ++static int __init ++bcmsdh_module_init(void) ++{ ++ int error = 0; ++ error = sdio_function_init(); ++ return error; ++} ++ ++static void __exit ++bcmsdh_module_cleanup(void) ++{ ++ sdio_function_cleanup(); ++} ++ ++module_init(bcmsdh_module_init); ++module_exit(bcmsdh_module_cleanup); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION(DESCRIPTION); ++MODULE_AUTHOR(AUTHOR); ++ ++#endif /* BCMSDH_MODULE */ ++/* ++ * module init ++*/ ++int sdio_function_init(void) ++{ ++ int error = 0; ++ sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__)); ++ ++ gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL); ++ if (!gInstance) ++ return -ENOMEM; ++ ++ error = sdio_register_driver(&bcmsdh_sdmmc_driver); ++ if (error && gInstance) { ++ kfree(gInstance); ++ gInstance = 0; ++ } ++ ++ return error; ++} ++ ++/* ++ * module cleanup ++*/ ++extern int bcmsdh_remove(struct device *dev); ++void sdio_function_cleanup(void) ++{ ++ sd_trace(("%s Enter\n", __FUNCTION__)); ++ ++ ++ sdio_unregister_driver(&bcmsdh_sdmmc_driver); ++ ++ if (gInstance) ++ kfree(gInstance); ++} +diff --git a/drivers/net/wireless/bcmdhd/bcmutils.c b/drivers/net/wireless/bcmdhd/bcmutils.c +new file mode 100644 +index 00000000..05405aba +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/bcmutils.c +@@ -0,0 +1,2090 @@ ++/* ++ * Driver O/S-independent utility routines ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * $Id: bcmutils.c 312855 2012-02-04 02:01:18Z $ ++ */ ++ ++#include ++#include ++#include ++#include ++#ifdef BCMDRIVER ++ ++#include ++#include ++ ++#else /* !BCMDRIVER */ ++ ++#include ++#include ++#include ++ ++#if defined(BCMEXTSUP) ++#include ++#endif ++ ++ ++#endif /* !BCMDRIVER */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++void *_bcmutils_dummy_fn = NULL; ++ ++ ++#ifdef BCMDRIVER ++ ++ ++ ++/* copy a pkt buffer chain into a buffer */ ++uint ++pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf) ++{ ++ uint n, ret = 0; ++ ++ if (len < 0) ++ len = 4096; /* "infinite" */ ++ ++ /* skip 'offset' bytes */ ++ for (; p && offset; p = PKTNEXT(osh, p)) { ++ if (offset < (uint)PKTLEN(osh, p)) ++ break; ++ offset -= PKTLEN(osh, p); ++ } ++ ++ if (!p) ++ return 0; ++ ++ /* copy the data */ ++ for (; p && len; p = PKTNEXT(osh, p)) { ++ n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len); ++ bcopy(PKTDATA(osh, p) + offset, buf, n); ++ buf += n; ++ len -= n; ++ ret += n; ++ offset = 0; ++ } ++ ++ return ret; ++} ++ ++/* copy a buffer into a pkt buffer chain */ ++uint ++pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf) ++{ ++ uint n, ret = 0; ++ ++ /* skip 'offset' bytes */ ++ for (; p && offset; p = PKTNEXT(osh, p)) { ++ if (offset < (uint)PKTLEN(osh, p)) ++ break; ++ offset -= PKTLEN(osh, p); ++ } ++ ++ if (!p) ++ return 0; ++ ++ /* copy the data */ ++ for (; p && len; p = PKTNEXT(osh, p)) { ++ n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len); ++ bcopy(buf, PKTDATA(osh, p) + offset, n); ++ buf += n; ++ len -= n; ++ ret += n; ++ offset = 0; ++ } ++ ++ return ret; ++} ++ ++ ++ ++/* return total length of buffer chain */ ++uint BCMFASTPATH ++pkttotlen(osl_t *osh, void *p) ++{ ++ uint total; ++ int len; ++ ++ total = 0; ++ for (; p; p = PKTNEXT(osh, p)) { ++ len = PKTLEN(osh, p); ++ total += len; ++ } ++ ++ return (total); ++} ++ ++/* return the last buffer of chained pkt */ ++void * ++pktlast(osl_t *osh, void *p) ++{ ++ for (; PKTNEXT(osh, p); p = PKTNEXT(osh, p)) ++ ; ++ ++ return (p); ++} ++ ++/* count segments of a chained packet */ ++uint BCMFASTPATH ++pktsegcnt(osl_t *osh, void *p) ++{ ++ uint cnt; ++ ++ for (cnt = 0; p; p = PKTNEXT(osh, p)) ++ cnt++; ++ ++ return cnt; ++} ++ ++ ++/* count segments of a chained packet */ ++uint BCMFASTPATH ++pktsegcnt_war(osl_t *osh, void *p) ++{ ++ uint cnt; ++ uint8 *pktdata; ++ uint len, remain, align64; ++ ++ for (cnt = 0; p; p = PKTNEXT(osh, p)) { ++ cnt++; ++ len = PKTLEN(osh, p); ++ if (len > 128) { ++ pktdata = (uint8 *)PKTDATA(osh, p); /* starting address of data */ ++ /* Check for page boundary straddle (2048B) */ ++ if (((uintptr)pktdata & ~0x7ff) != ((uintptr)(pktdata+len) & ~0x7ff)) ++ cnt++; ++ ++ align64 = (uint)((uintptr)pktdata & 0x3f); /* aligned to 64B */ ++ align64 = (64 - align64) & 0x3f; ++ len -= align64; /* bytes from aligned 64B to end */ ++ /* if aligned to 128B, check for MOD 128 between 1 to 4B */ ++ remain = len % 128; ++ if (remain > 0 && remain <= 4) ++ cnt++; /* add extra seg */ ++ } ++ } ++ ++ return cnt; ++} ++ ++uint8 * BCMFASTPATH ++pktoffset(osl_t *osh, void *p, uint offset) ++{ ++ uint total = pkttotlen(osh, p); ++ uint pkt_off = 0, len = 0; ++ uint8 *pdata = (uint8 *) PKTDATA(osh, p); ++ ++ if (offset > total) ++ return NULL; ++ ++ for (; p; p = PKTNEXT(osh, p)) { ++ pdata = (uint8 *) PKTDATA(osh, p); ++ pkt_off = offset - len; ++ len += PKTLEN(osh, p); ++ if (len > offset) ++ break; ++ } ++ return (uint8*) (pdata+pkt_off); ++} ++ ++/* ++ * osl multiple-precedence packet queue ++ * hi_prec is always >= the number of the highest non-empty precedence ++ */ ++void * BCMFASTPATH ++pktq_penq(struct pktq *pq, int prec, void *p) ++{ ++ struct pktq_prec *q; ++ ++ ASSERT(prec >= 0 && prec < pq->num_prec); ++ ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */ ++ ++ ASSERT(!pktq_full(pq)); ++ ASSERT(!pktq_pfull(pq, prec)); ++ ++ q = &pq->q[prec]; ++ ++ if (q->head) ++ PKTSETLINK(q->tail, p); ++ else ++ q->head = p; ++ ++ q->tail = p; ++ q->len++; ++ ++ pq->len++; ++ ++ if (pq->hi_prec < prec) ++ pq->hi_prec = (uint8)prec; ++ ++ return p; ++} ++ ++void * BCMFASTPATH ++pktq_penq_head(struct pktq *pq, int prec, void *p) ++{ ++ struct pktq_prec *q; ++ ++ ASSERT(prec >= 0 && prec < pq->num_prec); ++ ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */ ++ ++ ASSERT(!pktq_full(pq)); ++ ASSERT(!pktq_pfull(pq, prec)); ++ ++ q = &pq->q[prec]; ++ ++ if (q->head == NULL) ++ q->tail = p; ++ ++ PKTSETLINK(p, q->head); ++ q->head = p; ++ q->len++; ++ ++ pq->len++; ++ ++ if (pq->hi_prec < prec) ++ pq->hi_prec = (uint8)prec; ++ ++ return p; ++} ++ ++void * BCMFASTPATH ++pktq_pdeq(struct pktq *pq, int prec) ++{ ++ struct pktq_prec *q; ++ void *p; ++ ++ ASSERT(prec >= 0 && prec < pq->num_prec); ++ ++ q = &pq->q[prec]; ++ ++ if ((p = q->head) == NULL) ++ return NULL; ++ ++ if ((q->head = PKTLINK(p)) == NULL) ++ q->tail = NULL; ++ ++ q->len--; ++ ++ pq->len--; ++ ++ PKTSETLINK(p, NULL); ++ ++ return p; ++} ++ ++void * BCMFASTPATH ++pktq_pdeq_prev(struct pktq *pq, int prec, void *prev_p) ++{ ++ struct pktq_prec *q; ++ void *p; ++ ++ ASSERT(prec >= 0 && prec < pq->num_prec); ++ ++ q = &pq->q[prec]; ++ ++ if (prev_p == NULL) ++ return NULL; ++ ++ if ((p = PKTLINK(prev_p)) == NULL) ++ return NULL; ++ ++ q->len--; ++ ++ pq->len--; ++ ++ PKTSETLINK(prev_p, PKTLINK(p)); ++ PKTSETLINK(p, NULL); ++ ++ return p; ++} ++ ++void * BCMFASTPATH ++pktq_pdeq_tail(struct pktq *pq, int prec) ++{ ++ struct pktq_prec *q; ++ void *p, *prev; ++ ++ ASSERT(prec >= 0 && prec < pq->num_prec); ++ ++ q = &pq->q[prec]; ++ ++ if ((p = q->head) == NULL) ++ return NULL; ++ ++ for (prev = NULL; p != q->tail; p = PKTLINK(p)) ++ prev = p; ++ ++ if (prev) ++ PKTSETLINK(prev, NULL); ++ else ++ q->head = NULL; ++ ++ q->tail = prev; ++ q->len--; ++ ++ pq->len--; ++ ++ return p; ++} ++ ++void ++pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir, ifpkt_cb_t fn, int arg) ++{ ++ struct pktq_prec *q; ++ void *p, *prev = NULL; ++ ++ q = &pq->q[prec]; ++ p = q->head; ++ while (p) { ++ if (fn == NULL || (*fn)(p, arg)) { ++ bool head = (p == q->head); ++ if (head) ++ q->head = PKTLINK(p); ++ else ++ PKTSETLINK(prev, PKTLINK(p)); ++ PKTSETLINK(p, NULL); ++ PKTFREE(osh, p, dir); ++ q->len--; ++ pq->len--; ++ p = (head ? q->head : PKTLINK(prev)); ++ } else { ++ prev = p; ++ p = PKTLINK(p); ++ } ++ } ++ ++ if (q->head == NULL) { ++ ASSERT(q->len == 0); ++ q->tail = NULL; ++ } ++} ++ ++bool BCMFASTPATH ++pktq_pdel(struct pktq *pq, void *pktbuf, int prec) ++{ ++ struct pktq_prec *q; ++ void *p; ++ ++ ASSERT(prec >= 0 && prec < pq->num_prec); ++ ++ if (!pktbuf) ++ return FALSE; ++ ++ q = &pq->q[prec]; ++ ++ if (q->head == pktbuf) { ++ if ((q->head = PKTLINK(pktbuf)) == NULL) ++ q->tail = NULL; ++ } else { ++ for (p = q->head; p && PKTLINK(p) != pktbuf; p = PKTLINK(p)) ++ ; ++ if (p == NULL) ++ return FALSE; ++ ++ PKTSETLINK(p, PKTLINK(pktbuf)); ++ if (q->tail == pktbuf) ++ q->tail = p; ++ } ++ ++ q->len--; ++ pq->len--; ++ PKTSETLINK(pktbuf, NULL); ++ return TRUE; ++} ++ ++void ++pktq_init(struct pktq *pq, int num_prec, int max_len) ++{ ++ int prec; ++ ++ ASSERT(num_prec > 0 && num_prec <= PKTQ_MAX_PREC); ++ ++ /* pq is variable size; only zero out what's requested */ ++ bzero(pq, OFFSETOF(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec)); ++ ++ pq->num_prec = (uint16)num_prec; ++ ++ pq->max = (uint16)max_len; ++ ++ for (prec = 0; prec < num_prec; prec++) ++ pq->q[prec].max = pq->max; ++} ++ ++void ++pktq_set_max_plen(struct pktq *pq, int prec, int max_len) ++{ ++ ASSERT(prec >= 0 && prec < pq->num_prec); ++ ++ if (prec < pq->num_prec) ++ pq->q[prec].max = (uint16)max_len; ++} ++ ++void * BCMFASTPATH ++pktq_deq(struct pktq *pq, int *prec_out) ++{ ++ struct pktq_prec *q; ++ void *p; ++ int prec; ++ ++ if (pq->len == 0) ++ return NULL; ++ ++ while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) ++ pq->hi_prec--; ++ ++ q = &pq->q[prec]; ++ ++ if ((p = q->head) == NULL) ++ return NULL; ++ ++ if ((q->head = PKTLINK(p)) == NULL) ++ q->tail = NULL; ++ ++ q->len--; ++ ++ pq->len--; ++ ++ if (prec_out) ++ *prec_out = prec; ++ ++ PKTSETLINK(p, NULL); ++ ++ return p; ++} ++ ++void * BCMFASTPATH ++pktq_deq_tail(struct pktq *pq, int *prec_out) ++{ ++ struct pktq_prec *q; ++ void *p, *prev; ++ int prec; ++ ++ if (pq->len == 0) ++ return NULL; ++ ++ for (prec = 0; prec < pq->hi_prec; prec++) ++ if (pq->q[prec].head) ++ break; ++ ++ q = &pq->q[prec]; ++ ++ if ((p = q->head) == NULL) ++ return NULL; ++ ++ for (prev = NULL; p != q->tail; p = PKTLINK(p)) ++ prev = p; ++ ++ if (prev) ++ PKTSETLINK(prev, NULL); ++ else ++ q->head = NULL; ++ ++ q->tail = prev; ++ q->len--; ++ ++ pq->len--; ++ ++ if (prec_out) ++ *prec_out = prec; ++ ++ PKTSETLINK(p, NULL); ++ ++ return p; ++} ++ ++void * ++pktq_peek(struct pktq *pq, int *prec_out) ++{ ++ int prec; ++ ++ if (pq->len == 0) ++ return NULL; ++ ++ while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) ++ pq->hi_prec--; ++ ++ if (prec_out) ++ *prec_out = prec; ++ ++ return (pq->q[prec].head); ++} ++ ++void * ++pktq_peek_tail(struct pktq *pq, int *prec_out) ++{ ++ int prec; ++ ++ if (pq->len == 0) ++ return NULL; ++ ++ for (prec = 0; prec < pq->hi_prec; prec++) ++ if (pq->q[prec].head) ++ break; ++ ++ if (prec_out) ++ *prec_out = prec; ++ ++ return (pq->q[prec].tail); ++} ++ ++void ++pktq_flush(osl_t *osh, struct pktq *pq, bool dir, ifpkt_cb_t fn, int arg) ++{ ++ int prec; ++ ++ /* Optimize flush, if pktq len = 0, just return. ++ * pktq len of 0 means pktq's prec q's are all empty. ++ */ ++ if (pq->len == 0) { ++ return; ++ } ++ ++ for (prec = 0; prec < pq->num_prec; prec++) ++ pktq_pflush(osh, pq, prec, dir, fn, arg); ++ if (fn == NULL) ++ ASSERT(pq->len == 0); ++} ++ ++/* Return sum of lengths of a specific set of precedences */ ++int ++pktq_mlen(struct pktq *pq, uint prec_bmp) ++{ ++ int prec, len; ++ ++ len = 0; ++ ++ for (prec = 0; prec <= pq->hi_prec; prec++) ++ if (prec_bmp & (1 << prec)) ++ len += pq->q[prec].len; ++ ++ return len; ++} ++ ++/* Priority peek from a specific set of precedences */ ++void * BCMFASTPATH ++pktq_mpeek(struct pktq *pq, uint prec_bmp, int *prec_out) ++{ ++ struct pktq_prec *q; ++ void *p; ++ int prec; ++ ++ if (pq->len == 0) ++ { ++ return NULL; ++ } ++ while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) ++ pq->hi_prec--; ++ ++ while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL) ++ if (prec-- == 0) ++ return NULL; ++ ++ q = &pq->q[prec]; ++ ++ if ((p = q->head) == NULL) ++ return NULL; ++ ++ if (prec_out) ++ *prec_out = prec; ++ ++ return p; ++} ++/* Priority dequeue from a specific set of precedences */ ++void * BCMFASTPATH ++pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out) ++{ ++ struct pktq_prec *q; ++ void *p; ++ int prec; ++ ++ if (pq->len == 0) ++ return NULL; ++ ++ while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) ++ pq->hi_prec--; ++ ++ while ((pq->q[prec].head == NULL) || ((prec_bmp & (1 << prec)) == 0)) ++ if (prec-- == 0) ++ return NULL; ++ ++ q = &pq->q[prec]; ++ ++ if ((p = q->head) == NULL) ++ return NULL; ++ ++ if ((q->head = PKTLINK(p)) == NULL) ++ q->tail = NULL; ++ ++ q->len--; ++ ++ if (prec_out) ++ *prec_out = prec; ++ ++ pq->len--; ++ ++ PKTSETLINK(p, NULL); ++ ++ return p; ++} ++ ++#endif /* BCMDRIVER */ ++ ++const unsigned char bcm_ctype[] = { ++ ++ _BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 0-7 */ ++ _BCM_C, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C, ++ _BCM_C, /* 8-15 */ ++ _BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 16-23 */ ++ _BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 24-31 */ ++ _BCM_S|_BCM_SP,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 32-39 */ ++ _BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 40-47 */ ++ _BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D, /* 48-55 */ ++ _BCM_D,_BCM_D,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 56-63 */ ++ _BCM_P, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, ++ _BCM_U|_BCM_X, _BCM_U, /* 64-71 */ ++ _BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 72-79 */ ++ _BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 80-87 */ ++ _BCM_U,_BCM_U,_BCM_U,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 88-95 */ ++ _BCM_P, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, ++ _BCM_L|_BCM_X, _BCM_L, /* 96-103 */ ++ _BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 104-111 */ ++ _BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 112-119 */ ++ _BCM_L,_BCM_L,_BCM_L,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_C, /* 120-127 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128-143 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144-159 */ ++ _BCM_S|_BCM_SP, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, ++ _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 160-175 */ ++ _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, ++ _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 176-191 */ ++ _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, ++ _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, /* 192-207 */ ++ _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_P, _BCM_U, _BCM_U, _BCM_U, ++ _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_L, /* 208-223 */ ++ _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, ++ _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, /* 224-239 */ ++ _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_P, _BCM_L, _BCM_L, _BCM_L, ++ _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L /* 240-255 */ ++}; ++ ++ulong ++bcm_strtoul(const char *cp, char **endp, uint base) ++{ ++ ulong result, last_result = 0, value; ++ bool minus; ++ ++ minus = FALSE; ++ ++ while (bcm_isspace(*cp)) ++ cp++; ++ ++ if (cp[0] == '+') ++ cp++; ++ else if (cp[0] == '-') { ++ minus = TRUE; ++ cp++; ++ } ++ ++ if (base == 0) { ++ if (cp[0] == '0') { ++ if ((cp[1] == 'x') || (cp[1] == 'X')) { ++ base = 16; ++ cp = &cp[2]; ++ } else { ++ base = 8; ++ cp = &cp[1]; ++ } ++ } else ++ base = 10; ++ } else if (base == 16 && (cp[0] == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) { ++ cp = &cp[2]; ++ } ++ ++ result = 0; ++ ++ while (bcm_isxdigit(*cp) && ++ (value = bcm_isdigit(*cp) ? *cp-'0' : bcm_toupper(*cp)-'A'+10) < base) { ++ result = result*base + value; ++ /* Detected overflow */ ++ if (result < last_result && !minus) ++ return (ulong)-1; ++ last_result = result; ++ cp++; ++ } ++ ++ if (minus) ++ result = (ulong)(-(long)result); ++ ++ if (endp) ++ *endp = DISCARD_QUAL(cp, char); ++ ++ return (result); ++} ++ ++int ++bcm_atoi(const char *s) ++{ ++ return (int)bcm_strtoul(s, NULL, 10); ++} ++ ++/* return pointer to location of substring 'needle' in 'haystack' */ ++char * ++bcmstrstr(const char *haystack, const char *needle) ++{ ++ int len, nlen; ++ int i; ++ ++ if ((haystack == NULL) || (needle == NULL)) ++ return DISCARD_QUAL(haystack, char); ++ ++ nlen = strlen(needle); ++ len = strlen(haystack) - nlen + 1; ++ ++ for (i = 0; i < len; i++) ++ if (memcmp(needle, &haystack[i], nlen) == 0) ++ return DISCARD_QUAL(&haystack[i], char); ++ return (NULL); ++} ++ ++char * ++bcmstrcat(char *dest, const char *src) ++{ ++ char *p; ++ ++ p = dest + strlen(dest); ++ ++ while ((*p++ = *src++) != '\0') ++ ; ++ ++ return (dest); ++} ++ ++char * ++bcmstrncat(char *dest, const char *src, uint size) ++{ ++ char *endp; ++ char *p; ++ ++ p = dest + strlen(dest); ++ endp = p + size; ++ ++ while (p != endp && (*p++ = *src++) != '\0') ++ ; ++ ++ return (dest); ++} ++ ++ ++/**************************************************************************** ++* Function: bcmstrtok ++* ++* Purpose: ++* Tokenizes a string. This function is conceptually similiar to ANSI C strtok(), ++* but allows strToken() to be used by different strings or callers at the same ++* time. Each call modifies '*string' by substituting a NULL character for the ++* first delimiter that is encountered, and updates 'string' to point to the char ++* after the delimiter. Leading delimiters are skipped. ++* ++* Parameters: ++* string (mod) Ptr to string ptr, updated by token. ++* delimiters (in) Set of delimiter characters. ++* tokdelim (out) Character that delimits the returned token. (May ++* be set to NULL if token delimiter is not required). ++* ++* Returns: Pointer to the next token found. NULL when no more tokens are found. ++***************************************************************************** ++*/ ++char * ++bcmstrtok(char **string, const char *delimiters, char *tokdelim) ++{ ++ unsigned char *str; ++ unsigned long map[8]; ++ int count; ++ char *nextoken; ++ ++ if (tokdelim != NULL) { ++ /* Prime the token delimiter */ ++ *tokdelim = '\0'; ++ } ++ ++ /* Clear control map */ ++ for (count = 0; count < 8; count++) { ++ map[count] = 0; ++ } ++ ++ /* Set bits in delimiter table */ ++ do { ++ map[*delimiters >> 5] |= (1 << (*delimiters & 31)); ++ } ++ while (*delimiters++); ++ ++ str = (unsigned char*)*string; ++ ++ /* Find beginning of token (skip over leading delimiters). Note that ++ * there is no token iff this loop sets str to point to the terminal ++ * null (*str == '\0') ++ */ ++ while (((map[*str >> 5] & (1 << (*str & 31))) && *str) || (*str == ' ')) { ++ str++; ++ } ++ ++ nextoken = (char*)str; ++ ++ /* Find the end of the token. If it is not the end of the string, ++ * put a null there. ++ */ ++ for (; *str; str++) { ++ if (map[*str >> 5] & (1 << (*str & 31))) { ++ if (tokdelim != NULL) { ++ *tokdelim = *str; ++ } ++ ++ *str++ = '\0'; ++ break; ++ } ++ } ++ ++ *string = (char*)str; ++ ++ /* Determine if a token has been found. */ ++ if (nextoken == (char *) str) { ++ return NULL; ++ } ++ else { ++ return nextoken; ++ } ++} ++ ++ ++#define xToLower(C) \ ++ ((C >= 'A' && C <= 'Z') ? (char)((int)C - (int)'A' + (int)'a') : C) ++ ++ ++/**************************************************************************** ++* Function: bcmstricmp ++* ++* Purpose: Compare to strings case insensitively. ++* ++* Parameters: s1 (in) First string to compare. ++* s2 (in) Second string to compare. ++* ++* Returns: Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if ++* t1 > t2, when ignoring case sensitivity. ++***************************************************************************** ++*/ ++int ++bcmstricmp(const char *s1, const char *s2) ++{ ++ char dc, sc; ++ ++ while (*s2 && *s1) { ++ dc = xToLower(*s1); ++ sc = xToLower(*s2); ++ if (dc < sc) return -1; ++ if (dc > sc) return 1; ++ s1++; ++ s2++; ++ } ++ ++ if (*s1 && !*s2) return 1; ++ if (!*s1 && *s2) return -1; ++ return 0; ++} ++ ++ ++/**************************************************************************** ++* Function: bcmstrnicmp ++* ++* Purpose: Compare to strings case insensitively, upto a max of 'cnt' ++* characters. ++* ++* Parameters: s1 (in) First string to compare. ++* s2 (in) Second string to compare. ++* cnt (in) Max characters to compare. ++* ++* Returns: Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if ++* t1 > t2, when ignoring case sensitivity. ++***************************************************************************** ++*/ ++int ++bcmstrnicmp(const char* s1, const char* s2, int cnt) ++{ ++ char dc, sc; ++ ++ while (*s2 && *s1 && cnt) { ++ dc = xToLower(*s1); ++ sc = xToLower(*s2); ++ if (dc < sc) return -1; ++ if (dc > sc) return 1; ++ s1++; ++ s2++; ++ cnt--; ++ } ++ ++ if (!cnt) return 0; ++ if (*s1 && !*s2) return 1; ++ if (!*s1 && *s2) return -1; ++ return 0; ++} ++ ++/* parse a xx:xx:xx:xx:xx:xx format ethernet address */ ++int ++bcm_ether_atoe(const char *p, struct ether_addr *ea) ++{ ++ int i = 0; ++ char *ep; ++ ++ for (;;) { ++ ea->octet[i++] = (char) bcm_strtoul(p, &ep, 16); ++ p = ep; ++ if (!*p++ || i == 6) ++ break; ++ } ++ ++ return (i == 6); ++} ++ ++ ++#if defined(CONFIG_USBRNDIS_RETAIL) || defined(NDIS_MINIPORT_DRIVER) ++/* registry routine buffer preparation utility functions: ++ * parameter order is like strncpy, but returns count ++ * of bytes copied. Minimum bytes copied is null char(1)/wchar(2) ++ */ ++ulong ++wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen) ++{ ++ ulong copyct = 1; ++ ushort i; ++ ++ if (abuflen == 0) ++ return 0; ++ ++ /* wbuflen is in bytes */ ++ wbuflen /= sizeof(ushort); ++ ++ for (i = 0; i < wbuflen; ++i) { ++ if (--abuflen == 0) ++ break; ++ *abuf++ = (char) *wbuf++; ++ ++copyct; ++ } ++ *abuf = '\0'; ++ ++ return copyct; ++} ++#endif /* CONFIG_USBRNDIS_RETAIL || NDIS_MINIPORT_DRIVER */ ++ ++char * ++bcm_ether_ntoa(const struct ether_addr *ea, char *buf) ++{ ++ static const char hex[] = ++ { ++ '0', '1', '2', '3', '4', '5', '6', '7', ++ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' ++ }; ++ const uint8 *octet = ea->octet; ++ char *p = buf; ++ int i; ++ ++ for (i = 0; i < 6; i++, octet++) { ++ *p++ = hex[(*octet >> 4) & 0xf]; ++ *p++ = hex[*octet & 0xf]; ++ *p++ = ':'; ++ } ++ ++ *(p-1) = '\0'; ++ ++ return (buf); ++} ++ ++char * ++bcm_ip_ntoa(struct ipv4_addr *ia, char *buf) ++{ ++ snprintf(buf, 16, "%d.%d.%d.%d", ++ ia->addr[0], ia->addr[1], ia->addr[2], ia->addr[3]); ++ return (buf); ++} ++ ++#ifdef BCMDRIVER ++ ++void ++bcm_mdelay(uint ms) ++{ ++ uint i; ++ ++ for (i = 0; i < ms; i++) { ++ OSL_DELAY(1000); ++ } ++} ++ ++ ++ ++ ++ ++#if defined(DHD_DEBUG) ++/* pretty hex print a pkt buffer chain */ ++void ++prpkt(const char *msg, osl_t *osh, void *p0) ++{ ++ void *p; ++ ++ if (msg && (msg[0] != '\0')) ++ printf("%s:\n", msg); ++ ++ for (p = p0; p; p = PKTNEXT(osh, p)) ++ prhex(NULL, PKTDATA(osh, p), PKTLEN(osh, p)); ++} ++#endif ++ ++/* Takes an Ethernet frame and sets out-of-bound PKTPRIO. ++ * Also updates the inplace vlan tag if requested. ++ * For debugging, it returns an indication of what it did. ++ */ ++uint BCMFASTPATH ++pktsetprio(void *pkt, bool update_vtag) ++{ ++ struct ether_header *eh; ++ struct ethervlan_header *evh; ++ uint8 *pktdata; ++ int priority = 0; ++ int rc = 0; ++ ++ pktdata = (uint8 *)PKTDATA(NULL, pkt); ++ ASSERT(ISALIGNED((uintptr)pktdata, sizeof(uint16))); ++ ++ eh = (struct ether_header *) pktdata; ++ ++ if (ntoh16(eh->ether_type) == ETHER_TYPE_8021Q) { ++ uint16 vlan_tag; ++ int vlan_prio, dscp_prio = 0; ++ ++ evh = (struct ethervlan_header *)eh; ++ ++ vlan_tag = ntoh16(evh->vlan_tag); ++ vlan_prio = (int) (vlan_tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK; ++ ++ if (ntoh16(evh->ether_type) == ETHER_TYPE_IP) { ++ uint8 *ip_body = pktdata + sizeof(struct ethervlan_header); ++ uint8 tos_tc = IP_TOS46(ip_body); ++ dscp_prio = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT); ++ } ++ ++ /* DSCP priority gets precedence over 802.1P (vlan tag) */ ++ if (dscp_prio != 0) { ++ priority = dscp_prio; ++ rc |= PKTPRIO_VDSCP; ++ } else { ++ priority = vlan_prio; ++ rc |= PKTPRIO_VLAN; ++ } ++ /* ++ * If the DSCP priority is not the same as the VLAN priority, ++ * then overwrite the priority field in the vlan tag, with the ++ * DSCP priority value. This is required for Linux APs because ++ * the VLAN driver on Linux, overwrites the skb->priority field ++ * with the priority value in the vlan tag ++ */ ++ if (update_vtag && (priority != vlan_prio)) { ++ vlan_tag &= ~(VLAN_PRI_MASK << VLAN_PRI_SHIFT); ++ vlan_tag |= (uint16)priority << VLAN_PRI_SHIFT; ++ evh->vlan_tag = hton16(vlan_tag); ++ rc |= PKTPRIO_UPD; ++ } ++ } else if (ntoh16(eh->ether_type) == ETHER_TYPE_IP) { ++ uint8 *ip_body = pktdata + sizeof(struct ether_header); ++ uint8 tos_tc = IP_TOS46(ip_body); ++ priority = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT); ++ rc |= PKTPRIO_DSCP; ++ } ++ ++ ASSERT(priority >= 0 && priority <= MAXPRIO); ++ PKTSETPRIO(pkt, priority); ++ return (rc | priority); ++} ++ ++ ++static char bcm_undeferrstr[32]; ++static const char *bcmerrorstrtable[] = BCMERRSTRINGTABLE; ++ ++/* Convert the error codes into related error strings */ ++const char * ++bcmerrorstr(int bcmerror) ++{ ++ /* check if someone added a bcmerror code but forgot to add errorstring */ ++ ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(bcmerrorstrtable) - 1)); ++ ++ if (bcmerror > 0 || bcmerror < BCME_LAST) { ++ snprintf(bcm_undeferrstr, sizeof(bcm_undeferrstr), "Undefined error %d", bcmerror); ++ return bcm_undeferrstr; ++ } ++ ++ ASSERT(strlen(bcmerrorstrtable[-bcmerror]) < BCME_STRLEN); ++ ++ return bcmerrorstrtable[-bcmerror]; ++} ++ ++ ++ ++/* iovar table lookup */ ++const bcm_iovar_t* ++bcm_iovar_lookup(const bcm_iovar_t *table, const char *name) ++{ ++ const bcm_iovar_t *vi; ++ const char *lookup_name; ++ ++ /* skip any ':' delimited option prefixes */ ++ lookup_name = strrchr(name, ':'); ++ if (lookup_name != NULL) ++ lookup_name++; ++ else ++ lookup_name = name; ++ ++ ASSERT(table != NULL); ++ ++ for (vi = table; vi->name; vi++) { ++ if (!strcmp(vi->name, lookup_name)) ++ return vi; ++ } ++ /* ran to end of table */ ++ ++ return NULL; /* var name not found */ ++} ++ ++int ++bcm_iovar_lencheck(const bcm_iovar_t *vi, void *arg, int len, bool set) ++{ ++ int bcmerror = 0; ++ ++ /* length check on io buf */ ++ switch (vi->type) { ++ case IOVT_BOOL: ++ case IOVT_INT8: ++ case IOVT_INT16: ++ case IOVT_INT32: ++ case IOVT_UINT8: ++ case IOVT_UINT16: ++ case IOVT_UINT32: ++ /* all integers are int32 sized args at the ioctl interface */ ++ if (len < (int)sizeof(int)) { ++ bcmerror = BCME_BUFTOOSHORT; ++ } ++ break; ++ ++ case IOVT_BUFFER: ++ /* buffer must meet minimum length requirement */ ++ if (len < vi->minlen) { ++ bcmerror = BCME_BUFTOOSHORT; ++ } ++ break; ++ ++ case IOVT_VOID: ++ if (!set) { ++ /* Cannot return nil... */ ++ bcmerror = BCME_UNSUPPORTED; ++ } else if (len) { ++ /* Set is an action w/o parameters */ ++ bcmerror = BCME_BUFTOOLONG; ++ } ++ break; ++ ++ default: ++ /* unknown type for length check in iovar info */ ++ ASSERT(0); ++ bcmerror = BCME_UNSUPPORTED; ++ } ++ ++ return bcmerror; ++} ++ ++#endif /* BCMDRIVER */ ++ ++ ++/******************************************************************************* ++ * crc8 ++ * ++ * Computes a crc8 over the input data using the polynomial: ++ * ++ * x^8 + x^7 +x^6 + x^4 + x^2 + 1 ++ * ++ * The caller provides the initial value (either CRC8_INIT_VALUE ++ * or the previous returned value) to allow for processing of ++ * discontiguous blocks of data. When generating the CRC the ++ * caller is responsible for complementing the final return value ++ * and inserting it into the byte stream. When checking, a final ++ * return value of CRC8_GOOD_VALUE indicates a valid CRC. ++ * ++ * Reference: Dallas Semiconductor Application Note 27 ++ * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms", ++ * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd., ++ * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt ++ * ++ * **************************************************************************** ++ */ ++ ++static const uint8 crc8_table[256] = { ++ 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, ++ 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, ++ 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, ++ 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, ++ 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, ++ 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, ++ 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, ++ 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, ++ 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, ++ 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, ++ 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, ++ 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, ++ 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, ++ 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, ++ 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, ++ 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, ++ 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, ++ 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, ++ 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, ++ 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, ++ 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, ++ 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, ++ 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, ++ 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, ++ 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, ++ 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, ++ 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, ++ 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, ++ 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, ++ 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, ++ 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, ++ 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F ++}; ++ ++#define CRC_INNER_LOOP(n, c, x) \ ++ (c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff] ++ ++uint8 ++hndcrc8( ++ uint8 *pdata, /* pointer to array of data to process */ ++ uint nbytes, /* number of input data bytes to process */ ++ uint8 crc /* either CRC8_INIT_VALUE or previous return value */ ++) ++{ ++ /* hard code the crc loop instead of using CRC_INNER_LOOP macro ++ * to avoid the undefined and unnecessary (uint8 >> 8) operation. ++ */ ++ while (nbytes-- > 0) ++ crc = crc8_table[(crc ^ *pdata++) & 0xff]; ++ ++ return crc; ++} ++ ++/******************************************************************************* ++ * crc16 ++ * ++ * Computes a crc16 over the input data using the polynomial: ++ * ++ * x^16 + x^12 +x^5 + 1 ++ * ++ * The caller provides the initial value (either CRC16_INIT_VALUE ++ * or the previous returned value) to allow for processing of ++ * discontiguous blocks of data. When generating the CRC the ++ * caller is responsible for complementing the final return value ++ * and inserting it into the byte stream. When checking, a final ++ * return value of CRC16_GOOD_VALUE indicates a valid CRC. ++ * ++ * Reference: Dallas Semiconductor Application Note 27 ++ * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms", ++ * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd., ++ * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt ++ * ++ * **************************************************************************** ++ */ ++ ++static const uint16 crc16_table[256] = { ++ 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, ++ 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, ++ 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, ++ 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876, ++ 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, ++ 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5, ++ 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C, ++ 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974, ++ 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, ++ 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3, ++ 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, ++ 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72, ++ 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, ++ 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1, ++ 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738, ++ 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70, ++ 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, ++ 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF, ++ 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, ++ 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E, ++ 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, ++ 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD, ++ 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134, ++ 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C, ++ 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3, ++ 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB, ++ 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, ++ 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A, ++ 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, ++ 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9, ++ 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, ++ 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78 ++}; ++ ++uint16 ++hndcrc16( ++ uint8 *pdata, /* pointer to array of data to process */ ++ uint nbytes, /* number of input data bytes to process */ ++ uint16 crc /* either CRC16_INIT_VALUE or previous return value */ ++) ++{ ++ while (nbytes-- > 0) ++ CRC_INNER_LOOP(16, crc, *pdata++); ++ return crc; ++} ++ ++static const uint32 crc32_table[256] = { ++ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, ++ 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, ++ 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, ++ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, ++ 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, ++ 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, ++ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, ++ 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, ++ 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, ++ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, ++ 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, ++ 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, ++ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, ++ 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, ++ 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, ++ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, ++ 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, ++ 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, ++ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, ++ 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, ++ 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, ++ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, ++ 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, ++ 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, ++ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, ++ 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, ++ 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, ++ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, ++ 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, ++ 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, ++ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, ++ 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, ++ 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, ++ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, ++ 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, ++ 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, ++ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, ++ 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, ++ 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, ++ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, ++ 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, ++ 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, ++ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, ++ 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, ++ 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, ++ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, ++ 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, ++ 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, ++ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, ++ 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, ++ 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, ++ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, ++ 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, ++ 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, ++ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, ++ 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, ++ 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, ++ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, ++ 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, ++ 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, ++ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, ++ 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, ++ 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, ++ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D ++}; ++ ++/* ++ * crc input is CRC32_INIT_VALUE for a fresh start, or previous return value if ++ * accumulating over multiple pieces. ++ */ ++uint32 ++hndcrc32(uint8 *pdata, uint nbytes, uint32 crc) ++{ ++ uint8 *pend; ++ pend = pdata + nbytes; ++ while (pdata < pend) ++ CRC_INNER_LOOP(32, crc, *pdata++); ++ ++ return crc; ++} ++ ++#ifdef notdef ++#define CLEN 1499 /* CRC Length */ ++#define CBUFSIZ (CLEN+4) ++#define CNBUFS 5 /* # of bufs */ ++ ++void ++testcrc32(void) ++{ ++ uint j, k, l; ++ uint8 *buf; ++ uint len[CNBUFS]; ++ uint32 crcr; ++ uint32 crc32tv[CNBUFS] = ++ {0xd2cb1faa, 0xd385c8fa, 0xf5b4f3f3, 0x55789e20, 0x00343110}; ++ ++ ASSERT((buf = MALLOC(CBUFSIZ*CNBUFS)) != NULL); ++ ++ /* step through all possible alignments */ ++ for (l = 0; l <= 4; l++) { ++ for (j = 0; j < CNBUFS; j++) { ++ len[j] = CLEN; ++ for (k = 0; k < len[j]; k++) ++ *(buf + j*CBUFSIZ + (k+l)) = (j+k) & 0xff; ++ } ++ ++ for (j = 0; j < CNBUFS; j++) { ++ crcr = crc32(buf + j*CBUFSIZ + l, len[j], CRC32_INIT_VALUE); ++ ASSERT(crcr == crc32tv[j]); ++ } ++ } ++ ++ MFREE(buf, CBUFSIZ*CNBUFS); ++ return; ++} ++#endif /* notdef */ ++ ++/* ++ * Advance from the current 1-byte tag/1-byte length/variable-length value ++ * triple, to the next, returning a pointer to the next. ++ * If the current or next TLV is invalid (does not fit in given buffer length), ++ * NULL is returned. ++ * *buflen is not modified if the TLV elt parameter is invalid, or is decremented ++ * by the TLV parameter's length if it is valid. ++ */ ++bcm_tlv_t * ++bcm_next_tlv(bcm_tlv_t *elt, int *buflen) ++{ ++ int len; ++ ++ /* validate current elt */ ++ if (!bcm_valid_tlv(elt, *buflen)) ++ return NULL; ++ ++ /* advance to next elt */ ++ len = elt->len; ++ elt = (bcm_tlv_t*)(elt->data + len); ++ *buflen -= (TLV_HDR_LEN + len); ++ ++ /* validate next elt */ ++ if (!bcm_valid_tlv(elt, *buflen)) ++ return NULL; ++ ++ return elt; ++} ++ ++/* ++ * Traverse a string of 1-byte tag/1-byte length/variable-length value ++ * triples, returning a pointer to the substring whose first element ++ * matches tag ++ */ ++bcm_tlv_t * ++bcm_parse_tlvs(void *buf, int buflen, uint key) ++{ ++ bcm_tlv_t *elt; ++ int totlen; ++ ++ elt = (bcm_tlv_t*)buf; ++ totlen = buflen; ++ ++ /* find tagged parameter */ ++ while (totlen >= TLV_HDR_LEN) { ++ int len = elt->len; ++ ++ /* validate remaining totlen */ ++ if ((elt->id == key) && ++ (totlen >= (len + TLV_HDR_LEN))) ++ return (elt); ++ ++ elt = (bcm_tlv_t*)((uint8*)elt + (len + TLV_HDR_LEN)); ++ totlen -= (len + TLV_HDR_LEN); ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Traverse a string of 1-byte tag/1-byte length/variable-length value ++ * triples, returning a pointer to the substring whose first element ++ * matches tag. Stop parsing when we see an element whose ID is greater ++ * than the target key. ++ */ ++bcm_tlv_t * ++bcm_parse_ordered_tlvs(void *buf, int buflen, uint key) ++{ ++ bcm_tlv_t *elt; ++ int totlen; ++ ++ elt = (bcm_tlv_t*)buf; ++ totlen = buflen; ++ ++ /* find tagged parameter */ ++ while (totlen >= TLV_HDR_LEN) { ++ uint id = elt->id; ++ int len = elt->len; ++ ++ /* Punt if we start seeing IDs > than target key */ ++ if (id > key) ++ return (NULL); ++ ++ /* validate remaining totlen */ ++ if ((id == key) && ++ (totlen >= (len + TLV_HDR_LEN))) ++ return (elt); ++ ++ elt = (bcm_tlv_t*)((uint8*)elt + (len + TLV_HDR_LEN)); ++ totlen -= (len + TLV_HDR_LEN); ++ } ++ return NULL; ++} ++ ++#if defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || defined(WLMSG_ASSOC) || \ ++ defined(DHD_DEBUG) ++int ++bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char* buf, int len) ++{ ++ int i; ++ char* p = buf; ++ char hexstr[16]; ++ int slen = 0, nlen = 0; ++ uint32 bit; ++ const char* name; ++ ++ if (len < 2 || !buf) ++ return 0; ++ ++ buf[0] = '\0'; ++ ++ for (i = 0; flags != 0; i++) { ++ bit = bd[i].bit; ++ name = bd[i].name; ++ if (bit == 0 && flags != 0) { ++ /* print any unnamed bits */ ++ snprintf(hexstr, 16, "0x%X", flags); ++ name = hexstr; ++ flags = 0; /* exit loop */ ++ } else if ((flags & bit) == 0) ++ continue; ++ flags &= ~bit; ++ nlen = strlen(name); ++ slen += nlen; ++ /* count btwn flag space */ ++ if (flags != 0) ++ slen += 1; ++ /* need NULL char as well */ ++ if (len <= slen) ++ break; ++ /* copy NULL char but don't count it */ ++ strncpy(p, name, nlen + 1); ++ p += nlen; ++ /* copy btwn flag space and NULL char */ ++ if (flags != 0) ++ p += snprintf(p, 2, " "); ++ } ++ ++ /* indicate the str was too short */ ++ if (flags != 0) { ++ if (len < 2) ++ p -= 2 - len; /* overwrite last char */ ++ p += snprintf(p, 2, ">"); ++ } ++ ++ return (int)(p - buf); ++} ++ ++/* print bytes formatted as hex to a string. return the resulting string length */ ++int ++bcm_format_hex(char *str, const void *bytes, int len) ++{ ++ int i; ++ char *p = str; ++ const uint8 *src = (const uint8*)bytes; ++ ++ for (i = 0; i < len; i++) { ++ p += snprintf(p, 3, "%02X", *src); ++ src++; ++ } ++ return (int)(p - str); ++} ++#endif ++ ++/* pretty hex print a contiguous buffer */ ++void ++prhex(const char *msg, uchar *buf, uint nbytes) ++{ ++ char line[128], *p; ++ int len = sizeof(line); ++ int nchar; ++ uint i; ++ ++ if (msg && (msg[0] != '\0')) ++ printf("%s:\n", msg); ++ ++ p = line; ++ for (i = 0; i < nbytes; i++) { ++ if (i % 16 == 0) { ++ nchar = snprintf(p, len, " %04d: ", i); /* line prefix */ ++ p += nchar; ++ len -= nchar; ++ } ++ if (len > 0) { ++ nchar = snprintf(p, len, "%02x ", buf[i]); ++ p += nchar; ++ len -= nchar; ++ } ++ ++ if (i % 16 == 15) { ++ printf("%s\n", line); /* flush line */ ++ p = line; ++ len = sizeof(line); ++ } ++ } ++ ++ /* flush last partial line */ ++ if (p != line) ++ printf("%s\n", line); ++} ++ ++static const char *crypto_algo_names[] = { ++ "NONE", ++ "WEP1", ++ "TKIP", ++ "WEP128", ++ "AES_CCM", ++ "AES_OCB_MSDU", ++ "AES_OCB_MPDU", ++ "NALG" ++ "UNDEF", ++ "UNDEF", ++ "UNDEF", ++ "UNDEF" ++}; ++ ++const char * ++bcm_crypto_algo_name(uint algo) ++{ ++ return (algo < ARRAYSIZE(crypto_algo_names)) ? crypto_algo_names[algo] : "ERR"; ++} ++ ++ ++char * ++bcm_chipname(uint chipid, char *buf, uint len) ++{ ++ const char *fmt; ++ ++ fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; ++ snprintf(buf, len, fmt, chipid); ++ return buf; ++} ++ ++/* Produce a human-readable string for boardrev */ ++char * ++bcm_brev_str(uint32 brev, char *buf) ++{ ++ if (brev < 0x100) ++ snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf); ++ else ++ snprintf(buf, 8, "%c%03x", ((brev & 0xf000) == 0x1000) ? 'P' : 'A', brev & 0xfff); ++ ++ return (buf); ++} ++ ++#define BUFSIZE_TODUMP_ATONCE 512 /* Buffer size */ ++ ++/* dump large strings to console */ ++void ++printbig(char *buf) ++{ ++ uint len, max_len; ++ char c; ++ ++ len = strlen(buf); ++ ++ max_len = BUFSIZE_TODUMP_ATONCE; ++ ++ while (len > max_len) { ++ c = buf[max_len]; ++ buf[max_len] = '\0'; ++ printf("%s", buf); ++ buf[max_len] = c; ++ ++ buf += max_len; ++ len -= max_len; ++ } ++ /* print the remaining string */ ++ printf("%s\n", buf); ++ return; ++} ++ ++/* routine to dump fields in a fileddesc structure */ ++uint ++bcmdumpfields(bcmutl_rdreg_rtn read_rtn, void *arg0, uint arg1, struct fielddesc *fielddesc_array, ++ char *buf, uint32 bufsize) ++{ ++ uint filled_len; ++ int len; ++ struct fielddesc *cur_ptr; ++ ++ filled_len = 0; ++ cur_ptr = fielddesc_array; ++ ++ while (bufsize > 1) { ++ if (cur_ptr->nameandfmt == NULL) ++ break; ++ len = snprintf(buf, bufsize, cur_ptr->nameandfmt, ++ read_rtn(arg0, arg1, cur_ptr->offset)); ++ /* check for snprintf overflow or error */ ++ if (len < 0 || (uint32)len >= bufsize) ++ len = bufsize - 1; ++ buf += len; ++ bufsize -= len; ++ filled_len += len; ++ cur_ptr++; ++ } ++ return filled_len; ++} ++ ++uint ++bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen) ++{ ++ uint len; ++ ++ len = strlen(name) + 1; ++ ++ if ((len + datalen) > buflen) ++ return 0; ++ ++ strncpy(buf, name, buflen); ++ ++ /* append data onto the end of the name string */ ++ memcpy(&buf[len], data, datalen); ++ len += datalen; ++ ++ return len; ++} ++ ++/* Quarter dBm units to mW ++ * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153 ++ * Table is offset so the last entry is largest mW value that fits in ++ * a uint16. ++ */ ++ ++#define QDBM_OFFSET 153 /* Offset for first entry */ ++#define QDBM_TABLE_LEN 40 /* Table size */ ++ ++/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET. ++ * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2 ++ */ ++#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */ ++ ++/* Largest mW value that will round down to the last table entry, ++ * QDBM_OFFSET + QDBM_TABLE_LEN-1. ++ * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2. ++ */ ++#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */ ++ ++static const uint16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = { ++/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */ ++/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000, ++/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849, ++/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119, ++/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811, ++/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096 ++}; ++ ++uint16 ++bcm_qdbm_to_mw(uint8 qdbm) ++{ ++ uint factor = 1; ++ int idx = qdbm - QDBM_OFFSET; ++ ++ if (idx >= QDBM_TABLE_LEN) { ++ /* clamp to max uint16 mW value */ ++ return 0xFFFF; ++ } ++ ++ /* scale the qdBm index up to the range of the table 0-40 ++ * where an offset of 40 qdBm equals a factor of 10 mW. ++ */ ++ while (idx < 0) { ++ idx += 40; ++ factor *= 10; ++ } ++ ++ /* return the mW value scaled down to the correct factor of 10, ++ * adding in factor/2 to get proper rounding. ++ */ ++ return ((nqdBm_to_mW_map[idx] + factor/2) / factor); ++} ++ ++uint8 ++bcm_mw_to_qdbm(uint16 mw) ++{ ++ uint8 qdbm; ++ int offset; ++ uint mw_uint = mw; ++ uint boundary; ++ ++ /* handle boundary case */ ++ if (mw_uint <= 1) ++ return 0; ++ ++ offset = QDBM_OFFSET; ++ ++ /* move mw into the range of the table */ ++ while (mw_uint < QDBM_TABLE_LOW_BOUND) { ++ mw_uint *= 10; ++ offset -= 40; ++ } ++ ++ for (qdbm = 0; qdbm < QDBM_TABLE_LEN-1; qdbm++) { ++ boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm+1] - ++ nqdBm_to_mW_map[qdbm])/2; ++ if (mw_uint < boundary) break; ++ } ++ ++ qdbm += (uint8)offset; ++ ++ return (qdbm); ++} ++ ++ ++uint ++bcm_bitcount(uint8 *bitmap, uint length) ++{ ++ uint bitcount = 0, i; ++ uint8 tmp; ++ for (i = 0; i < length; i++) { ++ tmp = bitmap[i]; ++ while (tmp) { ++ bitcount++; ++ tmp &= (tmp - 1); ++ } ++ } ++ return bitcount; ++} ++ ++#ifdef BCMDRIVER ++ ++/* Initialization of bcmstrbuf structure */ ++void ++bcm_binit(struct bcmstrbuf *b, char *buf, uint size) ++{ ++ b->origsize = b->size = size; ++ b->origbuf = b->buf = buf; ++} ++ ++/* Buffer sprintf wrapper to guard against buffer overflow */ ++int ++bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...) ++{ ++ va_list ap; ++ int r; ++ ++ va_start(ap, fmt); ++ ++ r = vsnprintf(b->buf, b->size, fmt, ap); ++ ++ /* Non Ansi C99 compliant returns -1, ++ * Ansi compliant return r >= b->size, ++ * bcmstdlib returns 0, handle all ++ */ ++ /* r == 0 is also the case when strlen(fmt) is zero. ++ * typically the case when "" is passed as argument. ++ */ ++ if ((r == -1) || (r >= (int)b->size)) { ++ b->size = 0; ++ } else { ++ b->size -= r; ++ b->buf += r; ++ } ++ ++ va_end(ap); ++ ++ return r; ++} ++ ++void ++bcm_bprhex(struct bcmstrbuf *b, const char *msg, bool newline, uint8 *buf, int len) ++{ ++ int i; ++ ++ if (msg != NULL && msg[0] != '\0') ++ bcm_bprintf(b, "%s", msg); ++ for (i = 0; i < len; i ++) ++ bcm_bprintf(b, "%02X", buf[i]); ++ if (newline) ++ bcm_bprintf(b, "\n"); ++} ++ ++void ++bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount) ++{ ++ int i; ++ ++ for (i = 0; i < num_bytes; i++) { ++ num[i] += amount; ++ if (num[i] >= amount) ++ break; ++ amount = 1; ++ } ++} ++ ++int ++bcm_cmp_bytes(const uchar *arg1, const uchar *arg2, uint8 nbytes) ++{ ++ int i; ++ ++ for (i = nbytes - 1; i >= 0; i--) { ++ if (arg1[i] != arg2[i]) ++ return (arg1[i] - arg2[i]); ++ } ++ return 0; ++} ++ ++void ++bcm_print_bytes(const char *name, const uchar *data, int len) ++{ ++ int i; ++ int per_line = 0; ++ ++ printf("%s: %d \n", name ? name : "", len); ++ for (i = 0; i < len; i++) { ++ printf("%02x ", *data++); ++ per_line++; ++ if (per_line == 16) { ++ per_line = 0; ++ printf("\n"); ++ } ++ } ++ printf("\n"); ++} ++#if defined(WLTINYDUMP) || defined(WLMSG_INFORM) || defined(WLMSG_ASSOC) || \ ++ defined(WLMSG_PRPKT) || defined(WLMSG_WSEC) ++#define SSID_FMT_BUF_LEN ((4 * DOT11_MAX_SSID_LEN) + 1) ++ ++int ++bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len) ++{ ++ uint i, c; ++ char *p = buf; ++ char *endp = buf + SSID_FMT_BUF_LEN; ++ ++ if (ssid_len > DOT11_MAX_SSID_LEN) ssid_len = DOT11_MAX_SSID_LEN; ++ ++ for (i = 0; i < ssid_len; i++) { ++ c = (uint)ssid[i]; ++ if (c == '\\') { ++ *p++ = '\\'; ++ *p++ = '\\'; ++ } else if (bcm_isprint((uchar)c)) { ++ *p++ = (char)c; ++ } else { ++ p += snprintf(p, (endp - p), "\\x%02X", c); ++ } ++ } ++ *p = '\0'; ++ ASSERT(p < endp); ++ ++ return (int)(p - buf); ++} ++#endif ++ ++#endif /* BCMDRIVER */ ++ ++/* ++ * ProcessVars:Takes a buffer of "=\n" lines read from a file and ending in a NUL. ++ * also accepts nvram files which are already in the format of =\0\=\0 ++ * Removes carriage returns, empty lines, comment lines, and converts newlines to NULs. ++ * Shortens buffer as needed and pads with NULs. End of buffer is marked by two NULs. ++*/ ++ ++unsigned int ++process_nvram_vars(char *varbuf, unsigned int len) ++{ ++ char *dp; ++ bool findNewline; ++ int column; ++ unsigned int buf_len, n; ++ unsigned int pad = 0; ++ ++ dp = varbuf; ++ ++ findNewline = FALSE; ++ column = 0; ++ ++ for (n = 0; n < len; n++) { ++ if (varbuf[n] == '\r') ++ continue; ++ if (findNewline && varbuf[n] != '\n') ++ continue; ++ findNewline = FALSE; ++ if (varbuf[n] == '#') { ++ findNewline = TRUE; ++ continue; ++ } ++ if (varbuf[n] == '\n') { ++ if (column == 0) ++ continue; ++ *dp++ = 0; ++ column = 0; ++ continue; ++ } ++ *dp++ = varbuf[n]; ++ column++; ++ } ++ buf_len = (unsigned int)(dp - varbuf); ++ if (buf_len % 4) { ++ pad = 4 - buf_len % 4; ++ if (pad && (buf_len + pad <= len)) { ++ buf_len += pad; ++ } ++ } ++ ++ while (dp < varbuf + n) ++ *dp++ = 0; ++ ++ return buf_len; ++} +diff --git a/drivers/net/wireless/bcmdhd/bcmwifi_channels.c b/drivers/net/wireless/bcmdhd/bcmwifi_channels.c +new file mode 100644 +index 00000000..6b5b0a3a +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/bcmwifi_channels.c +@@ -0,0 +1,1179 @@ ++/* ++ * Misc utility routines used by kernel or app-level. ++ * Contents are wifi-specific, used by any kernel or app-level ++ * software that might want wifi things as it grows. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * $Id: bcmwifi_channels.c 309193 2012-01-19 00:03:57Z $ ++ */ ++ ++#include ++#include ++ ++#ifdef BCMDRIVER ++#include ++#include ++#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) ++#define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c)) ++#else ++#include ++#include ++#include ++#ifndef ASSERT ++#define ASSERT(exp) ++#endif ++#endif /* BCMDRIVER */ ++ ++#ifdef _bcmwifi_c_ ++/* temporary for transitional compatibility */ ++#include ++#else ++#include ++#endif ++ ++#if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL)) ++#include /* For wl/exe/GNUmakefile.brcm_wlu and GNUmakefile.wlm_dll */ ++#endif ++ ++#ifndef D11AC_IOTYPES ++ ++/* Definitions for legacy Chanspec type */ ++ ++/* Chanspec ASCII representation: ++ * ++ * digit [AB] [N] [UL] ++ * ++ * : channel number of the 10MHz or 20MHz channel, ++ * or control sideband channel of 40MHz channel. ++ * : A for 5GHz, B for 2.4GHz ++ * : N for 10MHz, nothing for 20MHz or 40MHz ++ * (ctl-sideband spec implies 40MHz) ++ * : U for upper, L for lower ++ * ++ * may be omitted on input, and will be assumed to be ++ * 2.4GHz if channel number <= 14. ++ * ++ * Examples: ++ * 8 -> 2.4GHz channel 8, 20MHz ++ * 8b -> 2.4GHz channel 8, 20MHz ++ * 8l -> 2.4GHz channel 8, 40MHz, lower ctl sideband ++ * 8a -> 5GHz channel 8 (low 5 GHz band), 20MHz ++ * 36 -> 5GHz channel 36, 20MHz ++ * 36l -> 5GHz channel 36, 40MHz, lower ctl sideband ++ * 40u -> 5GHz channel 40, 40MHz, upper ctl sideband ++ * 180n -> channel 180, 10MHz ++ */ ++ ++ ++/* given a chanspec and a string buffer, format the chanspec as a ++ * string, and return the original pointer a. ++ * Min buffer length must be CHANSPEC_STR_LEN. ++ * On error return NULL ++ */ ++char * ++wf_chspec_ntoa(chanspec_t chspec, char *buf) ++{ ++ const char *band, *bw, *sb; ++ uint channel; ++ ++ band = ""; ++ bw = ""; ++ sb = ""; ++ channel = CHSPEC_CHANNEL(chspec); ++ /* check for non-default band spec */ ++ if ((CHSPEC_IS2G(chspec) && channel > CH_MAX_2G_CHANNEL) || ++ (CHSPEC_IS5G(chspec) && channel <= CH_MAX_2G_CHANNEL)) ++ band = (CHSPEC_IS2G(chspec)) ? "b" : "a"; ++ if (CHSPEC_IS40(chspec)) { ++ if (CHSPEC_SB_UPPER(chspec)) { ++ sb = "u"; ++ channel += CH_10MHZ_APART; ++ } else { ++ sb = "l"; ++ channel -= CH_10MHZ_APART; ++ } ++ } else if (CHSPEC_IS10(chspec)) { ++ bw = "n"; ++ } ++ ++ /* Outputs a max of 6 chars including '\0' */ ++ snprintf(buf, 6, "%d%s%s%s", channel, band, bw, sb); ++ return (buf); ++} ++ ++/* given a chanspec string, convert to a chanspec. ++ * On error return 0 ++ */ ++chanspec_t ++wf_chspec_aton(const char *a) ++{ ++ char *endp = NULL; ++ uint channel, band, bw, ctl_sb; ++ char c; ++ ++ channel = strtoul(a, &endp, 10); ++ ++ /* check for no digits parsed */ ++ if (endp == a) ++ return 0; ++ ++ if (channel > MAXCHANNEL) ++ return 0; ++ ++ band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); ++ bw = WL_CHANSPEC_BW_20; ++ ctl_sb = WL_CHANSPEC_CTL_SB_NONE; ++ ++ a = endp; ++ ++ c = tolower(a[0]); ++ if (c == '\0') ++ goto done; ++ ++ /* parse the optional ['A' | 'B'] band spec */ ++ if (c == 'a' || c == 'b') { ++ band = (c == 'a') ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G; ++ a++; ++ c = tolower(a[0]); ++ if (c == '\0') ++ goto done; ++ } ++ ++ /* parse bandwidth 'N' (10MHz) or 40MHz ctl sideband ['L' | 'U'] */ ++ if (c == 'n') { ++ bw = WL_CHANSPEC_BW_10; ++ } else if (c == 'l') { ++ bw = WL_CHANSPEC_BW_40; ++ ctl_sb = WL_CHANSPEC_CTL_SB_LOWER; ++ /* adjust channel to center of 40MHz band */ ++ if (channel <= (MAXCHANNEL - CH_20MHZ_APART)) ++ channel += CH_10MHZ_APART; ++ else ++ return 0; ++ } else if (c == 'u') { ++ bw = WL_CHANSPEC_BW_40; ++ ctl_sb = WL_CHANSPEC_CTL_SB_UPPER; ++ /* adjust channel to center of 40MHz band */ ++ if (channel > CH_20MHZ_APART) ++ channel -= CH_10MHZ_APART; ++ else ++ return 0; ++ } else { ++ return 0; ++ } ++ ++done: ++ return (channel | band | bw | ctl_sb); ++} ++ ++/* ++ * Verify the chanspec is using a legal set of parameters, i.e. that the ++ * chanspec specified a band, bw, ctl_sb and channel and that the ++ * combination could be legal given any set of circumstances. ++ * RETURNS: TRUE is the chanspec is malformed, false if it looks good. ++ */ ++bool ++wf_chspec_malformed(chanspec_t chanspec) ++{ ++ /* must be 2G or 5G band */ ++ if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec)) ++ return TRUE; ++ /* must be 20 or 40 bandwidth */ ++ if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec)) ++ return TRUE; ++ ++ /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */ ++ if (CHSPEC_IS20(chanspec)) { ++ if (!CHSPEC_SB_NONE(chanspec)) ++ return TRUE; ++ } else { ++ if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++/* ++ * This function returns the channel number that control traffic is being sent on, for legacy ++ * channels this is just the channel number, for 40MHZ channels it is the upper or lower 20MHZ ++ * sideband depending on the chanspec selected ++ */ ++uint8 ++wf_chspec_ctlchan(chanspec_t chspec) ++{ ++ uint8 ctl_chan; ++ ++ /* Is there a sideband ? */ ++ if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) { ++ return CHSPEC_CHANNEL(chspec); ++ } else { ++ /* we only support 40MHZ with sidebands */ ++ ASSERT(CHSPEC_BW(chspec) == WL_CHANSPEC_BW_40); ++ /* chanspec channel holds the centre frequency, use that and the ++ * side band information to reconstruct the control channel number ++ */ ++ if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) { ++ /* control chan is the upper 20 MHZ SB of the 40MHZ channel */ ++ ctl_chan = UPPER_20_SB(CHSPEC_CHANNEL(chspec)); ++ } else { ++ ASSERT(CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_LOWER); ++ /* control chan is the lower 20 MHZ SB of the 40MHZ channel */ ++ ctl_chan = LOWER_20_SB(CHSPEC_CHANNEL(chspec)); ++ } ++ } ++ ++ return ctl_chan; ++} ++ ++chanspec_t ++wf_chspec_ctlchspec(chanspec_t chspec) ++{ ++ chanspec_t ctl_chspec = 0; ++ uint8 channel; ++ ++ ASSERT(!wf_chspec_malformed(chspec)); ++ ++ /* Is there a sideband ? */ ++ if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) { ++ return chspec; ++ } else { ++ if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) { ++ channel = UPPER_20_SB(CHSPEC_CHANNEL(chspec)); ++ } else { ++ channel = LOWER_20_SB(CHSPEC_CHANNEL(chspec)); ++ } ++ ctl_chspec = channel | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE; ++ ctl_chspec |= CHSPEC_BAND(chspec); ++ } ++ return ctl_chspec; ++} ++ ++#else /* D11AC_IOTYPES */ ++ ++/* Definitions for D11AC capable Chanspec type */ ++ ++/* Chanspec ASCII representation with 802.11ac capability: ++ * [ 'g'] ['/' []['/'<1st80channel>'-'<2nd80channel>]] ++ * ++ * : ++ * (optional) 2, 3, 4, 5 for 2.4GHz, 3GHz, 4GHz, and 5GHz respectively. ++ * Default value is 2g if channel <= 14, otherwise 5g. ++ * : ++ * channel number of the 5MHz, 10MHz, 20MHz channel, ++ * or primary channel of 40MHz, 80MHz, 160MHz, or 80+80MHz channel. ++ * : ++ * (optional) 5, 10, 20, 40, 80, 160, or 80+80. Default value is 20. ++ * : ++ * (only for 2.4GHz band 40MHz) U for upper sideband primary, L for lower. ++ * ++ * For 2.4GHz band 40MHz channels, the same primary channel may be the ++ * upper sideband for one 40MHz channel, and the lower sideband for an ++ * overlapping 40MHz channel. The U/L disambiguates which 40MHz channel ++ * is being specified. ++ * ++ * For 40MHz in the 5GHz band and all channel bandwidths greater than ++ * 40MHz, the U/L specificaion is not allowed since the channels are ++ * non-overlapping and the primary sub-band is derived from its ++ * position in the wide bandwidth channel. ++ * ++ * <1st80Channel>: ++ * <2nd80Channel>: ++ * Required for 80+80, otherwise not allowed. ++ * Specifies the center channel of the first and second 80MHz band. ++ * ++ * In its simplest form, it is a 20MHz channel number, with the implied band ++ * of 2.4GHz if channel number <= 14, and 5GHz otherwise. ++ * ++ * To allow for backward compatibility with scripts, the old form for ++ * 40MHz channels is also allowed: ++ * ++ * : ++ * primary channel of 40MHz, channel <= 14 is 2GHz, otherwise 5GHz ++ * : ++ * "U" for upper, "L" for lower (or lower case "u" "l") ++ * ++ * 5 GHz Examples: ++ * Chanspec BW Center Ch Channel Range Primary Ch ++ * 5g8 20MHz 8 - - ++ * 52 20MHz 52 - - ++ * 52/40 40MHz 54 52-56 52 ++ * 56/40 40MHz 54 52-56 56 ++ * 52/80 80MHz 58 52-64 52 ++ * 56/80 80MHz 58 52-64 56 ++ * 60/80 80MHz 58 52-64 60 ++ * 64/80 80MHz 58 52-64 64 ++ * 52/160 160MHz 50 36-64 52 ++ * 36/160 160MGz 50 36-64 36 ++ * 36/80+80/42-106 80+80MHz 42,106 36-48,100-112 36 ++ * ++ * 2 GHz Examples: ++ * Chanspec BW Center Ch Channel Range Primary Ch ++ * 2g8 20MHz 8 - - ++ * 8 20MHz 8 - - ++ * 6 20MHz 6 - - ++ * 6/40l 40MHz 8 6-10 6 ++ * 6l 40MHz 8 6-10 6 ++ * 6/40u 40MHz 4 2-6 6 ++ * 6u 40MHz 4 2-6 6 ++ */ ++ ++/* bandwidth ASCII string */ ++static const char *wf_chspec_bw_str[] = ++{ ++ "5", ++ "10", ++ "20", ++ "40", ++ "80", ++ "160", ++ "80+80", ++ "na" ++}; ++ ++static const uint8 wf_chspec_bw_mhz[] = ++{5, 10, 20, 40, 80, 160, 160}; ++ ++#define WF_NUM_BW \ ++ (sizeof(wf_chspec_bw_mhz)/sizeof(uint8)) ++ ++/* 40MHz channels in 5GHz band */ ++static const uint8 wf_5g_40m_chans[] = ++{38, 46, 54, 62, 102, 110, 118, 126, 134, 142, 151, 159}; ++#define WF_NUM_5G_40M_CHANS \ ++ (sizeof(wf_5g_40m_chans)/sizeof(uint8)) ++ ++/* 80MHz channels in 5GHz band */ ++static const uint8 wf_5g_80m_chans[] = ++{42, 58, 106, 122, 138, 155}; ++#define WF_NUM_5G_80M_CHANS \ ++ (sizeof(wf_5g_80m_chans)/sizeof(uint8)) ++ ++/* 160MHz channels in 5GHz band */ ++static const uint8 wf_5g_160m_chans[] = ++{50, 114}; ++#define WF_NUM_5G_160M_CHANS \ ++ (sizeof(wf_5g_160m_chans)/sizeof(uint8)) ++ ++ ++/* convert bandwidth from chanspec to MHz */ ++static uint ++bw_chspec_to_mhz(chanspec_t chspec) ++{ ++ uint bw; ++ ++ bw = (chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT; ++ return (bw >= WF_NUM_BW ? 0 : wf_chspec_bw_mhz[bw]); ++} ++ ++/* bw in MHz, return the channel count from the center channel to the ++ * the channel at the edge of the band ++ */ ++static uint8 ++center_chan_to_edge(uint bw) ++{ ++ /* edge channels separated by BW - 10MHz on each side ++ * delta from cf to edge is half of that, ++ * MHz to channel num conversion is 5MHz/channel ++ */ ++ return (uint8)(((bw - 20) / 2) / 5); ++} ++ ++/* return channel number of the low edge of the band ++ * given the center channel and BW ++ */ ++static uint8 ++channel_low_edge(uint center_ch, uint bw) ++{ ++ return (uint8)(center_ch - center_chan_to_edge(bw)); ++} ++ ++/* return side band number given center channel and control channel ++ * return -1 on error ++ */ ++static int ++channel_to_sb(uint center_ch, uint ctl_ch, uint bw) ++{ ++ uint lowest = channel_low_edge(center_ch, bw); ++ uint sb; ++ ++ if ((ctl_ch - lowest) % 4) { ++ /* bad ctl channel, not mult 4 */ ++ return -1; ++ } ++ ++ sb = ((ctl_ch - lowest) / 4); ++ ++ /* sb must be a index to a 20MHz channel in range */ ++ if (sb >= (bw / 20)) { ++ /* ctl_ch must have been too high for the center_ch */ ++ return -1; ++ } ++ ++ return sb; ++} ++ ++/* return control channel given center channel and side band */ ++static uint8 ++channel_to_ctl_chan(uint center_ch, uint bw, uint sb) ++{ ++ return (uint8)(channel_low_edge(center_ch, bw) + sb * 4); ++} ++ ++/* return index of 80MHz channel from channel number ++ * return -1 on error ++ */ ++static int ++channel_80mhz_to_id(uint ch) ++{ ++ uint i; ++ for (i = 0; i < WF_NUM_5G_80M_CHANS; i ++) { ++ if (ch == wf_5g_80m_chans[i]) ++ return i; ++ } ++ ++ return -1; ++} ++ ++/* given a chanspec and a string buffer, format the chanspec as a ++ * string, and return the original pointer a. ++ * Min buffer length must be CHANSPEC_STR_LEN. ++ * On error return NULL ++ */ ++char * ++wf_chspec_ntoa(chanspec_t chspec, char *buf) ++{ ++ const char *band; ++ uint ctl_chan; ++ ++ if (wf_chspec_malformed(chspec)) ++ return NULL; ++ ++ band = ""; ++ ++ /* check for non-default band spec */ ++ if ((CHSPEC_IS2G(chspec) && CHSPEC_CHANNEL(chspec) > CH_MAX_2G_CHANNEL) || ++ (CHSPEC_IS5G(chspec) && CHSPEC_CHANNEL(chspec) <= CH_MAX_2G_CHANNEL)) ++ band = (CHSPEC_IS2G(chspec)) ? "2g" : "5g"; ++ ++ /* ctl channel */ ++ ctl_chan = wf_chspec_ctlchan(chspec); ++ ++ /* bandwidth and ctl sideband */ ++ if (CHSPEC_IS20(chspec)) { ++ snprintf(buf, CHANSPEC_STR_LEN, "%s%d", band, ctl_chan); ++ } else if (!CHSPEC_IS8080(chspec)) { ++ const char *bw; ++ const char *sb = ""; ++ ++ bw = wf_chspec_bw_str[(chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT]; ++ ++#ifdef CHANSPEC_NEW_40MHZ_FORMAT ++ /* ctl sideband string if needed for 2g 40MHz */ ++ if (CHSPEC_IS40(chspec) && CHSPEC_IS2G(chspec)) { ++ sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l"; ++ } ++ ++ snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s%s", band, ctl_chan, bw, sb); ++#else ++ /* ctl sideband string instead of BW for 40MHz */ ++ if (CHSPEC_IS40(chspec)) { ++ sb = CHSPEC_SB_UPPER(chspec) ? "u" : "l"; ++ snprintf(buf, CHANSPEC_STR_LEN, "%s%d%s", band, ctl_chan, sb); ++ } else { ++ snprintf(buf, CHANSPEC_STR_LEN, "%s%d/%s", band, ctl_chan, bw); ++ } ++#endif /* CHANSPEC_NEW_40MHZ_FORMAT */ ++ ++ } else { ++ /* 80+80 */ ++ uint chan1 = (chspec & WL_CHANSPEC_CHAN1_MASK) >> WL_CHANSPEC_CHAN1_SHIFT; ++ uint chan2 = (chspec & WL_CHANSPEC_CHAN2_MASK) >> WL_CHANSPEC_CHAN2_SHIFT; ++ ++ /* convert to channel number */ ++ chan1 = (chan1 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan1] : 0; ++ chan2 = (chan2 < WF_NUM_5G_80M_CHANS) ? wf_5g_80m_chans[chan2] : 0; ++ ++ /* Outputs a max of CHANSPEC_STR_LEN chars including '\0' */ ++ snprintf(buf, CHANSPEC_STR_LEN, "%d/80+80/%d-%d", ctl_chan, chan1, chan2); ++ } ++ ++ return (buf); ++} ++ ++static int ++read_uint(const char **p, unsigned int *num) ++{ ++ unsigned long val; ++ char *endp = NULL; ++ ++ val = strtoul(*p, &endp, 10); ++ /* if endp is the initial pointer value, then a number was not read */ ++ if (endp == *p) ++ return 0; ++ ++ /* advance the buffer pointer to the end of the integer string */ ++ *p = endp; ++ /* return the parsed integer */ ++ *num = (unsigned int)val; ++ ++ return 1; ++} ++ ++/* given a chanspec string, convert to a chanspec. ++ * On error return 0 ++ */ ++chanspec_t ++wf_chspec_aton(const char *a) ++{ ++ chanspec_t chspec; ++ uint chspec_ch, chspec_band, bw, chspec_bw, chspec_sb; ++ uint num, ctl_ch; ++ uint ch1, ch2; ++ char c, sb_ul = '\0'; ++ int i; ++ ++ bw = 20; ++ chspec_sb = 0; ++ chspec_ch = ch1 = ch2 = 0; ++ ++ /* parse channel num or band */ ++ if (!read_uint(&a, &num)) ++ return 0; ++ ++ /* if we are looking at a 'g', then the first number was a band */ ++ c = tolower(a[0]); ++ if (c == 'g') { ++ a ++; /* consume the char */ ++ ++ /* band must be "2" or "5" */ ++ if (num == 2) ++ chspec_band = WL_CHANSPEC_BAND_2G; ++ else if (num == 5) ++ chspec_band = WL_CHANSPEC_BAND_5G; ++ else ++ return 0; ++ ++ /* read the channel number */ ++ if (!read_uint(&a, &ctl_ch)) ++ return 0; ++ ++ c = tolower(a[0]); ++ } ++ else { ++ /* first number is channel, use default for band */ ++ ctl_ch = num; ++ chspec_band = ((ctl_ch <= CH_MAX_2G_CHANNEL) ? ++ WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); ++ } ++ ++ if (c == '\0') { ++ /* default BW of 20MHz */ ++ chspec_bw = WL_CHANSPEC_BW_20; ++ goto done_read; ++ } ++ ++ a ++; /* consume the 'u','l', or '/' */ ++ ++ /* check 'u'/'l' */ ++ if (c == 'u' || c == 'l') { ++ sb_ul = c; ++ chspec_bw = WL_CHANSPEC_BW_40; ++ goto done_read; ++ } ++ ++ /* next letter must be '/' */ ++ if (c != '/') ++ return 0; ++ ++ /* read bandwidth */ ++ if (!read_uint(&a, &bw)) ++ return 0; ++ ++ /* convert to chspec value */ ++ if (bw == 20) { ++ chspec_bw = WL_CHANSPEC_BW_20; ++ } else if (bw == 40) { ++ chspec_bw = WL_CHANSPEC_BW_40; ++ } else if (bw == 80) { ++ chspec_bw = WL_CHANSPEC_BW_80; ++ } else if (bw == 160) { ++ chspec_bw = WL_CHANSPEC_BW_160; ++ } else { ++ return 0; ++ } ++ ++ /* So far we have g/ ++ * Can now be followed by u/l if bw = 40, ++ * or '+80' if bw = 80, to make '80+80' bw. ++ */ ++ ++ c = tolower(a[0]); ++ ++ /* if we have a 2g/40 channel, we should have a l/u spec now */ ++ if (chspec_band == WL_CHANSPEC_BAND_2G && bw == 40) { ++ if (c == 'u' || c == 'l') { ++ a ++; /* consume the u/l char */ ++ sb_ul = c; ++ goto done_read; ++ } ++ } ++ ++ /* check for 80+80 */ ++ if (c == '+') { ++ /* 80+80 */ ++ static const char *plus80 = "80/"; ++ ++ /* must be looking at '+80/' ++ * check and consume this string. ++ */ ++ chspec_bw = WL_CHANSPEC_BW_8080; ++ ++ a ++; /* consume the char '+' */ ++ ++ /* consume the '80/' string */ ++ for (i = 0; i < 3; i++) { ++ if (*a++ != *plus80++) { ++ return 0; ++ } ++ } ++ ++ /* read primary 80MHz channel */ ++ if (!read_uint(&a, &ch1)) ++ return 0; ++ ++ /* must followed by '-' */ ++ if (a[0] != '-') ++ return 0; ++ a ++; /* consume the char */ ++ ++ /* read secondary 80MHz channel */ ++ if (!read_uint(&a, &ch2)) ++ return 0; ++ } ++ ++done_read: ++ /* skip trailing white space */ ++ while (a[0] == ' ') { ++ a ++; ++ } ++ ++ /* must be end of string */ ++ if (a[0] != '\0') ++ return 0; ++ ++ /* Now have all the chanspec string parts read; ++ * chspec_band, ctl_ch, chspec_bw, sb_ul, ch1, ch2. ++ * chspec_band and chspec_bw are chanspec values. ++ * Need to convert ctl_ch, sb_ul, and ch1,ch2 into ++ * a center channel (or two) and sideband. ++ */ ++ ++ /* if a sb u/l string was given, just use that, ++ * guaranteed to be bw = 40 by sting parse. ++ */ ++ if (sb_ul != '\0') { ++ if (sb_ul == 'l') { ++ chspec_ch = UPPER_20_SB(ctl_ch); ++ chspec_sb = WL_CHANSPEC_CTL_SB_LLL; ++ } else if (sb_ul == 'u') { ++ chspec_ch = LOWER_20_SB(ctl_ch); ++ chspec_sb = WL_CHANSPEC_CTL_SB_LLU; ++ } ++ } ++ /* if the bw is 20, center and sideband are trivial */ ++ else if (chspec_bw == WL_CHANSPEC_BW_20) { ++ chspec_ch = ctl_ch; ++ chspec_sb = 0; ++ } ++ /* if the bw is 40/80/160, not 80+80, a single method ++ * can be used to to find the center and sideband ++ */ ++ else if (chspec_bw != WL_CHANSPEC_BW_8080) { ++ /* figure out ctl sideband based on ctl channel and bandwidth */ ++ const uint8 *center_ch = NULL; ++ int num_ch = 0; ++ int sb = -1; ++ ++ if (chspec_bw == WL_CHANSPEC_BW_40) { ++ center_ch = wf_5g_40m_chans; ++ num_ch = WF_NUM_5G_40M_CHANS; ++ } else if (chspec_bw == WL_CHANSPEC_BW_80) { ++ center_ch = wf_5g_80m_chans; ++ num_ch = WF_NUM_5G_80M_CHANS; ++ } else if (chspec_bw == WL_CHANSPEC_BW_160) { ++ center_ch = wf_5g_160m_chans; ++ num_ch = WF_NUM_5G_160M_CHANS; ++ } else { ++ return 0; ++ } ++ ++ for (i = 0; i < num_ch; i ++) { ++ sb = channel_to_sb(center_ch[i], ctl_ch, bw); ++ if (sb >= 0) { ++ chspec_ch = center_ch[i]; ++ chspec_sb = sb << WL_CHANSPEC_CTL_SB_SHIFT; ++ break; ++ } ++ } ++ ++ /* check for no matching sb/center */ ++ if (sb < 0) { ++ return 0; ++ } ++ } ++ /* Otherwise, bw is 80+80. Figure out channel pair and sb */ ++ else { ++ int ch1_id = 0, ch2_id = 0; ++ int sb; ++ ++ ch1_id = channel_80mhz_to_id(ch1); ++ ch2_id = channel_80mhz_to_id(ch2); ++ ++ /* validate channels */ ++ if (ch1 >= ch2 || ch1_id < 0 || ch2_id < 0) ++ return 0; ++ ++ /* combined channel in chspec */ ++ chspec_ch = (((uint16)ch1_id << WL_CHANSPEC_CHAN1_SHIFT) | ++ ((uint16)ch2_id << WL_CHANSPEC_CHAN2_SHIFT)); ++ ++ /* figure out ctl sideband */ ++ ++ /* does the primary channel fit with the 1st 80MHz channel ? */ ++ sb = channel_to_sb(ch1, ctl_ch, bw); ++ if (sb < 0) { ++ /* no, so does the primary channel fit with the 2nd 80MHz channel ? */ ++ sb = channel_to_sb(ch2, ctl_ch, bw); ++ if (sb < 0) { ++ /* no match for ctl_ch to either 80MHz center channel */ ++ return 0; ++ } ++ /* sb index is 0-3 for the low 80MHz channel, and 4-7 for ++ * the high 80MHz channel. Add 4 to to shift to high set. ++ */ ++ sb += 4; ++ } ++ ++ chspec_sb = sb << WL_CHANSPEC_CTL_SB_SHIFT; ++ } ++ ++ chspec = (chspec_ch | chspec_band | chspec_bw | chspec_sb); ++ ++ if (wf_chspec_malformed(chspec)) ++ return 0; ++ ++ return chspec; ++} ++ ++/* ++ * Verify the chanspec is using a legal set of parameters, i.e. that the ++ * chanspec specified a band, bw, ctl_sb and channel and that the ++ * combination could be legal given any set of circumstances. ++ * RETURNS: TRUE is the chanspec is malformed, false if it looks good. ++ */ ++bool ++wf_chspec_malformed(chanspec_t chanspec) ++{ ++ uint chspec_bw = CHSPEC_BW(chanspec); ++ uint chspec_ch = CHSPEC_CHANNEL(chanspec); ++ ++ /* must be 2G or 5G band */ ++ if (CHSPEC_IS2G(chanspec)) { ++ /* must be valid bandwidth */ ++ if (chspec_bw != WL_CHANSPEC_BW_20 && ++ chspec_bw != WL_CHANSPEC_BW_40) { ++ return TRUE; ++ } ++ } else if (CHSPEC_IS5G(chanspec)) { ++ if (chspec_bw == WL_CHANSPEC_BW_8080) { ++ uint ch1_id, ch2_id; ++ ++ /* channel number in 80+80 must be in range */ ++ ch1_id = CHSPEC_CHAN1(chanspec); ++ ch2_id = CHSPEC_CHAN2(chanspec); ++ if (ch1_id >= WF_NUM_5G_80M_CHANS || ch2_id >= WF_NUM_5G_80M_CHANS) ++ return TRUE; ++ ++ /* ch2 must be above ch1 for the chanspec */ ++ if (ch2_id <= ch1_id) ++ return TRUE; ++ } else if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40 || ++ chspec_bw == WL_CHANSPEC_BW_80 || chspec_bw == WL_CHANSPEC_BW_160) { ++ ++ if (chspec_ch > MAXCHANNEL) { ++ return TRUE; ++ } ++ } else { ++ /* invalid bandwidth */ ++ return TRUE; ++ } ++ } else { ++ /* must be 2G or 5G band */ ++ return TRUE; ++ } ++ ++ /* side band needs to be consistent with bandwidth */ ++ if (chspec_bw == WL_CHANSPEC_BW_20) { ++ if (CHSPEC_CTL_SB(chanspec) != WL_CHANSPEC_CTL_SB_LLL) ++ return TRUE; ++ } else if (chspec_bw == WL_CHANSPEC_BW_40) { ++ if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LLU) ++ return TRUE; ++ } else if (chspec_bw == WL_CHANSPEC_BW_80) { ++ if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LUU) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++/* ++ * Verify the chanspec specifies a valid channel according to 802.11. ++ * RETURNS: TRUE if the chanspec is a valid 802.11 channel ++ */ ++bool ++wf_chspec_valid(chanspec_t chanspec) ++{ ++ uint chspec_bw = CHSPEC_BW(chanspec); ++ uint chspec_ch = CHSPEC_CHANNEL(chanspec); ++ ++ if (wf_chspec_malformed(chanspec)) ++ return FALSE; ++ ++ if (CHSPEC_IS2G(chanspec)) { ++ /* must be valid bandwidth and channel range */ ++ if (chspec_bw == WL_CHANSPEC_BW_20) { ++ if (chspec_ch >= 1 && chspec_ch <= 14) ++ return TRUE; ++ } else if (chspec_bw == WL_CHANSPEC_BW_40) { ++ if (chspec_ch >= 3 && chspec_ch <= 11) ++ return TRUE; ++ } ++ } else if (CHSPEC_IS5G(chanspec)) { ++ if (chspec_bw == WL_CHANSPEC_BW_8080) { ++ uint16 ch1, ch2; ++ ++ ch1 = wf_5g_80m_chans[CHSPEC_CHAN1(chanspec)]; ++ ch2 = wf_5g_80m_chans[CHSPEC_CHAN2(chanspec)]; ++ ++ /* the two channels must be separated by more than 80MHz by VHT req, ++ * and ch2 above ch1 for the chanspec ++ */ ++ if (ch2 > ch1 + CH_80MHZ_APART) ++ return TRUE; ++ } else { ++ const uint8 *center_ch; ++ uint num_ch, i; ++ ++ if (chspec_bw == WL_CHANSPEC_BW_20 || chspec_bw == WL_CHANSPEC_BW_40) { ++ center_ch = wf_5g_40m_chans; ++ num_ch = WF_NUM_5G_40M_CHANS; ++ } else if (chspec_bw == WL_CHANSPEC_BW_80) { ++ center_ch = wf_5g_80m_chans; ++ num_ch = WF_NUM_5G_80M_CHANS; ++ } else if (chspec_bw == WL_CHANSPEC_BW_160) { ++ center_ch = wf_5g_160m_chans; ++ num_ch = WF_NUM_5G_160M_CHANS; ++ } else { ++ /* invalid bandwidth */ ++ return FALSE; ++ } ++ ++ /* check for a valid center channel */ ++ if (chspec_bw == WL_CHANSPEC_BW_20) { ++ /* We don't have an array of legal 20MHz 5G channels, but they are ++ * each side of the legal 40MHz channels. Check the chanspec ++ * channel against either side of the 40MHz channels. ++ */ ++ for (i = 0; i < num_ch; i ++) { ++ if (chspec_ch == (uint)LOWER_20_SB(center_ch[i]) || ++ chspec_ch == (uint)UPPER_20_SB(center_ch[i])) ++ break; /* match found */ ++ } ++ ++ if (i == num_ch) { ++ /* check for legacy JP channels on failure */ ++ if (chspec_ch == 34 || chspec_ch == 38 || ++ chspec_ch == 42 || chspec_ch == 46) ++ i = 0; ++ } ++ } else { ++ /* check the chanspec channel to each legal channel */ ++ for (i = 0; i < num_ch; i ++) { ++ if (chspec_ch == center_ch[i]) ++ break; /* match found */ ++ } ++ } ++ ++ if (i < num_ch) { ++ /* match found */ ++ return TRUE; ++ } ++ } ++ } ++ ++ return FALSE; ++} ++ ++/* ++ * This function returns the channel number that control traffic is being sent on, for 20MHz ++ * channels this is just the channel number, for 40MHZ, 80MHz, 160MHz channels it is the 20MHZ ++ * sideband depending on the chanspec selected ++ */ ++uint8 ++wf_chspec_ctlchan(chanspec_t chspec) ++{ ++ uint center_chan; ++ uint bw_mhz; ++ uint sb; ++ ++ ASSERT(!wf_chspec_malformed(chspec)); ++ ++ /* Is there a sideband ? */ ++ if (CHSPEC_IS20(chspec)) { ++ return CHSPEC_CHANNEL(chspec); ++ } else { ++ sb = CHSPEC_CTL_SB(chspec) >> WL_CHANSPEC_CTL_SB_SHIFT; ++ ++ if (CHSPEC_IS8080(chspec)) { ++ bw_mhz = 80; ++ ++ if (sb < 4) { ++ center_chan = CHSPEC_CHAN1(chspec); ++ } ++ else { ++ center_chan = CHSPEC_CHAN2(chspec); ++ sb -= 4; ++ } ++ ++ /* convert from channel index to channel number */ ++ center_chan = wf_5g_80m_chans[center_chan]; ++ } ++ else { ++ bw_mhz = bw_chspec_to_mhz(chspec); ++ center_chan = CHSPEC_CHANNEL(chspec) >> WL_CHANSPEC_CHAN_SHIFT; ++ } ++ ++ return (channel_to_ctl_chan(center_chan, bw_mhz, sb)); ++ } ++} ++ ++/* ++ * This function returns the chanspec of the control channel of a given chanspec ++ */ ++chanspec_t ++wf_chspec_ctlchspec(chanspec_t chspec) ++{ ++ chanspec_t ctl_chspec = chspec; ++ uint8 ctl_chan; ++ ++ ASSERT(!wf_chspec_malformed(chspec)); ++ ++ /* Is there a sideband ? */ ++ if (!CHSPEC_IS20(chspec)) { ++ ctl_chan = wf_chspec_ctlchan(chspec); ++ ctl_chspec = ctl_chan | WL_CHANSPEC_BW_20; ++ ctl_chspec |= CHSPEC_BAND(chspec); ++ } ++ return ctl_chspec; ++} ++ ++/* return chanspec given control channel and bandwidth ++ * return 0 on error ++ */ ++uint16 ++wf_channel2chspec(uint ctl_ch, uint bw) ++{ ++ uint16 chspec; ++ const uint8 *center_ch = NULL; ++ int num_ch = 0; ++ int sb = -1; ++ int i = 0; ++ ++ chspec = ((ctl_ch <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); ++ ++ chspec |= bw; ++ ++ if (bw == WL_CHANSPEC_BW_40) { ++ center_ch = wf_5g_40m_chans; ++ num_ch = WF_NUM_5G_40M_CHANS; ++ bw = 40; ++ } else if (bw == WL_CHANSPEC_BW_80) { ++ center_ch = wf_5g_80m_chans; ++ num_ch = WF_NUM_5G_80M_CHANS; ++ bw = 80; ++ } else if (bw == WL_CHANSPEC_BW_160) { ++ center_ch = wf_5g_160m_chans; ++ num_ch = WF_NUM_5G_160M_CHANS; ++ bw = 160; ++ } else if (bw == WL_CHANSPEC_BW_20) { ++ chspec |= ctl_ch; ++ return chspec; ++ } else { ++ return 0; ++ } ++ ++ for (i = 0; i < num_ch; i ++) { ++ sb = channel_to_sb(center_ch[i], ctl_ch, bw); ++ if (sb >= 0) { ++ chspec |= center_ch[i]; ++ chspec |= (sb << WL_CHANSPEC_CTL_SB_SHIFT); ++ break; ++ } ++ } ++ ++ /* check for no matching sb/center */ ++ if (sb < 0) { ++ return 0; ++ } ++ ++ return chspec; ++} ++#endif /* D11AC_IOTYPES */ ++ ++/* ++ * This function returns the chanspec for the primary 40MHz of an 80MHz channel. ++ * The control sideband specifies the same 20MHz channel that the 80MHz channel is using ++ * as the primary 20MHz channel. ++ */ ++extern chanspec_t wf_chspec_primary40_chspec(chanspec_t chspec) ++{ ++ chanspec_t chspec40 = chspec; ++ uint center_chan; ++ uint sb; ++ ++ ASSERT(!wf_chspec_malformed(chspec)); ++ ++ if (CHSPEC_IS80(chspec)) { ++ center_chan = CHSPEC_CHANNEL(chspec); ++ sb = CHSPEC_CTL_SB(chspec); ++ ++ if (sb == WL_CHANSPEC_CTL_SB_UL) { ++ /* Primary 40MHz is on upper side */ ++ sb = WL_CHANSPEC_CTL_SB_L; ++ center_chan += CH_20MHZ_APART; ++ } else if (sb == WL_CHANSPEC_CTL_SB_UU) { ++ /* Primary 40MHz is on upper side */ ++ sb = WL_CHANSPEC_CTL_SB_U; ++ center_chan += CH_20MHZ_APART; ++ } else { ++ /* Primary 40MHz is on lower side */ ++ /* sideband bits are the same for LL/LU and L/U */ ++ center_chan -= CH_20MHZ_APART; ++ } ++ ++ /* Create primary 40MHz chanspec */ ++ chspec40 = (WL_CHANSPEC_BAND_5G | WL_CHANSPEC_BW_40 | ++ sb | center_chan); ++ } ++ ++ return chspec40; ++} ++ ++/* ++ * Return the channel number for a given frequency and base frequency. ++ * The returned channel number is relative to the given base frequency. ++ * If the given base frequency is zero, a base frequency of 5 GHz is assumed for ++ * frequencies from 5 - 6 GHz, and 2.407 GHz is assumed for 2.4 - 2.5 GHz. ++ * ++ * Frequency is specified in MHz. ++ * The base frequency is specified as (start_factor * 500 kHz). ++ * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G are defined for ++ * 2.4 GHz and 5 GHz bands. ++ * ++ * The returned channel will be in the range [1, 14] in the 2.4 GHz band ++ * and [0, 200] otherwise. ++ * -1 is returned if the start_factor is WF_CHAN_FACTOR_2_4_G and the ++ * frequency is not a 2.4 GHz channel, or if the frequency is not and even ++ * multiple of 5 MHz from the base frequency to the base plus 1 GHz. ++ * ++ * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2 ++ */ ++int ++wf_mhz2channel(uint freq, uint start_factor) ++{ ++ int ch = -1; ++ uint base; ++ int offset; ++ ++ /* take the default channel start frequency */ ++ if (start_factor == 0) { ++ if (freq >= 2400 && freq <= 2500) ++ start_factor = WF_CHAN_FACTOR_2_4_G; ++ else if (freq >= 5000 && freq <= 6000) ++ start_factor = WF_CHAN_FACTOR_5_G; ++ } ++ ++ if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G) ++ return 14; ++ ++ base = start_factor / 2; ++ ++ /* check that the frequency is in 1GHz range of the base */ ++ if ((freq < base) || (freq > base + 1000)) ++ return -1; ++ ++ offset = freq - base; ++ ch = offset / 5; ++ ++ /* check that frequency is a 5MHz multiple from the base */ ++ if (offset != (ch * 5)) ++ return -1; ++ ++ /* restricted channel range check for 2.4G */ ++ if (start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 13)) ++ return -1; ++ ++ return ch; ++} ++ ++/* ++ * Return the center frequency in MHz of the given channel and base frequency. ++ * The channel number is interpreted relative to the given base frequency. ++ * ++ * The valid channel range is [1, 14] in the 2.4 GHz band and [0, 200] otherwise. ++ * The base frequency is specified as (start_factor * 500 kHz). ++ * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_4_G, and WF_CHAN_FACTOR_5_G ++ * are defined for 2.4 GHz, 4 GHz, and 5 GHz bands. ++ * The channel range of [1, 14] is only checked for a start_factor of ++ * WF_CHAN_FACTOR_2_4_G (4814 = 2407 * 2). ++ * Odd start_factors produce channels on .5 MHz boundaries, in which case ++ * the answer is rounded down to an integral MHz. ++ * -1 is returned for an out of range channel. ++ * ++ * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2 ++ */ ++int ++wf_channel2mhz(uint ch, uint start_factor) ++{ ++ int freq; ++ ++ if ((start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 14)) || ++ (ch > 200)) ++ freq = -1; ++ else if ((start_factor == WF_CHAN_FACTOR_2_4_G) && (ch == 14)) ++ freq = 2484; ++ else ++ freq = ch * 5 + start_factor / 2; ++ ++ return freq; ++} +diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h +new file mode 100644 +index 00000000..84866162 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd.h +@@ -0,0 +1,875 @@ ++/* ++ * Header file describing the internal (inter-module) DHD interfaces. ++ * ++ * Provides type definitions and function prototypes used to link the ++ * DHD OS, bus, and protocol modules. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhd.h 373887 2012-12-10 21:58:02Z $ ++ */ ++ ++/**************** ++ * Common types * ++ */ ++ ++#ifndef _dhd_h_ ++#define _dhd_h_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_HAS_WAKELOCK) ++#include ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined (CONFIG_HAS_WAKELOCK) */ ++/* The kernel threading is sdio-specific */ ++struct task_struct; ++struct sched_param; ++int setScheduler(struct task_struct *p, int policy, struct sched_param *param); ++ ++#define ALL_INTERFACES 0xff ++ ++#include ++#include ++ ++ ++/* Forward decls */ ++struct dhd_bus; ++struct dhd_prot; ++struct dhd_info; ++ ++/* The level of bus communication with the dongle */ ++enum dhd_bus_state { ++ DHD_BUS_DOWN, /* Not ready for frame transfers */ ++ DHD_BUS_LOAD, /* Download access only (CPU reset) */ ++ DHD_BUS_DATA /* Ready for frame transfers */ ++}; ++ ++enum dhd_op_flags { ++/* Firmware requested operation mode */ ++ DHD_FLAG_STA_MODE = BIT(0), /* STA only */ ++ DHD_FLAG_HOSTAP_MODE = BIT(1), /* SOFTAP only */ ++ DHD_FLAG_P2P_MODE = BIT(2), /* P2P Only */ ++ /* STA + P2P */ ++ DHD_FLAG_CONCURR_SINGLE_CHAN_MODE = (DHD_FLAG_STA_MODE | DHD_FLAG_P2P_MODE), ++ DHD_FLAG_CONCURR_MULTI_CHAN_MODE = BIT(4), /* STA + P2P */ ++ /* Current P2P mode for P2P connection */ ++ DHD_FLAG_P2P_GC_MODE = BIT(5), ++ DHD_FLAG_P2P_GO_MODE = BIT(6), ++ DHD_FLAG_MBSS_MODE = BIT(7) /* MBSS in future */ ++}; ++ ++#define MANUFACTRING_FW "WLTEST" ++ ++/* max sequential rxcntl timeouts to set HANG event */ ++#ifndef MAX_CNTL_TIMEOUT ++#define MAX_CNTL_TIMEOUT 2 ++#endif ++ ++#define DHD_SCAN_ASSOC_ACTIVE_TIME 40 /* ms: Embedded default Active setting from DHD */ ++#define DHD_SCAN_UNASSOC_ACTIVE_TIME 80 /* ms: Embedded def. Unassoc Active setting from DHD */ ++#define DHD_SCAN_PASSIVE_TIME 130 /* ms: Embedded default Passive setting from DHD */ ++ ++#ifndef POWERUP_MAX_RETRY ++#define POWERUP_MAX_RETRY 3 /* how many times we retry to power up the chip */ ++#endif ++#ifndef POWERUP_WAIT_MS ++#define POWERUP_WAIT_MS 2000 /* ms: time out in waiting wifi to come up */ ++#endif ++ ++enum dhd_bus_wake_state { ++ WAKE_LOCK_OFF, ++ WAKE_LOCK_PRIV, ++ WAKE_LOCK_DPC, ++ WAKE_LOCK_IOCTL, ++ WAKE_LOCK_DOWNLOAD, ++ WAKE_LOCK_TMOUT, ++ WAKE_LOCK_WATCHDOG, ++ WAKE_LOCK_LINK_DOWN_TMOUT, ++ WAKE_LOCK_PNO_FIND_TMOUT, ++ WAKE_LOCK_SOFTAP_SET, ++ WAKE_LOCK_SOFTAP_STOP, ++ WAKE_LOCK_SOFTAP_START, ++ WAKE_LOCK_SOFTAP_THREAD, ++ WAKE_LOCK_MAX ++}; ++ ++enum dhd_prealloc_index { ++ DHD_PREALLOC_PROT = 0, ++ DHD_PREALLOC_RXBUF, ++ DHD_PREALLOC_DATABUF, ++#if defined(STATIC_WL_PRIV_STRUCT) ++ DHD_PREALLOC_OSL_BUF, ++ DHD_PREALLOC_WIPHY_ESCAN0 = 5, ++#else ++ DHD_PREALLOC_OSL_BUF ++#endif /* STATIC_WL_PRIV_STRUCT */ ++}; ++ ++typedef enum { ++ DHD_IF_NONE = 0, ++ DHD_IF_ADD, ++ DHD_IF_DEL, ++ DHD_IF_CHANGE, ++ DHD_IF_DELETING ++} dhd_if_state_t; ++ ++ ++#if defined(CONFIG_DHD_USE_STATIC_BUF) ++ ++uint8* dhd_os_prealloc(void *osh, int section, uint size); ++void dhd_os_prefree(void *osh, void *addr, uint size); ++#define DHD_OS_PREALLOC(osh, section, size) dhd_os_prealloc(osh, section, size) ++#define DHD_OS_PREFREE(osh, addr, size) dhd_os_prefree(osh, addr, size) ++ ++#else ++ ++#define DHD_OS_PREALLOC(osh, section, size) MALLOC(osh, size) ++#define DHD_OS_PREFREE(osh, addr, size) MFREE(osh, addr, size) ++ ++#endif /* defined(CONFIG_DHD_USE_STATIC_BUF) */ ++ ++/* Packet alignment for most efficient SDIO (can change based on platform) */ ++#ifndef DHD_SDALIGN ++#define DHD_SDALIGN 32 ++#endif ++ ++/* host reordering packts logic */ ++/* followed the structure to hold the reorder buffers (void **p) */ ++typedef struct reorder_info { ++ void **p; ++ uint8 flow_id; ++ uint8 cur_idx; ++ uint8 exp_idx; ++ uint8 max_idx; ++ uint8 pend_pkts; ++} reorder_info_t; ++ ++/* Common structure for module and instance linkage */ ++typedef struct dhd_pub { ++ /* Linkage ponters */ ++ osl_t *osh; /* OSL handle */ ++ struct dhd_bus *bus; /* Bus module handle */ ++ struct dhd_prot *prot; /* Protocol module handle */ ++ struct dhd_info *info; /* Info module handle */ ++ ++ /* Internal dhd items */ ++ bool up; /* Driver up/down (to OS) */ ++ bool txoff; /* Transmit flow-controlled */ ++ bool dongle_reset; /* TRUE = DEVRESET put dongle into reset */ ++ enum dhd_bus_state busstate; ++ uint hdrlen; /* Total DHD header length (proto + bus) */ ++ uint maxctl; /* Max size rxctl request from proto to bus */ ++ uint rxsz; /* Rx buffer size bus module should use */ ++ uint8 wme_dp; /* wme discard priority */ ++ ++ /* Dongle media info */ ++ bool iswl; /* Dongle-resident driver is wl */ ++ ulong drv_version; /* Version of dongle-resident driver */ ++ struct ether_addr mac; /* MAC address obtained from dongle */ ++ dngl_stats_t dstats; /* Stats for dongle-based data */ ++ ++ /* Additional stats for the bus level */ ++ ulong tx_packets; /* Data packets sent to dongle */ ++ ulong tx_multicast; /* Multicast data packets sent to dongle */ ++ ulong tx_errors; /* Errors in sending data to dongle */ ++ ulong tx_ctlpkts; /* Control packets sent to dongle */ ++ ulong tx_ctlerrs; /* Errors sending control frames to dongle */ ++ ulong rx_packets; /* Packets sent up the network interface */ ++ ulong rx_multicast; /* Multicast packets sent up the network interface */ ++ ulong rx_errors; /* Errors processing rx data packets */ ++ ulong rx_ctlpkts; /* Control frames processed from dongle */ ++ ulong rx_ctlerrs; /* Errors in processing rx control frames */ ++ ulong rx_dropped; /* Packets dropped locally (no memory) */ ++ ulong rx_flushed; /* Packets flushed due to unscheduled sendup thread */ ++ ulong wd_dpc_sched; /* Number of times dhd dpc scheduled by watchdog timer */ ++ ++ ulong rx_readahead_cnt; /* Number of packets where header read-ahead was used. */ ++ ulong tx_realloc; /* Number of tx packets we had to realloc for headroom */ ++ ulong fc_packets; /* Number of flow control pkts recvd */ ++ ++ /* Last error return */ ++ int bcmerror; ++ uint tickcnt; ++ ++ /* Last error from dongle */ ++ int dongle_error; ++ ++ uint8 country_code[WLC_CNTRY_BUF_SZ]; ++ ++ /* Suspend disable flag and "in suspend" flag */ ++ int suspend_disable_flag; /* "1" to disable all extra powersaving during suspend */ ++ int in_suspend; /* flag set to 1 when early suspend called */ ++#ifdef PNO_SUPPORT ++ int pno_enable; /* pno status : "1" is pno enable */ ++ int pno_suspend; /* pno suspend status : "1" is pno suspended */ ++#endif /* PNO_SUPPORT */ ++ /* DTIM skip value, default 0(or 1) means wake each DTIM ++ * 3 means skip 2 DTIMs and wake up 3rd DTIM(9th beacon when AP DTIM is 3) ++ */ ++ int suspend_bcn_li_dtim; /* bcn_li_dtim value in suspend mode */ ++#ifdef PKT_FILTER_SUPPORT ++ int early_suspended; /* Early suspend status */ ++ int dhcp_in_progress; /* DHCP period */ ++#endif ++ ++ /* Pkt filter defination */ ++ char * pktfilter[100]; ++ int pktfilter_count; ++ ++ wl_country_t dhd_cspec; /* Current Locale info */ ++ char eventmask[WL_EVENTING_MASK_LEN]; ++ int op_mode; /* STA, HostAPD, WFD, SoftAP */ ++ ++/* Set this to 1 to use a seperate interface (p2p0) for p2p operations. ++ * For ICS MR1 releases it should be disable to be compatable with ICS MR1 Framework ++ * see target dhd-cdc-sdmmc-panda-cfg80211-icsmr1-gpl-debug in Makefile ++ */ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 ++ struct mutex wl_start_stop_lock; /* lock/unlock for Android start/stop */ ++ struct mutex wl_softap_lock; /* lock/unlock for any SoftAP/STA settings */ ++#endif ++ ++#ifdef WLBTAMP ++ uint16 maxdatablks; ++#endif /* WLBTAMP */ ++#ifdef PROP_TXSTATUS ++ int wlfc_enabled; ++ void* wlfc_state; ++#endif ++ bool dongle_isolation; ++ bool dongle_trap_occured; /* flag for sending HANG event to upper layer */ ++ int hang_was_sent; ++ int rxcnt_timeout; /* counter rxcnt timeout to send HANG */ ++ int txcnt_timeout; /* counter txcnt timeout to send HANG */ ++#ifdef WLMEDIA_HTSF ++ uint8 htsfdlystat_sz; /* Size of delay stats, max 255B */ ++#endif ++ struct reorder_info *reorder_bufs[WLHOST_REORDERDATA_MAXFLOWS]; ++#if defined(ARP_OFFLOAD_SUPPORT) ++ uint32 arp_version; ++#endif ++} dhd_pub_t; ++ ++ ++ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) ++ ++ #define DHD_PM_RESUME_WAIT_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a); ++ #define _DHD_PM_RESUME_WAIT(a, b) do {\ ++ int retry = 0; \ ++ SMP_RD_BARRIER_DEPENDS(); \ ++ while (dhd_mmc_suspend && retry++ != b) { \ ++ SMP_RD_BARRIER_DEPENDS(); \ ++ wait_event_interruptible_timeout(a, !dhd_mmc_suspend, 1); \ ++ } \ ++ } while (0) ++ #define DHD_PM_RESUME_WAIT(a) _DHD_PM_RESUME_WAIT(a, 200) ++ #define DHD_PM_RESUME_WAIT_FOREVER(a) _DHD_PM_RESUME_WAIT(a, ~0) ++ #define DHD_PM_RESUME_RETURN_ERROR(a) do { if (dhd_mmc_suspend) return a; } while (0) ++ #define DHD_PM_RESUME_RETURN do { if (dhd_mmc_suspend) return; } while (0) ++ ++ #define DHD_SPINWAIT_SLEEP_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a); ++ #define SPINWAIT_SLEEP(a, exp, us) do { \ ++ uint countdown = (us) + 9999; \ ++ while ((exp) && (countdown >= 10000)) { \ ++ wait_event_interruptible_timeout(a, FALSE, 1); \ ++ countdown -= 10000; \ ++ } \ ++ } while (0) ++ ++ #else ++ ++ #define DHD_PM_RESUME_WAIT_INIT(a) ++ #define DHD_PM_RESUME_WAIT(a) ++ #define DHD_PM_RESUME_WAIT_FOREVER(a) ++ #define DHD_PM_RESUME_RETURN_ERROR(a) ++ #define DHD_PM_RESUME_RETURN ++ ++ #define DHD_SPINWAIT_SLEEP_INIT(a) ++ #define SPINWAIT_SLEEP(a, exp, us) do { \ ++ uint countdown = (us) + 9; \ ++ while ((exp) && (countdown >= 10)) { \ ++ OSL_DELAY(10); \ ++ countdown -= 10; \ ++ } \ ++ } while (0) ++ ++ #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ ++#ifndef DHDTHREAD ++#undef SPINWAIT_SLEEP ++#define SPINWAIT_SLEEP(a, exp, us) SPINWAIT(exp, us) ++#endif /* DHDTHREAD */ ++#define DHD_IF_VIF 0x01 /* Virtual IF (Hidden from user) */ ++ ++unsigned long dhd_os_spin_lock(dhd_pub_t *pub); ++void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags); ++ ++/* Wakelock Functions */ ++extern int dhd_os_wake_lock(dhd_pub_t *pub); ++extern int dhd_os_wake_unlock(dhd_pub_t *pub); ++extern int dhd_os_wake_lock_timeout(dhd_pub_t *pub); ++extern int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val); ++extern int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val); ++extern int dhd_os_wd_wake_lock(dhd_pub_t *pub); ++extern int dhd_os_wd_wake_unlock(dhd_pub_t *pub); ++ ++inline static void MUTEX_LOCK_SOFTAP_SET_INIT(dhd_pub_t * dhdp) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 ++ mutex_init(&dhdp->wl_softap_lock); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++} ++ ++inline static void MUTEX_LOCK_SOFTAP_SET(dhd_pub_t * dhdp) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 ++ mutex_lock(&dhdp->wl_softap_lock); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++} ++ ++inline static void MUTEX_UNLOCK_SOFTAP_SET(dhd_pub_t * dhdp) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 ++ mutex_unlock(&dhdp->wl_softap_lock); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++} ++ ++#define DHD_OS_WAKE_LOCK(pub) dhd_os_wake_lock(pub) ++#define DHD_OS_WAKE_UNLOCK(pub) dhd_os_wake_unlock(pub) ++#define DHD_OS_WD_WAKE_LOCK(pub) dhd_os_wd_wake_lock(pub) ++#define DHD_OS_WD_WAKE_UNLOCK(pub) dhd_os_wd_wake_unlock(pub) ++#define DHD_OS_WAKE_LOCK_TIMEOUT(pub) dhd_os_wake_lock_timeout(pub) ++#define DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(pub, val) \ ++ dhd_os_wake_lock_rx_timeout_enable(pub, val) ++#define DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(pub, val) \ ++ dhd_os_wake_lock_ctrl_timeout_enable(pub, val) ++#define DHD_PACKET_TIMEOUT_MS 1000 ++#define DHD_EVENT_TIMEOUT_MS 1500 ++ ++/* interface operations (register, remove) should be atomic, use this lock to prevent race ++ * condition among wifi on/off and interface operation functions ++ */ ++void dhd_net_if_lock(struct net_device *dev); ++void dhd_net_if_unlock(struct net_device *dev); ++ ++typedef struct dhd_if_event { ++ uint8 ifidx; ++ uint8 action; ++ uint8 flags; ++ uint8 bssidx; ++ uint8 is_AP; ++} dhd_if_event_t; ++ ++typedef enum dhd_attach_states ++{ ++ DHD_ATTACH_STATE_INIT = 0x0, ++ DHD_ATTACH_STATE_NET_ALLOC = 0x1, ++ DHD_ATTACH_STATE_DHD_ALLOC = 0x2, ++ DHD_ATTACH_STATE_ADD_IF = 0x4, ++ DHD_ATTACH_STATE_PROT_ATTACH = 0x8, ++ DHD_ATTACH_STATE_WL_ATTACH = 0x10, ++ DHD_ATTACH_STATE_THREADS_CREATED = 0x20, ++ DHD_ATTACH_STATE_WAKELOCKS_INIT = 0x40, ++ DHD_ATTACH_STATE_CFG80211 = 0x80, ++ DHD_ATTACH_STATE_EARLYSUSPEND_DONE = 0x100, ++ DHD_ATTACH_STATE_DONE = 0x200 ++} dhd_attach_states_t; ++ ++/* Value -1 means we are unsuccessful in creating the kthread. */ ++#define DHD_PID_KT_INVALID -1 ++/* Value -2 means we are unsuccessful in both creating the kthread and tasklet */ ++#define DHD_PID_KT_TL_INVALID -2 ++ ++/* ++ * Exported from dhd OS modules (dhd_linux/dhd_ndis) ++ */ ++ ++/* To allow osl_attach/detach calls from os-independent modules */ ++osl_t *dhd_osl_attach(void *pdev, uint bustype); ++void dhd_osl_detach(osl_t *osh); ++ ++/* Indication from bus module regarding presence/insertion of dongle. ++ * Return dhd_pub_t pointer, used as handle to OS module in later calls. ++ * Returned structure should have bus and prot pointers filled in. ++ * bus_hdrlen specifies required headroom for bus module header. ++ */ ++extern dhd_pub_t *dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen); ++#if defined(WLP2P) && defined(WL_CFG80211) ++/* To allow attach/detach calls corresponding to p2p0 interface */ ++extern int dhd_attach_p2p(dhd_pub_t *); ++extern int dhd_detach_p2p(dhd_pub_t *); ++#endif /* WLP2P && WL_CFG80211 */ ++extern int dhd_net_attach(dhd_pub_t *dhdp, int idx); ++ ++/* Indication from bus module regarding removal/absence of dongle */ ++extern void dhd_detach(dhd_pub_t *dhdp); ++extern void dhd_free(dhd_pub_t *dhdp); ++ ++/* Indication from bus module to change flow-control state */ ++extern void dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool on); ++ ++extern bool dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, void *pkt, int prec); ++ ++/* Receive frame for delivery to OS. Callee disposes of rxp. */ ++extern void dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *rxp, int numpkt, uint8 chan); ++ ++/* Return pointer to interface name */ ++extern char *dhd_ifname(dhd_pub_t *dhdp, int idx); ++ ++/* Request scheduling of the bus dpc */ ++extern void dhd_sched_dpc(dhd_pub_t *dhdp); ++ ++/* Notify tx completion */ ++extern void dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success); ++ ++/* OS independent layer functions */ ++extern int dhd_os_proto_block(dhd_pub_t * pub); ++extern int dhd_os_proto_unblock(dhd_pub_t * pub); ++extern int dhd_os_ioctl_resp_wait(dhd_pub_t * pub, uint * condition, bool * pending); ++extern int dhd_os_ioctl_resp_wake(dhd_pub_t * pub); ++extern unsigned int dhd_os_get_ioctl_resp_timeout(void); ++extern void dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec); ++extern void * dhd_os_open_image(char * filename); ++extern int dhd_os_get_image_block(char * buf, int len, void * image); ++extern void dhd_os_close_image(void * image); ++extern void dhd_os_wd_timer(void *bus, uint wdtick); ++extern void dhd_os_sdlock(dhd_pub_t * pub); ++extern void dhd_os_sdunlock(dhd_pub_t * pub); ++extern void dhd_os_sdlock_txq(dhd_pub_t * pub); ++extern void dhd_os_sdunlock_txq(dhd_pub_t * pub); ++extern void dhd_os_sdlock_rxq(dhd_pub_t * pub); ++extern void dhd_os_sdunlock_rxq(dhd_pub_t * pub); ++extern void dhd_os_sdlock_sndup_rxq(dhd_pub_t * pub); ++extern void dhd_customer_gpio_wlan_ctrl(int onoff); ++extern int dhd_custom_get_mac_address(unsigned char *buf); ++extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub); ++extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); ++extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); ++extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); ++extern int dhd_os_send_hang_message(dhd_pub_t *dhdp); ++extern int net_os_send_hang_message(struct net_device *dev); ++extern void dhd_set_version_info(dhd_pub_t *pub, char *fw); ++ ++#ifdef PNO_SUPPORT ++extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); ++extern int dhd_pno_clean(dhd_pub_t *dhd); ++extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ++ ushort scan_fr, int pno_repeat, int pno_freq_expo_max); ++extern int dhd_pno_get_status(dhd_pub_t *dhd); ++extern int dhd_dev_pno_reset(struct net_device *dev); ++extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, ++ int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); ++extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled); ++extern int dhd_dev_get_pno_status(struct net_device *dev); ++#endif /* PNO_SUPPORT */ ++ ++#ifdef PKT_FILTER_SUPPORT ++#define DHD_UNICAST_FILTER_NUM 0 ++#define DHD_BROADCAST_FILTER_NUM 1 ++#define DHD_MULTICAST4_FILTER_NUM 2 ++#define DHD_MULTICAST6_FILTER_NUM 3 ++#define DHD_MDNS_FILTER_NUM 4 ++extern int dhd_os_enable_packet_filter(dhd_pub_t *dhdp, int val); ++extern void dhd_enable_packet_filter(int value, dhd_pub_t *dhd); ++extern int net_os_enable_packet_filter(struct net_device *dev, int val); ++extern int net_os_rxfilter_add_remove(struct net_device *dev, int val, int num); ++#endif /* PKT_FILTER_SUPPORT */ ++ ++extern int dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd); ++extern bool dhd_support_sta_mode(dhd_pub_t *dhd); ++ ++#ifdef DHD_DEBUG ++extern int write_to_file(dhd_pub_t *dhd, uint8 *buf, int size); ++#endif /* DHD_DEBUG */ ++#if defined(OOB_INTR_ONLY) ++extern int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr); ++#endif /* defined(OOB_INTR_ONLY) */ ++extern void dhd_os_sdtxlock(dhd_pub_t * pub); ++extern void dhd_os_sdtxunlock(dhd_pub_t * pub); ++ ++typedef struct { ++ uint32 limit; /* Expiration time (usec) */ ++ uint32 increment; /* Current expiration increment (usec) */ ++ uint32 elapsed; /* Current elapsed time (usec) */ ++ uint32 tick; /* O/S tick time (usec) */ ++} dhd_timeout_t; ++ ++extern void dhd_timeout_start(dhd_timeout_t *tmo, uint usec); ++extern int dhd_timeout_expired(dhd_timeout_t *tmo); ++ ++extern int dhd_ifname2idx(struct dhd_info *dhd, char *name); ++extern int dhd_net2idx(struct dhd_info *dhd, struct net_device *net); ++extern struct net_device * dhd_idx2net(void *pub, int ifidx); ++extern int wl_host_event(dhd_pub_t *dhd_pub, int *idx, void *pktdata, ++ wl_event_msg_t *, void **data_ptr); ++extern void wl_event_to_host_order(wl_event_msg_t * evt); ++ ++extern int dhd_wl_ioctl(dhd_pub_t *dhd_pub, int ifindex, wl_ioctl_t *ioc, void *buf, int len); ++extern int dhd_wl_ioctl_cmd(dhd_pub_t *dhd_pub, int cmd, void *arg, int len, uint8 set, ++ int ifindex); ++ ++extern void dhd_common_init(osl_t *osh); ++ ++extern int dhd_do_driver_init(struct net_device *net); ++extern int dhd_add_if(struct dhd_info *dhd, int ifidx, void *handle, ++ char *name, uint8 *mac_addr, uint32 flags, uint8 bssidx); ++extern void dhd_del_if(struct dhd_info *dhd, int ifidx); ++ ++extern void dhd_vif_add(struct dhd_info *dhd, int ifidx, char * name); ++extern void dhd_vif_del(struct dhd_info *dhd, int ifidx); ++ ++extern void dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx); ++extern void dhd_vif_sendup(struct dhd_info *dhd, int ifidx, uchar *cp, int len); ++ ++ ++/* Send packet to dongle via data channel */ ++extern int dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pkt); ++ ++/* send up locally generated event */ ++extern void dhd_sendup_event_common(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data); ++/* Send event to host */ ++extern void dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data); ++extern int dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag); ++extern uint dhd_bus_status(dhd_pub_t *dhdp); ++extern int dhd_bus_start(dhd_pub_t *dhdp); ++extern int dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *data, uint size); ++extern void dhd_print_buf(void *pbuf, int len, int bytes_per_line); ++extern bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval); ++extern uint dhd_bus_chip_id(dhd_pub_t *dhdp); ++extern uint dhd_bus_chiprev_id(dhd_pub_t *dhdp); ++extern uint dhd_bus_chippkg_id(dhd_pub_t *dhdp); ++ ++#if defined(KEEP_ALIVE) ++extern int dhd_keep_alive_onoff(dhd_pub_t *dhd); ++#endif /* KEEP_ALIVE */ ++ ++extern bool dhd_is_concurrent_mode(dhd_pub_t *dhd); ++ ++typedef enum cust_gpio_modes { ++ WLAN_RESET_ON, ++ WLAN_RESET_OFF, ++ WLAN_POWER_ON, ++ WLAN_POWER_OFF ++} cust_gpio_modes_t; ++ ++extern int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag); ++extern int wl_iw_send_priv_event(struct net_device *dev, char *flag); ++/* ++ * Insmod parameters for debug/test ++ */ ++ ++/* Watchdog timer interval */ ++extern uint dhd_watchdog_ms; ++ ++#if defined(DHD_DEBUG) ++/* Console output poll interval */ ++extern uint dhd_console_ms; ++extern uint wl_msg_level; ++#endif /* defined(DHD_DEBUG) */ ++ ++extern uint dhd_slpauto; ++ ++/* Use interrupts */ ++extern uint dhd_intr; ++ ++/* Use polling */ ++extern uint dhd_poll; ++ ++/* ARP offload agent mode */ ++extern uint dhd_arp_mode; ++ ++/* ARP offload enable */ ++extern uint dhd_arp_enable; ++ ++/* Pkt filte enable control */ ++extern uint dhd_pkt_filter_enable; ++ ++/* Pkt filter init setup */ ++extern uint dhd_pkt_filter_init; ++ ++/* Pkt filter mode control */ ++extern uint dhd_master_mode; ++ ++/* Roaming mode control */ ++extern uint dhd_roam_disable; ++ ++/* Roaming mode control */ ++extern uint dhd_radio_up; ++ ++/* Initial idletime ticks (may be -1 for immediate idle, 0 for no idle) */ ++extern int dhd_idletime; ++#ifdef DHD_USE_IDLECOUNT ++#define DHD_IDLETIME_TICKS 5 ++#else ++#define DHD_IDLETIME_TICKS 1 ++#endif /* DHD_USE_IDLECOUNT */ ++ ++/* SDIO Drive Strength */ ++extern uint dhd_sdiod_drive_strength; ++ ++/* Override to force tx queueing all the time */ ++extern uint dhd_force_tx_queueing; ++/* Default KEEP_ALIVE Period is 55 sec to prevent AP from sending Keep Alive probe frame */ ++#define DEFAULT_KEEP_ALIVE_VALUE 55000 /* msec */ ++#ifndef CUSTOM_KEEP_ALIVE_SETTING ++#define CUSTOM_KEEP_ALIVE_SETTING DEFAULT_KEEP_ALIVE_VALUE ++#endif /* DEFAULT_KEEP_ALIVE_VALUE */ ++ ++#define NULL_PKT_STR "null_pkt" ++ ++/* hooks for custom glom setting option via Makefile */ ++#define DEFAULT_GLOM_VALUE -1 ++#ifndef CUSTOM_GLOM_SETTING ++#define CUSTOM_GLOM_SETTING DEFAULT_GLOM_VALUE ++#endif ++ ++/* hooks for custom Roaming Trigger setting via Makefile */ ++#define DEFAULT_ROAM_TRIGGER_VALUE -75 /* dBm default roam trigger all band */ ++#define DEFAULT_ROAM_TRIGGER_SETTING -1 ++#ifndef CUSTOM_ROAM_TRIGGER_SETTING ++#define CUSTOM_ROAM_TRIGGER_SETTING DEFAULT_ROAM_TRIGGER_VALUE ++#endif ++ ++/* hooks for custom Roaming Romaing setting via Makefile */ ++#define DEFAULT_ROAM_DELTA_VALUE 10 /* dBm default roam delta all band */ ++#define DEFAULT_ROAM_DELTA_SETTING -1 ++#ifndef CUSTOM_ROAM_DELTA_SETTING ++#define CUSTOM_ROAM_DELTA_SETTING DEFAULT_ROAM_DELTA_VALUE ++#endif ++ ++/* hooks for custom PNO Event wake lock to guarantee enough time ++ for the Platform to detect Event before system suspended ++*/ ++#define DEFAULT_PNO_EVENT_LOCK_xTIME 2 /* multiplay of DHD_PACKET_TIMEOUT_MS */ ++#ifndef CUSTOM_PNO_EVENT_LOCK_xTIME ++#define CUSTOM_PNO_EVENT_LOCK_xTIME DEFAULT_PNO_EVENT_LOCK_xTIME ++#endif ++ ++/* hooks for custom dhd_dpc_prio setting option via Makefile */ ++#define DEFAULT_DHP_DPC_PRIO 1 ++#ifndef CUSTOM_DPC_PRIO_SETTING ++#define CUSTOM_DPC_PRIO_SETTING DEFAULT_DHP_DPC_PRIO ++#endif ++ ++#define DEFAULT_SUSPEND_BCN_LI_DTIM 3 ++#ifndef CUSTOM_SUSPEND_BCN_LI_DTIM ++#define CUSTOM_SUSPEND_BCN_LI_DTIM DEFAULT_SUSPEND_BCN_LI_DTIM ++#endif ++ ++#define MAX_DTIM_SKIP_BEACON_ITERVAL 100 /* max allowed associated AP beacon for dtim skip */ ++ ++#ifdef SDTEST ++/* Echo packet generator (SDIO), pkts/s */ ++extern uint dhd_pktgen; ++ ++/* Echo packet len (0 => sawtooth, max 1800) */ ++extern uint dhd_pktgen_len; ++#define MAX_PKTGEN_LEN 1800 ++#endif ++ ++ ++/* optionally set by a module_param_string() */ ++#define MOD_PARAM_PATHLEN 2048 ++extern char fw_path[MOD_PARAM_PATHLEN]; ++extern char nv_path[MOD_PARAM_PATHLEN]; ++ ++#define MOD_PARAM_INFOLEN 512 ++ ++#ifdef SOFTAP ++extern char fw_path2[MOD_PARAM_PATHLEN]; ++#endif ++ ++/* Flag to indicate if we should download firmware on driver load */ ++extern uint dhd_download_fw_on_driverload; ++ ++ ++/* For supporting multiple interfaces */ ++#define DHD_MAX_IFS 16 ++#define DHD_DEL_IF -0xe ++#define DHD_BAD_IF -0xf ++#define WL_AUTO_ROAM_TRIGGER -75 ++ ++#ifdef PROP_TXSTATUS ++/* Please be mindful that total pkttag space is 32 octets only */ ++typedef struct dhd_pkttag { ++ /* ++ b[11 ] - 1 = this packet was sent in response to one time packet request, ++ do not increment credit on status for this one. [WLFC_CTL_TYPE_MAC_REQUEST_PACKET]. ++ b[10 ] - 1 = signal-only-packet to firmware [i.e. nothing to piggyback on] ++ b[9 ] - 1 = packet is host->firmware (transmit direction) ++ - 0 = packet received from firmware (firmware->host) ++ b[8 ] - 1 = packet was sent due to credit_request (pspoll), ++ packet does not count against FIFO credit. ++ - 0 = normal transaction, packet counts against FIFO credit ++ b[7 ] - 1 = AP, 0 = STA ++ b[6:4] - AC FIFO number ++ b[3:0] - interface index ++ */ ++ uint16 if_flags; ++ /* destination MAC address for this packet so that not every ++ module needs to open the packet to find this ++ */ ++ uint8 dstn_ether[ETHER_ADDR_LEN]; ++ /* ++ This 32-bit goes from host to device for every packet. ++ */ ++ uint32 htod_tag; ++ /* bus specific stuff */ ++ union { ++ struct { ++ void* stuff; ++ uint32 thing1; ++ uint32 thing2; ++ } sd; ++ struct { ++ void* bus; ++ void* urb; ++ } usb; ++ } bus_specific; ++} dhd_pkttag_t; ++ ++#define DHD_PKTTAG_SET_H2DTAG(tag, h2dvalue) ((dhd_pkttag_t*)(tag))->htod_tag = (h2dvalue) ++#define DHD_PKTTAG_H2DTAG(tag) (((dhd_pkttag_t*)(tag))->htod_tag) ++ ++#define DHD_PKTTAG_IFMASK 0xf ++#define DHD_PKTTAG_IFTYPE_MASK 0x1 ++#define DHD_PKTTAG_IFTYPE_SHIFT 7 ++#define DHD_PKTTAG_FIFO_MASK 0x7 ++#define DHD_PKTTAG_FIFO_SHIFT 4 ++ ++#define DHD_PKTTAG_SIGNALONLY_MASK 0x1 ++#define DHD_PKTTAG_SIGNALONLY_SHIFT 10 ++ ++#define DHD_PKTTAG_ONETIMEPKTRQST_MASK 0x1 ++#define DHD_PKTTAG_ONETIMEPKTRQST_SHIFT 11 ++ ++#define DHD_PKTTAG_PKTDIR_MASK 0x1 ++#define DHD_PKTTAG_PKTDIR_SHIFT 9 ++ ++#define DHD_PKTTAG_CREDITCHECK_MASK 0x1 ++#define DHD_PKTTAG_CREDITCHECK_SHIFT 8 ++ ++#define DHD_PKTTAG_INVALID_FIFOID 0x7 ++ ++#define DHD_PKTTAG_SETFIFO(tag, fifo) ((dhd_pkttag_t*)(tag))->if_flags = \ ++ (((dhd_pkttag_t*)(tag))->if_flags & ~(DHD_PKTTAG_FIFO_MASK << DHD_PKTTAG_FIFO_SHIFT)) | \ ++ (((fifo) & DHD_PKTTAG_FIFO_MASK) << DHD_PKTTAG_FIFO_SHIFT) ++#define DHD_PKTTAG_FIFO(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ ++ DHD_PKTTAG_FIFO_SHIFT) & DHD_PKTTAG_FIFO_MASK) ++ ++#define DHD_PKTTAG_SETIF(tag, if) ((dhd_pkttag_t*)(tag))->if_flags = \ ++ (((dhd_pkttag_t*)(tag))->if_flags & ~DHD_PKTTAG_IFMASK) | ((if) & DHD_PKTTAG_IFMASK) ++#define DHD_PKTTAG_IF(tag) (((dhd_pkttag_t*)(tag))->if_flags & DHD_PKTTAG_IFMASK) ++ ++#define DHD_PKTTAG_SETIFTYPE(tag, isAP) ((dhd_pkttag_t*)(tag))->if_flags = \ ++ (((dhd_pkttag_t*)(tag))->if_flags & \ ++ ~(DHD_PKTTAG_IFTYPE_MASK << DHD_PKTTAG_IFTYPE_SHIFT)) | \ ++ (((isAP) & DHD_PKTTAG_IFTYPE_MASK) << DHD_PKTTAG_IFTYPE_SHIFT) ++#define DHD_PKTTAG_IFTYPE(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ ++ DHD_PKTTAG_IFTYPE_SHIFT) & DHD_PKTTAG_IFTYPE_MASK) ++ ++#define DHD_PKTTAG_SETCREDITCHECK(tag, check) ((dhd_pkttag_t*)(tag))->if_flags = \ ++ (((dhd_pkttag_t*)(tag))->if_flags & \ ++ ~(DHD_PKTTAG_CREDITCHECK_MASK << DHD_PKTTAG_CREDITCHECK_SHIFT)) | \ ++ (((check) & DHD_PKTTAG_CREDITCHECK_MASK) << DHD_PKTTAG_CREDITCHECK_SHIFT) ++#define DHD_PKTTAG_CREDITCHECK(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ ++ DHD_PKTTAG_CREDITCHECK_SHIFT) & DHD_PKTTAG_CREDITCHECK_MASK) ++ ++#define DHD_PKTTAG_SETPKTDIR(tag, dir) ((dhd_pkttag_t*)(tag))->if_flags = \ ++ (((dhd_pkttag_t*)(tag))->if_flags & \ ++ ~(DHD_PKTTAG_PKTDIR_MASK << DHD_PKTTAG_PKTDIR_SHIFT)) | \ ++ (((dir) & DHD_PKTTAG_PKTDIR_MASK) << DHD_PKTTAG_PKTDIR_SHIFT) ++#define DHD_PKTTAG_PKTDIR(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ ++ DHD_PKTTAG_PKTDIR_SHIFT) & DHD_PKTTAG_PKTDIR_MASK) ++ ++#define DHD_PKTTAG_SETSIGNALONLY(tag, signalonly) ((dhd_pkttag_t*)(tag))->if_flags = \ ++ (((dhd_pkttag_t*)(tag))->if_flags & \ ++ ~(DHD_PKTTAG_SIGNALONLY_MASK << DHD_PKTTAG_SIGNALONLY_SHIFT)) | \ ++ (((signalonly) & DHD_PKTTAG_SIGNALONLY_MASK) << DHD_PKTTAG_SIGNALONLY_SHIFT) ++#define DHD_PKTTAG_SIGNALONLY(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ ++ DHD_PKTTAG_SIGNALONLY_SHIFT) & DHD_PKTTAG_SIGNALONLY_MASK) ++ ++#define DHD_PKTTAG_SETONETIMEPKTRQST(tag) ((dhd_pkttag_t*)(tag))->if_flags = \ ++ (((dhd_pkttag_t*)(tag))->if_flags & \ ++ ~(DHD_PKTTAG_ONETIMEPKTRQST_MASK << DHD_PKTTAG_ONETIMEPKTRQST_SHIFT)) | \ ++ (1 << DHD_PKTTAG_ONETIMEPKTRQST_SHIFT) ++#define DHD_PKTTAG_ONETIMEPKTRQST(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ ++ DHD_PKTTAG_ONETIMEPKTRQST_SHIFT) & DHD_PKTTAG_ONETIMEPKTRQST_MASK) ++ ++#define DHD_PKTTAG_SETDSTN(tag, dstn_MAC_ea) memcpy(((dhd_pkttag_t*)((tag)))->dstn_ether, \ ++ (dstn_MAC_ea), ETHER_ADDR_LEN) ++#define DHD_PKTTAG_DSTN(tag) ((dhd_pkttag_t*)(tag))->dstn_ether ++ ++typedef int (*f_commitpkt_t)(void* ctx, void* p); ++ ++#ifdef PROP_TXSTATUS_DEBUG ++#define DHD_WLFC_CTRINC_MAC_CLOSE(entry) do { (entry)->closed_ct++; } while (0) ++#define DHD_WLFC_CTRINC_MAC_OPEN(entry) do { (entry)->opened_ct++; } while (0) ++#else ++#define DHD_WLFC_CTRINC_MAC_CLOSE(entry) do {} while (0) ++#define DHD_WLFC_CTRINC_MAC_OPEN(entry) do {} while (0) ++#endif ++ ++#endif /* PROP_TXSTATUS */ ++ ++extern void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar); ++extern void dhd_wait_event_wakeup(dhd_pub_t*dhd); ++ ++#define IFLOCK_INIT(lock) *lock = 0 ++#define IFLOCK(lock) while (InterlockedCompareExchange((lock), 1, 0)) \ ++ NdisStallExecution(1); ++#define IFUNLOCK(lock) InterlockedExchange((lock), 0) ++#define IFLOCK_FREE(lock) ++ ++#ifdef PNO_SUPPORT ++extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); ++extern int dhd_pnoenable(dhd_pub_t *dhd, int pfn_enabled); ++extern int dhd_pno_clean(dhd_pub_t *dhd); ++extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ++ ushort scan_fr, int pno_repeat, int pno_freq_expo_max); ++extern int dhd_pno_get_status(dhd_pub_t *dhd); ++extern int dhd_pno_set_add(dhd_pub_t *dhd, wl_pfn_t *netinfo, int nssid, ushort scan_fr, ++ ushort slowscan_fr, uint8 pno_repeat, uint8 pno_freq_expo_max, int16 flags); ++extern int dhd_pno_cfg(dhd_pub_t *dhd, wl_pfn_cfg_t *pcfg); ++extern int dhd_pno_suspend(dhd_pub_t *dhd, int pfn_suspend); ++#endif /* PNO_SUPPORT */ ++#ifdef ARP_OFFLOAD_SUPPORT ++#define MAX_IPV4_ENTRIES 8 ++void dhd_arp_offload_set(dhd_pub_t * dhd, int arp_mode); ++void dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable); ++ ++/* dhd_commn arp offload wrapers */ ++void dhd_aoe_hostip_clr(dhd_pub_t *dhd, int idx); ++void dhd_aoe_arp_clr(dhd_pub_t *dhd, int idx); ++int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen, int idx); ++void dhd_arp_offload_add_ip(dhd_pub_t *dhd, uint32 ipaddr, int idx); ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++#endif /* _dhd_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/dhd_bta.c b/drivers/net/wireless/bcmdhd/dhd_bta.c +new file mode 100644 +index 00000000..15c605ea +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_bta.c +@@ -0,0 +1,338 @@ ++/* ++ * BT-AMP support routines ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhd_bta.c 303834 2011-12-20 06:17:39Z $ ++ */ ++#ifndef WLBTAMP ++#error "WLBTAMP is not defined" ++#endif /* WLBTAMP */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++ ++#ifdef SEND_HCI_CMD_VIA_IOCTL ++#define BTA_HCI_CMD_MAX_LEN HCI_CMD_PREAMBLE_SIZE + HCI_CMD_DATA_SIZE ++ ++/* Send HCI cmd via wl iovar HCI_cmd to the dongle. */ ++int ++dhd_bta_docmd(dhd_pub_t *pub, void *cmd_buf, uint cmd_len) ++{ ++ amp_hci_cmd_t *cmd = (amp_hci_cmd_t *)cmd_buf; ++ uint8 buf[BTA_HCI_CMD_MAX_LEN + 16]; ++ uint len = sizeof(buf); ++ wl_ioctl_t ioc; ++ ++ if (cmd_len < HCI_CMD_PREAMBLE_SIZE) ++ return BCME_BADLEN; ++ ++ if ((uint)cmd->plen + HCI_CMD_PREAMBLE_SIZE > cmd_len) ++ return BCME_BADLEN; ++ ++ len = bcm_mkiovar("HCI_cmd", ++ (char *)cmd, (uint)cmd->plen + HCI_CMD_PREAMBLE_SIZE, (char *)buf, len); ++ ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ++ ioc.cmd = WLC_SET_VAR; ++ ioc.buf = buf; ++ ioc.len = len; ++ ioc.set = TRUE; ++ ++ return dhd_wl_ioctl(pub, &ioc, ioc.buf, ioc.len); ++} ++#else /* !SEND_HCI_CMD_VIA_IOCTL */ ++ ++static void ++dhd_bta_flush_hcidata(dhd_pub_t *pub, uint16 llh) ++{ ++ int prec; ++ struct pktq *q; ++ uint count = 0; ++ ++ q = dhd_bus_txq(pub->bus); ++ if (q == NULL) ++ return; ++ ++ DHD_BTA(("dhd: flushing HCI ACL data for logical link %u...\n", llh)); ++ ++ dhd_os_sdlock_txq(pub); ++ ++ /* Walk through the txq and toss all HCI ACL data packets */ ++ PKTQ_PREC_ITER(q, prec) { ++ void *head_pkt = NULL; ++ ++ while (pktq_ppeek(q, prec) != head_pkt) { ++ void *pkt = pktq_pdeq(q, prec); ++ int ifidx; ++ ++ PKTPULL(pub->osh, pkt, dhd_bus_hdrlen(pub->bus)); ++ dhd_prot_hdrpull(pub, &ifidx, pkt, NULL, NULL); ++ ++ if (PKTLEN(pub->osh, pkt) >= RFC1042_HDR_LEN) { ++ struct ether_header *eh = ++ (struct ether_header *)PKTDATA(pub->osh, pkt); ++ ++ if (ntoh16(eh->ether_type) < ETHER_TYPE_MIN) { ++ struct dot11_llc_snap_header *lsh = ++ (struct dot11_llc_snap_header *)&eh[1]; ++ ++ if (bcmp(lsh, BT_SIG_SNAP_MPROT, ++ DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && ++ ntoh16(lsh->type) == BTA_PROT_L2CAP) { ++ amp_hci_ACL_data_t *ACL_data = ++ (amp_hci_ACL_data_t *)&lsh[1]; ++ uint16 handle = ltoh16(ACL_data->handle); ++ ++ if (HCI_ACL_DATA_HANDLE(handle) == llh) { ++ PKTFREE(pub->osh, pkt, TRUE); ++ count ++; ++ continue; ++ } ++ } ++ } ++ } ++ ++ dhd_prot_hdrpush(pub, ifidx, pkt); ++ PKTPUSH(pub->osh, pkt, dhd_bus_hdrlen(pub->bus)); ++ ++ if (head_pkt == NULL) ++ head_pkt = pkt; ++ pktq_penq(q, prec, pkt); ++ } ++ } ++ ++ dhd_os_sdunlock_txq(pub); ++ ++ DHD_BTA(("dhd: flushed %u packet(s) for logical link %u...\n", count, llh)); ++} ++ ++/* Handle HCI cmd locally. ++ * Return 0: continue to send the cmd across SDIO ++ * < 0: stop, fail ++ * > 0: stop, succuess ++ */ ++static int ++_dhd_bta_docmd(dhd_pub_t *pub, amp_hci_cmd_t *cmd) ++{ ++ int status = 0; ++ ++ switch (ltoh16_ua((uint8 *)&cmd->opcode)) { ++ case HCI_Enhanced_Flush: { ++ eflush_cmd_parms_t *cmdparms = (eflush_cmd_parms_t *)cmd->parms; ++ dhd_bta_flush_hcidata(pub, ltoh16_ua(cmdparms->llh)); ++ break; ++ } ++ default: ++ break; ++ } ++ ++ return status; ++} ++ ++/* Send HCI cmd encapsulated in BT-SIG frame via data channel to the dongle. */ ++int ++dhd_bta_docmd(dhd_pub_t *pub, void *cmd_buf, uint cmd_len) ++{ ++ amp_hci_cmd_t *cmd = (amp_hci_cmd_t *)cmd_buf; ++ struct ether_header *eh; ++ struct dot11_llc_snap_header *lsh; ++ osl_t *osh = pub->osh; ++ uint len; ++ void *p; ++ int status; ++ ++ if (cmd_len < HCI_CMD_PREAMBLE_SIZE) { ++ DHD_ERROR(("dhd_bta_docmd: short command, cmd_len %u\n", cmd_len)); ++ return BCME_BADLEN; ++ } ++ ++ if ((len = (uint)cmd->plen + HCI_CMD_PREAMBLE_SIZE) > cmd_len) { ++ DHD_ERROR(("dhd_bta_docmd: malformed command, len %u cmd_len %u\n", ++ len, cmd_len)); ++ /* return BCME_BADLEN; */ ++ } ++ ++ p = PKTGET(osh, pub->hdrlen + RFC1042_HDR_LEN + len, TRUE); ++ if (p == NULL) { ++ DHD_ERROR(("dhd_bta_docmd: out of memory\n")); ++ return BCME_NOMEM; ++ } ++ ++ ++ /* intercept and handle the HCI cmd locally */ ++ if ((status = _dhd_bta_docmd(pub, cmd)) > 0) ++ return 0; ++ else if (status < 0) ++ return status; ++ ++ /* copy in HCI cmd */ ++ PKTPULL(osh, p, pub->hdrlen + RFC1042_HDR_LEN); ++ bcopy(cmd, PKTDATA(osh, p), len); ++ ++ /* copy in partial Ethernet header with BT-SIG LLC/SNAP header */ ++ PKTPUSH(osh, p, RFC1042_HDR_LEN); ++ eh = (struct ether_header *)PKTDATA(osh, p); ++ bzero(eh->ether_dhost, ETHER_ADDR_LEN); ++ ETHER_SET_LOCALADDR(eh->ether_dhost); ++ bcopy(&pub->mac, eh->ether_shost, ETHER_ADDR_LEN); ++ eh->ether_type = hton16(len + DOT11_LLC_SNAP_HDR_LEN); ++ lsh = (struct dot11_llc_snap_header *)&eh[1]; ++ bcopy(BT_SIG_SNAP_MPROT, lsh, DOT11_LLC_SNAP_HDR_LEN - 2); ++ lsh->type = 0; ++ ++ return dhd_sendpkt(pub, 0, p); ++} ++#endif /* !SEND_HCI_CMD_VIA_IOCTL */ ++ ++/* Send HCI ACL data to dongle via data channel */ ++int ++dhd_bta_tx_hcidata(dhd_pub_t *pub, void *data_buf, uint data_len) ++{ ++ amp_hci_ACL_data_t *data = (amp_hci_ACL_data_t *)data_buf; ++ struct ether_header *eh; ++ struct dot11_llc_snap_header *lsh; ++ osl_t *osh = pub->osh; ++ uint len; ++ void *p; ++ ++ if (data_len < HCI_ACL_DATA_PREAMBLE_SIZE) { ++ DHD_ERROR(("dhd_bta_tx_hcidata: short data_buf, data_len %u\n", data_len)); ++ return BCME_BADLEN; ++ } ++ ++ if ((len = (uint)ltoh16(data->dlen) + HCI_ACL_DATA_PREAMBLE_SIZE) > data_len) { ++ DHD_ERROR(("dhd_bta_tx_hcidata: malformed hci data, len %u data_len %u\n", ++ len, data_len)); ++ /* return BCME_BADLEN; */ ++ } ++ ++ p = PKTGET(osh, pub->hdrlen + RFC1042_HDR_LEN + len, TRUE); ++ if (p == NULL) { ++ DHD_ERROR(("dhd_bta_tx_hcidata: out of memory\n")); ++ return BCME_NOMEM; ++ } ++ ++ ++ /* copy in HCI ACL data header and HCI ACL data */ ++ PKTPULL(osh, p, pub->hdrlen + RFC1042_HDR_LEN); ++ bcopy(data, PKTDATA(osh, p), len); ++ ++ /* copy in partial Ethernet header with BT-SIG LLC/SNAP header */ ++ PKTPUSH(osh, p, RFC1042_HDR_LEN); ++ eh = (struct ether_header *)PKTDATA(osh, p); ++ bzero(eh->ether_dhost, ETHER_ADDR_LEN); ++ bcopy(&pub->mac, eh->ether_shost, ETHER_ADDR_LEN); ++ eh->ether_type = hton16(len + DOT11_LLC_SNAP_HDR_LEN); ++ lsh = (struct dot11_llc_snap_header *)&eh[1]; ++ bcopy(BT_SIG_SNAP_MPROT, lsh, DOT11_LLC_SNAP_HDR_LEN - 2); ++ lsh->type = HTON16(BTA_PROT_L2CAP); ++ ++ return dhd_sendpkt(pub, 0, p); ++} ++ ++/* txcomplete callback */ ++void ++dhd_bta_tx_hcidata_complete(dhd_pub_t *dhdp, void *txp, bool success) ++{ ++ uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, txp); ++ amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *)(pktdata + RFC1042_HDR_LEN); ++ uint16 handle = ltoh16(ACL_data->handle); ++ uint16 llh = HCI_ACL_DATA_HANDLE(handle); ++ ++ wl_event_msg_t event; ++ uint8 data[HCI_EVT_PREAMBLE_SIZE + sizeof(num_completed_data_blocks_evt_parms_t)]; ++ amp_hci_event_t *evt; ++ num_completed_data_blocks_evt_parms_t *parms; ++ ++ uint16 len = HCI_EVT_PREAMBLE_SIZE + sizeof(num_completed_data_blocks_evt_parms_t); ++ ++ /* update the event struct */ ++ memset(&event, 0, sizeof(event)); ++ event.version = hton16(BCM_EVENT_MSG_VERSION); ++ event.event_type = hton32(WLC_E_BTA_HCI_EVENT); ++ event.status = 0; ++ event.reason = 0; ++ event.auth_type = 0; ++ event.datalen = hton32(len); ++ event.flags = 0; ++ ++ /* generate Number of Completed Blocks event */ ++ evt = (amp_hci_event_t *)data; ++ evt->ecode = HCI_Number_of_Completed_Data_Blocks; ++ evt->plen = sizeof(num_completed_data_blocks_evt_parms_t); ++ ++ parms = (num_completed_data_blocks_evt_parms_t *)evt->parms; ++ htol16_ua_store(dhdp->maxdatablks, (uint8 *)&parms->num_blocks); ++ parms->num_handles = 1; ++ htol16_ua_store(llh, (uint8 *)&parms->completed[0].handle); ++ parms->completed[0].pkts = 1; ++ parms->completed[0].blocks = 1; ++ ++ dhd_sendup_event_common(dhdp, &event, data); ++} ++ ++/* event callback */ ++void ++dhd_bta_doevt(dhd_pub_t *dhdp, void *data_buf, uint data_len) ++{ ++ amp_hci_event_t *evt = (amp_hci_event_t *)data_buf; ++ ++ switch (evt->ecode) { ++ case HCI_Command_Complete: { ++ cmd_complete_parms_t *parms = (cmd_complete_parms_t *)evt->parms; ++ switch (ltoh16_ua((uint8 *)&parms->opcode)) { ++ case HCI_Read_Data_Block_Size: { ++ read_data_block_size_evt_parms_t *parms2 = ++ (read_data_block_size_evt_parms_t *)parms->parms; ++ dhdp->maxdatablks = ltoh16_ua((uint8 *)&parms2->data_block_num); ++ break; ++ } ++ } ++ break; ++ } ++ ++ case HCI_Flush_Occurred: { ++ flush_occurred_evt_parms_t *evt_parms = (flush_occurred_evt_parms_t *)evt->parms; ++ dhd_bta_flush_hcidata(dhdp, ltoh16_ua((uint8 *)&evt_parms->handle)); ++ break; ++ } ++ default: ++ break; ++ } ++} +diff --git a/drivers/net/wireless/bcmdhd/dhd_bta.h b/drivers/net/wireless/bcmdhd/dhd_bta.h +new file mode 100644 +index 00000000..0337f15d +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_bta.h +@@ -0,0 +1,39 @@ ++/* ++ * BT-AMP support routines ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhd_bta.h 291086 2011-10-21 01:17:24Z $ ++ */ ++#ifndef __dhd_bta_h__ ++#define __dhd_bta_h__ ++ ++struct dhd_pub; ++ ++extern int dhd_bta_docmd(struct dhd_pub *pub, void *cmd_buf, uint cmd_len); ++ ++extern void dhd_bta_doevt(struct dhd_pub *pub, void *data_buf, uint data_len); ++ ++extern int dhd_bta_tx_hcidata(struct dhd_pub *pub, void *data_buf, uint data_len); ++extern void dhd_bta_tx_hcidata_complete(struct dhd_pub *dhdp, void *txp, bool success); ++ ++ ++#endif /* __dhd_bta_h__ */ +diff --git a/drivers/net/wireless/bcmdhd/dhd_bus.h b/drivers/net/wireless/bcmdhd/dhd_bus.h +new file mode 100644 +index 00000000..fcb4bbd6 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_bus.h +@@ -0,0 +1,111 @@ ++/* ++ * Header file describing the internal (inter-module) DHD interfaces. ++ * ++ * Provides type definitions and function prototypes used to link the ++ * DHD OS, bus, and protocol modules. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhd_bus.h 347614 2012-07-27 10:24:51Z $ ++ */ ++ ++#ifndef _dhd_bus_h_ ++#define _dhd_bus_h_ ++ ++/* ++ * Exported from dhd bus module (dhd_usb, dhd_sdio) ++ */ ++ ++/* Indicate (dis)interest in finding dongles. */ ++extern int dhd_bus_register(void); ++extern void dhd_bus_unregister(void); ++ ++/* Download firmware image and nvram image */ ++extern bool dhd_bus_download_firmware(struct dhd_bus *bus, osl_t *osh, ++ char *fw_path, char *nv_path); ++ ++/* Stop bus module: clear pending frames, disable data flow */ ++extern void dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex); ++ ++/* Initialize bus module: prepare for communication w/dongle */ ++extern int dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex); ++ ++/* Get the Bus Idle Time */ ++extern void dhd_bus_getidletime(dhd_pub_t *dhdp, int *idletime); ++ ++/* Set the Bus Idle Time */ ++extern void dhd_bus_setidletime(dhd_pub_t *dhdp, int idle_time); ++ ++/* Send a data frame to the dongle. Callee disposes of txp. */ ++extern int dhd_bus_txdata(struct dhd_bus *bus, void *txp); ++ ++/* Send/receive a control message to/from the dongle. ++ * Expects caller to enforce a single outstanding transaction. ++ */ ++extern int dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen); ++extern int dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen); ++ ++/* Watchdog timer function */ ++extern bool dhd_bus_watchdog(dhd_pub_t *dhd); ++extern void dhd_disable_intr(dhd_pub_t *dhd); ++ ++#if defined(DHD_DEBUG) ++/* Device console input function */ ++extern int dhd_bus_console_in(dhd_pub_t *dhd, uchar *msg, uint msglen); ++#endif /* defined(DHD_DEBUG) */ ++ ++/* Deferred processing for the bus, return TRUE requests reschedule */ ++extern bool dhd_bus_dpc(struct dhd_bus *bus); ++extern void dhd_bus_isr(bool * InterruptRecognized, bool * QueueMiniportHandleInterrupt, void *arg); ++ ++ ++/* Check for and handle local prot-specific iovar commands */ ++extern int dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name, ++ void *params, int plen, void *arg, int len, bool set); ++ ++/* Add bus dump output to a buffer */ ++extern void dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf); ++ ++/* Clear any bus counters */ ++extern void dhd_bus_clearcounts(dhd_pub_t *dhdp); ++ ++/* return the dongle chipid */ ++extern uint dhd_bus_chip(struct dhd_bus *bus); ++ ++/* Set user-specified nvram parameters. */ ++extern void dhd_bus_set_nvram_params(struct dhd_bus * bus, const char *nvram_params); ++ ++extern void *dhd_bus_pub(struct dhd_bus *bus); ++extern void *dhd_bus_txq(struct dhd_bus *bus); ++extern uint dhd_bus_hdrlen(struct dhd_bus *bus); ++ ++ ++#define DHD_SET_BUS_STATE_DOWN(_bus) do { \ ++ (_bus)->dhd->busstate = DHD_BUS_DOWN; \ ++} while (0) ++ ++/* Register a dummy SDIO client driver in order to be notified of new SDIO device */ ++extern int dhd_bus_reg_sdio_notify(void* semaphore); ++extern void dhd_bus_unreg_sdio_notify(void); ++ ++extern void dhd_txglom_enable(dhd_pub_t *dhdp, bool enable); ++ ++#endif /* _dhd_bus_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/dhd_cdc.c b/drivers/net/wireless/bcmdhd/dhd_cdc.c +new file mode 100644 +index 00000000..b51dbc6d +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_cdc.c +@@ -0,0 +1,3189 @@ ++/* ++ * DHD Protocol Module for CDC and BDC. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhd_cdc.c 368762 2012-11-14 21:59:17Z $ ++ * ++ * BDC is like CDC, except it includes a header for data packets to convey ++ * packet priority over the bus, and flags (e.g. to indicate checksum status ++ * for dongle offload.) ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++#ifdef PROP_TXSTATUS ++#include ++#include ++#endif ++ ++ ++#define RETRIES 2 /* # of retries to retrieve matching ioctl response */ ++#define BUS_HEADER_LEN (24+DHD_SDALIGN) /* Must be at least SDPCM_RESERVE ++ * defined in dhd_sdio.c (amount of header tha might be added) ++ * plus any space that might be needed for alignment padding. ++ */ ++#define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for ++ * round off at the end of buffer ++ */ ++ ++#define BUS_RETRIES 1 /* # of retries before aborting a bus tx operation */ ++ ++#ifdef PROP_TXSTATUS ++typedef struct dhd_wlfc_commit_info { ++ uint8 needs_hdr; ++ uint8 ac_fifo_credit_spent; ++ ewlfc_packet_state_t pkt_type; ++ wlfc_mac_descriptor_t* mac_entry; ++ void* p; ++} dhd_wlfc_commit_info_t; ++#endif /* PROP_TXSTATUS */ ++ ++ ++typedef struct dhd_prot { ++ uint16 reqid; ++ uint8 pending; ++ uint32 lastcmd; ++ uint8 bus_header[BUS_HEADER_LEN]; ++ cdc_ioctl_t msg; ++ unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN]; ++} dhd_prot_t; ++ ++ ++static int ++dhdcdc_msg(dhd_pub_t *dhd) ++{ ++ int err = 0; ++ dhd_prot_t *prot = dhd->prot; ++ int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t); ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ DHD_OS_WAKE_LOCK(dhd); ++ ++ /* NOTE : cdc->msg.len holds the desired length of the buffer to be ++ * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area ++ * is actually sent to the dongle ++ */ ++ if (len > CDC_MAX_MSG_SIZE) ++ len = CDC_MAX_MSG_SIZE; ++ ++ /* Send request */ ++ err = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len); ++ ++ DHD_OS_WAKE_UNLOCK(dhd); ++ return err; ++} ++ ++static int ++dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len) ++{ ++ int ret; ++ int cdc_len = len + sizeof(cdc_ioctl_t); ++ dhd_prot_t *prot = dhd->prot; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ do { ++ ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, cdc_len); ++ if (ret < 0) ++ break; ++ } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id); ++ ++ return ret; ++} ++ ++static int ++dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action) ++{ ++ dhd_prot_t *prot = dhd->prot; ++ cdc_ioctl_t *msg = &prot->msg; ++ void *info; ++ int ret = 0, retries = 0; ++ uint32 id, flags = 0; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len)); ++ ++ ++ /* Respond "bcmerror" and "bcmerrorstr" with local cache */ ++ if (cmd == WLC_GET_VAR && buf) ++ { ++ if (!strcmp((char *)buf, "bcmerrorstr")) ++ { ++ strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN); ++ goto done; ++ } ++ else if (!strcmp((char *)buf, "bcmerror")) ++ { ++ *(int *)buf = dhd->dongle_error; ++ goto done; ++ } ++ } ++ ++ memset(msg, 0, sizeof(cdc_ioctl_t)); ++ ++ msg->cmd = htol32(cmd); ++ msg->len = htol32(len); ++ msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT); ++ CDC_SET_IF_IDX(msg, ifidx); ++ /* add additional action bits */ ++ action &= WL_IOCTL_ACTION_MASK; ++ msg->flags |= (action << CDCF_IOC_ACTION_SHIFT); ++ msg->flags = htol32(msg->flags); ++ ++ if (buf) ++ memcpy(prot->buf, buf, len); ++ ++ if ((ret = dhdcdc_msg(dhd)) < 0) { ++ if (!dhd->hang_was_sent) ++ DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret)); ++ goto done; ++ } ++ ++retry: ++ /* wait for interrupt and get first fragment */ ++ if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0) ++ goto done; ++ ++ flags = ltoh32(msg->flags); ++ id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT; ++ ++ if ((id < prot->reqid) && (++retries < RETRIES)) ++ goto retry; ++ if (id != prot->reqid) { ++ DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n", ++ dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid)); ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ /* Check info buffer */ ++ info = (void*)&msg[1]; ++ ++ /* Copy info buffer */ ++ if (buf) ++ { ++ if (ret < (int)len) ++ len = ret; ++ memcpy(buf, info, len); ++ } ++ ++ /* Check the ERROR flag */ ++ if (flags & CDCF_IOC_ERROR) ++ { ++ ret = ltoh32(msg->status); ++ /* Cache error from dongle */ ++ dhd->dongle_error = ret; ++ } ++ ++done: ++ return ret; ++} ++ ++static int ++dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action) ++{ ++ dhd_prot_t *prot = dhd->prot; ++ cdc_ioctl_t *msg = &prot->msg; ++ int ret = 0; ++ uint32 flags, id; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len)); ++ ++ if (dhd->busstate == DHD_BUS_DOWN) { ++ DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); ++ return -EIO; ++ } ++ ++ /* don't talk to the dongle if fw is about to be reloaded */ ++ if (dhd->hang_was_sent) { ++ DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n", ++ __FUNCTION__)); ++ return -EIO; ++ } ++ ++ memset(msg, 0, sizeof(cdc_ioctl_t)); ++ ++ msg->cmd = htol32(cmd); ++ msg->len = htol32(len); ++ msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT); ++ CDC_SET_IF_IDX(msg, ifidx); ++ /* add additional action bits */ ++ action &= WL_IOCTL_ACTION_MASK; ++ msg->flags |= (action << CDCF_IOC_ACTION_SHIFT) | CDCF_IOC_SET; ++ msg->flags = htol32(msg->flags); ++ ++ if (buf) ++ memcpy(prot->buf, buf, len); ++ ++ if ((ret = dhdcdc_msg(dhd)) < 0) { ++ DHD_ERROR(("%s: dhdcdc_msg failed w/status %d\n", __FUNCTION__, ret)); ++ goto done; ++ } ++ ++ if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0) ++ goto done; ++ ++ flags = ltoh32(msg->flags); ++ id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT; ++ ++ if (id != prot->reqid) { ++ DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n", ++ dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid)); ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ /* Check the ERROR flag */ ++ if (flags & CDCF_IOC_ERROR) ++ { ++ ret = ltoh32(msg->status); ++ /* Cache error from dongle */ ++ dhd->dongle_error = ret; ++ } ++ ++done: ++ return ret; ++} ++ ++ ++int ++dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len) ++{ ++ dhd_prot_t *prot = dhd->prot; ++ int ret = -1; ++ uint8 action; ++#if defined(NDIS630) ++ bool acquired = FALSE; ++#endif ++ ++ if ((dhd->busstate == DHD_BUS_DOWN) || dhd->hang_was_sent) { ++ DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); ++ goto done; ++ } ++#if defined(NDIS630) ++ if (dhd_os_proto_block(dhd)) ++ { ++ acquired = TRUE; ++ } ++ else ++ { ++ /* attempt to acquire protocol mutex timed out. */ ++ ret = -1; ++ return ret; ++ } ++#endif /* NDIS630 */ ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ ASSERT(len <= WLC_IOCTL_MAXLEN); ++ ++ if (len > WLC_IOCTL_MAXLEN) ++ goto done; ++ ++ if (prot->pending == TRUE) { ++ DHD_ERROR(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n", ++ ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd, ++ (unsigned long)prot->lastcmd)); ++ if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) { ++ DHD_TRACE(("iovar cmd=%s\n", (char*)buf)); ++ } ++ goto done; ++ } ++ ++ prot->pending = TRUE; ++ prot->lastcmd = ioc->cmd; ++ action = ioc->set; ++ if (action & WL_IOCTL_ACTION_SET) ++ ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len, action); ++ else { ++ ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len, action); ++ if (ret > 0) ++ ioc->used = ret - sizeof(cdc_ioctl_t); ++ } ++ ++ /* Too many programs assume ioctl() returns 0 on success */ ++ if (ret >= 0) ++ ret = 0; ++ else { ++ cdc_ioctl_t *msg = &prot->msg; ++ ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */ ++ } ++ ++ /* Intercept the wme_dp ioctl here */ ++ if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) { ++ int slen, val = 0; ++ ++ slen = strlen("wme_dp") + 1; ++ if (len >= (int)(slen + sizeof(int))) ++ bcopy(((char *)buf + slen), &val, sizeof(int)); ++ dhd->wme_dp = (uint8) ltoh32(val); ++ } ++ ++ prot->pending = FALSE; ++ ++done: ++#if defined(NDIS630) ++ if (acquired) ++ dhd_os_proto_unblock(dhd); ++#endif ++ return ret; ++} ++ ++int ++dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name, ++ void *params, int plen, void *arg, int len, bool set) ++{ ++ return BCME_UNSUPPORTED; ++} ++ ++#ifdef PROP_TXSTATUS ++void ++dhd_wlfc_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) ++{ ++ int i; ++ uint8* ea; ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhdp->wlfc_state; ++ wlfc_hanger_t* h; ++ wlfc_mac_descriptor_t* mac_table; ++ wlfc_mac_descriptor_t* interfaces; ++ char* iftypes[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"}; ++ ++ if (wlfc == NULL) { ++ bcm_bprintf(strbuf, "wlfc not initialized yet\n"); ++ return; ++ } ++ h = (wlfc_hanger_t*)wlfc->hanger; ++ if (h == NULL) { ++ bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n"); ++ } ++ ++ mac_table = wlfc->destination_entries.nodes; ++ interfaces = wlfc->destination_entries.interfaces; ++ bcm_bprintf(strbuf, "---- wlfc stats ----\n"); ++ if (h) { ++ bcm_bprintf(strbuf, "wlfc hanger (pushed,popped,f_push," ++ "f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n", ++ h->pushed, ++ h->popped, ++ h->failed_to_push, ++ h->failed_to_pop, ++ h->failed_slotfind, ++ (h->pushed - h->popped)); ++ } ++ ++ bcm_bprintf(strbuf, "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), " ++ "(dq_full,sendq_full, rollback_fail) = (%d,%d,%d,%d), (%d,%d,%d)\n", ++ wlfc->stats.tlv_parse_failed, ++ wlfc->stats.credit_request_failed, ++ wlfc->stats.mac_update_failed, ++ wlfc->stats.psmode_update_failed, ++ wlfc->stats.delayq_full_error, ++ wlfc->stats.sendq_full_error, ++ wlfc->stats.rollback_failed); ++ ++ bcm_bprintf(strbuf, "SENDQ (len,credit,sent) " ++ "(AC0[%d,%d,%d],AC1[%d,%d,%d],AC2[%d,%d,%d],AC3[%d,%d,%d],BC_MC[%d,%d,%d])\n", ++ wlfc->SENDQ.q[0].len, wlfc->FIFO_credit[0], wlfc->stats.sendq_pkts[0], ++ wlfc->SENDQ.q[1].len, wlfc->FIFO_credit[1], wlfc->stats.sendq_pkts[1], ++ wlfc->SENDQ.q[2].len, wlfc->FIFO_credit[2], wlfc->stats.sendq_pkts[2], ++ wlfc->SENDQ.q[3].len, wlfc->FIFO_credit[3], wlfc->stats.sendq_pkts[3], ++ wlfc->SENDQ.q[4].len, wlfc->FIFO_credit[4], wlfc->stats.sendq_pkts[4]); ++ ++#ifdef PROP_TXSTATUS_DEBUG ++ bcm_bprintf(strbuf, "SENDQ dropped: AC[0-3]:(%d,%d,%d,%d), (bcmc,atim):(%d,%d)\n", ++ wlfc->stats.dropped_qfull[0], wlfc->stats.dropped_qfull[1], ++ wlfc->stats.dropped_qfull[2], wlfc->stats.dropped_qfull[3], ++ wlfc->stats.dropped_qfull[4], wlfc->stats.dropped_qfull[5]); ++#endif ++ ++ bcm_bprintf(strbuf, "\n"); ++ for (i = 0; i < WLFC_MAX_IFNUM; i++) { ++ if (interfaces[i].occupied) { ++ char* iftype_desc; ++ ++ if (interfaces[i].iftype > WLC_E_IF_ROLE_P2P_CLIENT) ++ iftype_desc = "hostif_flow_state[i] == OFF) ++ ? " OFF":" ON")); ++ ++ bcm_bprintf(strbuf, "INTERFACE[%d].DELAYQ(len,state,credit)" ++ "= (%d,%s,%d)\n", ++ i, ++ interfaces[i].psq.len, ++ ((interfaces[i].state == ++ WLFC_STATE_OPEN) ? " OPEN":"CLOSE"), ++ interfaces[i].requested_credit); ++ ++ bcm_bprintf(strbuf, "INTERFACE[%d].DELAYQ" ++ "(sup,ac0),(sup,ac1),(sup,ac2),(sup,ac3) = " ++ "(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n", ++ i, ++ interfaces[i].psq.q[0].len, ++ interfaces[i].psq.q[1].len, ++ interfaces[i].psq.q[2].len, ++ interfaces[i].psq.q[3].len, ++ interfaces[i].psq.q[4].len, ++ interfaces[i].psq.q[5].len, ++ interfaces[i].psq.q[6].len, ++ interfaces[i].psq.q[7].len); ++ } ++ } ++ ++ bcm_bprintf(strbuf, "\n"); ++ for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) { ++ if (mac_table[i].occupied) { ++ ea = mac_table[i].ea; ++ bcm_bprintf(strbuf, "MAC_table[%d].ea = " ++ "[%02x:%02x:%02x:%02x:%02x:%02x], if:%d \n", i, ++ ea[0], ea[1], ea[2], ea[3], ea[4], ea[5], ++ mac_table[i].interface_id); ++ ++ bcm_bprintf(strbuf, "MAC_table[%d].DELAYQ(len,state,credit)" ++ "= (%d,%s,%d)\n", ++ i, ++ mac_table[i].psq.len, ++ ((mac_table[i].state == ++ WLFC_STATE_OPEN) ? " OPEN":"CLOSE"), ++ mac_table[i].requested_credit); ++#ifdef PROP_TXSTATUS_DEBUG ++ bcm_bprintf(strbuf, "MAC_table[%d]: (opened, closed) = (%d, %d)\n", ++ i, mac_table[i].opened_ct, mac_table[i].closed_ct); ++#endif ++ bcm_bprintf(strbuf, "MAC_table[%d].DELAYQ" ++ "(sup,ac0),(sup,ac1),(sup,ac2),(sup,ac3) = " ++ "(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n", ++ i, ++ mac_table[i].psq.q[0].len, ++ mac_table[i].psq.q[1].len, ++ mac_table[i].psq.q[2].len, ++ mac_table[i].psq.q[3].len, ++ mac_table[i].psq.q[4].len, ++ mac_table[i].psq.q[5].len, ++ mac_table[i].psq.q[6].len, ++ mac_table[i].psq.q[7].len); ++ } ++ } ++ ++#ifdef PROP_TXSTATUS_DEBUG ++ { ++ int avg; ++ int moving_avg = 0; ++ int moving_samples; ++ ++ if (wlfc->stats.latency_sample_count) { ++ moving_samples = sizeof(wlfc->stats.deltas)/sizeof(uint32); ++ ++ for (i = 0; i < moving_samples; i++) ++ moving_avg += wlfc->stats.deltas[i]; ++ moving_avg /= moving_samples; ++ ++ avg = (100 * wlfc->stats.total_status_latency) / ++ wlfc->stats.latency_sample_count; ++ bcm_bprintf(strbuf, "txstatus latency (average, last, moving[%d]) = " ++ "(%d.%d, %03d, %03d)\n", ++ moving_samples, avg/100, (avg - (avg/100)*100), ++ wlfc->stats.latency_most_recent, ++ moving_avg); ++ } ++ } ++ ++ bcm_bprintf(strbuf, "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), " ++ "back = (%d,%d,%d,%d,%d,%d)\n", ++ wlfc->stats.fifo_credits_sent[0], ++ wlfc->stats.fifo_credits_sent[1], ++ wlfc->stats.fifo_credits_sent[2], ++ wlfc->stats.fifo_credits_sent[3], ++ wlfc->stats.fifo_credits_sent[4], ++ wlfc->stats.fifo_credits_sent[5], ++ ++ wlfc->stats.fifo_credits_back[0], ++ wlfc->stats.fifo_credits_back[1], ++ wlfc->stats.fifo_credits_back[2], ++ wlfc->stats.fifo_credits_back[3], ++ wlfc->stats.fifo_credits_back[4], ++ wlfc->stats.fifo_credits_back[5]); ++ { ++ uint32 fifo_cr_sent = 0; ++ uint32 fifo_cr_acked = 0; ++ uint32 request_cr_sent = 0; ++ uint32 request_cr_ack = 0; ++ uint32 bc_mc_cr_ack = 0; ++ ++ for (i = 0; i < sizeof(wlfc->stats.fifo_credits_sent)/sizeof(uint32); i++) { ++ fifo_cr_sent += wlfc->stats.fifo_credits_sent[i]; ++ } ++ ++ for (i = 0; i < sizeof(wlfc->stats.fifo_credits_back)/sizeof(uint32); i++) { ++ fifo_cr_acked += wlfc->stats.fifo_credits_back[i]; ++ } ++ ++ for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) { ++ if (wlfc->destination_entries.nodes[i].occupied) { ++ request_cr_sent += ++ wlfc->destination_entries.nodes[i].dstncredit_sent_packets; ++ } ++ } ++ for (i = 0; i < WLFC_MAX_IFNUM; i++) { ++ if (wlfc->destination_entries.interfaces[i].occupied) { ++ request_cr_sent += ++ wlfc->destination_entries.interfaces[i].dstncredit_sent_packets; ++ } ++ } ++ for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) { ++ if (wlfc->destination_entries.nodes[i].occupied) { ++ request_cr_ack += ++ wlfc->destination_entries.nodes[i].dstncredit_acks; ++ } ++ } ++ for (i = 0; i < WLFC_MAX_IFNUM; i++) { ++ if (wlfc->destination_entries.interfaces[i].occupied) { ++ request_cr_ack += ++ wlfc->destination_entries.interfaces[i].dstncredit_acks; ++ } ++ } ++ bcm_bprintf(strbuf, "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d)," ++ "other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)", ++ fifo_cr_sent, fifo_cr_acked, ++ request_cr_sent, request_cr_ack, ++ wlfc->destination_entries.other.dstncredit_acks, ++ bc_mc_cr_ack, ++ wlfc->stats.signal_only_pkts_sent, wlfc->stats.signal_only_pkts_freed); ++ } ++#endif /* PROP_TXSTATUS_DEBUG */ ++ bcm_bprintf(strbuf, "\n"); ++ bcm_bprintf(strbuf, "wlfc- pkt((in,2bus,txstats,hdrpull),(dropped,hdr_only,wlc_tossed)" ++ "(freed,free_err,rollback)) = " ++ "((%d,%d,%d,%d),(%d,%d,%d),(%d,%d,%d))\n", ++ wlfc->stats.pktin, ++ wlfc->stats.pkt2bus, ++ wlfc->stats.txstatus_in, ++ wlfc->stats.dhd_hdrpulls, ++ ++ wlfc->stats.pktdropped, ++ wlfc->stats.wlfc_header_only_pkt, ++ wlfc->stats.wlc_tossed_pkts, ++ ++ wlfc->stats.pkt_freed, ++ wlfc->stats.pkt_free_err, wlfc->stats.rollback); ++ ++ bcm_bprintf(strbuf, "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = " ++ "((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n", ++ ++ wlfc->stats.d11_suppress, ++ wlfc->stats.wl_suppress, ++ wlfc->stats.bad_suppress, ++ ++ wlfc->stats.psq_d11sup_enq, ++ wlfc->stats.psq_wlsup_enq, ++ wlfc->stats.psq_hostq_enq, ++ wlfc->stats.mac_handle_notfound, ++ ++ wlfc->stats.psq_d11sup_retx, ++ wlfc->stats.psq_wlsup_retx, ++ wlfc->stats.psq_hostq_retx); ++ return; ++} ++ ++/* Create a place to store all packet pointers submitted to the firmware until ++ a status comes back, suppress or otherwise. ++ ++ hang-er: noun, a contrivance on which things are hung, as a hook. ++*/ ++static void* ++dhd_wlfc_hanger_create(osl_t *osh, int max_items) ++{ ++ int i; ++ wlfc_hanger_t* hanger; ++ ++ /* allow only up to a specific size for now */ ++ ASSERT(max_items == WLFC_HANGER_MAXITEMS); ++ ++ if ((hanger = (wlfc_hanger_t*)MALLOC(osh, WLFC_HANGER_SIZE(max_items))) == NULL) ++ return NULL; ++ ++ memset(hanger, 0, WLFC_HANGER_SIZE(max_items)); ++ hanger->max_items = max_items; ++ ++ for (i = 0; i < hanger->max_items; i++) { ++ hanger->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; ++ } ++ return hanger; ++} ++ ++static int ++dhd_wlfc_hanger_delete(osl_t *osh, void* hanger) ++{ ++ wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; ++ ++ if (h) { ++ MFREE(osh, h, WLFC_HANGER_SIZE(h->max_items)); ++ return BCME_OK; ++ } ++ return BCME_BADARG; ++} ++ ++static uint16 ++dhd_wlfc_hanger_get_free_slot(void* hanger) ++{ ++ uint32 i; ++ wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; ++ ++ if (h) { ++ for (i = (h->slot_pos + 1); i != h->slot_pos;) { ++ if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE) { ++ h->slot_pos = i; ++ return (uint16)i; ++ } ++ (i == h->max_items)? i = 0 : i++; ++ } ++ h->failed_slotfind++; ++ } ++ return WLFC_HANGER_MAXITEMS; ++} ++ ++static int ++dhd_wlfc_hanger_get_genbit(void* hanger, void* pkt, uint32 slot_id, int* gen) ++{ ++ int rc = BCME_OK; ++ wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; ++ ++ *gen = 0xff; ++ ++ /* this packet was not pushed at the time it went to the firmware */ ++ if (slot_id == WLFC_HANGER_MAXITEMS) ++ return BCME_NOTFOUND; ++ ++ if (h) { ++ if ((h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) || ++ (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED)) { ++ *gen = h->items[slot_id].gen; ++ } ++ else { ++ rc = BCME_NOTFOUND; ++ } ++ } ++ else ++ rc = BCME_BADARG; ++ return rc; ++} ++ ++static int ++dhd_wlfc_hanger_pushpkt(void* hanger, void* pkt, uint32 slot_id) ++{ ++ int rc = BCME_OK; ++ wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; ++ ++ if (h && (slot_id < WLFC_HANGER_MAXITEMS)) { ++ if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_FREE) { ++ h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE; ++ h->items[slot_id].pkt = pkt; ++ h->items[slot_id].identifier = slot_id; ++ h->pushed++; ++ } ++ else { ++ h->failed_to_push++; ++ rc = BCME_NOTFOUND; ++ } ++ } ++ else ++ rc = BCME_BADARG; ++ return rc; ++} ++ ++static int ++dhd_wlfc_hanger_poppkt(void* hanger, uint32 slot_id, void** pktout, int remove_from_hanger) ++{ ++ int rc = BCME_OK; ++ wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; ++ ++ /* this packet was not pushed at the time it went to the firmware */ ++ if (slot_id == WLFC_HANGER_MAXITEMS) ++ return BCME_NOTFOUND; ++ ++ if (h) { ++ if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) { ++ *pktout = h->items[slot_id].pkt; ++ if (remove_from_hanger) { ++ h->items[slot_id].state = ++ WLFC_HANGER_ITEM_STATE_FREE; ++ h->items[slot_id].pkt = NULL; ++ h->items[slot_id].identifier = 0; ++ h->items[slot_id].gen = 0xff; ++ h->popped++; ++ } ++ } ++ else { ++ h->failed_to_pop++; ++ rc = BCME_NOTFOUND; ++ } ++ } ++ else ++ rc = BCME_BADARG; ++ return rc; ++} ++ ++static int ++dhd_wlfc_hanger_mark_suppressed(void* hanger, uint32 slot_id, uint8 gen) ++{ ++ int rc = BCME_OK; ++ wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; ++ ++ /* this packet was not pushed at the time it went to the firmware */ ++ if (slot_id == WLFC_HANGER_MAXITEMS) ++ return BCME_NOTFOUND; ++ if (h) { ++ h->items[slot_id].gen = gen; ++ if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) { ++ h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED; ++ } ++ else ++ rc = BCME_BADARG; ++ } ++ else ++ rc = BCME_BADARG; ++ ++ return rc; ++} ++ ++static int ++_dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void* p, bool tim_signal, ++ uint8 tim_bmp, uint8 mac_handle, uint32 htodtag) ++{ ++ uint32 wl_pktinfo = 0; ++ uint8* wlh; ++ uint8 dataOffset; ++ uint8 fillers; ++ uint8 tim_signal_len = 0; ++ ++ struct bdc_header *h; ++ ++ if (tim_signal) { ++ tim_signal_len = 1 + 1 + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP; ++ } ++ ++ /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ ++ dataOffset = WLFC_CTL_VALUE_LEN_PKTTAG + 2 + tim_signal_len; ++ fillers = ROUNDUP(dataOffset, 4) - dataOffset; ++ dataOffset += fillers; ++ ++ PKTPUSH(ctx->osh, p, dataOffset); ++ wlh = (uint8*) PKTDATA(ctx->osh, p); ++ ++ wl_pktinfo = htol32(htodtag); ++ ++ wlh[0] = WLFC_CTL_TYPE_PKTTAG; ++ wlh[1] = WLFC_CTL_VALUE_LEN_PKTTAG; ++ memcpy(&wlh[2], &wl_pktinfo, sizeof(uint32)); ++ ++ if (tim_signal_len) { ++ wlh[dataOffset - fillers - tim_signal_len ] = ++ WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP; ++ wlh[dataOffset - fillers - tim_signal_len + 1] = ++ WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP; ++ wlh[dataOffset - fillers - tim_signal_len + 2] = mac_handle; ++ wlh[dataOffset - fillers - tim_signal_len + 3] = tim_bmp; ++ } ++ if (fillers) ++ memset(&wlh[dataOffset - fillers], WLFC_CTL_TYPE_FILLER, fillers); ++ ++ PKTPUSH(ctx->osh, p, BDC_HEADER_LEN); ++ h = (struct bdc_header *)PKTDATA(ctx->osh, p); ++ h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); ++ if (PKTSUMNEEDED(p)) ++ h->flags |= BDC_FLAG_SUM_NEEDED; ++ ++ ++ h->priority = (PKTPRIO(p) & BDC_PRIORITY_MASK); ++ h->flags2 = 0; ++ h->dataOffset = dataOffset >> 2; ++ BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p))); ++ return BCME_OK; ++} ++ ++static int ++_dhd_wlfc_pullheader(athost_wl_status_info_t* ctx, void* pktbuf) ++{ ++ struct bdc_header *h; ++ ++ if (PKTLEN(ctx->osh, pktbuf) < BDC_HEADER_LEN) { ++ WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__, ++ PKTLEN(ctx->osh, pktbuf), BDC_HEADER_LEN)); ++ return BCME_ERROR; ++ } ++ h = (struct bdc_header *)PKTDATA(ctx->osh, pktbuf); ++ ++ /* pull BDC header */ ++ PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN); ++ ++ if (PKTLEN(ctx->osh, pktbuf) < (h->dataOffset << 2)) { ++ WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__, ++ PKTLEN(ctx->osh, pktbuf), (h->dataOffset << 2))); ++ return BCME_ERROR; ++ } ++ /* pull wl-header */ ++ PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 2)); ++ return BCME_OK; ++} ++ ++static wlfc_mac_descriptor_t* ++_dhd_wlfc_find_table_entry(athost_wl_status_info_t* ctx, void* p) ++{ ++ int i; ++ wlfc_mac_descriptor_t* table = ctx->destination_entries.nodes; ++ uint8 ifid = DHD_PKTTAG_IF(PKTTAG(p)); ++ uint8* dstn = DHD_PKTTAG_DSTN(PKTTAG(p)); ++ ++ if (((ctx->destination_entries.interfaces[ifid].iftype == WLC_E_IF_ROLE_STA) || ++ ETHER_ISMULTI(dstn) || ++ (ctx->destination_entries.interfaces[ifid].iftype == WLC_E_IF_ROLE_P2P_CLIENT)) && ++ (ctx->destination_entries.interfaces[ifid].occupied)) { ++ return &ctx->destination_entries.interfaces[ifid]; ++ } ++ ++ for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) { ++ if (table[i].occupied) { ++ if (table[i].interface_id == ifid) { ++ if (!memcmp(table[i].ea, dstn, ETHER_ADDR_LEN)) ++ return &table[i]; ++ } ++ } ++ } ++ return &ctx->destination_entries.other; ++} ++ ++static int ++_dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t* ctx, ++ void* p, ewlfc_packet_state_t pkt_type, uint32 hslot) ++{ ++ /* ++ put the packet back to the head of queue ++ ++ - a packet from send-q will need to go back to send-q and not delay-q ++ since that will change the order of packets. ++ - suppressed packet goes back to suppress sub-queue ++ - pull out the header, if new or delayed packet ++ ++ Note: hslot is used only when header removal is done. ++ */ ++ wlfc_mac_descriptor_t* entry; ++ void* pktout; ++ int rc = BCME_OK; ++ int prec; ++ ++ entry = _dhd_wlfc_find_table_entry(ctx, p); ++ prec = DHD_PKTTAG_FIFO(PKTTAG(p)); ++ if (entry != NULL) { ++ if (pkt_type == eWLFC_PKTTYPE_SUPPRESSED) { ++ /* wl-header is saved for suppressed packets */ ++ if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, ((prec << 1) + 1), p) == NULL) { ++ WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); ++ rc = BCME_ERROR; ++ } ++ } ++ else { ++ /* remove header first */ ++ rc = _dhd_wlfc_pullheader(ctx, p); ++ if (rc != BCME_OK) { ++ WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); ++ /* free the hanger slot */ ++ dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1); ++ PKTFREE(ctx->osh, p, TRUE); ++ rc = BCME_ERROR; ++ return rc; ++ } ++ ++ if (pkt_type == eWLFC_PKTTYPE_DELAYED) { ++ /* delay-q packets are going to delay-q */ ++ if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, (prec << 1), p) == NULL) { ++ WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); ++ rc = BCME_ERROR; ++ } ++ } ++ else { ++ /* these are going to SENDQ */ ++ if (WLFC_PKTQ_PENQ_HEAD(&ctx->SENDQ, prec, p) == NULL) { ++ WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); ++ rc = BCME_ERROR; ++ } ++ } ++ /* free the hanger slot */ ++ dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1); ++ ++ /* decrement sequence count */ ++ WLFC_DECR_SEQCOUNT(entry, prec); ++ } ++ /* ++ if this packet did not count against FIFO credit, it must have ++ taken a requested_credit from the firmware (for pspoll etc.) ++ */ ++ if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) { ++ entry->requested_credit++; ++ } ++ } ++ else { ++ WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); ++ rc = BCME_ERROR; ++ } ++ if (rc != BCME_OK) ++ ctx->stats.rollback_failed++; ++ else ++ ctx->stats.rollback++; ++ ++ return rc; ++} ++ ++static void ++_dhd_wlfc_flow_control_check(athost_wl_status_info_t* ctx, struct pktq* pq, uint8 if_id) ++{ ++ if ((pq->len <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) { ++ /* start traffic */ ++ ctx->hostif_flow_state[if_id] = OFF; ++ /* ++ WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n", ++ pq->len, if_id, __FUNCTION__)); ++ */ ++ WLFC_DBGMESG(("F")); ++ dhd_txflowcontrol(ctx->dhdp, if_id, OFF); ++ ctx->toggle_host_if = 0; ++ } ++ if ((pq->len >= WLFC_FLOWCONTROL_HIWATER) && (ctx->hostif_flow_state[if_id] == OFF)) { ++ /* stop traffic */ ++ ctx->hostif_flow_state[if_id] = ON; ++ /* ++ WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic %s()\n", ++ pq->len, if_id, __FUNCTION__)); ++ */ ++ WLFC_DBGMESG(("N")); ++ dhd_txflowcontrol(ctx->dhdp, if_id, ON); ++ ctx->host_ifidx = if_id; ++ ctx->toggle_host_if = 1; ++ } ++ return; ++} ++ ++static int ++_dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry, ++ uint8 ta_bmp) ++{ ++ int rc = BCME_OK; ++ void* p = NULL; ++ int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 12; ++ ++ /* allocate a dummy packet */ ++ p = PKTGET(ctx->osh, dummylen, TRUE); ++ if (p) { ++ PKTPULL(ctx->osh, p, dummylen); ++ DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0); ++ _dhd_wlfc_pushheader(ctx, p, TRUE, ta_bmp, entry->mac_handle, 0); ++ DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1); ++#ifdef PROP_TXSTATUS_DEBUG ++ ctx->stats.signal_only_pkts_sent++; ++#endif ++ rc = dhd_bus_txdata(((dhd_pub_t *)ctx->dhdp)->bus, p); ++ if (rc != BCME_OK) { ++ PKTFREE(ctx->osh, p, TRUE); ++ } ++ } ++ else { ++ DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n", ++ __FUNCTION__, dummylen)); ++ rc = BCME_NOMEM; ++ } ++ return rc; ++} ++ ++/* Return TRUE if traffic availability changed */ ++static bool ++_dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry, ++ int prec) ++{ ++ bool rc = FALSE; ++ ++ if (entry->state == WLFC_STATE_CLOSE) { ++ if ((pktq_plen(&entry->psq, (prec << 1)) == 0) && ++ (pktq_plen(&entry->psq, ((prec << 1) + 1)) == 0)) { ++ ++ if (entry->traffic_pending_bmp & NBITVAL(prec)) { ++ rc = TRUE; ++ entry->traffic_pending_bmp = ++ entry->traffic_pending_bmp & ~ NBITVAL(prec); ++ } ++ } ++ else { ++ if (!(entry->traffic_pending_bmp & NBITVAL(prec))) { ++ rc = TRUE; ++ entry->traffic_pending_bmp = ++ entry->traffic_pending_bmp | NBITVAL(prec); ++ } ++ } ++ } ++ if (rc) { ++ /* request a TIM update to firmware at the next piggyback opportunity */ ++ if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) { ++ entry->send_tim_signal = 1; ++ _dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp); ++ entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; ++ entry->send_tim_signal = 0; ++ } ++ else { ++ rc = FALSE; ++ } ++ } ++ return rc; ++} ++ ++static int ++_dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p) ++{ ++ wlfc_mac_descriptor_t* entry; ++ ++ entry = _dhd_wlfc_find_table_entry(ctx, p); ++ if (entry == NULL) { ++ WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); ++ return BCME_NOTFOUND; ++ } ++ /* ++ - suppressed packets go to sub_queue[2*prec + 1] AND ++ - delayed packets go to sub_queue[2*prec + 0] to ensure ++ order of delivery. ++ */ ++ if (WLFC_PKTQ_PENQ(&entry->psq, ((prec << 1) + 1), p) == NULL) { ++ ctx->stats.delayq_full_error++; ++ /* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */ ++ WLFC_DBGMESG(("s")); ++ return BCME_ERROR; ++ } ++ /* A packet has been pushed, update traffic availability bitmap, if applicable */ ++ _dhd_wlfc_traffic_pending_check(ctx, entry, prec); ++ _dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p))); ++ return BCME_OK; ++} ++ ++static int ++_dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx, ++ wlfc_mac_descriptor_t* entry, void* p, int header_needed, uint32* slot) ++{ ++ int rc = BCME_OK; ++ int hslot = WLFC_HANGER_MAXITEMS; ++ bool send_tim_update = FALSE; ++ uint32 htod = 0; ++ uint8 free_ctr; ++ ++ *slot = hslot; ++ ++ if (entry == NULL) { ++ entry = _dhd_wlfc_find_table_entry(ctx, p); ++ } ++ ++ if (entry == NULL) { ++ WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); ++ return BCME_ERROR; ++ } ++ if (entry->send_tim_signal) { ++ send_tim_update = TRUE; ++ entry->send_tim_signal = 0; ++ entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; ++ } ++ if (header_needed) { ++ hslot = dhd_wlfc_hanger_get_free_slot(ctx->hanger); ++ free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p))); ++ DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod); ++ WLFC_PKTFLAG_SET_GENERATION(htod, entry->generation); ++ entry->transit_count++; ++ } ++ else { ++ hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); ++ free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); ++ } ++ WLFC_PKTID_HSLOT_SET(htod, hslot); ++ WLFC_PKTID_FREERUNCTR_SET(htod, free_ctr); ++ DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1); ++ WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST); ++ WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p))); ++ ++ if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) { ++ /* ++ Indicate that this packet is being sent in response to an ++ explicit request from the firmware side. ++ */ ++ WLFC_PKTFLAG_SET_PKTREQUESTED(htod); ++ } ++ else { ++ WLFC_PKTFLAG_CLR_PKTREQUESTED(htod); ++ } ++ if (header_needed) { ++ rc = _dhd_wlfc_pushheader(ctx, p, send_tim_update, ++ entry->traffic_lastreported_bmp, entry->mac_handle, htod); ++ if (rc == BCME_OK) { ++ DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod); ++ /* ++ a new header was created for this packet. ++ push to hanger slot and scrub q. Since bus ++ send succeeded, increment seq number as well. ++ */ ++ rc = dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot); ++ if (rc == BCME_OK) { ++ /* increment free running sequence count */ ++ WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p))); ++#ifdef PROP_TXSTATUS_DEBUG ++ ((wlfc_hanger_t*)(ctx->hanger))->items[hslot].push_time = ++ OSL_SYSUPTIME(); ++#endif ++ } ++ else { ++ WLFC_DBGMESG(("%s() hanger_pushpkt() failed, rc: %d\n", ++ __FUNCTION__, rc)); ++ } ++ } ++ } ++ else { ++ int gen; ++ ++ /* remove old header */ ++ rc = _dhd_wlfc_pullheader(ctx, p); ++ if (rc == BCME_OK) { ++ hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); ++ dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen); ++ ++ WLFC_PKTFLAG_SET_GENERATION(htod, gen); ++ free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); ++ /* push new header */ ++ _dhd_wlfc_pushheader(ctx, p, send_tim_update, ++ entry->traffic_lastreported_bmp, entry->mac_handle, htod); ++ } ++ } ++ *slot = hslot; ++ return rc; ++} ++ ++static int ++_dhd_wlfc_is_destination_closed(athost_wl_status_info_t* ctx, ++ wlfc_mac_descriptor_t* entry, int prec) ++{ ++ if (ctx->destination_entries.interfaces[entry->interface_id].iftype == ++ WLC_E_IF_ROLE_P2P_GO) { ++ /* - destination interface is of type p2p GO. ++ For a p2pGO interface, if the destination is OPEN but the interface is ++ CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is ++ destination-specific-credit left send packets. This is because the ++ firmware storing the destination-specific-requested packet in queue. ++ */ ++ if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) && ++ (entry->requested_packet == 0)) ++ return 1; ++ } ++ /* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */ ++ if (((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) && ++ (entry->requested_packet == 0)) || ++ (!(entry->ac_bitmap & (1 << prec)))) ++ return 1; ++ ++ return 0; ++} ++ ++static void* ++_dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx, ++ int prec, uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out) ++{ ++ wlfc_mac_descriptor_t* entry; ++ wlfc_mac_descriptor_t* table; ++ uint8 token_pos; ++ int total_entries; ++ void* p = NULL; ++ int pout; ++ int i; ++ ++ *entry_out = NULL; ++ token_pos = ctx->token_pos[prec]; ++ /* most cases a packet will count against FIFO credit */ ++ *ac_credit_spent = 1; ++ *needs_hdr = 1; ++ ++ /* search all entries, include nodes as well as interfaces */ ++ table = (wlfc_mac_descriptor_t*)&ctx->destination_entries; ++ total_entries = sizeof(ctx->destination_entries)/sizeof(wlfc_mac_descriptor_t); ++ ++ for (i = 0; i < total_entries; i++) { ++ entry = &table[(token_pos + i) % total_entries]; ++ if (entry->occupied) { ++ if (!_dhd_wlfc_is_destination_closed(ctx, entry, prec)) { ++ p = pktq_mdeq(&entry->psq, ++ /* higher precedence will be picked up first, ++ * i.e. suppressed packets before delayed ones ++ */ ++ NBITVAL((prec << 1) + 1), &pout); ++ *needs_hdr = 0; ++ ++ if (p == NULL) { ++ if (entry->suppressed == TRUE) { ++ if ((entry->suppr_transit_count <= ++ entry->suppress_count)) { ++ entry->suppressed = FALSE; ++ } else { ++ return NULL; ++ } ++ } ++ /* De-Q from delay Q */ ++ p = pktq_mdeq(&entry->psq, ++ NBITVAL((prec << 1)), ++ &pout); ++ *needs_hdr = 1; ++ } ++ ++ if (p != NULL) { ++ /* did the packet come from suppress sub-queue? */ ++ if (entry->requested_credit > 0) { ++ entry->requested_credit--; ++#ifdef PROP_TXSTATUS_DEBUG ++ entry->dstncredit_sent_packets++; ++#endif ++ /* ++ if the packet was pulled out while destination is in ++ closed state but had a non-zero packets requested, ++ then this should not count against the FIFO credit. ++ That is due to the fact that the firmware will ++ most likely hold onto this packet until a suitable ++ time later to push it to the appropriate AC FIFO. ++ */ ++ if (entry->state == WLFC_STATE_CLOSE) ++ *ac_credit_spent = 0; ++ } ++ else if (entry->requested_packet > 0) { ++ entry->requested_packet--; ++ DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p)); ++ if (entry->state == WLFC_STATE_CLOSE) ++ *ac_credit_spent = 0; ++ } ++ /* move token to ensure fair round-robin */ ++ ctx->token_pos[prec] = ++ (token_pos + i + 1) % total_entries; ++ *entry_out = entry; ++ _dhd_wlfc_flow_control_check(ctx, &entry->psq, ++ DHD_PKTTAG_IF(PKTTAG(p))); ++ /* ++ A packet has been picked up, update traffic ++ availability bitmap, if applicable ++ */ ++ _dhd_wlfc_traffic_pending_check(ctx, entry, prec); ++ return p; ++ } ++ } ++ } ++ } ++ return NULL; ++} ++ ++static void* ++_dhd_wlfc_deque_sendq(athost_wl_status_info_t* ctx, int prec) ++{ ++ wlfc_mac_descriptor_t* entry; ++ void* p; ++ ++ ++ p = pktq_pdeq(&ctx->SENDQ, prec); ++ if (p != NULL) { ++ if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(p)))) ++ /* bc/mc packets do not have a delay queue */ ++ return p; ++ ++ entry = _dhd_wlfc_find_table_entry(ctx, p); ++ ++ if (entry == NULL) { ++ WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); ++ return p; ++ } ++ ++ while ((p != NULL)) { ++ /* ++ - suppressed packets go to sub_queue[2*prec + 1] AND ++ - delayed packets go to sub_queue[2*prec + 0] to ensure ++ order of delivery. ++ */ ++ if (WLFC_PKTQ_PENQ(&entry->psq, (prec << 1), p) == NULL) { ++ WLFC_DBGMESG(("D")); ++ /* dhd_txcomplete(ctx->dhdp, p, FALSE); */ ++ PKTFREE(ctx->osh, p, TRUE); ++ ctx->stats.delayq_full_error++; ++ } ++ /* ++ A packet has been pushed, update traffic availability bitmap, ++ if applicable ++ */ ++ _dhd_wlfc_traffic_pending_check(ctx, entry, prec); ++ ++ p = pktq_pdeq(&ctx->SENDQ, prec); ++ if (p == NULL) ++ break; ++ ++ entry = _dhd_wlfc_find_table_entry(ctx, p); ++ ++ if ((entry == NULL) || (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(p))))) { ++ return p; ++ } ++ } ++ } ++ return p; ++} ++ ++static int ++_dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry, ++ ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) ++{ ++ int rc = BCME_OK; ++ ++ if (action == eWLFC_MAC_ENTRY_ACTION_ADD) { ++ entry->occupied = 1; ++ entry->state = WLFC_STATE_OPEN; ++ entry->requested_credit = 0; ++ entry->interface_id = ifid; ++ entry->iftype = iftype; ++ entry->ac_bitmap = 0xff; /* update this when handling APSD */ ++ /* for an interface entry we may not care about the MAC address */ ++ if (ea != NULL) ++ memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN); ++ pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN); ++ } ++ else if (action == eWLFC_MAC_ENTRY_ACTION_UPDATE) { ++ entry->occupied = 1; ++ entry->state = WLFC_STATE_OPEN; ++ entry->requested_credit = 0; ++ entry->interface_id = ifid; ++ entry->iftype = iftype; ++ entry->ac_bitmap = 0xff; /* update this when handling APSD */ ++ /* for an interface entry we may not care about the MAC address */ ++ if (ea != NULL) ++ memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN); ++ } ++ else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) { ++ entry->occupied = 0; ++ entry->state = WLFC_STATE_CLOSE; ++ entry->requested_credit = 0; ++ /* enable after packets are queued-deqeued properly. ++ pktq_flush(dhd->osh, &entry->psq, FALSE, NULL, 0); ++ */ ++ } ++ return rc; ++} ++ ++int ++_dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, uint8 available_credit_map, int borrower_ac) ++{ ++ int lender_ac; ++ int rc = BCME_ERROR; ++ ++ if (ctx == NULL || available_credit_map == 0) { ++ WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); ++ return BCME_BADARG; ++ } ++ ++ /* Borrow from lowest priority available AC (including BC/MC credits) */ ++ for (lender_ac = 0; lender_ac <= AC_COUNT; lender_ac++) { ++ if ((available_credit_map && (1 << lender_ac)) && ++ (ctx->FIFO_credit[lender_ac] > 0)) { ++ ctx->credits_borrowed[borrower_ac][lender_ac]++; ++ ctx->FIFO_credit[lender_ac]--; ++ rc = BCME_OK; ++ break; ++ } ++ } ++ ++ return rc; ++} ++ ++int ++dhd_wlfc_interface_entry_update(void* state, ++ ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) ++{ ++ athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; ++ wlfc_mac_descriptor_t* entry; ++ ++ if (ifid >= WLFC_MAX_IFNUM) ++ return BCME_BADARG; ++ ++ entry = &ctx->destination_entries.interfaces[ifid]; ++ return _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea); ++} ++ ++int ++dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits) ++{ ++ athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; ++ ++ /* update the AC FIFO credit map */ ++ ctx->FIFO_credit[0] = credits[0]; ++ ctx->FIFO_credit[1] = credits[1]; ++ ctx->FIFO_credit[2] = credits[2]; ++ ctx->FIFO_credit[3] = credits[3]; ++ /* credit for bc/mc packets */ ++ ctx->FIFO_credit[4] = credits[4]; ++ /* credit for ATIM FIFO is not used yet. */ ++ ctx->FIFO_credit[5] = 0; ++ return BCME_OK; ++} ++ ++int ++dhd_wlfc_enque_sendq(void* state, int prec, void* p) ++{ ++ athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; ++ ++ if ((state == NULL) || ++ /* prec = AC_COUNT is used for bc/mc queue */ ++ (prec > AC_COUNT) || ++ (p == NULL)) { ++ WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); ++ return BCME_BADARG; ++ } ++ if (FALSE == dhd_prec_enq(ctx->dhdp, &ctx->SENDQ, p, prec)) { ++ ctx->stats.sendq_full_error++; ++ /* ++ WLFC_DBGMESG(("Error: %s():%d, qlen:%d\n", ++ __FUNCTION__, __LINE__, ctx->SENDQ.len)); ++ */ ++ WLFC_HOST_FIFO_DROPPEDCTR_INC(ctx, prec); ++ WLFC_DBGMESG(("Q")); ++ PKTFREE(ctx->osh, p, TRUE); ++ return BCME_ERROR; ++ } ++ ctx->stats.pktin++; ++ /* _dhd_wlfc_flow_control_check(ctx, &ctx->SENDQ, DHD_PKTTAG_IF(PKTTAG(p))); */ ++ return BCME_OK; ++} ++ ++int ++_dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac, ++ dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx) ++{ ++ uint32 hslot; ++ int rc; ++ ++ /* ++ if ac_fifo_credit_spent = 0 ++ ++ This packet will not count against the FIFO credit. ++ To ensure the txstatus corresponding to this packet ++ does not provide an implied credit (default behavior) ++ mark the packet accordingly. ++ ++ if ac_fifo_credit_spent = 1 ++ ++ This is a normal packet and it counts against the FIFO ++ credit count. ++ */ ++ DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent); ++ rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, commit_info->p, ++ commit_info->needs_hdr, &hslot); ++ ++ if (rc == BCME_OK) ++ rc = fcommit(commit_ctx, commit_info->p); ++ else ++ ctx->stats.generic_error++; ++ ++ if (rc == BCME_OK) { ++ ctx->stats.pkt2bus++; ++ if (commit_info->ac_fifo_credit_spent) { ++ ctx->stats.sendq_pkts[ac]++; ++ WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac); ++ } ++ } else if (rc == BCME_NORESOURCE) ++ rc = BCME_ERROR; ++ else { ++ /* ++ bus commit has failed, rollback. ++ - remove wl-header for a delayed packet ++ - save wl-header header for suppressed packets ++ */ ++ rc = _dhd_wlfc_rollback_packet_toq(ctx, commit_info->p, ++ (commit_info->pkt_type), hslot); ++ if (rc != BCME_OK) ++ ctx->stats.rollback_failed++; ++ ++ rc = BCME_ERROR; ++ } ++ ++ return rc; ++} ++ ++int ++dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx) ++{ ++ int ac; ++ int credit; ++ int rc; ++ dhd_wlfc_commit_info_t commit_info; ++ athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; ++ int credit_count = 0; ++ int bus_retry_count = 0; ++ uint8 ac_available = 0; /* Bitmask for 4 ACs + BC/MC */ ++ ++ if ((state == NULL) || ++ (fcommit == NULL)) { ++ WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); ++ return BCME_BADARG; ++ } ++ ++ memset(&commit_info, 0, sizeof(commit_info)); ++ ++ /* ++ Commit packets for regular AC traffic. Higher priority first. ++ First, use up FIFO credits available to each AC. Based on distribution ++ and credits left, borrow from other ACs as applicable ++ ++ -NOTE: ++ If the bus between the host and firmware is overwhelmed by the ++ traffic from host, it is possible that higher priority traffic ++ starves the lower priority queue. If that occurs often, we may ++ have to employ weighted round-robin or ucode scheme to avoid ++ low priority packet starvation. ++ */ ++ ++ for (ac = AC_COUNT; ac >= 0; ac--) { ++ ++ int initial_credit_count = ctx->FIFO_credit[ac]; ++ ++ /* packets from SENDQ are fresh and they'd need header and have no MAC entry */ ++ commit_info.needs_hdr = 1; ++ commit_info.mac_entry = NULL; ++ commit_info.pkt_type = eWLFC_PKTTYPE_NEW; ++ ++ do { ++ commit_info.p = _dhd_wlfc_deque_sendq(ctx, ac); ++ if (commit_info.p == NULL) ++ break; ++ else if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(commit_info.p)))) { ++ ASSERT(ac == AC_COUNT); ++ ++ if (ctx->FIFO_credit[ac]) { ++ rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, ++ fcommit, commit_ctx); ++ ++ /* Bus commits may fail (e.g. flow control); abort after retries */ ++ if (rc == BCME_OK) { ++ if (commit_info.ac_fifo_credit_spent) { ++ (void) _dhd_wlfc_borrow_credit(ctx, ++ ac_available, ac); ++ credit_count--; ++ } ++ } else { ++ bus_retry_count++; ++ if (bus_retry_count >= BUS_RETRIES) { ++ DHD_ERROR((" %s: bus error\n", ++ __FUNCTION__)); ++ return rc; ++ } ++ } ++ } ++ } ++ ++ } while (commit_info.p); ++ ++ for (credit = 0; credit < ctx->FIFO_credit[ac];) { ++ commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac, ++ &(commit_info.ac_fifo_credit_spent), ++ &(commit_info.needs_hdr), ++ &(commit_info.mac_entry)); ++ ++ if (commit_info.p == NULL) ++ break; ++ ++ commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED : ++ eWLFC_PKTTYPE_SUPPRESSED; ++ ++ rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, ++ fcommit, commit_ctx); ++ ++ /* Bus commits may fail (e.g. flow control); abort after retries */ ++ if (rc == BCME_OK) { ++ if (commit_info.ac_fifo_credit_spent) { ++ credit++; ++ } ++ } ++ else { ++ bus_retry_count++; ++ if (bus_retry_count >= BUS_RETRIES) { ++ DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); ++ ctx->FIFO_credit[ac] -= credit; ++ return rc; ++ } ++ } ++ } ++ ++ ctx->FIFO_credit[ac] -= credit; ++ ++ ++ /* If no credits were used, the queue is idle and can be re-used ++ Note that resv credits cannot be borrowed ++ */ ++ if (initial_credit_count == ctx->FIFO_credit[ac]) { ++ ac_available |= (1 << ac); ++ credit_count += ctx->FIFO_credit[ac]; ++ } ++ } ++ ++ /* We borrow only for AC_BE and only if no other traffic seen for DEFER_PERIOD ++ ++ Note that (ac_available & WLFC_AC_BE_TRAFFIC_ONLY) is done to: ++ a) ignore BC/MC for deferring borrow ++ b) ignore AC_BE being available along with other ACs ++ (this should happen only for pure BC/MC traffic) ++ ++ i.e. AC_VI, AC_VO, AC_BK all MUST be available (i.e. no traffic) and ++ we do not care if AC_BE and BC/MC are available or not ++ */ ++ if ((ac_available & WLFC_AC_BE_TRAFFIC_ONLY) == WLFC_AC_BE_TRAFFIC_ONLY) { ++ ++ if (ctx->allow_credit_borrow) { ++ ac = 1; /* Set ac to AC_BE and borrow credits */ ++ } ++ else { ++ int delta; ++ int curr_t = OSL_SYSUPTIME(); ++ ++ if (curr_t > ctx->borrow_defer_timestamp) ++ delta = curr_t - ctx->borrow_defer_timestamp; ++ else ++ delta = 0xffffffff + curr_t - ctx->borrow_defer_timestamp; ++ ++ if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) { ++ /* Reset borrow but defer to next iteration (defensive borrowing) */ ++ ctx->allow_credit_borrow = TRUE; ++ ctx->borrow_defer_timestamp = 0; ++ } ++ return BCME_OK; ++ } ++ } ++ else { ++ /* If we have multiple AC traffic, turn off borrowing, mark time and bail out */ ++ ctx->allow_credit_borrow = FALSE; ++ ctx->borrow_defer_timestamp = OSL_SYSUPTIME(); ++ return BCME_OK; ++ } ++ ++ /* At this point, borrow all credits only for "ac" (which should be set above to AC_BE) ++ Generically use "ac" only in case we extend to all ACs in future ++ */ ++ for (; (credit_count > 0);) { ++ ++ commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac, ++ &(commit_info.ac_fifo_credit_spent), ++ &(commit_info.needs_hdr), ++ &(commit_info.mac_entry)); ++ if (commit_info.p == NULL) ++ break; ++ ++ commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED : ++ eWLFC_PKTTYPE_SUPPRESSED; ++ ++ rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, ++ fcommit, commit_ctx); ++ ++ /* Bus commits may fail (e.g. flow control); abort after retries */ ++ if (rc == BCME_OK) { ++ if (commit_info.ac_fifo_credit_spent) { ++ (void) _dhd_wlfc_borrow_credit(ctx, ac_available, ac); ++ credit_count--; ++ } ++ } ++ else { ++ bus_retry_count++; ++ if (bus_retry_count >= BUS_RETRIES) { ++ DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); ++ return rc; ++ } ++ } ++ } ++ ++ return BCME_OK; ++} ++ ++static uint8 ++dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8* ea) ++{ ++ wlfc_mac_descriptor_t* table = ++ ((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes; ++ uint8 table_index; ++ ++ if (ea != NULL) { ++ for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) { ++ if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) && ++ table[table_index].occupied) ++ return table_index; ++ } ++ } ++ return WLFC_MAC_DESC_ID_INVALID; ++} ++ ++void ++dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success) ++{ ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhd->wlfc_state; ++ void* p; ++ int fifo_id; ++ ++ dhd_os_wlfc_block(dhd); ++ ++ if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) { ++#ifdef PROP_TXSTATUS_DEBUG ++ wlfc->stats.signal_only_pkts_freed++; ++#endif ++ if (success) ++ /* is this a signal-only packet? */ ++ PKTFREE(wlfc->osh, txp, TRUE); ++ dhd_os_wlfc_unblock(dhd); ++ return; ++ } ++ if (!success) { ++ WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n", ++ __FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp)))); ++ dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG ++ (PKTTAG(txp))), &p, 1); ++ ++ /* indicate failure and free the packet */ ++ dhd_txcomplete(dhd, txp, FALSE); ++ ++ /* return the credit, if necessary */ ++ if (DHD_PKTTAG_CREDITCHECK(PKTTAG(txp))) { ++ int lender, credit_returned = 0; /* Note that borrower is fifo_id */ ++ ++ fifo_id = DHD_PKTTAG_FIFO(PKTTAG(txp)); ++ ++ /* Return credits to highest priority lender first */ ++ for (lender = AC_COUNT; lender >= 0; lender--) { ++ if (wlfc->credits_borrowed[fifo_id][lender] > 0) { ++ wlfc->FIFO_credit[lender]++; ++ wlfc->credits_borrowed[fifo_id][lender]--; ++ credit_returned = 1; ++ break; ++ } ++ } ++ ++ if (!credit_returned) { ++ wlfc->FIFO_credit[fifo_id]++; ++ } ++ } ++ ++ PKTFREE(wlfc->osh, txp, TRUE); ++ } ++ dhd_os_wlfc_unblock(dhd); ++ return; ++} ++ ++static int ++dhd_wlfc_compressed_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info, uint8 len) ++{ ++ uint8 status_flag; ++ uint32 status; ++ int ret; ++ int remove_from_hanger = 1; ++ void* pktbuf; ++ uint8 fifo_id; ++ uint8 count = 0; ++ uint32 status_g; ++ uint32 hslot, hcnt; ++ wlfc_mac_descriptor_t* entry = NULL; ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhd->wlfc_state; ++ ++ memcpy(&status, pkt_info, sizeof(uint32)); ++ status_flag = WL_TXSTATUS_GET_FLAGS(status); ++ status_g = status & 0xff000000; ++ hslot = (status & 0x00ffff00) >> 8; ++ hcnt = status & 0xff; ++ len = pkt_info[4]; ++ ++ wlfc->stats.txstatus_in++; ++ ++ if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) { ++ wlfc->stats.pkt_freed++; ++ } ++ ++ else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) { ++ wlfc->stats.d11_suppress++; ++ remove_from_hanger = 0; ++ } ++ ++ else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) { ++ wlfc->stats.wl_suppress++; ++ remove_from_hanger = 0; ++ } ++ ++ else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) { ++ wlfc->stats.wlc_tossed_pkts++; ++ } ++ while (count < len) { ++ status = (status_g << 24) | (hslot << 8) | (hcnt); ++ count++; ++ hslot++; ++ hcnt++; ++ ++ ret = dhd_wlfc_hanger_poppkt(wlfc->hanger, ++ WLFC_PKTID_HSLOT_GET(status), &pktbuf, remove_from_hanger); ++ if (ret != BCME_OK) { ++ /* do something */ ++ continue; ++ } ++ ++ entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf); ++ ++ if (!remove_from_hanger) { ++ /* this packet was suppressed */ ++ if (!entry->suppressed || entry->generation != WLFC_PKTID_GEN(status)) { ++ entry->suppressed = TRUE; ++ entry->suppress_count = pktq_mlen(&entry->psq, ++ NBITVAL((WL_TXSTATUS_GET_FIFO(status) << 1) + 1)); ++ entry->suppr_transit_count = entry->transit_count; ++ } ++ entry->generation = WLFC_PKTID_GEN(status); ++ } ++ ++#ifdef PROP_TXSTATUS_DEBUG ++ { ++ uint32 new_t = OSL_SYSUPTIME(); ++ uint32 old_t; ++ uint32 delta; ++ old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[ ++ WLFC_PKTID_HSLOT_GET(status)].push_time; ++ ++ ++ wlfc->stats.latency_sample_count++; ++ if (new_t > old_t) ++ delta = new_t - old_t; ++ else ++ delta = 0xffffffff + new_t - old_t; ++ wlfc->stats.total_status_latency += delta; ++ wlfc->stats.latency_most_recent = delta; ++ ++ wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta; ++ if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32)) ++ wlfc->stats.idx_delta = 0; ++ } ++#endif /* PROP_TXSTATUS_DEBUG */ ++ ++ fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf)); ++ ++ /* pick up the implicit credit from this packet */ ++ if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) { ++ if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) { ++ ++ int lender, credit_returned = 0; /* Note that borrower is fifo_id */ ++ ++ /* Return credits to highest priority lender first */ ++ for (lender = AC_COUNT; lender >= 0; lender--) { ++ if (wlfc->credits_borrowed[fifo_id][lender] > 0) { ++ wlfc->FIFO_credit[lender]++; ++ wlfc->credits_borrowed[fifo_id][lender]--; ++ credit_returned = 1; ++ break; ++ } ++ } ++ ++ if (!credit_returned) { ++ wlfc->FIFO_credit[fifo_id]++; ++ } ++ } ++ } ++ else { ++ /* ++ if this packet did not count against FIFO credit, it must have ++ taken a requested_credit from the destination entry (for pspoll etc.) ++ */ ++ if (!entry) { ++ ++ entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf); ++ } ++ if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf))) ++ entry->requested_credit++; ++#ifdef PROP_TXSTATUS_DEBUG ++ entry->dstncredit_acks++; ++#endif ++ } ++ if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) || ++ (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) { ++ ++ ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf); ++ if (ret != BCME_OK) { ++ /* delay q is full, drop this packet */ ++ dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(status), ++ &pktbuf, 1); ++ ++ /* indicate failure and free the packet */ ++ dhd_txcomplete(dhd, pktbuf, FALSE); ++ entry->transit_count--; ++ /* packet is transmitted Successfully by dongle ++ * after first suppress. ++ */ ++ if (entry->suppressed) { ++ entry->suppr_transit_count--; ++ } ++ PKTFREE(wlfc->osh, pktbuf, TRUE); ++ } else { ++ /* Mark suppressed to avoid a double free during wlfc cleanup */ ++ ++ dhd_wlfc_hanger_mark_suppressed(wlfc->hanger, ++ WLFC_PKTID_HSLOT_GET(status), WLFC_PKTID_GEN(status)); ++ entry->suppress_count++; ++ } ++ } ++ else { ++ dhd_txcomplete(dhd, pktbuf, TRUE); ++ entry->transit_count--; ++ ++ /* This packet is transmitted Successfully by dongle ++ * even after first suppress. ++ */ ++ if (entry->suppressed) { ++ entry->suppr_transit_count--; ++ } ++ /* free the packet */ ++ PKTFREE(wlfc->osh, pktbuf, TRUE); ++ } ++ } ++ return BCME_OK; ++} ++ ++/* Handle discard or suppress indication */ ++static int ++dhd_wlfc_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info) ++{ ++ uint8 status_flag; ++ uint32 status; ++ int ret; ++ int remove_from_hanger = 1; ++ void* pktbuf; ++ uint8 fifo_id; ++ wlfc_mac_descriptor_t* entry = NULL; ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhd->wlfc_state; ++ ++ memcpy(&status, pkt_info, sizeof(uint32)); ++ status_flag = WL_TXSTATUS_GET_FLAGS(status); ++ wlfc->stats.txstatus_in++; ++ ++ if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) { ++ wlfc->stats.pkt_freed++; ++ } ++ ++ else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) { ++ wlfc->stats.d11_suppress++; ++ remove_from_hanger = 0; ++ } ++ ++ else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) { ++ wlfc->stats.wl_suppress++; ++ remove_from_hanger = 0; ++ } ++ ++ else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) { ++ wlfc->stats.wlc_tossed_pkts++; ++ } ++ ++ ret = dhd_wlfc_hanger_poppkt(wlfc->hanger, ++ WLFC_PKTID_HSLOT_GET(status), &pktbuf, remove_from_hanger); ++ if (ret != BCME_OK) { ++ /* do something */ ++ return ret; ++ } ++ ++ entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf); ++ ++ if (!remove_from_hanger) { ++ /* this packet was suppressed */ ++ if (!entry->suppressed || entry->generation != WLFC_PKTID_GEN(status)) { ++ entry->suppressed = TRUE; ++ entry->suppress_count = pktq_mlen(&entry->psq, ++ NBITVAL((WL_TXSTATUS_GET_FIFO(status) << 1) + 1)); ++ entry->suppr_transit_count = entry->transit_count; ++ } ++ entry->generation = WLFC_PKTID_GEN(status); ++ } ++ ++#ifdef PROP_TXSTATUS_DEBUG ++ { ++ uint32 new_t = OSL_SYSUPTIME(); ++ uint32 old_t; ++ uint32 delta; ++ old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[ ++ WLFC_PKTID_HSLOT_GET(status)].push_time; ++ ++ ++ wlfc->stats.latency_sample_count++; ++ if (new_t > old_t) ++ delta = new_t - old_t; ++ else ++ delta = 0xffffffff + new_t - old_t; ++ wlfc->stats.total_status_latency += delta; ++ wlfc->stats.latency_most_recent = delta; ++ ++ wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta; ++ if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32)) ++ wlfc->stats.idx_delta = 0; ++ } ++#endif /* PROP_TXSTATUS_DEBUG */ ++ ++ fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf)); ++ ++ /* pick up the implicit credit from this packet */ ++ if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) { ++ if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) { ++ ++ int lender, credit_returned = 0; /* Note that borrower is fifo_id */ ++ ++ /* Return credits to highest priority lender first */ ++ for (lender = AC_COUNT; lender >= 0; lender--) { ++ if (wlfc->credits_borrowed[fifo_id][lender] > 0) { ++ wlfc->FIFO_credit[lender]++; ++ wlfc->credits_borrowed[fifo_id][lender]--; ++ credit_returned = 1; ++ break; ++ } ++ } ++ ++ if (!credit_returned) { ++ wlfc->FIFO_credit[fifo_id]++; ++ } ++ } ++ } ++ else { ++ /* ++ if this packet did not count against FIFO credit, it must have ++ taken a requested_credit from the destination entry (for pspoll etc.) ++ */ ++ if (!entry) { ++ ++ entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf); ++ } ++ if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf))) ++ entry->requested_credit++; ++#ifdef PROP_TXSTATUS_DEBUG ++ entry->dstncredit_acks++; ++#endif ++ } ++ if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) || ++ (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) { ++ ++ ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf); ++ if (ret != BCME_OK) { ++ /* delay q is full, drop this packet */ ++ dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(status), ++ &pktbuf, 1); ++ ++ /* indicate failure and free the packet */ ++ dhd_txcomplete(dhd, pktbuf, FALSE); ++ entry->transit_count--; ++ /* This packet is transmitted Successfully by ++ * dongle even after first suppress. ++ */ ++ if (entry->suppressed) { ++ entry->suppr_transit_count--; ++ } ++ PKTFREE(wlfc->osh, pktbuf, TRUE); ++ } else { ++ /* Mark suppressed to avoid a double free during wlfc cleanup */ ++ dhd_wlfc_hanger_mark_suppressed(wlfc->hanger, ++ WLFC_PKTID_HSLOT_GET(status), WLFC_PKTID_GEN(status)); ++ entry->suppress_count++; ++ } ++ } ++ else { ++ dhd_txcomplete(dhd, pktbuf, TRUE); ++ entry->transit_count--; ++ ++ /* This packet is transmitted Successfully by dongle even after first suppress. */ ++ if (entry->suppressed) { ++ entry->suppr_transit_count--; ++ } ++ /* free the packet */ ++ PKTFREE(wlfc->osh, pktbuf, TRUE); ++ } ++ return BCME_OK; ++} ++ ++static int ++dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits) ++{ ++ int i; ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhd->wlfc_state; ++ for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) { ++#ifdef PROP_TXSTATUS_DEBUG ++ wlfc->stats.fifo_credits_back[i] += credits[i]; ++#endif ++ /* update FIFO credits */ ++ if (wlfc->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT) ++ { ++ int lender; /* Note that borrower is i */ ++ ++ /* Return credits to highest priority lender first */ ++ for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) { ++ if (wlfc->credits_borrowed[i][lender] > 0) { ++ if (credits[i] >= wlfc->credits_borrowed[i][lender]) { ++ credits[i] -= wlfc->credits_borrowed[i][lender]; ++ wlfc->FIFO_credit[lender] += ++ wlfc->credits_borrowed[i][lender]; ++ wlfc->credits_borrowed[i][lender] = 0; ++ } ++ else { ++ wlfc->credits_borrowed[i][lender] -= credits[i]; ++ wlfc->FIFO_credit[lender] += credits[i]; ++ credits[i] = 0; ++ } ++ } ++ } ++ ++ /* If we have more credits left over, these must belong to the AC */ ++ if (credits[i] > 0) { ++ wlfc->FIFO_credit[i] += credits[i]; ++ } ++ } ++ } ++ ++ return BCME_OK; ++} ++ ++static int ++dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value) ++{ ++ uint32 timestamp; ++ ++ (void)dhd; ++ ++ bcopy(&value[2], ×tamp, sizeof(uint32)); ++ DHD_INFO(("RXPKT: SEQ: %d, timestamp %d\n", value[1], timestamp)); ++ return BCME_OK; ++} ++ ++ ++static int ++dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi) ++{ ++ (void)dhd; ++ (void)rssi; ++ return BCME_OK; ++} ++ ++static int ++dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type) ++{ ++ int rc; ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhd->wlfc_state; ++ wlfc_mac_descriptor_t* table; ++ uint8 existing_index; ++ uint8 table_index; ++ uint8 ifid; ++ uint8* ea; ++ ++ WLFC_DBGMESG(("%s(), mac [%02x:%02x:%02x:%02x:%02x:%02x],%s,idx:%d,id:0x%02x\n", ++ __FUNCTION__, value[2], value[3], value[4], value[5], value[6], value[7], ++ ((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"), ++ WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0])); ++ ++ table = wlfc->destination_entries.nodes; ++ table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]); ++ ifid = value[1]; ++ ea = &value[2]; ++ ++ if (type == WLFC_CTL_TYPE_MACDESC_ADD) { ++ existing_index = dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]); ++ if (existing_index == WLFC_MAC_DESC_ID_INVALID) { ++ /* this MAC entry does not exist, create one */ ++ if (!table[table_index].occupied) { ++ table[table_index].mac_handle = value[0]; ++ rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index], ++ eWLFC_MAC_ENTRY_ACTION_ADD, ifid, ++ wlfc->destination_entries.interfaces[ifid].iftype, ++ ea); ++ } ++ else { ++ /* the space should have been empty, but it's not */ ++ wlfc->stats.mac_update_failed++; ++ } ++ } ++ else { ++ /* ++ there is an existing entry, move it to new index ++ if necessary. ++ */ ++ if (existing_index != table_index) { ++ /* if we already have an entry, free the old one */ ++ table[existing_index].occupied = 0; ++ table[existing_index].state = WLFC_STATE_CLOSE; ++ table[existing_index].requested_credit = 0; ++ table[existing_index].interface_id = 0; ++ /* enable after packets are queued-deqeued properly. ++ pktq_flush(dhd->osh, &table[existing_index].psq, FALSE, NULL, 0); ++ */ ++ } ++ } ++ } ++ if (type == WLFC_CTL_TYPE_MACDESC_DEL) { ++ if (table[table_index].occupied) { ++ rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index], ++ eWLFC_MAC_ENTRY_ACTION_DEL, ifid, ++ wlfc->destination_entries.interfaces[ifid].iftype, ++ ea); ++ } ++ else { ++ /* the space should have been occupied, but it's not */ ++ wlfc->stats.mac_update_failed++; ++ } ++ } ++ BCM_REFERENCE(rc); ++ return BCME_OK; ++} ++ ++static int ++dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type) ++{ ++ /* Handle PS on/off indication */ ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhd->wlfc_state; ++ wlfc_mac_descriptor_t* table; ++ wlfc_mac_descriptor_t* desc; ++ uint8 mac_handle = value[0]; ++ int i; ++ ++ table = wlfc->destination_entries.nodes; ++ desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)]; ++ if (desc->occupied) { ++ /* a fresh PS mode should wipe old ps credits? */ ++ desc->requested_credit = 0; ++ if (type == WLFC_CTL_TYPE_MAC_OPEN) { ++ desc->state = WLFC_STATE_OPEN; ++ DHD_WLFC_CTRINC_MAC_OPEN(desc); ++ } ++ else { ++ desc->state = WLFC_STATE_CLOSE; ++ DHD_WLFC_CTRINC_MAC_CLOSE(desc); ++ /* ++ Indicate to firmware if there is any traffic pending. ++ */ ++ for (i = AC_BE; i < AC_COUNT; i++) { ++ _dhd_wlfc_traffic_pending_check(wlfc, desc, i); ++ } ++ } ++ } ++ else { ++ wlfc->stats.psmode_update_failed++; ++ } ++ return BCME_OK; ++} ++ ++static int ++dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type) ++{ ++ /* Handle PS on/off indication */ ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhd->wlfc_state; ++ wlfc_mac_descriptor_t* table; ++ uint8 if_id = value[0]; ++ ++ if (if_id < WLFC_MAX_IFNUM) { ++ table = wlfc->destination_entries.interfaces; ++ if (table[if_id].occupied) { ++ if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) { ++ table[if_id].state = WLFC_STATE_OPEN; ++ /* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */ ++ } ++ else { ++ table[if_id].state = WLFC_STATE_CLOSE; ++ /* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */ ++ } ++ return BCME_OK; ++ } ++ } ++ wlfc->stats.interface_update_failed++; ++ ++ return BCME_OK; ++} ++ ++static int ++dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value) ++{ ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhd->wlfc_state; ++ wlfc_mac_descriptor_t* table; ++ wlfc_mac_descriptor_t* desc; ++ uint8 mac_handle; ++ uint8 credit; ++ ++ table = wlfc->destination_entries.nodes; ++ mac_handle = value[1]; ++ credit = value[0]; ++ ++ desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)]; ++ if (desc->occupied) { ++ desc->requested_credit = credit; ++ ++ desc->ac_bitmap = value[2]; ++ } ++ else { ++ wlfc->stats.credit_request_failed++; ++ } ++ return BCME_OK; ++} ++ ++static int ++dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value) ++{ ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhd->wlfc_state; ++ wlfc_mac_descriptor_t* table; ++ wlfc_mac_descriptor_t* desc; ++ uint8 mac_handle; ++ uint8 packet_count; ++ ++ table = wlfc->destination_entries.nodes; ++ mac_handle = value[1]; ++ packet_count = value[0]; ++ ++ desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)]; ++ if (desc->occupied) { ++ desc->requested_packet = packet_count; ++ ++ desc->ac_bitmap = value[2]; ++ } ++ else { ++ wlfc->stats.packet_request_failed++; ++ } ++ return BCME_OK; ++} ++ ++static void ++dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len, uchar *info_buf, uint *info_len) ++{ ++ if (info_len) { ++ if (info_buf) { ++ bcopy(val, info_buf, len); ++ *info_len = len; ++ } ++ else ++ *info_len = 0; ++ } ++} ++ ++static int ++dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar *reorder_info_buf, ++ uint *reorder_info_len) ++{ ++ uint8 type, len; ++ uint8* value; ++ uint8* tmpbuf; ++ uint16 remainder = tlv_hdr_len; ++ uint16 processed = 0; ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhd->wlfc_state; ++ tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf); ++ if (remainder) { ++ while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) { ++ type = tmpbuf[processed]; ++ if (type == WLFC_CTL_TYPE_FILLER) { ++ remainder -= 1; ++ processed += 1; ++ continue; ++ } ++ ++ len = tmpbuf[processed + 1]; ++ value = &tmpbuf[processed + 2]; ++ ++ if (remainder < (2 + len)) ++ break; ++ ++ remainder -= 2 + len; ++ processed += 2 + len; ++ if (type == WLFC_CTL_TYPE_TXSTATUS) ++ dhd_wlfc_txstatus_update(dhd, value); ++ if (type == WLFC_CTL_TYPE_COMP_TXSTATUS) ++ dhd_wlfc_compressed_txstatus_update(dhd, value, len); ++ ++ else if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS) ++ dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf, ++ reorder_info_len); ++ else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK) ++ dhd_wlfc_fifocreditback_indicate(dhd, value); ++ ++ else if (type == WLFC_CTL_TYPE_RSSI) ++ dhd_wlfc_rssi_indicate(dhd, value); ++ ++ else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT) ++ dhd_wlfc_credit_request(dhd, value); ++ ++ else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET) ++ dhd_wlfc_packet_request(dhd, value); ++ ++ else if ((type == WLFC_CTL_TYPE_MAC_OPEN) || ++ (type == WLFC_CTL_TYPE_MAC_CLOSE)) ++ dhd_wlfc_psmode_update(dhd, value, type); ++ ++ else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) || ++ (type == WLFC_CTL_TYPE_MACDESC_DEL)) ++ dhd_wlfc_mac_table_update(dhd, value, type); ++ ++ else if (type == WLFC_CTL_TYPE_TRANS_ID) ++ dhd_wlfc_dbg_senum_check(dhd, value); ++ ++ else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) || ++ (type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) { ++ dhd_wlfc_interface_update(dhd, value, type); ++ } ++ } ++ if (remainder != 0) { ++ /* trouble..., something is not right */ ++ wlfc->stats.tlv_parse_failed++; ++ } ++ } ++ return BCME_OK; ++} ++ ++int ++dhd_wlfc_init(dhd_pub_t *dhd) ++{ ++ char iovbuf[12]; /* Room for "tlv" + '\0' + parameter */ ++ /* enable all signals & indicate host proptxstatus logic is active */ ++ uint32 tlv = dhd->wlfc_enabled? ++ WLFC_FLAGS_RSSI_SIGNALS | ++ WLFC_FLAGS_XONXOFF_SIGNALS | ++ WLFC_FLAGS_CREDIT_STATUS_SIGNALS | ++ WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE | ++ WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0; ++ /* WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE | WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0; */ ++ ++ ++ /* ++ try to enable/disable signaling by sending "tlv" iovar. if that fails, ++ fallback to no flow control? Print a message for now. ++ */ ++ ++ /* enable proptxtstatus signaling by default */ ++ bcm_mkiovar("tlv", (char *)&tlv, 4, iovbuf, sizeof(iovbuf)); ++ if (dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0) < 0) { ++ DHD_ERROR(("dhd_wlfc_init(): failed to enable/disable bdcv2 tlv signaling\n")); ++ } ++ else { ++ /* ++ Leaving the message for now, it should be removed after a while; once ++ the tlv situation is stable. ++ */ ++ DHD_ERROR(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n", ++ dhd->wlfc_enabled?"enabled":"disabled", tlv)); ++ } ++ return BCME_OK; ++} ++ ++int ++dhd_wlfc_enable(dhd_pub_t *dhd) ++{ ++ int i; ++ athost_wl_status_info_t* wlfc; ++ ++ DHD_TRACE(("Enter %s\n", __FUNCTION__)); ++ ++ if (!dhd->wlfc_enabled || dhd->wlfc_state) ++ return BCME_OK; ++ ++ /* allocate space to track txstatus propagated from firmware */ ++ dhd->wlfc_state = MALLOC(dhd->osh, sizeof(athost_wl_status_info_t)); ++ if (dhd->wlfc_state == NULL) ++ return BCME_NOMEM; ++ ++ /* initialize state space */ ++ wlfc = (athost_wl_status_info_t*)dhd->wlfc_state; ++ memset(wlfc, 0, sizeof(athost_wl_status_info_t)); ++ ++ /* remember osh & dhdp */ ++ wlfc->osh = dhd->osh; ++ wlfc->dhdp = dhd; ++ ++ wlfc->hanger = ++ dhd_wlfc_hanger_create(dhd->osh, WLFC_HANGER_MAXITEMS); ++ if (wlfc->hanger == NULL) { ++ MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t)); ++ dhd->wlfc_state = NULL; ++ return BCME_NOMEM; ++ } ++ ++ /* initialize all interfaces to accept traffic */ ++ for (i = 0; i < WLFC_MAX_IFNUM; i++) { ++ wlfc->hostif_flow_state[i] = OFF; ++ } ++ ++ /* ++ create the SENDQ containing ++ sub-queues for all AC precedences + 1 for bc/mc traffic ++ */ ++ pktq_init(&wlfc->SENDQ, (AC_COUNT + 1), WLFC_SENDQ_LEN); ++ ++ wlfc->destination_entries.other.state = WLFC_STATE_OPEN; ++ /* bc/mc FIFO is always open [credit aside], i.e. b[5] */ ++ wlfc->destination_entries.other.ac_bitmap = 0x1f; ++ wlfc->destination_entries.other.interface_id = 0; ++ ++ wlfc->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT; ++ ++ wlfc->allow_credit_borrow = TRUE; ++ wlfc->borrow_defer_timestamp = 0; ++ ++ return BCME_OK; ++} ++ ++/* release all packet resources */ ++void ++dhd_wlfc_cleanup(dhd_pub_t *dhd) ++{ ++ int i; ++ int total_entries; ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhd->wlfc_state; ++ wlfc_mac_descriptor_t* table; ++ wlfc_hanger_t* h; ++ int prec; ++ void *pkt = NULL; ++ struct pktq *txq = NULL; ++ ++ DHD_TRACE(("Enter %s\n", __FUNCTION__)); ++ if (dhd->wlfc_state == NULL) ++ return; ++ /* flush bus->txq */ ++ txq = dhd_bus_txq(dhd->bus); ++ ++ /* any in the hanger? */ ++ h = (wlfc_hanger_t*)wlfc->hanger; ++ total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t); ++ /* search all entries, include nodes as well as interfaces */ ++ table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries; ++ ++ for (i = 0; i < total_entries; i++) { ++ if (table[i].occupied) { ++ if (table[i].psq.len) { ++ WLFC_DBGMESG(("%s(): DELAYQ[%d].len = %d\n", ++ __FUNCTION__, i, table[i].psq.len)); ++ /* release packets held in DELAYQ */ ++ pktq_flush(wlfc->osh, &table[i].psq, TRUE, NULL, 0); ++ } ++ table[i].occupied = 0; ++ } ++ } ++ /* release packets held in SENDQ */ ++ if (wlfc->SENDQ.len) ++ pktq_flush(wlfc->osh, &wlfc->SENDQ, TRUE, NULL, 0); ++ for (prec = 0; prec < txq->num_prec; prec++) { ++ pkt = pktq_pdeq(txq, prec); ++ while (pkt) { ++ for (i = 0; i < h->max_items; i++) { ++ if (pkt == h->items[i].pkt) { ++ if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) { ++ PKTFREE(wlfc->osh, h->items[i].pkt, TRUE); ++ h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; ++ h->items[i].pkt = NULL; ++ h->items[i].identifier = 0; ++ } else if (h->items[i].state == ++ WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) { ++ /* These are already freed from the psq */ ++ h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; ++ } ++ break; ++ } ++ } ++ pkt = pktq_pdeq(txq, prec); ++ } ++ } ++ /* flush remained pkt in hanger queue, not in bus->txq */ ++ for (i = 0; i < h->max_items; i++) { ++ if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) { ++ if (!dhd->hang_was_sent) { ++ PKTFREE(wlfc->osh, h->items[i].pkt, TRUE); ++ } else { ++ printk("%s: Skip freeing skb %p\n", __func__, h->items[i].pkt); ++ } ++ h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; ++ h->items[i].pkt = NULL; ++ h->items[i].identifier = 0; ++ } else if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) { ++ /* These are freed from the psq so no need to free again */ ++ h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; ++ } ++ } ++ return; ++} ++ ++void ++dhd_wlfc_deinit(dhd_pub_t *dhd) ++{ ++ /* cleanup all psq related resources */ ++ athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) ++ dhd->wlfc_state; ++ ++ DHD_TRACE(("Enter %s\n", __FUNCTION__)); ++ ++ dhd_os_wlfc_block(dhd); ++ if (dhd->wlfc_state == NULL) { ++ dhd_os_wlfc_unblock(dhd); ++ return; ++ } ++#ifdef PROP_TXSTATUS_DEBUG ++ { ++ int i; ++ wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger; ++ for (i = 0; i < h->max_items; i++) { ++ if (h->items[i].state != WLFC_HANGER_ITEM_STATE_FREE) { ++ WLFC_DBGMESG(("%s() pkt[%d] = 0x%p, FIFO_credit_used:%d\n", ++ __FUNCTION__, i, h->items[i].pkt, ++ DHD_PKTTAG_CREDITCHECK(PKTTAG(h->items[i].pkt)))); ++ } ++ } ++ } ++#endif ++ /* delete hanger */ ++ dhd_wlfc_hanger_delete(dhd->osh, wlfc->hanger); ++ ++ /* free top structure */ ++ MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t)); ++ dhd->wlfc_state = NULL; ++ dhd_os_wlfc_unblock(dhd); ++ return; ++} ++#endif /* PROP_TXSTATUS */ ++ ++void ++dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) ++{ ++ bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid); ++#ifdef PROP_TXSTATUS ++ dhd_os_wlfc_block(dhdp); ++ if (dhdp->wlfc_state) ++ dhd_wlfc_dump(dhdp, strbuf); ++ dhd_os_wlfc_unblock(dhdp); ++#endif ++} ++ ++void ++dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf) ++{ ++#ifdef BDC ++ struct bdc_header *h; ++#endif /* BDC */ ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++#ifdef BDC ++ /* Push BDC header used to convey priority for buses that don't */ ++ ++ PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN); ++ ++ h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf); ++ ++ h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); ++ if (PKTSUMNEEDED(pktbuf)) ++ h->flags |= BDC_FLAG_SUM_NEEDED; ++ ++ ++ h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK); ++ h->flags2 = 0; ++ h->dataOffset = 0; ++#endif /* BDC */ ++ BDC_SET_IF_IDX(h, ifidx); ++} ++ ++int ++dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf, uchar *reorder_buf_info, ++ uint *reorder_info_len) ++{ ++#ifdef BDC ++ struct bdc_header *h; ++#endif ++ uint8 data_offset = 0; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++#ifdef BDC ++ if (reorder_info_len) ++ *reorder_info_len = 0; ++ /* Pop BDC header used to convey priority for buses that don't */ ++ ++ if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) { ++ DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__, ++ PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN)); ++ return BCME_ERROR; ++ } ++ ++ h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf); ++ ++#if defined(NDIS630) ++ h->dataOffset = 0; ++#endif ++ ++ if (!ifidx) { ++ /* for tx packet, skip the analysis and just exit */ ++ data_offset = h->dataOffset; ++ PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN); ++ goto exit; ++ } ++ ++ if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) { ++ DHD_ERROR(("%s: rx data ifnum out of range (%d)\n", ++ __FUNCTION__, *ifidx)); ++ return BCME_ERROR; ++ } ++ ++ if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) { ++ DHD_ERROR(("%s: non-BDC packet received, flags = 0x%x\n", ++ dhd_ifname(dhd, *ifidx), h->flags)); ++ if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) == BDC_PROTO_VER_1) ++ h->dataOffset = 0; ++ else ++ return BCME_ERROR; ++ } ++ ++ if (h->flags & BDC_FLAG_SUM_GOOD) { ++ DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n", ++ dhd_ifname(dhd, *ifidx), h->flags)); ++ PKTSETSUMGOOD(pktbuf, TRUE); ++ } ++ ++ PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK)); ++ data_offset = h->dataOffset; ++ PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN); ++#endif /* BDC */ ++ ++#if !defined(NDIS630) ++ if (PKTLEN(dhd->osh, pktbuf) < (uint32) (data_offset << 2)) { ++ DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__, ++ PKTLEN(dhd->osh, pktbuf), (data_offset * 4))); ++ return BCME_ERROR; ++ } ++#endif ++#ifdef PROP_TXSTATUS ++ if (dhd->wlfc_state && ++ ((athost_wl_status_info_t*)dhd->wlfc_state)->proptxstatus_mode ++ != WLFC_FCMODE_NONE && ++ (!DHD_PKTTAG_PKTDIR(PKTTAG(pktbuf)))) { ++ /* ++ - parse txstatus only for packets that came from the firmware ++ */ ++ dhd_os_wlfc_block(dhd); ++ dhd_wlfc_parse_header_info(dhd, pktbuf, (data_offset << 2), ++ reorder_buf_info, reorder_info_len); ++ ((athost_wl_status_info_t*)dhd->wlfc_state)->stats.dhd_hdrpulls++; ++ dhd_os_wlfc_unblock(dhd); ++ } ++#endif /* PROP_TXSTATUS */ ++exit: ++#if !defined(NDIS630) ++ PKTPULL(dhd->osh, pktbuf, (data_offset << 2)); ++#endif ++ return 0; ++} ++ ++#if defined(PROP_TXSTATUS) ++void ++dhd_wlfc_trigger_pktcommit(dhd_pub_t *dhd) ++{ ++ if (dhd->wlfc_state && ++ (((athost_wl_status_info_t*)dhd->wlfc_state)->proptxstatus_mode ++ != WLFC_FCMODE_NONE)) { ++ dhd_os_wlfc_block(dhd); ++ dhd_wlfc_commit_packets(dhd->wlfc_state, (f_commitpkt_t)dhd_bus_txdata, ++ (void *)dhd->bus); ++ dhd_os_wlfc_unblock(dhd); ++ } ++} ++#endif ++ ++int ++dhd_prot_attach(dhd_pub_t *dhd) ++{ ++ dhd_prot_t *cdc; ++ ++ if (!(cdc = (dhd_prot_t *)DHD_OS_PREALLOC(dhd->osh, DHD_PREALLOC_PROT, ++ sizeof(dhd_prot_t)))) { ++ DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); ++ goto fail; ++ } ++ memset(cdc, 0, sizeof(dhd_prot_t)); ++ ++ /* ensure that the msg buf directly follows the cdc msg struct */ ++ if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) { ++ DHD_ERROR(("dhd_prot_t is not correctly defined\n")); ++ goto fail; ++ } ++ ++ dhd->prot = cdc; ++#ifdef BDC ++ dhd->hdrlen += BDC_HEADER_LEN; ++#endif ++ dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN; ++ return 0; ++ ++fail: ++#ifndef CONFIG_DHD_USE_STATIC_BUF ++ if (cdc != NULL) ++ MFREE(dhd->osh, cdc, sizeof(dhd_prot_t)); ++#endif /* CONFIG_DHD_USE_STATIC_BUF */ ++ return BCME_NOMEM; ++} ++ ++/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */ ++void ++dhd_prot_detach(dhd_pub_t *dhd) ++{ ++#ifdef PROP_TXSTATUS ++ dhd_wlfc_deinit(dhd); ++#endif ++#ifndef CONFIG_DHD_USE_STATIC_BUF ++ MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t)); ++#endif /* CONFIG_DHD_USE_STATIC_BUF */ ++ dhd->prot = NULL; ++} ++ ++void ++dhd_prot_dstats(dhd_pub_t *dhd) ++{ ++ /* No stats from dongle added yet, copy bus stats */ ++ dhd->dstats.tx_packets = dhd->tx_packets; ++ dhd->dstats.tx_errors = dhd->tx_errors; ++ dhd->dstats.rx_packets = dhd->rx_packets; ++ dhd->dstats.rx_errors = dhd->rx_errors; ++ dhd->dstats.rx_dropped = dhd->rx_dropped; ++ dhd->dstats.multicast = dhd->rx_multicast; ++ return; ++} ++ ++int ++dhd_prot_init(dhd_pub_t *dhd) ++{ ++ int ret = 0; ++ wlc_rev_info_t revinfo; ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ ++ /* Get the device rev info */ ++ memset(&revinfo, 0, sizeof(revinfo)); ++ ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0); ++ if (ret < 0) ++ goto done; ++ ++ ++#if defined(WL_CFG80211) ++ if (dhd_download_fw_on_driverload) ++#endif /* defined(WL_CFG80211) */ ++ ret = dhd_preinit_ioctls(dhd); ++ ++#ifdef PROP_TXSTATUS ++ ret = dhd_wlfc_init(dhd); ++#endif ++ ++ /* Always assumes wl for now */ ++ dhd->iswl = TRUE; ++ ++done: ++ return ret; ++} ++ ++void ++dhd_prot_stop(dhd_pub_t *dhd) ++{ ++ /* Nothing to do for CDC */ ++} ++ ++ ++static void ++dhd_get_hostreorder_pkts(void *osh, struct reorder_info *ptr, void **pkt, ++ uint32 *pkt_count, void **pplast, uint8 start, uint8 end) ++{ ++ uint i; ++ void *plast = NULL, *p; ++ uint32 pkt_cnt = 0; ++ ++ if (ptr->pend_pkts == 0) { ++ DHD_REORDER(("%s: no packets in reorder queue \n", __FUNCTION__)); ++ *pplast = NULL; ++ *pkt_count = 0; ++ *pkt = NULL; ++ return; ++ } ++ if (start == end) ++ i = ptr->max_idx + 1; ++ else { ++ if (start > end) ++ i = ((ptr->max_idx + 1) - start) + end; ++ else ++ i = end - start; ++ } ++ while (i) { ++ p = (void *)(ptr->p[start]); ++ ptr->p[start] = NULL; ++ ++ if (p != NULL) { ++ if (plast == NULL) ++ *pkt = p; ++ else ++ PKTSETNEXT(osh, plast, p); ++ ++ plast = p; ++ pkt_cnt++; ++ } ++ i--; ++ if (start++ == ptr->max_idx) ++ start = 0; ++ } ++ *pplast = plast; ++ *pkt_count = (uint32)pkt_cnt; ++} ++ ++int ++dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf, uint reorder_info_len, ++ void **pkt, uint32 *pkt_count) ++{ ++ uint8 flow_id, max_idx, cur_idx, exp_idx; ++ struct reorder_info *ptr; ++ uint8 flags; ++ void *cur_pkt, *plast = NULL; ++ uint32 cnt = 0; ++ ++ if (pkt == NULL) { ++ if (pkt_count != NULL) ++ *pkt_count = 0; ++ return 0; ++ } ++ ++ flow_id = reorder_info_buf[WLHOST_REORDERDATA_FLOWID_OFFSET]; ++ flags = reorder_info_buf[WLHOST_REORDERDATA_FLAGS_OFFSET]; ++ ++ DHD_REORDER(("flow_id %d, flags 0x%02x, idx(%d, %d, %d)\n", flow_id, flags, ++ reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET], ++ reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET], ++ reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET])); ++ ++ /* validate flags and flow id */ ++ if (flags == 0xFF) { ++ DHD_ERROR(("%s: invalid flags...so ignore this packet\n", __FUNCTION__)); ++ *pkt_count = 1; ++ return 0; ++ } ++ ++ cur_pkt = *pkt; ++ *pkt = NULL; ++ ++ ptr = dhd->reorder_bufs[flow_id]; ++ if (flags & WLHOST_REORDERDATA_DEL_FLOW) { ++ uint32 buf_size = sizeof(struct reorder_info); ++ ++ DHD_REORDER(("%s: Flags indicating to delete a flow id %d\n", ++ __FUNCTION__, flow_id)); ++ ++ if (ptr == NULL) { ++ DHD_ERROR(("%s: received flags to cleanup, but no flow (%d) yet\n", ++ __FUNCTION__, flow_id)); ++ *pkt_count = 1; ++ *pkt = cur_pkt; ++ return 0; ++ } ++ ++ dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ++ ptr->exp_idx, ptr->exp_idx); ++ /* set it to the last packet */ ++ if (plast) { ++ PKTSETNEXT(dhd->osh, plast, cur_pkt); ++ cnt++; ++ } ++ else { ++ if (cnt != 0) { ++ DHD_ERROR(("%s: del flow: something fishy, pending packets %d\n", ++ __FUNCTION__, cnt)); ++ } ++ *pkt = cur_pkt; ++ cnt = 1; ++ } ++ buf_size += ((ptr->max_idx + 1) * sizeof(void *)); ++ MFREE(dhd->osh, ptr, buf_size); ++ dhd->reorder_bufs[flow_id] = NULL; ++ *pkt_count = cnt; ++ return 0; ++ } ++ /* all the other cases depend on the existance of the reorder struct for that flow id */ ++ if (ptr == NULL) { ++ uint32 buf_size_alloc = sizeof(reorder_info_t); ++ max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET]; ++ ++ buf_size_alloc += ((max_idx + 1) * sizeof(void*)); ++ /* allocate space to hold the buffers, index etc */ ++ ++ DHD_REORDER(("%s: alloc buffer of size %d size, reorder info id %d, maxidx %d\n", ++ __FUNCTION__, buf_size_alloc, flow_id, max_idx)); ++ ptr = (struct reorder_info *)MALLOC(dhd->osh, buf_size_alloc); ++ if (ptr == NULL) { ++ DHD_ERROR(("%s: Malloc failed to alloc buffer\n", __FUNCTION__)); ++ *pkt_count = 1; ++ return 0; ++ } ++ bzero(ptr, buf_size_alloc); ++ dhd->reorder_bufs[flow_id] = ptr; ++ ptr->p = (void *)(ptr+1); ++ ptr->max_idx = max_idx; ++ } ++ if (flags & WLHOST_REORDERDATA_NEW_HOLE) { ++ DHD_REORDER(("%s: new hole, so cleanup pending buffers\n", __FUNCTION__)); ++ if (ptr->pend_pkts) { ++ dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ++ ptr->exp_idx, ptr->exp_idx); ++ ptr->pend_pkts = 0; ++ } ++ ptr->cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET]; ++ ptr->exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET]; ++ ptr->max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET]; ++ ptr->p[ptr->cur_idx] = cur_pkt; ++ ptr->pend_pkts++; ++ *pkt_count = cnt; ++ } ++ else if (flags & WLHOST_REORDERDATA_CURIDX_VALID) { ++ cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET]; ++ exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET]; ++ ++ ++ if ((exp_idx == ptr->exp_idx) && (cur_idx != ptr->exp_idx)) { ++ /* still in the current hole */ ++ /* enqueue the current on the buffer chain */ ++ if (ptr->p[cur_idx] != NULL) { ++ DHD_REORDER(("%s: HOLE: ERROR buffer pending..free it\n", ++ __FUNCTION__)); ++ PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE); ++ ptr->p[cur_idx] = NULL; ++ } ++ ptr->p[cur_idx] = cur_pkt; ++ ptr->pend_pkts++; ++ ptr->cur_idx = cur_idx; ++ DHD_REORDER(("%s: fill up a hole..pending packets is %d\n", ++ __FUNCTION__, ptr->pend_pkts)); ++ *pkt_count = 0; ++ *pkt = NULL; ++ } ++ else if (ptr->exp_idx == cur_idx) { ++ /* got the right one ..flush from cur to exp and update exp */ ++ DHD_REORDER(("%s: got the right one now, cur_idx is %d\n", ++ __FUNCTION__, cur_idx)); ++ if (ptr->p[cur_idx] != NULL) { ++ DHD_REORDER(("%s: Error buffer pending..free it\n", ++ __FUNCTION__)); ++ PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE); ++ ptr->p[cur_idx] = NULL; ++ } ++ ptr->p[cur_idx] = cur_pkt; ++ ptr->pend_pkts++; ++ ++ ptr->cur_idx = cur_idx; ++ ptr->exp_idx = exp_idx; ++ ++ dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ++ cur_idx, exp_idx); ++ ptr->pend_pkts -= (uint8)cnt; ++ *pkt_count = cnt; ++ DHD_REORDER(("%s: freeing up buffers %d, still pending %d\n", ++ __FUNCTION__, cnt, ptr->pend_pkts)); ++ } ++ else { ++ uint8 end_idx; ++ bool flush_current = FALSE; ++ /* both cur and exp are moved now .. */ ++ DHD_REORDER(("%s:, flow %d, both moved, cur %d(%d), exp %d(%d)\n", ++ __FUNCTION__, flow_id, ptr->cur_idx, cur_idx, ++ ptr->exp_idx, exp_idx)); ++ if (flags & WLHOST_REORDERDATA_FLUSH_ALL) ++ end_idx = ptr->exp_idx; ++ else ++ end_idx = exp_idx; ++ ++ /* flush pkts first */ ++ dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ++ ptr->exp_idx, end_idx); ++ ++ if (cur_idx == ptr->max_idx) { ++ if (exp_idx == 0) ++ flush_current = TRUE; ++ } else { ++ if (exp_idx == cur_idx + 1) ++ flush_current = TRUE; ++ } ++ if (flush_current) { ++ if (plast) ++ PKTSETNEXT(dhd->osh, plast, cur_pkt); ++ else ++ *pkt = cur_pkt; ++ cnt++; ++ } ++ else { ++ ptr->p[cur_idx] = cur_pkt; ++ ptr->pend_pkts++; ++ } ++ ptr->exp_idx = exp_idx; ++ ptr->cur_idx = cur_idx; ++ *pkt_count = cnt; ++ } ++ } ++ else { ++ uint8 end_idx; ++ /* no real packet but update to exp_seq...that means explicit window move */ ++ exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET]; ++ ++ DHD_REORDER(("%s: move the window, cur_idx is %d, exp is %d, new exp is %d\n", ++ __FUNCTION__, ptr->cur_idx, ptr->exp_idx, exp_idx)); ++ if (flags & WLHOST_REORDERDATA_FLUSH_ALL) ++ end_idx = ptr->exp_idx; ++ else ++ end_idx = exp_idx; ++ ++ dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ptr->exp_idx, end_idx); ++ ptr->pend_pkts -= (uint8)cnt; ++ if (plast) ++ PKTSETNEXT(dhd->osh, plast, cur_pkt); ++ else ++ *pkt = cur_pkt; ++ cnt++; ++ *pkt_count = cnt; ++ /* set the new expected idx */ ++ ptr->exp_idx = exp_idx; ++ } ++ return 0; ++} +diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c +new file mode 100644 +index 00000000..03671c46 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c +@@ -0,0 +1,678 @@ ++/* ++ * Linux cfg80211 driver - Dongle Host Driver (DHD) related ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#ifdef PKT_FILTER_SUPPORT ++#include ++#include ++#endif ++ ++extern struct wl_priv *wlcfg_drv_priv; ++ ++#ifdef PKT_FILTER_SUPPORT ++extern uint dhd_pkt_filter_enable; ++extern uint dhd_master_mode; ++extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode); ++#endif ++ ++static int dhd_dongle_up = FALSE; ++ ++#include ++#include ++#include ++#include ++#include ++ ++static s32 wl_dongle_up(struct net_device *ndev, u32 up); ++ ++/** ++ * Function implementations ++ */ ++ ++s32 dhd_cfg80211_init(struct wl_priv *wl) ++{ ++ dhd_dongle_up = FALSE; ++ return 0; ++} ++ ++s32 dhd_cfg80211_deinit(struct wl_priv *wl) ++{ ++ dhd_dongle_up = FALSE; ++ return 0; ++} ++ ++s32 dhd_cfg80211_down(struct wl_priv *wl) ++{ ++ dhd_dongle_up = FALSE; ++ return 0; ++} ++ ++s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val) ++{ ++ dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); ++ dhd->op_mode |= val; ++ WL_ERR(("Set : op_mode=0x%04x\n", dhd->op_mode)); ++#ifdef ARP_OFFLOAD_SUPPORT ++ if (dhd->arp_version == 1) { ++ /* IF P2P is enabled, disable arpoe */ ++ dhd_arp_offload_set(dhd, 0); ++ dhd_arp_offload_enable(dhd, false); ++ } ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++ return 0; ++} ++ ++s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl) ++{ ++ dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); ++ dhd->op_mode &= ~(DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE); ++ WL_ERR(("Clean : op_mode=0x%04x\n", dhd->op_mode)); ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++ if (dhd->arp_version == 1) { ++ /* IF P2P is disabled, enable arpoe back for STA mode. */ ++ dhd_arp_offload_set(dhd, dhd_arp_mode); ++ dhd_arp_offload_enable(dhd, true); ++ } ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++ return 0; ++} ++ ++static s32 wl_dongle_up(struct net_device *ndev, u32 up) ++{ ++ s32 err = 0; ++ ++ err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), true); ++ if (unlikely(err)) { ++ WL_ERR(("WLC_UP error (%d)\n", err)); ++ } ++ return err; ++} ++s32 dhd_config_dongle(struct wl_priv *wl, bool need_lock) ++{ ++#ifndef DHD_SDALIGN ++#define DHD_SDALIGN 32 ++#endif ++ struct net_device *ndev; ++ s32 err = 0; ++ ++ WL_TRACE(("In\n")); ++ if (dhd_dongle_up) { ++ WL_ERR(("Dongle is already up\n")); ++ return err; ++ } ++ ++ ndev = wl_to_prmry_ndev(wl); ++ ++ if (need_lock) ++ rtnl_lock(); ++ ++ err = wl_dongle_up(ndev, 0); ++ if (unlikely(err)) { ++ WL_ERR(("wl_dongle_up failed\n")); ++ goto default_conf_out; ++ } ++ dhd_dongle_up = true; ++ ++default_conf_out: ++ if (need_lock) ++ rtnl_unlock(); ++ return err; ++ ++} ++ ++ ++/* TODO: clean up the BT-Coex code, it still have some legacy ioctl/iovar functions */ ++#define COEX_DHCP ++ ++#if defined(COEX_DHCP) ++ ++/* use New SCO/eSCO smart YG suppression */ ++#define BT_DHCP_eSCO_FIX ++/* this flag boost wifi pkt priority to max, caution: -not fair to sco */ ++#define BT_DHCP_USE_FLAGS ++/* T1 start SCO/ESCo priority suppression */ ++#define BT_DHCP_OPPR_WIN_TIME 2500 ++/* T2 turn off SCO/SCO supperesion is (timeout) */ ++#define BT_DHCP_FLAG_FORCE_TIME 5500 ++ ++enum wl_cfg80211_btcoex_status { ++ BT_DHCP_IDLE, ++ BT_DHCP_START, ++ BT_DHCP_OPPR_WIN, ++ BT_DHCP_FLAG_FORCE_TIMEOUT ++}; ++ ++/* ++ * get named driver variable to uint register value and return error indication ++ * calling example: dev_wlc_intvar_get_reg(dev, "btc_params",66, ®_value) ++ */ ++static int ++dev_wlc_intvar_get_reg(struct net_device *dev, char *name, ++ uint reg, int *retval) ++{ ++ union { ++ char buf[WLC_IOCTL_SMLEN]; ++ int val; ++ } var; ++ int error; ++ ++ bcm_mkiovar(name, (char *)(®), sizeof(reg), ++ (char *)(&var), sizeof(var.buf)); ++ error = wldev_ioctl(dev, WLC_GET_VAR, (char *)(&var), sizeof(var.buf), false); ++ ++ *retval = dtoh32(var.val); ++ return (error); ++} ++ ++static int ++dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len) ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) ++ char ioctlbuf_local[1024]; ++#else ++ static char ioctlbuf_local[1024]; ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ ++ ++ bcm_mkiovar(name, buf, len, ioctlbuf_local, sizeof(ioctlbuf_local)); ++ ++ return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf_local, sizeof(ioctlbuf_local), true)); ++} ++/* ++get named driver variable to uint register value and return error indication ++calling example: dev_wlc_intvar_set_reg(dev, "btc_params",66, value) ++*/ ++static int ++dev_wlc_intvar_set_reg(struct net_device *dev, char *name, char *addr, char * val) ++{ ++ char reg_addr[8]; ++ ++ memset(reg_addr, 0, sizeof(reg_addr)); ++ memcpy((char *)®_addr[0], (char *)addr, 4); ++ memcpy((char *)®_addr[4], (char *)val, 4); ++ ++ return (dev_wlc_bufvar_set(dev, name, (char *)®_addr[0], sizeof(reg_addr))); ++} ++ ++static bool btcoex_is_sco_active(struct net_device *dev) ++{ ++ int ioc_res = 0; ++ bool res = FALSE; ++ int sco_id_cnt = 0; ++ int param27; ++ int i; ++ ++ for (i = 0; i < 12; i++) { ++ ++ ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27); ++ ++ WL_TRACE(("%s, sample[%d], btc params: 27:%x\n", ++ __FUNCTION__, i, param27)); ++ ++ if (ioc_res < 0) { ++ WL_ERR(("%s ioc read btc params error\n", __FUNCTION__)); ++ break; ++ } ++ ++ if ((param27 & 0x6) == 2) { /* count both sco & esco */ ++ sco_id_cnt++; ++ } ++ ++ if (sco_id_cnt > 2) { ++ WL_TRACE(("%s, sco/esco detected, pkt id_cnt:%d samples:%d\n", ++ __FUNCTION__, sco_id_cnt, i)); ++ res = TRUE; ++ break; ++ } ++ ++ msleep(5); ++ } ++ ++ return res; ++} ++ ++#if defined(BT_DHCP_eSCO_FIX) ++/* Enhanced BT COEX settings for eSCO compatibility during DHCP window */ ++static int set_btc_esco_params(struct net_device *dev, bool trump_sco) ++{ ++ static bool saved_status = FALSE; ++ ++ char buf_reg50va_dhcp_on[8] = ++ { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 }; ++ char buf_reg51va_dhcp_on[8] = ++ { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; ++ char buf_reg64va_dhcp_on[8] = ++ { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; ++ char buf_reg65va_dhcp_on[8] = ++ { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; ++ char buf_reg71va_dhcp_on[8] = ++ { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; ++ uint32 regaddr; ++ static uint32 saved_reg50; ++ static uint32 saved_reg51; ++ static uint32 saved_reg64; ++ static uint32 saved_reg65; ++ static uint32 saved_reg71; ++ ++ if (trump_sco) { ++ /* this should reduce eSCO agressive retransmit ++ * w/o breaking it ++ */ ++ ++ /* 1st save current */ ++ WL_TRACE(("Do new SCO/eSCO coex algo {save &" ++ "override}\n")); ++ if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) && ++ (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) && ++ (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) && ++ (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) && ++ (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) { ++ saved_status = TRUE; ++ WL_TRACE(("%s saved bt_params[50,51,64,65,71]:" ++ "0x%x 0x%x 0x%x 0x%x 0x%x\n", ++ __FUNCTION__, saved_reg50, saved_reg51, ++ saved_reg64, saved_reg65, saved_reg71)); ++ } else { ++ WL_ERR((":%s: save btc_params failed\n", ++ __FUNCTION__)); ++ saved_status = FALSE; ++ return -1; ++ } ++ ++ WL_TRACE(("override with [50,51,64,65,71]:" ++ "0x%x 0x%x 0x%x 0x%x 0x%x\n", ++ *(u32 *)(buf_reg50va_dhcp_on+4), ++ *(u32 *)(buf_reg51va_dhcp_on+4), ++ *(u32 *)(buf_reg64va_dhcp_on+4), ++ *(u32 *)(buf_reg65va_dhcp_on+4), ++ *(u32 *)(buf_reg71va_dhcp_on+4))); ++ ++ dev_wlc_bufvar_set(dev, "btc_params", ++ (char *)&buf_reg50va_dhcp_on[0], 8); ++ dev_wlc_bufvar_set(dev, "btc_params", ++ (char *)&buf_reg51va_dhcp_on[0], 8); ++ dev_wlc_bufvar_set(dev, "btc_params", ++ (char *)&buf_reg64va_dhcp_on[0], 8); ++ dev_wlc_bufvar_set(dev, "btc_params", ++ (char *)&buf_reg65va_dhcp_on[0], 8); ++ dev_wlc_bufvar_set(dev, "btc_params", ++ (char *)&buf_reg71va_dhcp_on[0], 8); ++ ++ saved_status = TRUE; ++ } else if (saved_status) { ++ /* restore previously saved bt params */ ++ WL_TRACE(("Do new SCO/eSCO coex algo {save &" ++ "override}\n")); ++ ++ regaddr = 50; ++ dev_wlc_intvar_set_reg(dev, "btc_params", ++ (char *)®addr, (char *)&saved_reg50); ++ regaddr = 51; ++ dev_wlc_intvar_set_reg(dev, "btc_params", ++ (char *)®addr, (char *)&saved_reg51); ++ regaddr = 64; ++ dev_wlc_intvar_set_reg(dev, "btc_params", ++ (char *)®addr, (char *)&saved_reg64); ++ regaddr = 65; ++ dev_wlc_intvar_set_reg(dev, "btc_params", ++ (char *)®addr, (char *)&saved_reg65); ++ regaddr = 71; ++ dev_wlc_intvar_set_reg(dev, "btc_params", ++ (char *)®addr, (char *)&saved_reg71); ++ ++ WL_TRACE(("restore bt_params[50,51,64,65,71]:" ++ "0x%x 0x%x 0x%x 0x%x 0x%x\n", ++ saved_reg50, saved_reg51, saved_reg64, ++ saved_reg65, saved_reg71)); ++ ++ saved_status = FALSE; ++ } else { ++ WL_ERR((":%s att to restore not saved BTCOEX params\n", ++ __FUNCTION__)); ++ return -1; ++ } ++ return 0; ++} ++#endif /* BT_DHCP_eSCO_FIX */ ++ ++static void ++wl_cfg80211_bt_setflag(struct net_device *dev, bool set) ++{ ++#if defined(BT_DHCP_USE_FLAGS) ++ char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 }; ++ char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; ++#endif ++ ++ ++#if defined(BT_DHCP_eSCO_FIX) ++ /* set = 1, save & turn on 0 - off & restore prev settings */ ++ set_btc_esco_params(dev, set); ++#endif ++ ++#if defined(BT_DHCP_USE_FLAGS) ++ WL_TRACE(("WI-FI priority boost via bt flags, set:%d\n", set)); ++ if (set == TRUE) ++ /* Forcing bt_flag7 */ ++ dev_wlc_bufvar_set(dev, "btc_flags", ++ (char *)&buf_flag7_dhcp_on[0], ++ sizeof(buf_flag7_dhcp_on)); ++ else ++ /* Restoring default bt flag7 */ ++ dev_wlc_bufvar_set(dev, "btc_flags", ++ (char *)&buf_flag7_default[0], ++ sizeof(buf_flag7_default)); ++#endif ++} ++ ++static void wl_cfg80211_bt_timerfunc(ulong data) ++{ ++ struct btcoex_info *bt_local = (struct btcoex_info *)data; ++ WL_TRACE(("%s\n", __FUNCTION__)); ++ bt_local->timer_on = 0; ++ schedule_work(&bt_local->work); ++} ++ ++static void wl_cfg80211_bt_handler(struct work_struct *work) ++{ ++ struct btcoex_info *btcx_inf; ++ ++ btcx_inf = container_of(work, struct btcoex_info, work); ++ ++ if (btcx_inf->timer_on) { ++ btcx_inf->timer_on = 0; ++ del_timer_sync(&btcx_inf->timer); ++ } ++ ++ switch (btcx_inf->bt_state) { ++ case BT_DHCP_START: ++ /* DHCP started ++ * provide OPPORTUNITY window to get DHCP address ++ */ ++ WL_TRACE(("%s bt_dhcp stm: started \n", ++ __FUNCTION__)); ++ btcx_inf->bt_state = BT_DHCP_OPPR_WIN; ++ mod_timer(&btcx_inf->timer, ++ jiffies + msecs_to_jiffies(BT_DHCP_OPPR_WIN_TIME)); ++ btcx_inf->timer_on = 1; ++ break; ++ ++ case BT_DHCP_OPPR_WIN: ++ if (btcx_inf->dhcp_done) { ++ WL_TRACE(("%s DHCP Done before T1 expiration\n", ++ __FUNCTION__)); ++ goto btc_coex_idle; ++ } ++ ++ /* DHCP is not over yet, start lowering BT priority ++ * enforce btc_params + flags if necessary ++ */ ++ WL_TRACE(("%s DHCP T1:%d expired\n", __FUNCTION__, ++ BT_DHCP_OPPR_WIN_TIME)); ++ if (btcx_inf->dev) ++ wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE); ++ btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; ++ mod_timer(&btcx_inf->timer, ++ jiffies + msecs_to_jiffies(BT_DHCP_FLAG_FORCE_TIME)); ++ btcx_inf->timer_on = 1; ++ break; ++ ++ case BT_DHCP_FLAG_FORCE_TIMEOUT: ++ if (btcx_inf->dhcp_done) { ++ WL_TRACE(("%s DHCP Done before T2 expiration\n", ++ __FUNCTION__)); ++ } else { ++ /* Noo dhcp during T1+T2, restore BT priority */ ++ WL_TRACE(("%s DHCP wait interval T2:%d" ++ "msec expired\n", __FUNCTION__, ++ BT_DHCP_FLAG_FORCE_TIME)); ++ } ++ ++ /* Restoring default bt priority */ ++ if (btcx_inf->dev) ++ wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); ++btc_coex_idle: ++ btcx_inf->bt_state = BT_DHCP_IDLE; ++ btcx_inf->timer_on = 0; ++ break; ++ ++ default: ++ WL_ERR(("%s error g_status=%d !!!\n", __FUNCTION__, ++ btcx_inf->bt_state)); ++ if (btcx_inf->dev) ++ wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); ++ btcx_inf->bt_state = BT_DHCP_IDLE; ++ btcx_inf->timer_on = 0; ++ break; ++ } ++ ++ net_os_wake_unlock(btcx_inf->dev); ++} ++ ++int wl_cfg80211_btcoex_init(struct wl_priv *wl) ++{ ++ struct btcoex_info *btco_inf = NULL; ++ ++ btco_inf = kmalloc(sizeof(struct btcoex_info), GFP_KERNEL); ++ if (!btco_inf) ++ return -ENOMEM; ++ ++ btco_inf->bt_state = BT_DHCP_IDLE; ++ btco_inf->ts_dhcp_start = 0; ++ btco_inf->ts_dhcp_ok = 0; ++ /* Set up timer for BT */ ++ btco_inf->timer_ms = 10; ++ init_timer(&btco_inf->timer); ++ btco_inf->timer.data = (ulong)btco_inf; ++ btco_inf->timer.function = wl_cfg80211_bt_timerfunc; ++ ++ btco_inf->dev = wl->wdev->netdev; ++ ++ INIT_WORK(&btco_inf->work, wl_cfg80211_bt_handler); ++ ++ wl->btcoex_info = btco_inf; ++ return 0; ++} ++ ++void wl_cfg80211_btcoex_deinit(struct wl_priv *wl) ++{ ++ if (!wl->btcoex_info) ++ return; ++ ++ if (wl->btcoex_info->timer_on) { ++ wl->btcoex_info->timer_on = 0; ++ del_timer_sync(&wl->btcoex_info->timer); ++ } ++ ++ cancel_work_sync(&wl->btcoex_info->work); ++ ++ kfree(wl->btcoex_info); ++ wl->btcoex_info = NULL; ++} ++#endif ++ ++int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) ++{ ++ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ char powermode_val = 0; ++ char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 }; ++ char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 }; ++ char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 }; ++ ++ uint32 regaddr; ++ static uint32 saved_reg66; ++ static uint32 saved_reg41; ++ static uint32 saved_reg68; ++ static bool saved_status = FALSE; ++ ++#ifdef COEX_DHCP ++ char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; ++ struct btcoex_info *btco_inf = wl->btcoex_info; ++#endif /* COEX_DHCP */ ++ ++#ifdef PKT_FILTER_SUPPORT ++ dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); ++#endif ++ ++ /* Figure out powermode 1 or o command */ ++ strncpy((char *)&powermode_val, command + strlen("BTCOEXMODE") +1, 1); ++ ++ if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) { ++ WL_TRACE_HW4(("%s: DHCP session starts\n", __FUNCTION__)); ++ ++#if defined(DHCP_SCAN_SUPPRESS) ++ /* Suppress scan during the DHCP */ ++ wl_cfg80211_scan_suppress(dev, 1); ++#endif ++ ++#ifdef PKT_FILTER_SUPPORT ++ dhd->dhcp_in_progress = 1; ++ ++ if (dhd->early_suspended) { ++ WL_TRACE_HW4(("DHCP in progressing , disable packet filter!!!\n")); ++ dhd_enable_packet_filter(0, dhd); ++ } ++#endif ++ ++ /* Retrieve and saved orig regs value */ ++ if ((saved_status == FALSE) && ++ (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && ++ (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && ++ (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { ++ saved_status = TRUE; ++ WL_TRACE(("Saved 0x%x 0x%x 0x%x\n", ++ saved_reg66, saved_reg41, saved_reg68)); ++ ++ /* Disable PM mode during dhpc session */ ++ ++ /* Disable PM mode during dhpc session */ ++#ifdef COEX_DHCP ++ /* Start BT timer only for SCO connection */ ++ if (btcoex_is_sco_active(dev)) { ++ /* btc_params 66 */ ++ dev_wlc_bufvar_set(dev, "btc_params", ++ (char *)&buf_reg66va_dhcp_on[0], ++ sizeof(buf_reg66va_dhcp_on)); ++ /* btc_params 41 0x33 */ ++ dev_wlc_bufvar_set(dev, "btc_params", ++ (char *)&buf_reg41va_dhcp_on[0], ++ sizeof(buf_reg41va_dhcp_on)); ++ /* btc_params 68 0x190 */ ++ dev_wlc_bufvar_set(dev, "btc_params", ++ (char *)&buf_reg68va_dhcp_on[0], ++ sizeof(buf_reg68va_dhcp_on)); ++ saved_status = TRUE; ++ ++ btco_inf->bt_state = BT_DHCP_START; ++ btco_inf->timer_on = 1; ++ mod_timer(&btco_inf->timer, btco_inf->timer.expires); ++ WL_TRACE(("%s enable BT DHCP Timer\n", ++ __FUNCTION__)); ++ } ++#endif /* COEX_DHCP */ ++ } ++ else if (saved_status == TRUE) { ++ WL_ERR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__)); ++ } ++ } ++ else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) { ++ ++ ++#ifdef PKT_FILTER_SUPPORT ++ dhd->dhcp_in_progress = 0; ++ WL_TRACE_HW4(("%s: DHCP is complete \n", __FUNCTION__)); ++ ++#if defined(DHCP_SCAN_SUPPRESS) ++ /* Since DHCP is complete, enable the scan back */ ++ wl_cfg80211_scan_suppress(dev, 0); ++#endif ++ ++ /* Enable packet filtering */ ++ if (dhd->early_suspended) { ++ WL_TRACE_HW4(("DHCP is complete , enable packet filter!!!\n")); ++ dhd_enable_packet_filter(1, dhd); ++ } ++#endif /* PKT_FILTER_SUPPORT */ ++ ++ /* Restoring PM mode */ ++ ++#ifdef COEX_DHCP ++ /* Stop any bt timer because DHCP session is done */ ++ WL_TRACE(("%s disable BT DHCP Timer\n", __FUNCTION__)); ++ if (btco_inf->timer_on) { ++ btco_inf->timer_on = 0; ++ del_timer_sync(&btco_inf->timer); ++ ++ if (btco_inf->bt_state != BT_DHCP_IDLE) { ++ /* need to restore original btc flags & extra btc params */ ++ WL_TRACE(("%s bt->bt_state:%d\n", ++ __FUNCTION__, btco_inf->bt_state)); ++ /* wake up btcoex thread to restore btlags+params */ ++ schedule_work(&btco_inf->work); ++ } ++ } ++ ++ /* Restoring btc_flag paramter anyway */ ++ if (saved_status == TRUE) ++ dev_wlc_bufvar_set(dev, "btc_flags", ++ (char *)&buf_flag7_default[0], sizeof(buf_flag7_default)); ++#endif /* COEX_DHCP */ ++ ++ /* Restore original values */ ++ if (saved_status == TRUE) { ++ regaddr = 66; ++ dev_wlc_intvar_set_reg(dev, "btc_params", ++ (char *)®addr, (char *)&saved_reg66); ++ regaddr = 41; ++ dev_wlc_intvar_set_reg(dev, "btc_params", ++ (char *)®addr, (char *)&saved_reg41); ++ regaddr = 68; ++ dev_wlc_intvar_set_reg(dev, "btc_params", ++ (char *)®addr, (char *)&saved_reg68); ++ ++ WL_TRACE(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", ++ saved_reg66, saved_reg41, saved_reg68)); ++ } ++ saved_status = FALSE; ++ ++ } ++ else { ++ WL_ERR(("%s Unkwown yet power setting, ignored\n", ++ __FUNCTION__)); ++ } ++ ++ snprintf(command, 3, "OK"); ++ ++ return (strlen("OK")); ++} +diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.h b/drivers/net/wireless/bcmdhd/dhd_cfg80211.h +new file mode 100644 +index 00000000..922d6edd +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.h +@@ -0,0 +1,44 @@ ++/* ++ * Linux cfg80211 driver - Dongle Host Driver (DHD) related ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ ++ */ ++ ++ ++#ifndef __DHD_CFG80211__ ++#define __DHD_CFG80211__ ++ ++#include ++#include ++ ++s32 dhd_cfg80211_init(struct wl_priv *wl); ++s32 dhd_cfg80211_deinit(struct wl_priv *wl); ++s32 dhd_cfg80211_down(struct wl_priv *wl); ++s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val); ++s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl); ++s32 dhd_config_dongle(struct wl_priv *wl, bool need_lock); ++ ++int wl_cfg80211_btcoex_init(struct wl_priv *wl); ++void wl_cfg80211_btcoex_deinit(struct wl_priv *wl); ++ ++#endif /* __DHD_CFG80211__ */ +diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c +new file mode 100644 +index 00000000..bb765562 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_common.c +@@ -0,0 +1,2205 @@ ++/* ++ * Broadcom Dongle Host Driver (DHD), common DHD core. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhd_common.c 375022 2012-12-17 06:11:41Z $ ++ */ ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#ifdef WL_CFG80211 ++#include ++#endif ++#ifdef WLBTAMP ++#include ++#include ++#endif ++#ifdef SET_RANDOM_MAC_SOFTAP ++#include ++#include ++#endif ++ ++#define htod32(i) i ++#define htod16(i) i ++#define dtoh32(i) i ++#define dtoh16(i) i ++#define htodchanspec(i) i ++#define dtohchanspec(i) i ++ ++#ifdef PROP_TXSTATUS ++#include ++#include ++#endif ++ ++ ++#ifdef WLMEDIA_HTSF ++extern void htsf_update(struct dhd_info *dhd, void *data); ++#endif ++int dhd_msg_level = DHD_ERROR_VAL; ++ ++ ++#include ++ ++char fw_path[MOD_PARAM_PATHLEN]; ++char nv_path[MOD_PARAM_PATHLEN]; ++ ++#ifdef SOFTAP ++char fw_path2[MOD_PARAM_PATHLEN]; ++extern bool softap_enabled; ++#endif ++ ++/* Last connection success/failure status */ ++uint32 dhd_conn_event; ++uint32 dhd_conn_status; ++uint32 dhd_conn_reason; ++ ++extern int dhd_iscan_request(void * dhdp, uint16 action); ++extern void dhd_ind_scan_confirm(void *h, bool status); ++extern int dhd_iscan_in_progress(void *h); ++void dhd_iscan_lock(void); ++void dhd_iscan_unlock(void); ++extern int dhd_change_mtu(dhd_pub_t *dhd, int new_mtu, int ifidx); ++#if !defined(AP) && defined(WLP2P) ++extern int dhd_get_concurrent_capabilites(dhd_pub_t *dhd); ++#endif ++bool ap_cfg_running = FALSE; ++bool ap_fw_loaded = FALSE; ++ ++ ++#ifdef DHD_DEBUG ++const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR "\nCompiled on " ++ __DATE__ " at " __TIME__; ++#else ++const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR; ++#endif ++ ++void dhd_set_timer(void *bus, uint wdtick); ++ ++/* IOVar table */ ++enum { ++ IOV_VERSION = 1, ++ IOV_MSGLEVEL, ++ IOV_BCMERRORSTR, ++ IOV_BCMERROR, ++ IOV_WDTICK, ++ IOV_DUMP, ++ IOV_CLEARCOUNTS, ++ IOV_LOGDUMP, ++ IOV_LOGCAL, ++ IOV_LOGSTAMP, ++ IOV_GPIOOB, ++ IOV_IOCTLTIMEOUT, ++#ifdef WLBTAMP ++ IOV_HCI_CMD, /* HCI command */ ++ IOV_HCI_ACL_DATA, /* HCI data packet */ ++#endif ++#if defined(DHD_DEBUG) ++ IOV_CONS, ++ IOV_DCONSOLE_POLL, ++#endif /* defined(DHD_DEBUG) */ ++#ifdef PROP_TXSTATUS ++ IOV_PROPTXSTATUS_ENABLE, ++ IOV_PROPTXSTATUS_MODE, ++#endif ++ IOV_BUS_TYPE, ++#ifdef WLMEDIA_HTSF ++ IOV_WLPKTDLYSTAT_SZ, ++#endif ++ IOV_CHANGEMTU, ++ IOV_HOSTREORDER_FLOWS, ++ IOV_LAST ++}; ++ ++const bcm_iovar_t dhd_iovars[] = { ++ {"version", IOV_VERSION, 0, IOVT_BUFFER, sizeof(dhd_version) }, ++#ifdef DHD_DEBUG ++ {"msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 }, ++#endif /* DHD_DEBUG */ ++ {"bcmerrorstr", IOV_BCMERRORSTR, 0, IOVT_BUFFER, BCME_STRLEN }, ++ {"bcmerror", IOV_BCMERROR, 0, IOVT_INT8, 0 }, ++ {"wdtick", IOV_WDTICK, 0, IOVT_UINT32, 0 }, ++ {"dump", IOV_DUMP, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN }, ++#ifdef DHD_DEBUG ++ {"cons", IOV_CONS, 0, IOVT_BUFFER, 0 }, ++ {"dconpoll", IOV_DCONSOLE_POLL, 0, IOVT_UINT32, 0 }, ++#endif ++ {"clearcounts", IOV_CLEARCOUNTS, 0, IOVT_VOID, 0 }, ++ {"gpioob", IOV_GPIOOB, 0, IOVT_UINT32, 0 }, ++ {"ioctl_timeout", IOV_IOCTLTIMEOUT, 0, IOVT_UINT32, 0 }, ++#ifdef WLBTAMP ++ {"HCI_cmd", IOV_HCI_CMD, 0, IOVT_BUFFER, 0}, ++ {"HCI_ACL_data", IOV_HCI_ACL_DATA, 0, IOVT_BUFFER, 0}, ++#endif ++#ifdef PROP_TXSTATUS ++ {"proptx", IOV_PROPTXSTATUS_ENABLE, 0, IOVT_UINT32, 0 }, ++ /* ++ set the proptxtstatus operation mode: ++ 0 - Do not do any proptxtstatus flow control ++ 1 - Use implied credit from a packet status ++ 2 - Use explicit credit ++ */ ++ {"ptxmode", IOV_PROPTXSTATUS_MODE, 0, IOVT_UINT32, 0 }, ++#endif ++ {"bustype", IOV_BUS_TYPE, 0, IOVT_UINT32, 0}, ++#ifdef WLMEDIA_HTSF ++ {"pktdlystatsz", IOV_WLPKTDLYSTAT_SZ, 0, IOVT_UINT8, 0 }, ++#endif ++ {"changemtu", IOV_CHANGEMTU, 0, IOVT_UINT32, 0 }, ++ {"host_reorder_flows", IOV_HOSTREORDER_FLOWS, 0, IOVT_BUFFER, ++ (WLHOST_REORDERDATA_MAXFLOWS + 1) }, ++ {NULL, 0, 0, 0, 0 } ++}; ++ ++void ++dhd_common_init(osl_t *osh) ++{ ++#ifdef CONFIG_BCMDHD_FW_PATH ++ bcm_strncpy_s(fw_path, sizeof(fw_path), CONFIG_BCMDHD_FW_PATH, MOD_PARAM_PATHLEN-1); ++#else /* CONFIG_BCMDHD_FW_PATH */ ++ fw_path[0] = '\0'; ++#endif /* CONFIG_BCMDHD_FW_PATH */ ++#ifdef CONFIG_BCMDHD_NVRAM_PATH ++ bcm_strncpy_s(nv_path, sizeof(nv_path), CONFIG_BCMDHD_NVRAM_PATH, MOD_PARAM_PATHLEN-1); ++#else /* CONFIG_BCMDHD_NVRAM_PATH */ ++ nv_path[0] = '\0'; ++#endif /* CONFIG_BCMDHD_NVRAM_PATH */ ++#ifdef SOFTAP ++ fw_path2[0] = '\0'; ++#endif ++} ++ ++static int ++dhd_dump(dhd_pub_t *dhdp, char *buf, int buflen) ++{ ++ char eabuf[ETHER_ADDR_STR_LEN]; ++ ++ struct bcmstrbuf b; ++ struct bcmstrbuf *strbuf = &b; ++ ++ bcm_binit(strbuf, buf, buflen); ++ ++ /* Base DHD info */ ++ bcm_bprintf(strbuf, "%s\n", dhd_version); ++ bcm_bprintf(strbuf, "\n"); ++ bcm_bprintf(strbuf, "pub.up %d pub.txoff %d pub.busstate %d\n", ++ dhdp->up, dhdp->txoff, dhdp->busstate); ++ bcm_bprintf(strbuf, "pub.hdrlen %d pub.maxctl %d pub.rxsz %d\n", ++ dhdp->hdrlen, dhdp->maxctl, dhdp->rxsz); ++ bcm_bprintf(strbuf, "pub.iswl %d pub.drv_version %ld pub.mac %s\n", ++ dhdp->iswl, dhdp->drv_version, bcm_ether_ntoa(&dhdp->mac, eabuf)); ++ bcm_bprintf(strbuf, "pub.bcmerror %d tickcnt %d\n", dhdp->bcmerror, dhdp->tickcnt); ++ ++ bcm_bprintf(strbuf, "dongle stats:\n"); ++ bcm_bprintf(strbuf, "tx_packets %ld tx_bytes %ld tx_errors %ld tx_dropped %ld\n", ++ dhdp->dstats.tx_packets, dhdp->dstats.tx_bytes, ++ dhdp->dstats.tx_errors, dhdp->dstats.tx_dropped); ++ bcm_bprintf(strbuf, "rx_packets %ld rx_bytes %ld rx_errors %ld rx_dropped %ld\n", ++ dhdp->dstats.rx_packets, dhdp->dstats.rx_bytes, ++ dhdp->dstats.rx_errors, dhdp->dstats.rx_dropped); ++ bcm_bprintf(strbuf, "multicast %ld\n", dhdp->dstats.multicast); ++ ++ bcm_bprintf(strbuf, "bus stats:\n"); ++ bcm_bprintf(strbuf, "tx_packets %ld tx_multicast %ld tx_errors %ld\n", ++ dhdp->tx_packets, dhdp->tx_multicast, dhdp->tx_errors); ++ bcm_bprintf(strbuf, "tx_ctlpkts %ld tx_ctlerrs %ld\n", ++ dhdp->tx_ctlpkts, dhdp->tx_ctlerrs); ++ bcm_bprintf(strbuf, "rx_packets %ld rx_multicast %ld rx_errors %ld \n", ++ dhdp->rx_packets, dhdp->rx_multicast, dhdp->rx_errors); ++ bcm_bprintf(strbuf, "rx_ctlpkts %ld rx_ctlerrs %ld rx_dropped %ld\n", ++ dhdp->rx_ctlpkts, dhdp->rx_ctlerrs, dhdp->rx_dropped); ++ bcm_bprintf(strbuf, "rx_readahead_cnt %ld tx_realloc %ld\n", ++ dhdp->rx_readahead_cnt, dhdp->tx_realloc); ++ bcm_bprintf(strbuf, "\n"); ++ ++ /* Add any prot info */ ++ dhd_prot_dump(dhdp, strbuf); ++ bcm_bprintf(strbuf, "\n"); ++ ++ /* Add any bus info */ ++ dhd_bus_dump(dhdp, strbuf); ++ ++ return (!strbuf->size ? BCME_BUFTOOSHORT : 0); ++} ++ ++int ++dhd_wl_ioctl_cmd(dhd_pub_t *dhd_pub, int cmd, void *arg, int len, uint8 set, int ifindex) ++{ ++ wl_ioctl_t ioc; ++ ++ ioc.cmd = cmd; ++ ioc.buf = arg; ++ ioc.len = len; ++ ioc.set = set; ++ ++ return dhd_wl_ioctl(dhd_pub, ifindex, &ioc, arg, len); ++} ++ ++ ++int ++dhd_wl_ioctl(dhd_pub_t *dhd_pub, int ifindex, wl_ioctl_t *ioc, void *buf, int len) ++{ ++ int ret; ++ ++ dhd_os_proto_block(dhd_pub); ++ ++ ret = dhd_prot_ioctl(dhd_pub, ifindex, ioc, buf, len); ++ if ((ret) && (dhd_pub->up)) ++ /* Send hang event only if dhd_open() was success */ ++ dhd_os_check_hang(dhd_pub, ifindex, ret); ++ ++ dhd_os_proto_unblock(dhd_pub); ++ ++ return ret; ++} ++ ++static int ++dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const char *name, ++ void *params, int plen, void *arg, int len, int val_size) ++{ ++ int bcmerror = 0; ++ int32 int_val = 0; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ DHD_TRACE(("%s: actionid = %d; name %s\n", __FUNCTION__, actionid, name)); ++ ++ if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0) ++ goto exit; ++ ++ if (plen >= (int)sizeof(int_val)) ++ bcopy(params, &int_val, sizeof(int_val)); ++ ++ switch (actionid) { ++ case IOV_GVAL(IOV_VERSION): ++ /* Need to have checked buffer length */ ++ bcm_strncpy_s((char*)arg, len, dhd_version, len); ++ break; ++ ++ case IOV_GVAL(IOV_MSGLEVEL): ++ int_val = (int32)dhd_msg_level; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_MSGLEVEL): ++#ifdef WL_CFG80211 ++ /* Enable DHD and WL logs in oneshot */ ++ if (int_val & DHD_WL_VAL2) ++ wl_cfg80211_enable_trace(TRUE, int_val & (~DHD_WL_VAL2)); ++ else if (int_val & DHD_WL_VAL) ++ wl_cfg80211_enable_trace(FALSE, WL_DBG_DBG); ++ if (!(int_val & DHD_WL_VAL2)) ++#endif /* WL_CFG80211 */ ++ dhd_msg_level = int_val; ++ break; ++ case IOV_GVAL(IOV_BCMERRORSTR): ++ bcm_strncpy_s((char *)arg, len, bcmerrorstr(dhd_pub->bcmerror), BCME_STRLEN); ++ ((char *)arg)[BCME_STRLEN - 1] = 0x00; ++ break; ++ ++ case IOV_GVAL(IOV_BCMERROR): ++ int_val = (int32)dhd_pub->bcmerror; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_GVAL(IOV_WDTICK): ++ int_val = (int32)dhd_watchdog_ms; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_WDTICK): ++ if (!dhd_pub->up) { ++ bcmerror = BCME_NOTUP; ++ break; ++ } ++ dhd_os_wd_timer(dhd_pub, (uint)int_val); ++ break; ++ ++ case IOV_GVAL(IOV_DUMP): ++ bcmerror = dhd_dump(dhd_pub, arg, len); ++ break; ++ ++#ifdef DHD_DEBUG ++ case IOV_GVAL(IOV_DCONSOLE_POLL): ++ int_val = (int32)dhd_console_ms; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_DCONSOLE_POLL): ++ dhd_console_ms = (uint)int_val; ++ break; ++ ++ case IOV_SVAL(IOV_CONS): ++ if (len > 0) ++ bcmerror = dhd_bus_console_in(dhd_pub, arg, len - 1); ++ break; ++#endif /* DHD_DEBUG */ ++ ++ case IOV_SVAL(IOV_CLEARCOUNTS): ++ dhd_pub->tx_packets = dhd_pub->rx_packets = 0; ++ dhd_pub->tx_errors = dhd_pub->rx_errors = 0; ++ dhd_pub->tx_ctlpkts = dhd_pub->rx_ctlpkts = 0; ++ dhd_pub->tx_ctlerrs = dhd_pub->rx_ctlerrs = 0; ++ dhd_pub->rx_dropped = 0; ++ dhd_pub->rx_readahead_cnt = 0; ++ dhd_pub->tx_realloc = 0; ++ dhd_pub->wd_dpc_sched = 0; ++ memset(&dhd_pub->dstats, 0, sizeof(dhd_pub->dstats)); ++ dhd_bus_clearcounts(dhd_pub); ++#ifdef PROP_TXSTATUS ++ /* clear proptxstatus related counters */ ++ if (dhd_pub->wlfc_state) { ++ athost_wl_status_info_t *wlfc = ++ (athost_wl_status_info_t*)dhd_pub->wlfc_state; ++ wlfc_hanger_t* hanger; ++ ++ memset(&wlfc->stats, 0, sizeof(athost_wl_stat_counters_t)); ++ ++ hanger = (wlfc_hanger_t*)wlfc->hanger; ++ hanger->pushed = 0; ++ hanger->popped = 0; ++ hanger->failed_slotfind = 0; ++ hanger->failed_to_pop = 0; ++ hanger->failed_to_push = 0; ++ } ++#endif /* PROP_TXSTATUS */ ++ break; ++ ++ ++ case IOV_GVAL(IOV_IOCTLTIMEOUT): { ++ int_val = (int32)dhd_os_get_ioctl_resp_timeout(); ++ bcopy(&int_val, arg, sizeof(int_val)); ++ break; ++ } ++ ++ case IOV_SVAL(IOV_IOCTLTIMEOUT): { ++ if (int_val <= 0) ++ bcmerror = BCME_BADARG; ++ else ++ dhd_os_set_ioctl_resp_timeout((unsigned int)int_val); ++ break; ++ } ++ ++#ifdef WLBTAMP ++ case IOV_SVAL(IOV_HCI_CMD): { ++ amp_hci_cmd_t *cmd = (amp_hci_cmd_t *)arg; ++ ++ /* sanity check: command preamble present */ ++ if (len < HCI_CMD_PREAMBLE_SIZE) ++ return BCME_BUFTOOSHORT; ++ ++ /* sanity check: command parameters are present */ ++ if (len < (int)(HCI_CMD_PREAMBLE_SIZE + cmd->plen)) ++ return BCME_BUFTOOSHORT; ++ ++ dhd_bta_docmd(dhd_pub, cmd, len); ++ break; ++ } ++ ++ case IOV_SVAL(IOV_HCI_ACL_DATA): { ++ amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *)arg; ++ ++ /* sanity check: HCI header present */ ++ if (len < HCI_ACL_DATA_PREAMBLE_SIZE) ++ return BCME_BUFTOOSHORT; ++ ++ /* sanity check: ACL data is present */ ++ if (len < (int)(HCI_ACL_DATA_PREAMBLE_SIZE + ACL_data->dlen)) ++ return BCME_BUFTOOSHORT; ++ ++ dhd_bta_tx_hcidata(dhd_pub, ACL_data, len); ++ break; ++ } ++#endif /* WLBTAMP */ ++ ++#ifdef PROP_TXSTATUS ++ case IOV_GVAL(IOV_PROPTXSTATUS_ENABLE): ++ int_val = dhd_pub->wlfc_enabled? 1 : 0; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_PROPTXSTATUS_ENABLE): ++ dhd_pub->wlfc_enabled = int_val? 1 : 0; ++ break; ++ ++ case IOV_GVAL(IOV_PROPTXSTATUS_MODE): { ++ athost_wl_status_info_t *wlfc = ++ (athost_wl_status_info_t*)dhd_pub->wlfc_state; ++ int_val = dhd_pub->wlfc_state ? (int32)wlfc->proptxstatus_mode : 0; ++ bcopy(&int_val, arg, val_size); ++ break; ++ } ++ ++ case IOV_SVAL(IOV_PROPTXSTATUS_MODE): ++ if (dhd_pub->wlfc_state) { ++ athost_wl_status_info_t *wlfc = ++ (athost_wl_status_info_t*)dhd_pub->wlfc_state; ++ wlfc->proptxstatus_mode = int_val & 0xff; ++ } ++ break; ++#endif /* PROP_TXSTATUS */ ++ ++ case IOV_GVAL(IOV_BUS_TYPE): ++ /* The dhd application queries the driver to check if its usb or sdio. */ ++#ifdef BCMDHDUSB ++ int_val = BUS_TYPE_USB; ++#endif ++ int_val = BUS_TYPE_SDIO; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ ++#ifdef WLMEDIA_HTSF ++ case IOV_GVAL(IOV_WLPKTDLYSTAT_SZ): ++ int_val = dhd_pub->htsfdlystat_sz; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_WLPKTDLYSTAT_SZ): ++ dhd_pub->htsfdlystat_sz = int_val & 0xff; ++ printf("Setting tsfdlystat_sz:%d\n", dhd_pub->htsfdlystat_sz); ++ break; ++#endif ++ case IOV_SVAL(IOV_CHANGEMTU): ++ int_val &= 0xffff; ++ bcmerror = dhd_change_mtu(dhd_pub, int_val, 0); ++ break; ++ ++ case IOV_GVAL(IOV_HOSTREORDER_FLOWS): ++ { ++ uint i = 0; ++ uint8 *ptr = (uint8 *)arg; ++ uint8 count = 0; ++ ++ ptr++; ++ for (i = 0; i < WLHOST_REORDERDATA_MAXFLOWS; i++) { ++ if (dhd_pub->reorder_bufs[i] != NULL) { ++ *ptr = dhd_pub->reorder_bufs[i]->flow_id; ++ ptr++; ++ count++; ++ } ++ } ++ ptr = (uint8 *)arg; ++ *ptr = count; ++ break; ++ } ++ ++ default: ++ bcmerror = BCME_UNSUPPORTED; ++ break; ++ } ++ ++exit: ++ DHD_TRACE(("%s: actionid %d, bcmerror %d\n", __FUNCTION__, actionid, bcmerror)); ++ return bcmerror; ++} ++ ++/* Store the status of a connection attempt for later retrieval by an iovar */ ++void ++dhd_store_conn_status(uint32 event, uint32 status, uint32 reason) ++{ ++ /* Do not overwrite a WLC_E_PRUNE with a WLC_E_SET_SSID ++ * because an encryption/rsn mismatch results in both events, and ++ * the important information is in the WLC_E_PRUNE. ++ */ ++ if (!(event == WLC_E_SET_SSID && status == WLC_E_STATUS_FAIL && ++ dhd_conn_event == WLC_E_PRUNE)) { ++ dhd_conn_event = event; ++ dhd_conn_status = status; ++ dhd_conn_reason = reason; ++ } ++} ++ ++bool ++dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, void *pkt, int prec) ++{ ++ void *p; ++ int eprec = -1; /* precedence to evict from */ ++ bool discard_oldest; ++ ++ /* Fast case, precedence queue is not full and we are also not ++ * exceeding total queue length ++ */ ++ if (!pktq_pfull(q, prec) && !pktq_full(q)) { ++ pktq_penq(q, prec, pkt); ++ return TRUE; ++ } ++ ++ /* Determine precedence from which to evict packet, if any */ ++ if (pktq_pfull(q, prec)) ++ eprec = prec; ++ else if (pktq_full(q)) { ++ pktq_peek_tail(q, &eprec); ++ if (eprec > prec || eprec < 0) ++ return FALSE; ++ } ++ ++ /* Evict if needed */ ++ if (eprec >= 0) { ++ /* Detect queueing to unconfigured precedence */ ++ ASSERT(!pktq_pempty(q, eprec)); ++ discard_oldest = AC_BITMAP_TST(dhdp->wme_dp, eprec); ++ if (eprec == prec && !discard_oldest) ++ return FALSE; /* refuse newer (incoming) packet */ ++ /* Evict packet according to discard policy */ ++ p = discard_oldest ? pktq_pdeq(q, eprec) : pktq_pdeq_tail(q, eprec); ++ ASSERT(p); ++ ++ PKTFREE(dhdp->osh, p, TRUE); ++ } ++ ++ /* Enqueue */ ++ pktq_penq(q, prec, pkt); ++ ++ return TRUE; ++} ++ ++static int ++dhd_iovar_op(dhd_pub_t *dhd_pub, const char *name, ++ void *params, int plen, void *arg, int len, bool set) ++{ ++ int bcmerror = 0; ++ int val_size; ++ const bcm_iovar_t *vi = NULL; ++ uint32 actionid; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ ASSERT(name); ++ ASSERT(len >= 0); ++ ++ /* Get MUST have return space */ ++ ASSERT(set || (arg && len)); ++ ++ /* Set does NOT take qualifiers */ ++ ASSERT(!set || (!params && !plen)); ++ ++ if ((vi = bcm_iovar_lookup(dhd_iovars, name)) == NULL) { ++ bcmerror = BCME_UNSUPPORTED; ++ goto exit; ++ } ++ ++ DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__, ++ name, (set ? "set" : "get"), len, plen)); ++ ++ /* set up 'params' pointer in case this is a set command so that ++ * the convenience int and bool code can be common to set and get ++ */ ++ if (params == NULL) { ++ params = arg; ++ plen = len; ++ } ++ ++ if (vi->type == IOVT_VOID) ++ val_size = 0; ++ else if (vi->type == IOVT_BUFFER) ++ val_size = len; ++ else ++ /* all other types are integer sized */ ++ val_size = sizeof(int); ++ ++ actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); ++ ++ bcmerror = dhd_doiovar(dhd_pub, vi, actionid, name, params, plen, arg, len, val_size); ++ ++exit: ++ return bcmerror; ++} ++ ++int ++dhd_ioctl(dhd_pub_t * dhd_pub, dhd_ioctl_t *ioc, void * buf, uint buflen) ++{ ++ int bcmerror = 0; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (!buf) { ++ return BCME_BADARG; ++ } ++ ++ switch (ioc->cmd) { ++ case DHD_GET_MAGIC: ++ if (buflen < sizeof(int)) ++ bcmerror = BCME_BUFTOOSHORT; ++ else ++ *(int*)buf = DHD_IOCTL_MAGIC; ++ break; ++ ++ case DHD_GET_VERSION: ++ if (buflen < sizeof(int)) ++ bcmerror = BCME_BUFTOOSHORT; ++ else ++ *(int*)buf = DHD_IOCTL_VERSION; ++ break; ++ ++ case DHD_GET_VAR: ++ case DHD_SET_VAR: { ++ char *arg; ++ uint arglen; ++ ++ /* scan past the name to any arguments */ ++ for (arg = buf, arglen = buflen; *arg && arglen; arg++, arglen--) ++ ; ++ ++ if (*arg) { ++ bcmerror = BCME_BUFTOOSHORT; ++ break; ++ } ++ ++ /* account for the NUL terminator */ ++ arg++, arglen--; ++ ++ /* call with the appropriate arguments */ ++ if (ioc->cmd == DHD_GET_VAR) ++ bcmerror = dhd_iovar_op(dhd_pub, buf, arg, arglen, ++ buf, buflen, IOV_GET); ++ else ++ bcmerror = dhd_iovar_op(dhd_pub, buf, NULL, 0, arg, arglen, IOV_SET); ++ if (bcmerror != BCME_UNSUPPORTED) ++ break; ++ ++ /* not in generic table, try protocol module */ ++ if (ioc->cmd == DHD_GET_VAR) ++ bcmerror = dhd_prot_iovar_op(dhd_pub, buf, arg, ++ arglen, buf, buflen, IOV_GET); ++ else ++ bcmerror = dhd_prot_iovar_op(dhd_pub, buf, ++ NULL, 0, arg, arglen, IOV_SET); ++ if (bcmerror != BCME_UNSUPPORTED) ++ break; ++ ++ /* if still not found, try bus module */ ++ if (ioc->cmd == DHD_GET_VAR) { ++ bcmerror = dhd_bus_iovar_op(dhd_pub, buf, ++ arg, arglen, buf, buflen, IOV_GET); ++ } else { ++ bcmerror = dhd_bus_iovar_op(dhd_pub, buf, ++ NULL, 0, arg, arglen, IOV_SET); ++ } ++ ++ break; ++ } ++ ++ default: ++ bcmerror = BCME_UNSUPPORTED; ++ } ++ ++ return bcmerror; ++} ++ ++#ifdef SHOW_EVENTS ++static void ++wl_show_host_event(wl_event_msg_t *event, void *event_data) ++{ ++ uint i, status, reason; ++ bool group = FALSE, flush_txq = FALSE, link = FALSE; ++ const char *auth_str; ++ const char *event_name; ++ uchar *buf; ++ char err_msg[256], eabuf[ETHER_ADDR_STR_LEN]; ++ uint event_type, flags, auth_type, datalen; ++ ++ event_type = ntoh32(event->event_type); ++ flags = ntoh16(event->flags); ++ status = ntoh32(event->status); ++ reason = ntoh32(event->reason); ++ BCM_REFERENCE(reason); ++ auth_type = ntoh32(event->auth_type); ++ datalen = ntoh32(event->datalen); ++ ++ /* debug dump of event messages */ ++ snprintf(eabuf, sizeof(eabuf), "%02x:%02x:%02x:%02x:%02x:%02x", ++ (uchar)event->addr.octet[0]&0xff, ++ (uchar)event->addr.octet[1]&0xff, ++ (uchar)event->addr.octet[2]&0xff, ++ (uchar)event->addr.octet[3]&0xff, ++ (uchar)event->addr.octet[4]&0xff, ++ (uchar)event->addr.octet[5]&0xff); ++ ++ event_name = "UNKNOWN"; ++ for (i = 0; i < (uint)bcmevent_names_size; i++) ++ if (bcmevent_names[i].event == event_type) ++ event_name = bcmevent_names[i].name; ++ ++ if (flags & WLC_EVENT_MSG_LINK) ++ link = TRUE; ++ if (flags & WLC_EVENT_MSG_GROUP) ++ group = TRUE; ++ if (flags & WLC_EVENT_MSG_FLUSHTXQ) ++ flush_txq = TRUE; ++ ++ switch (event_type) { ++ case WLC_E_START: ++ case WLC_E_DEAUTH: ++ case WLC_E_DISASSOC: ++ DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf)); ++ break; ++ ++ case WLC_E_ASSOC_IND: ++ case WLC_E_REASSOC_IND: ++ ++ DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf)); ++ break; ++ ++ case WLC_E_ASSOC: ++ case WLC_E_REASSOC: ++ if (status == WLC_E_STATUS_SUCCESS) { ++ DHD_EVENT(("MACEVENT: %s, MAC %s, SUCCESS\n", event_name, eabuf)); ++ } else if (status == WLC_E_STATUS_TIMEOUT) { ++ DHD_EVENT(("MACEVENT: %s, MAC %s, TIMEOUT\n", event_name, eabuf)); ++ } else if (status == WLC_E_STATUS_FAIL) { ++ DHD_EVENT(("MACEVENT: %s, MAC %s, FAILURE, reason %d\n", ++ event_name, eabuf, (int)reason)); ++ } else { ++ DHD_EVENT(("MACEVENT: %s, MAC %s, unexpected status %d\n", ++ event_name, eabuf, (int)status)); ++ } ++ break; ++ ++ case WLC_E_DEAUTH_IND: ++ case WLC_E_DISASSOC_IND: ++ DHD_EVENT(("MACEVENT: %s, MAC %s, reason %d\n", event_name, eabuf, (int)reason)); ++ break; ++ ++ case WLC_E_AUTH: ++ case WLC_E_AUTH_IND: ++ if (auth_type == DOT11_OPEN_SYSTEM) ++ auth_str = "Open System"; ++ else if (auth_type == DOT11_SHARED_KEY) ++ auth_str = "Shared Key"; ++ else { ++ snprintf(err_msg, sizeof(err_msg), "AUTH unknown: %d", (int)auth_type); ++ auth_str = err_msg; ++ } ++ if (event_type == WLC_E_AUTH_IND) { ++ DHD_EVENT(("MACEVENT: %s, MAC %s, %s\n", event_name, eabuf, auth_str)); ++ } else if (status == WLC_E_STATUS_SUCCESS) { ++ DHD_EVENT(("MACEVENT: %s, MAC %s, %s, SUCCESS\n", ++ event_name, eabuf, auth_str)); ++ } else if (status == WLC_E_STATUS_TIMEOUT) { ++ DHD_EVENT(("MACEVENT: %s, MAC %s, %s, TIMEOUT\n", ++ event_name, eabuf, auth_str)); ++ } else if (status == WLC_E_STATUS_FAIL) { ++ DHD_EVENT(("MACEVENT: %s, MAC %s, %s, FAILURE, reason %d\n", ++ event_name, eabuf, auth_str, (int)reason)); ++ } ++ BCM_REFERENCE(auth_str); ++ ++ break; ++ ++ case WLC_E_JOIN: ++ case WLC_E_ROAM: ++ case WLC_E_SET_SSID: ++ if (status == WLC_E_STATUS_SUCCESS) { ++ DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf)); ++ } else if (status == WLC_E_STATUS_FAIL) { ++ DHD_EVENT(("MACEVENT: %s, failed\n", event_name)); ++ } else if (status == WLC_E_STATUS_NO_NETWORKS) { ++ DHD_EVENT(("MACEVENT: %s, no networks found\n", event_name)); ++ } else { ++ DHD_EVENT(("MACEVENT: %s, unexpected status %d\n", ++ event_name, (int)status)); ++ } ++ break; ++ ++ case WLC_E_BEACON_RX: ++ if (status == WLC_E_STATUS_SUCCESS) { ++ DHD_EVENT(("MACEVENT: %s, SUCCESS\n", event_name)); ++ } else if (status == WLC_E_STATUS_FAIL) { ++ DHD_EVENT(("MACEVENT: %s, FAIL\n", event_name)); ++ } else { ++ DHD_EVENT(("MACEVENT: %s, status %d\n", event_name, status)); ++ } ++ break; ++ ++ case WLC_E_LINK: ++ DHD_EVENT(("MACEVENT: %s %s\n", event_name, link?"UP":"DOWN")); ++ BCM_REFERENCE(link); ++ break; ++ ++ case WLC_E_MIC_ERROR: ++ DHD_EVENT(("MACEVENT: %s, MAC %s, Group %d, Flush %d\n", ++ event_name, eabuf, group, flush_txq)); ++ BCM_REFERENCE(group); ++ BCM_REFERENCE(flush_txq); ++ break; ++ ++ case WLC_E_ICV_ERROR: ++ case WLC_E_UNICAST_DECODE_ERROR: ++ case WLC_E_MULTICAST_DECODE_ERROR: ++ DHD_EVENT(("MACEVENT: %s, MAC %s\n", ++ event_name, eabuf)); ++ break; ++ ++ case WLC_E_TXFAIL: ++ DHD_EVENT(("MACEVENT: %s, RA %s\n", event_name, eabuf)); ++ break; ++ ++ case WLC_E_SCAN_COMPLETE: ++ case WLC_E_ASSOC_REQ_IE: ++ case WLC_E_ASSOC_RESP_IE: ++ case WLC_E_PMKID_CACHE: ++ DHD_EVENT(("MACEVENT: %s\n", event_name)); ++ break; ++ ++ case WLC_E_PFN_NET_FOUND: ++ case WLC_E_PFN_NET_LOST: ++ case WLC_E_PFN_SCAN_COMPLETE: ++ case WLC_E_PFN_SCAN_NONE: ++ case WLC_E_PFN_SCAN_ALLGONE: ++ DHD_EVENT(("PNOEVENT: %s\n", event_name)); ++ break; ++ ++ case WLC_E_PSK_SUP: ++ case WLC_E_PRUNE: ++ DHD_EVENT(("MACEVENT: %s, status %d, reason %d\n", ++ event_name, (int)status, (int)reason)); ++ break; ++ ++#ifdef WIFI_ACT_FRAME ++ case WLC_E_ACTION_FRAME: ++ DHD_TRACE(("MACEVENT: %s Bssid %s\n", event_name, eabuf)); ++ break; ++#endif /* WIFI_ACT_FRAME */ ++ ++ case WLC_E_TRACE: { ++ static uint32 seqnum_prev = 0; ++ msgtrace_hdr_t hdr; ++ uint32 nblost; ++ char *s, *p; ++ ++ buf = (uchar *) event_data; ++ memcpy(&hdr, buf, MSGTRACE_HDRLEN); ++ ++ if (hdr.version != MSGTRACE_VERSION) { ++ printf("\nMACEVENT: %s [unsupported version --> " ++ "dhd version:%d dongle version:%d]\n", ++ event_name, MSGTRACE_VERSION, hdr.version); ++ /* Reset datalen to avoid display below */ ++ datalen = 0; ++ break; ++ } ++ ++ /* There are 2 bytes available at the end of data */ ++ buf[MSGTRACE_HDRLEN + ntoh16(hdr.len)] = '\0'; ++ ++ if (ntoh32(hdr.discarded_bytes) || ntoh32(hdr.discarded_printf)) { ++ printf("\nWLC_E_TRACE: [Discarded traces in dongle -->" ++ "discarded_bytes %d discarded_printf %d]\n", ++ ntoh32(hdr.discarded_bytes), ntoh32(hdr.discarded_printf)); ++ } ++ ++ nblost = ntoh32(hdr.seqnum) - seqnum_prev - 1; ++ if (nblost > 0) { ++ printf("\nWLC_E_TRACE: [Event lost --> seqnum %d nblost %d\n", ++ ntoh32(hdr.seqnum), nblost); ++ } ++ seqnum_prev = ntoh32(hdr.seqnum); ++ ++ /* Display the trace buffer. Advance from \n to \n to avoid display big ++ * printf (issue with Linux printk ) ++ */ ++ p = (char *)&buf[MSGTRACE_HDRLEN]; ++ while ((s = strstr(p, "\n")) != NULL) { ++ *s = '\0'; ++ printf("%s\n", p); ++ p = s+1; ++ } ++ printf("%s\n", p); ++ ++ /* Reset datalen to avoid display below */ ++ datalen = 0; ++ break; ++ } ++ ++ ++ case WLC_E_RSSI: ++ DHD_EVENT(("MACEVENT: %s %d\n", event_name, ntoh32(*((int *)event_data)))); ++ break; ++ ++ case WLC_E_SERVICE_FOUND: ++ case WLC_E_P2PO_ADD_DEVICE: ++ case WLC_E_P2PO_DEL_DEVICE: ++ DHD_EVENT(("MACEVENT: %s, MAC: %s\n", event_name, eabuf)); ++ break; ++ ++ default: ++ DHD_EVENT(("MACEVENT: %s %d, MAC %s, status %d, reason %d, auth %d\n", ++ event_name, event_type, eabuf, (int)status, (int)reason, ++ (int)auth_type)); ++ break; ++ } ++ ++ /* show any appended data */ ++ if (datalen) { ++ buf = (uchar *) event_data; ++ DHD_EVENT((" data (%d) : ", datalen)); ++ for (i = 0; i < datalen; i++) ++ DHD_EVENT((" 0x%02x ", *buf++)); ++ DHD_EVENT(("\n")); ++ } ++} ++#endif /* SHOW_EVENTS */ ++ ++int ++wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, ++ wl_event_msg_t *event, void **data_ptr) ++{ ++ /* check whether packet is a BRCM event pkt */ ++ bcm_event_t *pvt_data = (bcm_event_t *)pktdata; ++ uint8 *event_data; ++ uint32 type, status, datalen; ++ uint16 flags; ++ int evlen; ++ ++ if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) { ++ DHD_ERROR(("%s: mismatched OUI, bailing\n", __FUNCTION__)); ++ return (BCME_ERROR); ++ } ++ ++ /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */ ++ if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) != BCMILCP_BCM_SUBTYPE_EVENT) { ++ DHD_ERROR(("%s: mismatched subtype, bailing\n", __FUNCTION__)); ++ return (BCME_ERROR); ++ } ++ ++ *data_ptr = &pvt_data[1]; ++ event_data = *data_ptr; ++ ++ /* memcpy since BRCM event pkt may be unaligned. */ ++ memcpy(event, &pvt_data->event, sizeof(wl_event_msg_t)); ++ ++ type = ntoh32_ua((void *)&event->event_type); ++ flags = ntoh16_ua((void *)&event->flags); ++ status = ntoh32_ua((void *)&event->status); ++ datalen = ntoh32_ua((void *)&event->datalen); ++ evlen = datalen + sizeof(bcm_event_t); ++ ++ switch (type) { ++#ifdef PROP_TXSTATUS ++ case WLC_E_FIFO_CREDIT_MAP: ++ dhd_wlfc_event(dhd_pub->info); ++ dhd_wlfc_FIFOcreditmap_event(dhd_pub->info, event_data); ++ WLFC_DBGMESG(("WLC_E_FIFO_CREDIT_MAP:(AC0,AC1,AC2,AC3),(BC_MC),(OTHER): " ++ "(%d,%d,%d,%d),(%d),(%d)\n", event_data[0], event_data[1], ++ event_data[2], ++ event_data[3], event_data[4], event_data[5])); ++ break; ++#endif ++ ++ case WLC_E_IF: ++ { ++ dhd_if_event_t *ifevent = (dhd_if_event_t *)event_data; ++#ifdef PROP_TXSTATUS ++ { ++ uint8* ea = pvt_data->eth.ether_dhost; ++ WLFC_DBGMESG(("WLC_E_IF: idx:%d, action:%s, iftype:%s, " ++ "[%02x:%02x:%02x:%02x:%02x:%02x]\n", ++ ifevent->ifidx, ++ ((ifevent->action == WLC_E_IF_ADD) ? "ADD":"DEL"), ++ ((ifevent->is_AP == 0) ? "STA":"AP "), ++ ea[0], ea[1], ea[2], ea[3], ea[4], ea[5])); ++ (void)ea; ++ if (ifevent->action == WLC_E_IF_CHANGE) ++ dhd_wlfc_interface_event(dhd_pub->info, ++ eWLFC_MAC_ENTRY_ACTION_UPDATE, ++ ifevent->ifidx, ifevent->is_AP, ea); ++ else ++ dhd_wlfc_interface_event(dhd_pub->info, ++ ((ifevent->action == WLC_E_IF_ADD) ? ++ eWLFC_MAC_ENTRY_ACTION_ADD : eWLFC_MAC_ENTRY_ACTION_DEL), ++ ifevent->ifidx, ifevent->is_AP, ea); ++ ++ ++ /* dhd already has created an interface by default, for 0 */ ++ if (ifevent->ifidx == 0) ++ break; ++ } ++#endif /* PROP_TXSTATUS */ ++ ++#ifdef WL_CFG80211 ++ if (wl_cfg80211_is_progress_ifchange()) { ++ DHD_ERROR(("%s: ifidx %d for %s action %d\n", ++ __FUNCTION__, ifevent->ifidx, ++ event->ifname, ifevent->action)); ++ if (ifevent->action == WLC_E_IF_ADD || ++ ifevent->action == WLC_E_IF_CHANGE) ++ wl_cfg80211_notify_ifchange(); ++ return (BCME_OK); ++ } ++#endif /* WL_CFG80211 */ ++ if (ifevent->ifidx > 0 && ifevent->ifidx < DHD_MAX_IFS) { ++ if (ifevent->action == WLC_E_IF_ADD) { ++ if (dhd_add_if(dhd_pub->info, ifevent->ifidx, ++ NULL, event->ifname, ++ event->addr.octet, ++ ifevent->flags, ifevent->bssidx)) { ++ DHD_ERROR(("%s: dhd_add_if failed!!" ++ " ifidx: %d for %s\n", ++ __FUNCTION__, ++ ifevent->ifidx, ++ event->ifname)); ++ return (BCME_ERROR); ++ } ++ } ++ else if (ifevent->action == WLC_E_IF_DEL) ++ dhd_del_if(dhd_pub->info, ifevent->ifidx); ++ } else { ++#ifndef PROP_TXSTATUS ++ DHD_ERROR(("%s: Invalid ifidx %d for %s\n", ++ __FUNCTION__, ifevent->ifidx, event->ifname)); ++#endif /* !PROP_TXSTATUS */ ++ } ++ } ++ /* send up the if event: btamp user needs it */ ++ *ifidx = dhd_ifname2idx(dhd_pub->info, event->ifname); ++ /* push up to external supp/auth */ ++ dhd_event(dhd_pub->info, (char *)pvt_data, evlen, *ifidx); ++ break; ++ ++ ++#ifdef WLMEDIA_HTSF ++ case WLC_E_HTSFSYNC: ++ htsf_update(dhd_pub->info, event_data); ++ break; ++#endif /* WLMEDIA_HTSF */ ++#if defined(NDIS630) ++ case WLC_E_NDIS_LINK: ++ break; ++#else /* defined(NDIS630) && defined(BCMDONGLEHOST) */ ++ case WLC_E_NDIS_LINK: { ++ uint32 temp = hton32(WLC_E_LINK); ++ ++ memcpy((void *)(&pvt_data->event.event_type), &temp, ++ sizeof(pvt_data->event.event_type)); ++ } ++#endif ++ /* These are what external supplicant/authenticator wants */ ++ /* fall through */ ++ case WLC_E_LINK: ++ case WLC_E_DEAUTH: ++ case WLC_E_DEAUTH_IND: ++ case WLC_E_DISASSOC: ++ case WLC_E_DISASSOC_IND: ++ DHD_EVENT(("%s: Link event %d, flags %x, status %x\n", ++ __FUNCTION__, type, flags, status)); ++ /* fall through */ ++ default: ++ *ifidx = dhd_ifname2idx(dhd_pub->info, event->ifname); ++ /* push up to external supp/auth */ ++ dhd_event(dhd_pub->info, (char *)pvt_data, evlen, *ifidx); ++ DHD_TRACE(("%s: MAC event %d, flags %x, status %x\n", ++ __FUNCTION__, type, flags, status)); ++ BCM_REFERENCE(flags); ++ BCM_REFERENCE(status); ++ ++ /* put it back to WLC_E_NDIS_LINK */ ++ if (type == WLC_E_NDIS_LINK) { ++ uint32 temp; ++ ++ temp = ntoh32_ua((void *)&event->event_type); ++ DHD_TRACE(("Converted to WLC_E_LINK type %d\n", temp)); ++ ++ temp = ntoh32(WLC_E_NDIS_LINK); ++ memcpy((void *)(&pvt_data->event.event_type), &temp, ++ sizeof(pvt_data->event.event_type)); ++ } ++ break; ++ } ++ ++#ifdef SHOW_EVENTS ++ wl_show_host_event(event, (void *)event_data); ++#endif /* SHOW_EVENTS */ ++ ++ return (BCME_OK); ++} ++ ++void ++wl_event_to_host_order(wl_event_msg_t * evt) ++{ ++ /* Event struct members passed from dongle to host are stored in network ++ * byte order. Convert all members to host-order. ++ */ ++ evt->event_type = ntoh32(evt->event_type); ++ evt->flags = ntoh16(evt->flags); ++ evt->status = ntoh32(evt->status); ++ evt->reason = ntoh32(evt->reason); ++ evt->auth_type = ntoh32(evt->auth_type); ++ evt->datalen = ntoh32(evt->datalen); ++ evt->version = ntoh16(evt->version); ++} ++ ++void ++dhd_print_buf(void *pbuf, int len, int bytes_per_line) ++{ ++#ifdef DHD_DEBUG ++ int i, j = 0; ++ unsigned char *buf = pbuf; ++ ++ if (bytes_per_line == 0) { ++ bytes_per_line = len; ++ } ++ ++ for (i = 0; i < len; i++) { ++ printf("%2.2x", *buf++); ++ j++; ++ if (j == bytes_per_line) { ++ printf("\n"); ++ j = 0; ++ } else { ++ printf(":"); ++ } ++ } ++ printf("\n"); ++#endif /* DHD_DEBUG */ ++} ++ ++#ifndef strtoul ++#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) ++#endif ++ ++#ifdef PKT_FILTER_SUPPORT ++/* Convert user's input in hex pattern to byte-size mask */ ++static int ++wl_pattern_atoh(char *src, char *dst) ++{ ++ int i; ++ if (strncmp(src, "0x", 2) != 0 && ++ strncmp(src, "0X", 2) != 0) { ++ DHD_ERROR(("Mask invalid format. Needs to start with 0x\n")); ++ return -1; ++ } ++ src = src + 2; /* Skip past 0x */ ++ if (strlen(src) % 2 != 0) { ++ DHD_ERROR(("Mask invalid format. Needs to be of even length\n")); ++ return -1; ++ } ++ for (i = 0; *src != '\0'; i++) { ++ char num[3]; ++ bcm_strncpy_s(num, sizeof(num), src, 2); ++ num[2] = '\0'; ++ dst[i] = (uint8)strtoul(num, NULL, 16); ++ src += 2; ++ } ++ return i; ++} ++ ++void ++dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode) ++{ ++ char *argv[8]; ++ int i = 0; ++ const char *str; ++ int buf_len; ++ int str_len; ++ char *arg_save = 0, *arg_org = 0; ++ int rc; ++ char buf[128]; ++ wl_pkt_filter_enable_t enable_parm; ++ wl_pkt_filter_enable_t * pkt_filterp; ++ ++ if (!arg) ++ return; ++ ++ if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) { ++ DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); ++ goto fail; ++ } ++ arg_org = arg_save; ++ memcpy(arg_save, arg, strlen(arg) + 1); ++ ++ argv[i] = bcmstrtok(&arg_save, " ", 0); ++ ++ i = 0; ++ if (argv[i] == NULL) { ++ DHD_ERROR(("No args provided\n")); ++ goto fail; ++ } ++ ++ str = "pkt_filter_enable"; ++ str_len = strlen(str); ++ bcm_strncpy_s(buf, sizeof(buf), str, str_len); ++ buf[str_len] = '\0'; ++ buf_len = str_len + 1; ++ ++ pkt_filterp = (wl_pkt_filter_enable_t *)(buf + str_len + 1); ++ ++ /* Parse packet filter id. */ ++ enable_parm.id = htod32(strtoul(argv[i], NULL, 0)); ++ ++ /* Parse enable/disable value. */ ++ enable_parm.enable = htod32(enable); ++ ++ buf_len += sizeof(enable_parm); ++ memcpy((char *)pkt_filterp, ++ &enable_parm, ++ sizeof(enable_parm)); ++ ++ /* Enable/disable the specified filter. */ ++ rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0); ++ rc = rc >= 0 ? 0 : rc; ++ if (rc) ++ DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", ++ __FUNCTION__, arg, rc)); ++ else ++ DHD_TRACE(("%s: successfully added pktfilter %s\n", ++ __FUNCTION__, arg)); ++ ++ /* Contorl the master mode */ ++ bcm_mkiovar("pkt_filter_mode", (char *)&master_mode, 4, buf, sizeof(buf)); ++ rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); ++ rc = rc >= 0 ? 0 : rc; ++ if (rc) ++ DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", ++ __FUNCTION__, arg, rc)); ++ ++fail: ++ if (arg_org) ++ MFREE(dhd->osh, arg_org, strlen(arg) + 1); ++} ++ ++void ++dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg) ++{ ++ const char *str; ++ wl_pkt_filter_t pkt_filter; ++ wl_pkt_filter_t *pkt_filterp; ++ int buf_len; ++ int str_len; ++ int rc; ++ uint32 mask_size; ++ uint32 pattern_size; ++ char *argv[8], * buf = 0; ++ int i = 0; ++ char *arg_save = 0, *arg_org = 0; ++#define BUF_SIZE 2048 ++ ++ if (!arg) ++ return; ++ ++ if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) { ++ DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ arg_org = arg_save; ++ ++ if (!(buf = MALLOC(dhd->osh, BUF_SIZE))) { ++ DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ memcpy(arg_save, arg, strlen(arg) + 1); ++ ++ if (strlen(arg) > BUF_SIZE) { ++ DHD_ERROR(("Not enough buffer %d < %d\n", (int)strlen(arg), (int)sizeof(buf))); ++ goto fail; ++ } ++ ++ argv[i] = bcmstrtok(&arg_save, " ", 0); ++ while (argv[i++]) ++ argv[i] = bcmstrtok(&arg_save, " ", 0); ++ ++ i = 0; ++ if (argv[i] == NULL) { ++ DHD_ERROR(("No args provided\n")); ++ goto fail; ++ } ++ ++ str = "pkt_filter_add"; ++ str_len = strlen(str); ++ bcm_strncpy_s(buf, BUF_SIZE, str, str_len); ++ buf[ str_len ] = '\0'; ++ buf_len = str_len + 1; ++ ++ pkt_filterp = (wl_pkt_filter_t *) (buf + str_len + 1); ++ ++ /* Parse packet filter id. */ ++ pkt_filter.id = htod32(strtoul(argv[i], NULL, 0)); ++ ++ if (argv[++i] == NULL) { ++ DHD_ERROR(("Polarity not provided\n")); ++ goto fail; ++ } ++ ++ /* Parse filter polarity. */ ++ pkt_filter.negate_match = htod32(strtoul(argv[i], NULL, 0)); ++ ++ if (argv[++i] == NULL) { ++ DHD_ERROR(("Filter type not provided\n")); ++ goto fail; ++ } ++ ++ /* Parse filter type. */ ++ pkt_filter.type = htod32(strtoul(argv[i], NULL, 0)); ++ ++ if (argv[++i] == NULL) { ++ DHD_ERROR(("Offset not provided\n")); ++ goto fail; ++ } ++ ++ /* Parse pattern filter offset. */ ++ pkt_filter.u.pattern.offset = htod32(strtoul(argv[i], NULL, 0)); ++ ++ if (argv[++i] == NULL) { ++ DHD_ERROR(("Bitmask not provided\n")); ++ goto fail; ++ } ++ ++ /* Parse pattern filter mask. */ ++ mask_size = ++ htod32(wl_pattern_atoh(argv[i], (char *) pkt_filterp->u.pattern.mask_and_pattern)); ++ ++ if (argv[++i] == NULL) { ++ DHD_ERROR(("Pattern not provided\n")); ++ goto fail; ++ } ++ ++ /* Parse pattern filter pattern. */ ++ pattern_size = ++ htod32(wl_pattern_atoh(argv[i], ++ (char *) &pkt_filterp->u.pattern.mask_and_pattern[mask_size])); ++ ++ if (mask_size != pattern_size) { ++ DHD_ERROR(("Mask and pattern not the same size\n")); ++ goto fail; ++ } ++ ++ pkt_filter.u.pattern.size_bytes = mask_size; ++ buf_len += WL_PKT_FILTER_FIXED_LEN; ++ buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size); ++ ++ /* Keep-alive attributes are set in local variable (keep_alive_pkt), and ++ ** then memcpy'ed into buffer (keep_alive_pktp) since there is no ++ ** guarantee that the buffer is properly aligned. ++ */ ++ memcpy((char *)pkt_filterp, ++ &pkt_filter, ++ WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN); ++ ++ rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0); ++ rc = rc >= 0 ? 0 : rc; ++ ++ if (rc) ++ DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", ++ __FUNCTION__, arg, rc)); ++ else ++ DHD_TRACE(("%s: successfully added pktfilter %s\n", ++ __FUNCTION__, arg)); ++ ++fail: ++ if (arg_org) ++ MFREE(dhd->osh, arg_org, strlen(arg) + 1); ++ ++ if (buf) ++ MFREE(dhd->osh, buf, BUF_SIZE); ++} ++#endif /* PKT_FILTER_SUPPORT */ ++ ++/* ========================== */ ++/* ==== ARP OFFLOAD SUPPORT = */ ++/* ========================== */ ++#ifdef ARP_OFFLOAD_SUPPORT ++void ++dhd_arp_offload_set(dhd_pub_t * dhd, int arp_mode) ++{ ++ char iovbuf[32]; ++ int retcode; ++ ++ bcm_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf)); ++ retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ retcode = retcode >= 0 ? 0 : retcode; ++ if (retcode) ++ DHD_TRACE(("%s: failed to set ARP offload mode to 0x%x, retcode = %d\n", ++ __FUNCTION__, arp_mode, retcode)); ++ else ++ DHD_TRACE(("%s: successfully set ARP offload mode to 0x%x\n", ++ __FUNCTION__, arp_mode)); ++} ++ ++void ++dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable) ++{ ++ char iovbuf[32]; ++ int retcode; ++ ++ bcm_mkiovar("arpoe", (char *)&arp_enable, 4, iovbuf, sizeof(iovbuf)); ++ retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ retcode = retcode >= 0 ? 0 : retcode; ++ if (retcode) ++ DHD_TRACE(("%s: failed to enabe ARP offload to %d, retcode = %d\n", ++ __FUNCTION__, arp_enable, retcode)); ++ else ++ DHD_TRACE(("%s: successfully enabed ARP offload to %d\n", ++ __FUNCTION__, arp_enable)); ++ if (arp_enable) { ++ uint32 version; ++ bcm_mkiovar("arp_version", 0, 0, iovbuf, sizeof(iovbuf)); ++ retcode = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0); ++ if (retcode) { ++ DHD_INFO(("%s: fail to get version (maybe version 1:retcode = %d\n", ++ __FUNCTION__, retcode)); ++ dhd->arp_version = 1; ++ } ++ else { ++ memcpy(&version, iovbuf, sizeof(version)); ++ DHD_INFO(("%s: ARP Version= %x\n", __FUNCTION__, version)); ++ dhd->arp_version = version; ++ } ++ } ++} ++ ++void ++dhd_aoe_arp_clr(dhd_pub_t *dhd, int idx) ++{ ++ int ret = 0; ++ int iov_len = 0; ++ char iovbuf[128]; ++ ++ if (dhd == NULL) return; ++ if (dhd->arp_version == 1) ++ idx = 0; ++ ++ iov_len = bcm_mkiovar("arp_table_clear", 0, 0, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, idx)) < 0) ++ DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); ++} ++ ++void ++dhd_aoe_hostip_clr(dhd_pub_t *dhd, int idx) ++{ ++ int ret = 0; ++ int iov_len = 0; ++ char iovbuf[128]; ++ ++ if (dhd == NULL) return; ++ if (dhd->arp_version == 1) ++ idx = 0; ++ ++ iov_len = bcm_mkiovar("arp_hostip_clear", 0, 0, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, idx)) < 0) ++ DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); ++} ++ ++void ++dhd_arp_offload_add_ip(dhd_pub_t *dhd, uint32 ipaddr, int idx) ++{ ++ int iov_len = 0; ++ char iovbuf[32]; ++ int retcode; ++ ++ ++ if (dhd == NULL) return; ++ if (dhd->arp_version == 1) ++ idx = 0; ++ iov_len = bcm_mkiovar("arp_hostip", (char *)&ipaddr, ++ sizeof(ipaddr), iovbuf, sizeof(iovbuf)); ++ retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, idx); ++ ++ if (retcode) ++ DHD_TRACE(("%s: ARP ip addr add failed, retcode = %d\n", ++ __FUNCTION__, retcode)); ++ else ++ DHD_TRACE(("%s: sARP H ipaddr entry added \n", ++ __FUNCTION__)); ++} ++ ++int ++dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen, int idx) ++{ ++ int retcode, i; ++ int iov_len; ++ uint32 *ptr32 = buf; ++ bool clr_bottom = FALSE; ++ ++ if (!buf) ++ return -1; ++ if (dhd == NULL) return -1; ++ if (dhd->arp_version == 1) ++ idx = 0; ++ ++ iov_len = bcm_mkiovar("arp_hostip", 0, 0, buf, buflen); ++ BCM_REFERENCE(iov_len); ++ retcode = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, buflen, FALSE, idx); ++ ++ if (retcode) { ++ DHD_TRACE(("%s: ioctl WLC_GET_VAR error %d\n", ++ __FUNCTION__, retcode)); ++ ++ return -1; ++ } ++ ++ /* clean up the buf, ascii reminder */ ++ for (i = 0; i < MAX_IPV4_ENTRIES; i++) { ++ if (!clr_bottom) { ++ if (*ptr32 == 0) ++ clr_bottom = TRUE; ++ } else { ++ *ptr32 = 0; ++ } ++ ptr32++; ++ } ++ ++ return 0; ++} ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++/* send up locally generated event */ ++void ++dhd_sendup_event_common(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data) ++{ ++ switch (ntoh32(event->event_type)) { ++#ifdef WLBTAMP ++ case WLC_E_BTA_HCI_EVENT: ++ break; ++#endif /* WLBTAMP */ ++ default: ++ break; ++ } ++ ++ /* Call per-port handler. */ ++ dhd_sendup_event(dhdp, event, data); ++} ++ ++ ++/* ++ * returns = TRUE if associated, FALSE if not associated ++ */ ++bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval) ++{ ++ char bssid[6], zbuf[6]; ++ int ret = -1; ++ ++ bzero(bssid, 6); ++ bzero(zbuf, 6); ++ ++ ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_BSSID, (char *)&bssid, ETHER_ADDR_LEN, FALSE, 0); ++ DHD_TRACE((" %s WLC_GET_BSSID ioctl res = %d\n", __FUNCTION__, ret)); ++ ++ if (ret == BCME_NOTASSOCIATED) { ++ DHD_TRACE(("%s: not associated! res:%d\n", __FUNCTION__, ret)); ++ } ++ ++ if (retval) ++ *retval = ret; ++ ++ if (ret < 0) ++ return FALSE; ++ ++ if ((memcmp(bssid, zbuf, ETHER_ADDR_LEN) != 0)) { ++ /* STA is assocoated BSSID is non zero */ ++ ++ if (bss_buf) { ++ /* return bss if caller provided buf */ ++ memcpy(bss_buf, bssid, ETHER_ADDR_LEN); ++ } ++ return TRUE; ++ } else { ++ DHD_TRACE(("%s: WLC_GET_BSSID ioctl returned zero bssid\n", __FUNCTION__)); ++ return FALSE; ++ } ++} ++ ++ ++/* Function to estimate possible DTIM_SKIP value */ ++int ++dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd) ++{ ++ int bcn_li_dtim = 1; /* deafult no dtim skip setting */ ++ int ret = -1; ++ int dtim_assoc = 0; ++ int ap_beacon = 0; ++ ++ /* Check if associated */ ++ if (dhd_is_associated(dhd, NULL, NULL) == FALSE) { ++ DHD_TRACE(("%s NOT assoc ret %d\n", __FUNCTION__, ret)); ++ goto exit; ++ } ++ ++ /* read associated AP beacon interval */ ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_BCNPRD, ++ &ap_beacon, sizeof(ap_beacon), FALSE, 0)) < 0) { ++ DHD_ERROR(("%s get beacon failed code %d\n", __FUNCTION__, ret)); ++ goto exit; ++ } ++ ++ /* if associated APs Beacon more that 100msec do no dtim skip */ ++ if (ap_beacon > MAX_DTIM_SKIP_BEACON_ITERVAL) { ++ DHD_ERROR(("%s NO dtim skip for AP with beacon %d ms\n", __FUNCTION__, ap_beacon)); ++ goto exit; ++ } ++ ++ /* read associated ap's dtim setup */ ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_DTIMPRD, ++ &dtim_assoc, sizeof(dtim_assoc), FALSE, 0)) < 0) { ++ DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); ++ goto exit; ++ } ++ ++ /* if not assocated just eixt */ ++ if (dtim_assoc == 0) { ++ goto exit; ++ } ++ ++ /* attemp to use platform defined dtim skip interval */ ++ bcn_li_dtim = dhd->suspend_bcn_li_dtim; ++ ++ /* check if sta listen interval fits into AP dtim */ ++ if (dtim_assoc > LISTEN_INTERVAL) { ++ /* AP DTIM to big for our Listen Interval : no dtim skiping */ ++ bcn_li_dtim = 1; ++ DHD_ERROR(("%s DTIM=%d > Listen=%d : too big ...\n", ++ __FUNCTION__, dtim_assoc, LISTEN_INTERVAL)); ++ goto exit; ++ } ++ ++ if ((bcn_li_dtim * dtim_assoc) > LISTEN_INTERVAL) { ++ /* Round up dtim_skip to fit into STAs Listen Interval */ ++ bcn_li_dtim = (int)(LISTEN_INTERVAL / dtim_assoc); ++ DHD_TRACE(("%s agjust dtim_skip as %d\n", __FUNCTION__, bcn_li_dtim)); ++ } ++ ++ DHD_ERROR(("%s beacon=%d bcn_li_dtim=%d DTIM=%d Listen=%d\n", ++ __FUNCTION__, ap_beacon, bcn_li_dtim, dtim_assoc, LISTEN_INTERVAL)); ++ ++exit: ++ return bcn_li_dtim; ++} ++ ++/* Check if the mode supports STA MODE */ ++bool dhd_support_sta_mode(dhd_pub_t *dhd) ++{ ++ ++#ifdef WL_CFG80211 ++ if (!(dhd->op_mode & DHD_FLAG_STA_MODE)) ++ return FALSE; ++ else ++#endif /* WL_CFG80211 */ ++ return TRUE; ++} ++ ++#if defined(PNO_SUPPORT) ++int ++dhd_pno_clean(dhd_pub_t *dhd) ++{ ++ char iovbuf[128]; ++ int pfn_enabled = 0; ++ int iov_len = 0; ++ int ret; ++ ++ /* Disable pfn */ ++ iov_len = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) >= 0) { ++ /* clear pfn */ ++ iov_len = bcm_mkiovar("pfnclear", 0, 0, iovbuf, sizeof(iovbuf)); ++ if (iov_len) { ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ iov_len, TRUE, 0)) < 0) { ++ DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); ++ } ++ } ++ else { ++ ret = -1; ++ DHD_ERROR(("%s failed code %d\n", __FUNCTION__, iov_len)); ++ } ++ } ++ else ++ DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); ++ ++ return ret; ++} ++ ++int ++dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) ++{ ++ char iovbuf[128]; ++ int ret = -1; ++ ++ if ((!dhd) && ((pfn_enabled != 0) || (pfn_enabled != 1))) { ++ DHD_ERROR(("%s error exit\n", __FUNCTION__)); ++ return ret; ++ } ++ ++#ifndef WL_SCHED_SCAN ++ if (!dhd_support_sta_mode(dhd)) ++ return (ret); ++ ++ memset(iovbuf, 0, sizeof(iovbuf)); ++ ++ if ((pfn_enabled) && (dhd_is_associated(dhd, NULL, NULL) == TRUE)) { ++ DHD_ERROR(("%s pno is NOT enable : called in assoc mode , ignore\n", __FUNCTION__)); ++ return ret; ++ } ++#endif /* !WL_SCHED_SCAN */ ++ ++ /* Enable/disable PNO */ ++ if ((ret = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf))) > 0) { ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, ++ iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s failed for error=%d\n", __FUNCTION__, ret)); ++ return ret; ++ } ++ else { ++ dhd->pno_enable = pfn_enabled; ++ DHD_TRACE(("%s set pno as %s\n", ++ __FUNCTION__, dhd->pno_enable ? "Enable" : "Disable")); ++ } ++ } ++ else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, ret)); ++ ++ return ret; ++} ++ ++/* Function to execute combined scan */ ++int ++dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, ++ int pno_repeat, int pno_freq_expo_max) ++{ ++ int err = -1; ++ char iovbuf[128]; ++ int k, i; ++ wl_pfn_param_t pfn_param; ++ wl_pfn_t pfn_element; ++ uint len = 0; ++ ++ DHD_TRACE(("%s nssid=%d nchan=%d\n", __FUNCTION__, nssid, scan_fr)); ++ ++ if ((!dhd) || (!ssids_local)) { ++ DHD_ERROR(("%s error exit(%s %s)\n", __FUNCTION__, ++ (!dhd)?"dhd is null":"", (!ssids_local)?"ssid is null":"")); ++ err = -1; ++ return err; ++ } ++#ifndef WL_SCHED_SCAN ++ if (!dhd_support_sta_mode(dhd)) ++ return err; ++#endif /* !WL_SCHED_SCAN */ ++ ++ /* Check for broadcast ssid */ ++ for (k = 0; k < nssid; k++) { ++ if (!ssids_local[k].SSID_len) { ++ DHD_ERROR(("%d: Broadcast SSID is ilegal for PNO setting\n", k)); ++ return err; ++ } ++ } ++/* #define PNO_DUMP 1 */ ++#ifdef PNO_DUMP ++ { ++ int j; ++ for (j = 0; j < nssid; j++) { ++ DHD_ERROR(("%d: scan for %s size =%d\n", j, ++ ssids_local[j].SSID, ssids_local[j].SSID_len)); ++ } ++ } ++#endif /* PNO_DUMP */ ++ ++ /* clean up everything */ ++ if ((err = dhd_pno_clean(dhd)) < 0) { ++ DHD_ERROR(("%s failed error=%d\n", __FUNCTION__, err)); ++ return err; ++ } ++ memset(iovbuf, 0, sizeof(iovbuf)); ++ memset(&pfn_param, 0, sizeof(pfn_param)); ++ memset(&pfn_element, 0, sizeof(pfn_element)); ++ ++ /* set pfn parameters */ ++ pfn_param.version = htod32(PFN_VERSION); ++ pfn_param.flags = htod16((PFN_LIST_ORDER << SORT_CRITERIA_BIT)); ++ ++ /* check and set extra pno params */ ++ if ((pno_repeat != 0) || (pno_freq_expo_max != 0)) { ++ pfn_param.flags |= htod16(ENABLE << ENABLE_ADAPTSCAN_BIT); ++ pfn_param.repeat = (uchar) (pno_repeat); ++ pfn_param.exp = (uchar) (pno_freq_expo_max); ++ } ++ /* set up pno scan fr */ ++ if (scan_fr != 0) ++ pfn_param.scan_freq = htod32(scan_fr); ++ ++ if (pfn_param.scan_freq > PNO_SCAN_MAX_FW_SEC) { ++ DHD_ERROR(("%s pno freq above %d sec\n", __FUNCTION__, PNO_SCAN_MAX_FW_SEC)); ++ return err; ++ } ++ if (pfn_param.scan_freq < PNO_SCAN_MIN_FW_SEC) { ++ DHD_ERROR(("%s pno freq less %d sec\n", __FUNCTION__, PNO_SCAN_MIN_FW_SEC)); ++ return err; ++ } ++ ++ len = bcm_mkiovar("pfn_set", (char *)&pfn_param, sizeof(pfn_param), iovbuf, sizeof(iovbuf)); ++ if ((err = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, len, TRUE, 0)) < 0) { ++ DHD_ERROR(("%s pfn_set failed for error=%d\n", ++ __FUNCTION__, err)); ++ return err; ++ } ++ ++ /* set all pfn ssid */ ++ for (i = 0; i < nssid; i++) { ++ ++ pfn_element.infra = htod32(DOT11_BSSTYPE_INFRASTRUCTURE); ++ pfn_element.auth = (DOT11_OPEN_SYSTEM); ++ pfn_element.wpa_auth = htod32(WPA_AUTH_PFN_ANY); ++ pfn_element.wsec = htod32(0); ++ pfn_element.infra = htod32(1); ++ pfn_element.flags = htod32(ENABLE << WL_PFN_HIDDEN_BIT); ++ memcpy((char *)pfn_element.ssid.SSID, ssids_local[i].SSID, ssids_local[i].SSID_len); ++ pfn_element.ssid.SSID_len = ssids_local[i].SSID_len; ++ ++ if ((len = ++ bcm_mkiovar("pfn_add", (char *)&pfn_element, ++ sizeof(pfn_element), iovbuf, sizeof(iovbuf))) > 0) { ++ if ((err = ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, len, TRUE, 0)) < 0) { ++ DHD_ERROR(("%s failed for i=%d error=%d\n", ++ __FUNCTION__, i, err)); ++ return err; ++ } ++ else ++ DHD_TRACE(("%s set OK with PNO time=%d repeat=%d max_adjust=%d\n", ++ __FUNCTION__, pfn_param.scan_freq, ++ pfn_param.repeat, pfn_param.exp)); ++ } ++ else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, err)); ++ } ++ ++ /* Enable PNO */ ++ /* dhd_pno_enable(dhd, 1); */ ++ return err; ++} ++ ++int ++dhd_pno_get_status(dhd_pub_t *dhd) ++{ ++ int ret = -1; ++ ++ if (!dhd) ++ return ret; ++ else ++ return (dhd->pno_enable); ++} ++ ++#endif /* OEM_ANDROID && PNO_SUPPORT */ ++ ++#if defined(KEEP_ALIVE) ++int dhd_keep_alive_onoff(dhd_pub_t *dhd) ++{ ++ char buf[256]; ++ const char *str; ++ wl_mkeep_alive_pkt_t mkeep_alive_pkt; ++ wl_mkeep_alive_pkt_t *mkeep_alive_pktp; ++ int buf_len; ++ int str_len; ++ int res = -1; ++ ++ if (!dhd_support_sta_mode(dhd)) ++ return res; ++ ++ DHD_TRACE(("%s execution\n", __FUNCTION__)); ++ ++ str = "mkeep_alive"; ++ str_len = strlen(str); ++ strncpy(buf, str, str_len); ++ buf[ str_len ] = '\0'; ++ mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) (buf + str_len + 1); ++ mkeep_alive_pkt.period_msec = CUSTOM_KEEP_ALIVE_SETTING; ++ buf_len = str_len + 1; ++ mkeep_alive_pkt.version = htod16(WL_MKEEP_ALIVE_VERSION); ++ mkeep_alive_pkt.length = htod16(WL_MKEEP_ALIVE_FIXED_LEN); ++ /* Setup keep alive zero for null packet generation */ ++ mkeep_alive_pkt.keep_alive_id = 0; ++ mkeep_alive_pkt.len_bytes = 0; ++ buf_len += WL_MKEEP_ALIVE_FIXED_LEN; ++ bzero(mkeep_alive_pkt.data, sizeof(mkeep_alive_pkt.data)); ++ /* Keep-alive attributes are set in local variable (mkeep_alive_pkt), and ++ * then memcpy'ed into buffer (mkeep_alive_pktp) since there is no ++ * guarantee that the buffer is properly aligned. ++ */ ++ memcpy((char *)mkeep_alive_pktp, &mkeep_alive_pkt, WL_MKEEP_ALIVE_FIXED_LEN); ++ ++ res = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0); ++ ++ return res; ++} ++#endif /* defined(KEEP_ALIVE) */ ++/* Android ComboSCAN support */ ++ ++/* ++ * data parsing from ComboScan tlv list ++*/ ++int ++wl_iw_parse_data_tlv(char** list_str, void *dst, int dst_size, const char token, ++ int input_size, int *bytes_left) ++{ ++ char* str; ++ uint16 short_temp; ++ uint32 int_temp; ++ ++ if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) { ++ DHD_ERROR(("%s error paramters\n", __FUNCTION__)); ++ return -1; ++ } ++ str = *list_str; ++ ++ /* Clean all dest bytes */ ++ memset(dst, 0, dst_size); ++ while (*bytes_left > 0) { ++ ++ if (str[0] != token) { ++ DHD_TRACE(("%s NOT Type=%d get=%d left_parse=%d \n", ++ __FUNCTION__, token, str[0], *bytes_left)); ++ return -1; ++ } ++ ++ *bytes_left -= 1; ++ str += 1; ++ ++ if (input_size == 1) { ++ memcpy(dst, str, input_size); ++ } ++ else if (input_size == 2) { ++ memcpy(dst, (char *)htod16(memcpy(&short_temp, str, input_size)), ++ input_size); ++ } ++ else if (input_size == 4) { ++ memcpy(dst, (char *)htod32(memcpy(&int_temp, str, input_size)), ++ input_size); ++ } ++ ++ *bytes_left -= input_size; ++ str += input_size; ++ *list_str = str; ++ return 1; ++ } ++ return 1; ++} ++ ++/* ++ * channel list parsing from cscan tlv list ++*/ ++int ++wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list, ++ int channel_num, int *bytes_left) ++{ ++ char* str; ++ int idx = 0; ++ ++ if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) { ++ DHD_ERROR(("%s error paramters\n", __FUNCTION__)); ++ return -1; ++ } ++ str = *list_str; ++ ++ while (*bytes_left > 0) { ++ ++ if (str[0] != CSCAN_TLV_TYPE_CHANNEL_IE) { ++ *list_str = str; ++ DHD_TRACE(("End channel=%d left_parse=%d %d\n", idx, *bytes_left, str[0])); ++ return idx; ++ } ++ /* Get proper CSCAN_TLV_TYPE_CHANNEL_IE */ ++ *bytes_left -= 1; ++ str += 1; ++ ++ if (str[0] == 0) { ++ /* All channels */ ++ channel_list[idx] = 0x0; ++ } ++ else { ++ channel_list[idx] = (uint16)str[0]; ++ DHD_TRACE(("%s channel=%d \n", __FUNCTION__, channel_list[idx])); ++ } ++ *bytes_left -= 1; ++ str += 1; ++ ++ if (idx++ > 255) { ++ DHD_ERROR(("%s Too many channels \n", __FUNCTION__)); ++ return -1; ++ } ++ } ++ ++ *list_str = str; ++ return idx; ++} ++ ++/* ++ * SSIDs list parsing from cscan tlv list ++ */ ++int ++wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, int max, int *bytes_left) ++{ ++ char* str; ++ int idx = 0; ++ ++ if ((list_str == NULL) || (*list_str == NULL) || (*bytes_left < 0)) { ++ DHD_ERROR(("%s error paramters\n", __FUNCTION__)); ++ return -1; ++ } ++ str = *list_str; ++ while (*bytes_left > 0) { ++ ++ if (str[0] != CSCAN_TLV_TYPE_SSID_IE) { ++ *list_str = str; ++ DHD_TRACE(("nssid=%d left_parse=%d %d\n", idx, *bytes_left, str[0])); ++ return idx; ++ } ++ ++ /* Get proper CSCAN_TLV_TYPE_SSID_IE */ ++ *bytes_left -= 1; ++ str += 1; ++ ++ if (str[0] == 0) { ++ /* Broadcast SSID */ ++ ssid[idx].SSID_len = 0; ++ memset((char*)ssid[idx].SSID, 0x0, DOT11_MAX_SSID_LEN); ++ *bytes_left -= 1; ++ str += 1; ++ ++ DHD_TRACE(("BROADCAST SCAN left=%d\n", *bytes_left)); ++ } ++ else if (str[0] <= DOT11_MAX_SSID_LEN) { ++ /* Get proper SSID size */ ++ ssid[idx].SSID_len = str[0]; ++ *bytes_left -= 1; ++ str += 1; ++ ++ /* Get SSID */ ++ if (ssid[idx].SSID_len > *bytes_left) { ++ DHD_ERROR(("%s out of memory range len=%d but left=%d\n", ++ __FUNCTION__, ssid[idx].SSID_len, *bytes_left)); ++ return -1; ++ } ++ ++ memcpy((char*)ssid[idx].SSID, str, ssid[idx].SSID_len); ++ ++ *bytes_left -= ssid[idx].SSID_len; ++ str += ssid[idx].SSID_len; ++ ++ DHD_TRACE(("%s :size=%d left=%d\n", ++ (char*)ssid[idx].SSID, ssid[idx].SSID_len, *bytes_left)); ++ } ++ else { ++ DHD_ERROR(("### SSID size more that %d\n", str[0])); ++ return -1; ++ } ++ ++ if (idx++ > max) { ++ DHD_ERROR(("%s number of SSIDs more that %d\n", __FUNCTION__, idx)); ++ return -1; ++ } ++ } ++ ++ *list_str = str; ++ return idx; ++} ++ ++/* Parse a comma-separated list from list_str into ssid array, starting ++ * at index idx. Max specifies size of the ssid array. Parses ssids ++ * and returns updated idx; if idx >= max not all fit, the excess have ++ * not been copied. Returns -1 on empty string, or on ssid too long. ++ */ ++int ++wl_iw_parse_ssid_list(char** list_str, wlc_ssid_t* ssid, int idx, int max) ++{ ++ char* str, *ptr; ++ ++ if ((list_str == NULL) || (*list_str == NULL)) ++ return -1; ++ ++ for (str = *list_str; str != NULL; str = ptr) { ++ ++ /* check for next TAG */ ++ if (!strncmp(str, GET_CHANNEL, strlen(GET_CHANNEL))) { ++ *list_str = str + strlen(GET_CHANNEL); ++ return idx; ++ } ++ ++ if ((ptr = strchr(str, ',')) != NULL) { ++ *ptr++ = '\0'; ++ } ++ ++ if (strlen(str) > DOT11_MAX_SSID_LEN) { ++ DHD_ERROR(("ssid <%s> exceeds %d\n", str, DOT11_MAX_SSID_LEN)); ++ return -1; ++ } ++ ++ if (strlen(str) == 0) ++ ssid[idx].SSID_len = 0; ++ ++ if (idx < max) { ++ bzero(ssid[idx].SSID, sizeof(ssid[idx].SSID)); ++ strncpy((char*)ssid[idx].SSID, str, sizeof(ssid[idx].SSID) - 1); ++ ssid[idx].SSID_len = strlen(str); ++ } ++ idx++; ++ } ++ return idx; ++} ++ ++/* ++ * Parse channel list from iwpriv CSCAN ++ */ ++int ++wl_iw_parse_channel_list(char** list_str, uint16* channel_list, int channel_num) ++{ ++ int num; ++ int val; ++ char* str; ++ char* endptr = NULL; ++ ++ if ((list_str == NULL)||(*list_str == NULL)) ++ return -1; ++ ++ str = *list_str; ++ num = 0; ++ while (strncmp(str, GET_NPROBE, strlen(GET_NPROBE))) { ++ val = (int)strtoul(str, &endptr, 0); ++ if (endptr == str) { ++ printf("could not parse channel number starting at" ++ " substring \"%s\" in list:\n%s\n", ++ str, *list_str); ++ return -1; ++ } ++ str = endptr + strspn(endptr, " ,"); ++ ++ if (num == channel_num) { ++ DHD_ERROR(("too many channels (more than %d) in channel list:\n%s\n", ++ channel_num, *list_str)); ++ return -1; ++ } ++ ++ channel_list[num++] = (uint16)val; ++ } ++ *list_str = str; ++ return num; ++} +diff --git a/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c b/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c +new file mode 100644 +index 00000000..cfd1ac31 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c +@@ -0,0 +1,293 @@ ++/* ++* Customer code to add GPIO control during WLAN start/stop ++* Copyright (C) 1999-2012, Broadcom Corporation ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2 (the "GPL"), ++* available at http://www.broadcom.com/licenses/GPLv2.php, with the ++* following added to such license: ++* ++* As a special exception, the copyright holders of this software give you ++* permission to link this software with independent modules, and to copy and ++* distribute the resulting executable under terms of your choice, provided that ++* you also meet, for each linked independent module, the terms and conditions of ++* the license of that module. An independent module is a module which is not ++* derived from this software. The special exception does not apply to any ++* modifications of the software. ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a license ++* other than the GPL, without Broadcom's express prior written consent. ++* ++* $Id: dhd_custom_gpio.c 345514 2012-07-18 07:47:36Z $ ++*/ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#define WL_ERROR(x) printf x ++#define WL_TRACE(x) ++ ++#ifdef CUSTOMER_HW ++extern void bcm_wlan_power_off(int); ++extern void bcm_wlan_power_on(int); ++#endif /* CUSTOMER_HW */ ++#if defined(CUSTOMER_HW2) ++#ifdef CONFIG_WIFI_CONTROL_FUNC ++int wifi_set_power(int on, unsigned long msec); ++int wifi_get_irq_number(unsigned long *irq_flags_ptr); ++int wifi_get_mac_addr(unsigned char *buf); ++void *wifi_get_country_code(char *ccode); ++#else ++int wifi_set_power(int on, unsigned long msec) { return -1; } ++int wifi_get_irq_number(unsigned long *irq_flags_ptr) { return -1; } ++int wifi_get_mac_addr(unsigned char *buf) { return -1; } ++void *wifi_get_country_code(char *ccode) { return NULL; } ++#endif /* CONFIG_WIFI_CONTROL_FUNC */ ++#endif /* CUSTOMER_HW2 */ ++ ++#if defined(OOB_INTR_ONLY) ++ ++#if defined(BCMLXSDMMC) ++extern int sdioh_mmc_irq(int irq); ++#endif /* (BCMLXSDMMC) */ ++ ++#ifdef CUSTOMER_HW3 ++#include ++#endif ++ ++/* Customer specific Host GPIO defintion */ ++static int dhd_oob_gpio_num = -1; ++ ++module_param(dhd_oob_gpio_num, int, 0644); ++MODULE_PARM_DESC(dhd_oob_gpio_num, "DHD oob gpio number"); ++ ++/* This function will return: ++ * 1) return : Host gpio interrupt number per customer platform ++ * 2) irq_flags_ptr : Type of Host interrupt as Level or Edge ++ * ++ * NOTE : ++ * Customer should check his platform definitions ++ * and his Host Interrupt spec ++ * to figure out the proper setting for his platform. ++ * Broadcom provides just reference settings as example. ++ * ++ */ ++int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr) ++{ ++ int host_oob_irq = 0; ++ ++#if defined(CUSTOMER_HW2) ++ host_oob_irq = wifi_get_irq_number(irq_flags_ptr); ++ ++#else ++#if defined(CUSTOM_OOB_GPIO_NUM) ++ if (dhd_oob_gpio_num < 0) { ++ dhd_oob_gpio_num = CUSTOM_OOB_GPIO_NUM; ++ } ++#endif /* CUSTOMER_OOB_GPIO_NUM */ ++ ++ if (dhd_oob_gpio_num < 0) { ++ WL_ERROR(("%s: ERROR customer specific Host GPIO is NOT defined \n", ++ __FUNCTION__)); ++ return (dhd_oob_gpio_num); ++ } ++ ++ WL_ERROR(("%s: customer specific Host GPIO number is (%d)\n", ++ __FUNCTION__, dhd_oob_gpio_num)); ++ ++#if defined CUSTOMER_HW ++ host_oob_irq = MSM_GPIO_TO_INT(dhd_oob_gpio_num); ++#elif defined CUSTOMER_HW3 ++ gpio_request(dhd_oob_gpio_num, "oob irq"); ++ host_oob_irq = gpio_to_irq(dhd_oob_gpio_num); ++ gpio_direction_input(dhd_oob_gpio_num); ++#endif /* CUSTOMER_HW */ ++#endif /* CUSTOMER_HW2 */ ++ ++ return (host_oob_irq); ++} ++#endif /* defined(OOB_INTR_ONLY) */ ++ ++/* Customer function to control hw specific wlan gpios */ ++void ++dhd_customer_gpio_wlan_ctrl(int onoff) ++{ ++ switch (onoff) { ++ case WLAN_RESET_OFF: ++ WL_TRACE(("%s: call customer specific GPIO to insert WLAN RESET\n", ++ __FUNCTION__)); ++#ifdef CUSTOMER_HW ++ bcm_wlan_power_off(2); ++#endif /* CUSTOMER_HW */ ++#if defined(CUSTOMER_HW2) ++ wifi_set_power(0, 0); ++#endif ++ WL_ERROR(("=========== WLAN placed in RESET ========\n")); ++ break; ++ ++ case WLAN_RESET_ON: ++ WL_TRACE(("%s: callc customer specific GPIO to remove WLAN RESET\n", ++ __FUNCTION__)); ++#ifdef CUSTOMER_HW ++ bcm_wlan_power_on(2); ++#endif /* CUSTOMER_HW */ ++#if defined(CUSTOMER_HW2) ++ wifi_set_power(1, 0); ++#endif ++ WL_ERROR(("=========== WLAN going back to live ========\n")); ++ break; ++ ++ case WLAN_POWER_OFF: ++ WL_TRACE(("%s: call customer specific GPIO to turn off WL_REG_ON\n", ++ __FUNCTION__)); ++#ifdef CUSTOMER_HW ++ bcm_wlan_power_off(1); ++#endif /* CUSTOMER_HW */ ++ break; ++ ++ case WLAN_POWER_ON: ++ WL_TRACE(("%s: call customer specific GPIO to turn on WL_REG_ON\n", ++ __FUNCTION__)); ++#ifdef CUSTOMER_HW ++ bcm_wlan_power_on(1); ++ /* Lets customer power to get stable */ ++ OSL_DELAY(200); ++#endif /* CUSTOMER_HW */ ++ break; ++ } ++} ++ ++#ifdef GET_CUSTOM_MAC_ENABLE ++/* Function to get custom MAC address */ ++int ++dhd_custom_get_mac_address(unsigned char *buf) ++{ ++ int ret = 0; ++ ++ WL_TRACE(("%s Enter\n", __FUNCTION__)); ++ if (!buf) ++ return -EINVAL; ++ ++ /* Customer access to MAC address stored outside of DHD driver */ ++#if defined(CUSTOMER_HW2) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) ++ ret = wifi_get_mac_addr(buf); ++#endif ++ ++#ifdef EXAMPLE_GET_MAC ++ /* EXAMPLE code */ ++ { ++ struct ether_addr ea_example = {{0x00, 0x11, 0x22, 0x33, 0x44, 0xFF}}; ++ bcopy((char *)&ea_example, buf, sizeof(struct ether_addr)); ++ } ++#endif /* EXAMPLE_GET_MAC */ ++ ++ return ret; ++} ++#endif /* GET_CUSTOM_MAC_ENABLE */ ++ ++/* Customized Locale table : OPTIONAL feature */ ++const struct cntry_locales_custom translate_custom_table[] = { ++/* Table should be filled out based on custom platform regulatory requirement */ ++#ifdef EXAMPLE_TABLE ++ {"", "XY", 4}, /* Universal if Country code is unknown or empty */ ++ {"US", "US", 69}, /* input ISO "US" to : US regrev 69 */ ++ {"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */ ++ {"EU", "EU", 5}, /* European union countries to : EU regrev 05 */ ++ {"AT", "EU", 5}, ++ {"BE", "EU", 5}, ++ {"BG", "EU", 5}, ++ {"CY", "EU", 5}, ++ {"CZ", "EU", 5}, ++ {"DK", "EU", 5}, ++ {"EE", "EU", 5}, ++ {"FI", "EU", 5}, ++ {"FR", "EU", 5}, ++ {"DE", "EU", 5}, ++ {"GR", "EU", 5}, ++ {"HU", "EU", 5}, ++ {"IE", "EU", 5}, ++ {"IT", "EU", 5}, ++ {"LV", "EU", 5}, ++ {"LI", "EU", 5}, ++ {"LT", "EU", 5}, ++ {"LU", "EU", 5}, ++ {"MT", "EU", 5}, ++ {"NL", "EU", 5}, ++ {"PL", "EU", 5}, ++ {"PT", "EU", 5}, ++ {"RO", "EU", 5}, ++ {"SK", "EU", 5}, ++ {"SI", "EU", 5}, ++ {"ES", "EU", 5}, ++ {"SE", "EU", 5}, ++ {"GB", "EU", 5}, ++ {"KR", "XY", 3}, ++ {"AU", "XY", 3}, ++ {"CN", "XY", 3}, /* input ISO "CN" to : XY regrev 03 */ ++ {"TW", "XY", 3}, ++ {"AR", "XY", 3}, ++ {"MX", "XY", 3}, ++ {"IL", "IL", 0}, ++ {"CH", "CH", 0}, ++ {"TR", "TR", 0}, ++ {"NO", "NO", 0}, ++#endif /* EXMAPLE_TABLE */ ++}; ++ ++ ++/* Customized Locale convertor ++* input : ISO 3166-1 country abbreviation ++* output: customized cspec ++*/ ++void get_customized_country_code(char *country_iso_code, wl_country_t *cspec) ++{ ++#if defined(CUSTOMER_HW2) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ ++ struct cntry_locales_custom *cloc_ptr; ++ ++ if (!cspec) ++ return; ++ ++ cloc_ptr = wifi_get_country_code(country_iso_code); ++ if (cloc_ptr) { ++ strlcpy(cspec->ccode, cloc_ptr->custom_locale, WLC_CNTRY_BUF_SZ); ++ cspec->rev = cloc_ptr->custom_locale_rev; ++ } ++ return; ++#else ++ int size, i; ++ ++ size = ARRAYSIZE(translate_custom_table); ++ ++ if (cspec == 0) ++ return; ++ ++ if (size == 0) ++ return; ++ ++ for (i = 0; i < size; i++) { ++ if (strcmp(country_iso_code, translate_custom_table[i].iso_abbrev) == 0) { ++ memcpy(cspec->ccode, ++ translate_custom_table[i].custom_locale, WLC_CNTRY_BUF_SZ); ++ cspec->rev = translate_custom_table[i].custom_locale_rev; ++ return; ++ } ++ } ++#ifdef EXAMPLE_TABLE ++ /* if no country code matched return first universal code from translate_custom_table */ ++ memcpy(cspec->ccode, translate_custom_table[0].custom_locale, WLC_CNTRY_BUF_SZ); ++ cspec->rev = translate_custom_table[0].custom_locale_rev; ++#endif /* EXMAPLE_TABLE */ ++ return; ++#endif /* defined(CUSTOMER_HW2) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)) */ ++} +diff --git a/drivers/net/wireless/bcmdhd/dhd_dbg.h b/drivers/net/wireless/bcmdhd/dhd_dbg.h +new file mode 100644 +index 00000000..e1660ce1 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_dbg.h +@@ -0,0 +1,116 @@ ++/* ++ * Debug/trace/assert driver definitions for Dongle Host Driver. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhd_dbg.h 353490 2012-08-27 21:10:02Z $ ++ */ ++ ++#ifndef _dhd_dbg_ ++#define _dhd_dbg_ ++ ++#define USE_NET_RATELIMIT net_ratelimit() ++ ++#if defined(DHD_DEBUG) ++ ++#define DHD_ERROR(args) do {if ((dhd_msg_level & DHD_ERROR_VAL) && USE_NET_RATELIMIT) \ ++ printf args;} while (0) ++#define DHD_TRACE(args) do {if (dhd_msg_level & DHD_TRACE_VAL) printf args;} while (0) ++#define DHD_INFO(args) do {if (dhd_msg_level & DHD_INFO_VAL) printf args;} while (0) ++#define DHD_DATA(args) do {if (dhd_msg_level & DHD_DATA_VAL) printf args;} while (0) ++#define DHD_CTL(args) do {if (dhd_msg_level & DHD_CTL_VAL) printf args;} while (0) ++#define DHD_TIMER(args) do {if (dhd_msg_level & DHD_TIMER_VAL) printf args;} while (0) ++#define DHD_HDRS(args) do {if (dhd_msg_level & DHD_HDRS_VAL) printf args;} while (0) ++#define DHD_BYTES(args) do {if (dhd_msg_level & DHD_BYTES_VAL) printf args;} while (0) ++#define DHD_INTR(args) do {if (dhd_msg_level & DHD_INTR_VAL) printf args;} while (0) ++#define DHD_GLOM(args) do {if (dhd_msg_level & DHD_GLOM_VAL) printf args;} while (0) ++#define DHD_EVENT(args) do {if (dhd_msg_level & DHD_EVENT_VAL) printf args;} while (0) ++#define DHD_BTA(args) do {if (dhd_msg_level & DHD_BTA_VAL) printf args;} while (0) ++#define DHD_ISCAN(args) do {if (dhd_msg_level & DHD_ISCAN_VAL) printf args;} while (0) ++#define DHD_ARPOE(args) do {if (dhd_msg_level & DHD_ARPOE_VAL) printf args;} while (0) ++#define DHD_REORDER(args) do {if (dhd_msg_level & DHD_REORDER_VAL) printf args;} while (0) ++ ++#define DHD_TRACE_HW4 DHD_TRACE ++ ++#define DHD_ERROR_ON() (dhd_msg_level & DHD_ERROR_VAL) ++#define DHD_TRACE_ON() (dhd_msg_level & DHD_TRACE_VAL) ++#define DHD_INFO_ON() (dhd_msg_level & DHD_INFO_VAL) ++#define DHD_DATA_ON() (dhd_msg_level & DHD_DATA_VAL) ++#define DHD_CTL_ON() (dhd_msg_level & DHD_CTL_VAL) ++#define DHD_TIMER_ON() (dhd_msg_level & DHD_TIMER_VAL) ++#define DHD_HDRS_ON() (dhd_msg_level & DHD_HDRS_VAL) ++#define DHD_BYTES_ON() (dhd_msg_level & DHD_BYTES_VAL) ++#define DHD_INTR_ON() (dhd_msg_level & DHD_INTR_VAL) ++#define DHD_GLOM_ON() (dhd_msg_level & DHD_GLOM_VAL) ++#define DHD_EVENT_ON() (dhd_msg_level & DHD_EVENT_VAL) ++#define DHD_BTA_ON() (dhd_msg_level & DHD_BTA_VAL) ++#define DHD_ISCAN_ON() (dhd_msg_level & DHD_ISCAN_VAL) ++#define DHD_ARPOE_ON() (dhd_msg_level & DHD_ARPOE_VAL) ++#define DHD_REORDER_ON() (dhd_msg_level & DHD_REORDER_VAL) ++ ++#else /* defined(BCMDBG) || defined(DHD_DEBUG) */ ++ ++#define DHD_ERROR(args) do {if (USE_NET_RATELIMIT) printf args;} while (0) ++#define DHD_TRACE(args) ++#define DHD_INFO(args) ++#define DHD_DATA(args) ++#define DHD_CTL(args) ++#define DHD_TIMER(args) ++#define DHD_HDRS(args) ++#define DHD_BYTES(args) ++#define DHD_INTR(args) ++#define DHD_GLOM(args) ++#define DHD_EVENT(args) ++#define DHD_BTA(args) ++#define DHD_ISCAN(args) ++#define DHD_ARPOE(args) ++#define DHD_REORDER(args) ++ ++#define DHD_TRACE_HW4 DHD_TRACE ++ ++#define DHD_ERROR_ON() 0 ++#define DHD_TRACE_ON() 0 ++#define DHD_INFO_ON() 0 ++#define DHD_DATA_ON() 0 ++#define DHD_CTL_ON() 0 ++#define DHD_TIMER_ON() 0 ++#define DHD_HDRS_ON() 0 ++#define DHD_BYTES_ON() 0 ++#define DHD_INTR_ON() 0 ++#define DHD_GLOM_ON() 0 ++#define DHD_EVENT_ON() 0 ++#define DHD_BTA_ON() 0 ++#define DHD_ISCAN_ON() 0 ++#define DHD_ARPOE_ON() 0 ++#define DHD_REORDER_ON() 0 ++#endif ++ ++#define DHD_LOG(args) ++ ++#define DHD_BLOG(cp, size) ++ ++#define DHD_NONE(args) ++extern int dhd_msg_level; ++ ++/* Defines msg bits */ ++#include ++ ++#endif /* _dhd_dbg_ */ +diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c +new file mode 100644 +index 00000000..76824951 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_linux.c +@@ -0,0 +1,6007 @@ ++/* ++ * Broadcom Dongle Host Driver (DHD), Linux-specific network interface ++ * Basically selected code segments from usb-cdc.c and usb-rndis.c ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhd_linux.c 380566 2013-01-23 05:29:02Z $ ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_HAS_WAKELOCK ++#include ++#endif ++#ifdef WL_CFG80211 ++#include ++#endif ++ ++#ifdef WLBTAMP ++#include ++#include ++#include ++#endif ++ ++#ifdef WLMEDIA_HTSF ++#include ++#include ++ ++#define HTSF_MINLEN 200 /* min. packet length to timestamp */ ++#define HTSF_BUS_DELAY 150 /* assume a fix propagation in us */ ++#define TSMAX 1000 /* max no. of timing record kept */ ++#define NUMBIN 34 ++static uint32 tsidx = 0; ++static uint32 htsf_seqnum = 0; ++uint32 tsfsync; ++struct timeval tsync; ++static uint32 tsport = 5010; ++ ++typedef struct histo_ { ++ uint32 bin[NUMBIN]; ++} histo_t; ++ ++#if !ISPOWEROF2(DHD_SDALIGN) ++#error DHD_SDALIGN is not a power of 2! ++#endif ++ ++static histo_t vi_d1, vi_d2, vi_d3, vi_d4; ++#endif /* WLMEDIA_HTSF */ ++ ++#if defined(PKT_FILTER_SUPPORT) ++#endif /* PKT_FILTER_SUPPORT */ ++ ++#if defined(SOFTAP) ++extern bool ap_cfg_running; ++extern bool ap_fw_loaded; ++#endif ++ ++/* enable HOSTIP cache update from the host side when an eth0:N is up */ ++#define AOE_IP_ALIAS_SUPPORT 1 ++ ++#ifdef BCM_FD_AGGR ++#include ++#include ++#endif ++#ifdef PROP_TXSTATUS ++#include ++#include ++#endif ++ ++#include ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++void aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add, int idx); ++static int dhd_device_event(struct notifier_block *this, ++ unsigned long event, ++ void *ptr); ++ ++static struct notifier_block dhd_notifier = { ++ .notifier_call = dhd_device_event ++}; ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) ++#include ++volatile bool dhd_mmc_suspend = FALSE; ++DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ ++ ++#if defined(OOB_INTR_ONLY) ++extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable); ++#endif /* defined(OOB_INTR_ONLY) */ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++static void dhd_hang_process(struct work_struct *work); ++#endif ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) ++MODULE_LICENSE("GPL v2"); ++#endif /* LinuxVer */ ++ ++#include ++ ++#ifdef BCM_FD_AGGR ++#define DBUS_RX_BUFFER_SIZE_DHD(net) (BCM_RPC_TP_DNGL_AGG_MAX_BYTE) ++#else ++#ifndef PROP_TXSTATUS ++#define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen) ++#else ++#define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen + 128) ++#endif ++#endif /* BCM_FD_AGGR */ ++ ++#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) ++const char * ++print_tainted() ++{ ++ return ""; ++} ++#endif /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */ ++ ++/* Linux wireless extension support */ ++#if defined(CONFIG_WIRELESS_EXT) ++#include ++extern wl_iw_extra_params_t g_wl_iw_params; ++#endif /* defined(CONFIG_WIRELESS_EXT) */ ++ ++#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ++#include ++#endif /* defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) */ ++ ++extern int dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd); ++ ++#ifdef PKT_FILTER_SUPPORT ++extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg); ++extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode); ++#endif ++ ++#ifdef READ_MACADDR ++extern int dhd_read_macaddr(struct dhd_info *dhd, struct ether_addr *mac); ++#endif ++#ifdef RDWR_MACADDR ++extern int dhd_check_rdwr_macaddr(struct dhd_info *dhd, dhd_pub_t *dhdp, struct ether_addr *mac); ++extern int dhd_write_rdwr_macaddr(struct ether_addr *mac); ++#endif ++#ifdef WRITE_MACADDR ++extern int dhd_write_macaddr(struct ether_addr *mac); ++#endif ++#ifdef GET_MAC_FROM_OTP ++extern int dhd_check_module_mac(dhd_pub_t *dhd, struct ether_addr *mac); ++#endif ++#ifdef MIMO_ANT_SETTING ++extern int dhd_sel_ant_from_file(dhd_pub_t *dhd); ++#endif ++ ++#ifdef GLOBALCONFIG_WLAN_COUNTRY_CODE ++int dhd_customer_set_country(dhd_pub_t *dhd); ++#endif ++ ++/* Interface control information */ ++typedef struct dhd_if { ++ struct dhd_info *info; /* back pointer to dhd_info */ ++ /* OS/stack specifics */ ++ struct net_device *net; ++ struct net_device_stats stats; ++ int idx; /* iface idx in dongle */ ++ dhd_if_state_t state; /* interface state */ ++ uint subunit; /* subunit */ ++ uint8 mac_addr[ETHER_ADDR_LEN]; /* assigned MAC address */ ++ bool attached; /* Delayed attachment when unset */ ++ bool txflowcontrol; /* Per interface flow control indicator */ ++ char name[IFNAMSIZ+1]; /* linux interface name */ ++ uint8 bssidx; /* bsscfg index for the interface */ ++ bool set_multicast; ++ bool event2cfg80211; /* To determine if pass event to cfg80211 */ ++} dhd_if_t; ++ ++#ifdef WLMEDIA_HTSF ++typedef struct { ++ uint32 low; ++ uint32 high; ++} tsf_t; ++ ++typedef struct { ++ uint32 last_cycle; ++ uint32 last_sec; ++ uint32 last_tsf; ++ uint32 coef; /* scaling factor */ ++ uint32 coefdec1; /* first decimal */ ++ uint32 coefdec2; /* second decimal */ ++} htsf_t; ++ ++typedef struct { ++ uint32 t1; ++ uint32 t2; ++ uint32 t3; ++ uint32 t4; ++} tstamp_t; ++ ++static tstamp_t ts[TSMAX]; ++static tstamp_t maxdelayts; ++static uint32 maxdelay = 0, tspktcnt = 0, maxdelaypktno = 0; ++ ++#endif /* WLMEDIA_HTSF */ ++ ++/* Local private structure (extension of pub) */ ++typedef struct dhd_info { ++#if defined(CONFIG_WIRELESS_EXT) ++ wl_iw_t iw; /* wireless extensions state (must be first) */ ++#endif /* defined(CONFIG_WIRELESS_EXT) */ ++ ++ dhd_pub_t pub; ++ ++ /* For supporting multiple interfaces */ ++ dhd_if_t *iflist[DHD_MAX_IFS]; ++ ++ struct semaphore proto_sem; ++#ifdef PROP_TXSTATUS ++ spinlock_t wlfc_spinlock; ++#endif /* PROP_TXSTATUS */ ++#ifdef WLMEDIA_HTSF ++ htsf_t htsf; ++#endif ++ wait_queue_head_t ioctl_resp_wait; ++ struct timer_list timer; ++ bool wd_timer_valid; ++ struct tasklet_struct tasklet; ++ spinlock_t sdlock; ++ spinlock_t txqlock; ++ spinlock_t dhd_lock; ++#ifdef DHDTHREAD ++ /* Thread based operation */ ++ bool threads_only; ++ struct semaphore sdsem; ++ ++ tsk_ctl_t thr_dpc_ctl; ++ tsk_ctl_t thr_wdt_ctl; ++#endif /* DHDTHREAD */ ++ bool dhd_tasklet_create; ++ tsk_ctl_t thr_sysioc_ctl; ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ struct work_struct work_hang; ++#endif ++ ++ /* Wakelocks */ ++#if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ struct wake_lock wl_wifi; /* Wifi wakelock */ ++ struct wake_lock wl_rxwake; /* Wifi rx wakelock */ ++ struct wake_lock wl_ctrlwake; /* Wifi ctrl wakelock */ ++ struct wake_lock wl_wdwake; /* Wifi wd wakelock */ ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ /* net_device interface lock, prevent race conditions among net_dev interface ++ * calls and wifi_on or wifi_off ++ */ ++ struct mutex dhd_net_if_mutex; ++ struct mutex dhd_suspend_mutex; ++#endif ++ spinlock_t wakelock_spinlock; ++ int wakelock_counter; ++ int wakelock_wd_counter; ++ int wakelock_rx_timeout_enable; ++ int wakelock_ctrl_timeout_enable; ++ ++ /* Thread to issue ioctl for multicast */ ++ unsigned char set_macaddress; ++ struct ether_addr macvalue; ++ wait_queue_head_t ctrl_wait; ++ atomic_t pend_8021x_cnt; ++ dhd_attach_states_t dhd_state; ++ ++#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ++ struct early_suspend early_suspend; ++#endif /* CONFIG_HAS_EARLYSUSPEND && defined(DHD_USE_EARLYSUSPEND) */ ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++ u32 pend_ipaddr; ++#endif /* ARP_OFFLOAD_SUPPORT */ ++#ifdef BCM_FD_AGGR ++ void *rpc_th; ++ void *rpc_osh; ++ struct timer_list rpcth_timer; ++ bool rpcth_timer_active; ++ bool fdaggr; ++#endif ++} dhd_info_t; ++ ++/* Flag to indicate if we should download firmware on driver load */ ++uint dhd_download_fw_on_driverload = TRUE; ++ ++/* Definitions to provide path to the firmware and nvram ++ * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt" ++ */ ++char firmware_path[MOD_PARAM_PATHLEN]; ++char nvram_path[MOD_PARAM_PATHLEN]; ++ ++/* information string to keep firmware, chio, cheip version info visiable from log */ ++char info_string[MOD_PARAM_INFOLEN]; ++module_param_string(info_string, info_string, MOD_PARAM_INFOLEN, 0444); ++ ++int op_mode = 0; ++int disable_proptx = 0; ++module_param(op_mode, int, 0644); ++extern int wl_control_wl_start(struct net_device *dev); ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++struct semaphore dhd_registration_sem; ++struct semaphore dhd_chipup_sem; ++int dhd_registration_check = FALSE; ++ ++#define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */ ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++ ++/* Spawn a thread for system ioctls (set mac, set mcast) */ ++uint dhd_sysioc = TRUE; ++module_param(dhd_sysioc, uint, 0); ++ ++/* Error bits */ ++module_param(dhd_msg_level, int, 0); ++ ++/* Disable Prop tx */ ++module_param(disable_proptx, int, 0644); ++ ++/* load firmware and/or nvram values from the filesystem */ ++module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660); ++module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0); ++ ++/* Watchdog interval */ ++uint dhd_watchdog_ms = 10; ++module_param(dhd_watchdog_ms, uint, 0); ++ ++#if defined(DHD_DEBUG) ++/* Console poll interval */ ++uint dhd_console_ms = 0; ++module_param(dhd_console_ms, uint, 0644); ++#endif /* defined(DHD_DEBUG) */ ++ ++extern uint dhd_doflow; ++/* tunable paramter to update tx credit in each dpc */ ++extern uint dhd_dpcpoll; ++module_param(dhd_doflow, uint, 0644); ++module_param(dhd_dpcpoll, uint, 0644); ++uint dhd_slpauto = TRUE; ++module_param(dhd_slpauto, uint, 0); ++ ++/* ARP offload agent mode : Enable ARP Peer Auto-Reply */ ++uint dhd_arp_mode = ARP_OL_AGENT | ARP_OL_PEER_AUTO_REPLY; ++module_param(dhd_arp_mode, uint, 0); ++ ++/* ARP offload enable */ ++uint dhd_arp_enable = TRUE; ++module_param(dhd_arp_enable, uint, 0); ++ ++#ifdef PKT_FILTER_SUPPORT ++/* Global Pkt filter enable control */ ++uint dhd_pkt_filter_enable = TRUE; ++module_param(dhd_pkt_filter_enable, uint, 0); ++#endif ++ ++/* Pkt filter init setup */ ++uint dhd_pkt_filter_init = 0; ++module_param(dhd_pkt_filter_init, uint, 0); ++ ++/* Pkt filter mode control */ ++#ifdef GAN_LITE_NAT_KEEPALIVE_FILTER ++uint dhd_master_mode = FALSE; ++#else ++uint dhd_master_mode = TRUE; ++#endif /* GAL_LITE_NAT_KEEPALIVE_FILTER */ ++module_param(dhd_master_mode, uint, 0); ++ ++#ifdef DHDTHREAD ++/* Watchdog thread priority, -1 to use kernel timer */ ++int dhd_watchdog_prio = 0; ++module_param(dhd_watchdog_prio, int, 0); ++ ++/* DPC thread priority */ ++int dhd_dpc_prio = CUSTOM_DPC_PRIO_SETTING; ++module_param(dhd_dpc_prio, int, 0); ++ ++extern int dhd_dongle_memsize; ++module_param(dhd_dongle_memsize, int, 0); ++#endif /* DHDTHREAD */ ++/* Control fw roaming */ ++uint dhd_roam_disable = 0; ++ ++/* Control radio state */ ++uint dhd_radio_up = 1; ++ ++/* Network inteface name */ ++char iface_name[IFNAMSIZ] = {'\0'}; ++module_param_string(iface_name, iface_name, IFNAMSIZ, 0); ++ ++/* The following are specific to the SDIO dongle */ ++ ++/* IOCTL response timeout */ ++int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT; ++ ++/* Idle timeout for backplane clock */ ++int dhd_idletime = DHD_IDLETIME_TICKS; ++module_param(dhd_idletime, int, 0); ++ ++/* Use polling */ ++uint dhd_poll = FALSE; ++module_param(dhd_poll, uint, 0); ++ ++/* Use interrupts */ ++uint dhd_intr = TRUE; ++module_param(dhd_intr, uint, 0); ++ ++/* SDIO Drive Strength (in milliamps) */ ++uint dhd_sdiod_drive_strength = 6; ++module_param(dhd_sdiod_drive_strength, uint, 0); ++ ++/* Tx/Rx bounds */ ++extern uint dhd_txbound; ++extern uint dhd_rxbound; ++module_param(dhd_txbound, uint, 0); ++module_param(dhd_rxbound, uint, 0); ++ ++/* Deferred transmits */ ++extern uint dhd_deferred_tx; ++module_param(dhd_deferred_tx, uint, 0); ++ ++#ifdef BCMDBGFS ++extern void dhd_dbg_init(dhd_pub_t *dhdp); ++extern void dhd_dbg_remove(void); ++#endif /* BCMDBGFS */ ++ ++ ++ ++#ifdef SDTEST ++/* Echo packet generator (pkts/s) */ ++uint dhd_pktgen = 0; ++module_param(dhd_pktgen, uint, 0); ++ ++/* Echo packet len (0 => sawtooth, max 2040) */ ++uint dhd_pktgen_len = 0; ++module_param(dhd_pktgen_len, uint, 0); ++#endif /* SDTEST */ ++ ++/* Version string to report */ ++#ifdef DHD_DEBUG ++#ifndef SRCBASE ++#define SRCBASE "drivers/net/wireless/bcmdhd" ++#endif ++#define DHD_COMPILED "\nCompiled in " SRCBASE ++#else ++#define DHD_COMPILED ++#endif /* DHD_DEBUG */ ++ ++static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR ++#ifdef DHD_DEBUG ++"\nCompiled in " SRCBASE " on " __DATE__ " at " __TIME__ ++#endif ++; ++static void dhd_net_if_lock_local(dhd_info_t *dhd); ++static void dhd_net_if_unlock_local(dhd_info_t *dhd); ++static void dhd_suspend_lock(dhd_pub_t *dhdp); ++static void dhd_suspend_unlock(dhd_pub_t *dhdp); ++ ++#ifdef WLMEDIA_HTSF ++void htsf_update(dhd_info_t *dhd, void *data); ++tsf_t prev_tsf, cur_tsf; ++ ++uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx); ++static int dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx); ++static void dhd_dump_latency(void); ++static void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf); ++static void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf); ++static void dhd_dump_htsfhisto(histo_t *his, char *s); ++#endif /* WLMEDIA_HTSF */ ++ ++/* Monitor interface */ ++int dhd_monitor_init(void *dhd_pub); ++int dhd_monitor_uninit(void); ++ ++ ++#if defined(CONFIG_WIRELESS_EXT) ++struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); ++#endif /* defined(CONFIG_WIRELESS_EXT) */ ++ ++static void dhd_dpc(ulong data); ++/* forward decl */ ++extern int dhd_wait_pend8021x(struct net_device *dev); ++ ++#ifdef TOE ++#ifndef BDC ++#error TOE requires BDC ++#endif /* !BDC */ ++static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol); ++static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol); ++#endif /* TOE */ ++ ++static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, ++ wl_event_msg_t *event_ptr, void **data_ptr); ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) ++static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored) ++{ ++ int ret = NOTIFY_DONE; ++ ++#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) ++ switch (action) { ++ case PM_HIBERNATION_PREPARE: ++ case PM_SUSPEND_PREPARE: ++ dhd_mmc_suspend = TRUE; ++ ret = NOTIFY_OK; ++ break; ++ case PM_POST_HIBERNATION: ++ case PM_POST_SUSPEND: ++ dhd_mmc_suspend = FALSE; ++ ret = NOTIFY_OK; ++ break; ++ } ++ smp_mb(); ++#endif ++ return ret; ++} ++ ++static struct notifier_block dhd_sleep_pm_notifier = { ++ .notifier_call = dhd_sleep_pm_callback, ++ .priority = 10 ++}; ++extern int register_pm_notifier(struct notifier_block *nb); ++extern int unregister_pm_notifier(struct notifier_block *nb); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ ++ ++void dhd_set_packet_filter(dhd_pub_t *dhd) ++{ ++#ifdef PKT_FILTER_SUPPORT ++ int i; ++ ++ DHD_TRACE(("%s: enter\n", __FUNCTION__)); ++ if (dhd_pkt_filter_enable) { ++ for (i = 0; i < dhd->pktfilter_count; i++) { ++ dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]); ++ } ++ } ++#endif /* PKT_FILTER_SUPPORT */ ++} ++ ++void dhd_enable_packet_filter(int value, dhd_pub_t *dhd) ++{ ++#ifdef PKT_FILTER_SUPPORT ++ int i; ++ ++ DHD_TRACE(("%s: enter, value = %d\n", __FUNCTION__, value)); ++ /* 1 - Enable packet filter, only allow unicast packet to send up */ ++ /* 0 - Disable packet filter */ ++ if (dhd_pkt_filter_enable && (!value || ++ (dhd_support_sta_mode(dhd) && !dhd->dhcp_in_progress))) { ++ for (i = 0; i < dhd->pktfilter_count; i++) { ++#ifdef PASS_ARP_PACKET ++ if (value && (i == dhd->pktfilter_count -1) && ++ !(dhd->op_mode & (DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE))) { ++ DHD_TRACE_HW4(("Do not turn on ARP white list pkt filter:" ++ "val %d, cnt %d, op_mode 0x%x\n", ++ value, i, dhd->op_mode)); ++ continue; ++ } ++#endif ++ dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i], ++ value, dhd_master_mode); ++ } ++ } ++#endif /* PKT_FILTER_SUPPORT */ ++} ++ ++static int dhd_set_suspend(int value, dhd_pub_t *dhd) ++{ ++#if !defined(SUPPORT_PM2_ONLY) ++ int power_mode = PM_MAX; ++#endif ++ /* wl_pkt_filter_enable_t enable_parm; */ ++ char iovbuf[32]; ++ int bcn_li_dtim = 0; /* Default bcn_li_dtim in resume mode is 0 */ ++#ifndef DISABLE_FW_ROAM_SUSPEND ++ uint roamvar = 1; ++#endif ++#ifdef ENABLE_BCN_LI_BCN_WAKEUP ++ int bcn_li_bcn; ++#endif /* ENABLE_BCN_LI_BCN_WAKEUP */ ++#ifdef PASS_ALL_MCAST_PKTS ++ struct dhd_info *dhdinfo = dhd->info; ++ uint32 allmulti; ++ uint i; ++#endif /* PASS_ALL_MCAST_PKTS */ ++ ++ DHD_TRACE(("%s: enter, value = %d in_suspend=%d\n", ++ __FUNCTION__, value, dhd->in_suspend)); ++ ++ dhd_suspend_lock(dhd); ++ if (dhd && dhd->up) { ++ if (value && dhd->in_suspend) { ++#ifdef PKT_FILTER_SUPPORT ++ dhd->early_suspended = 1; ++#endif ++ /* Kernel suspended */ ++ DHD_ERROR(("%s: force extra Suspend setting\n", __FUNCTION__)); ++ ++#if !defined(SUPPORT_PM2_ONLY) ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, ++ sizeof(power_mode), TRUE, 0); ++#endif ++ /* Enable packet filter, only allow unicast packet to send up */ ++ dhd_enable_packet_filter(1, dhd); ++#ifdef PASS_ALL_MCAST_PKTS ++ allmulti = 0; ++ bcm_mkiovar("allmulti", (char *)&allmulti, ++ 4, iovbuf, sizeof(iovbuf)); ++ for (i = 0; i < DHD_MAX_IFS; i++) { ++ if (dhdinfo->iflist[i] && dhdinfo->iflist[i]->net) ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, i); ++ } ++#endif /* PASS_ALL_MCAST_PKTS */ ++ ++ /* If DTIM skip is set up as default, force it to wake ++ * each third DTIM for better power savings. Note that ++ * one side effect is a chance to miss BC/MC packet. ++ */ ++ bcn_li_dtim = dhd_get_suspend_bcn_li_dtim(dhd); ++ bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, ++ 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ ++#ifndef DISABLE_FW_ROAM_SUSPEND ++ /* Disable firmware roaming during suspend */ ++ bcm_mkiovar("roam_off", (char *)&roamvar, 4, ++ iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#endif ++#ifdef ENABLE_BCN_LI_BCN_WAKEUP ++ bcn_li_bcn = 0; ++ bcm_mkiovar("bcn_li_bcn", (char *)&bcn_li_bcn, ++ 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#endif /* ENABLE_BCN_LI_BCN_WAKEUP */ ++ ++ } else { ++#ifdef PKT_FILTER_SUPPORT ++ dhd->early_suspended = 0; ++#endif ++ /* Kernel resumed */ ++ DHD_ERROR(("%s: Remove extra suspend setting\n", __FUNCTION__)); ++ ++#if !defined(SUPPORT_PM2_ONLY) ++ power_mode = PM_FAST; ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, ++ sizeof(power_mode), TRUE, 0); ++#endif ++ /* disable pkt filter */ ++ dhd_enable_packet_filter(0, dhd); ++#ifdef PASS_ALL_MCAST_PKTS ++ allmulti = 1; ++ bcm_mkiovar("allmulti", (char *)&allmulti, ++ 4, iovbuf, sizeof(iovbuf)); ++ for (i = 0; i < DHD_MAX_IFS; i++) { ++ if (dhdinfo->iflist[i] && dhdinfo->iflist[i]->net) ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, i); ++ } ++#endif /* PASS_ALL_MCAST_PKTS */ ++ ++ /* restore pre-suspend setting for dtim_skip */ ++ bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, ++ 4, iovbuf, sizeof(iovbuf)); ++ ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#ifndef DISABLE_FW_ROAM_SUSPEND ++ roamvar = dhd_roam_disable; ++ bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, ++ sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#endif ++#ifdef ENABLE_BCN_LI_BCN_WAKEUP ++ bcn_li_bcn = 1; ++ bcm_mkiovar("bcn_li_bcn", (char *)&bcn_li_bcn, ++ 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#endif /* ENABLE_BCN_LI_BCN_WAKEUP */ ++ ++ } ++ } ++ ++ dhd_suspend_unlock(dhd); ++ return 0; ++} ++ ++static int dhd_suspend_resume_helper(struct dhd_info *dhd, int val, int force) ++{ ++ dhd_pub_t *dhdp = &dhd->pub; ++ int ret = 0; ++ ++ DHD_OS_WAKE_LOCK(dhdp); ++ /* Set flag when early suspend was called */ ++ dhdp->in_suspend = val; ++ if ((force || !dhdp->suspend_disable_flag) && ++ dhd_support_sta_mode(dhdp)) ++ { ++ ret = dhd_set_suspend(val, dhdp); ++ } ++ ++ DHD_OS_WAKE_UNLOCK(dhdp); ++ return ret; ++} ++ ++#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ++static void dhd_early_suspend(struct early_suspend *h) ++{ ++ struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); ++ DHD_TRACE_HW4(("%s: enter\n", __FUNCTION__)); ++ ++ if (dhd) ++ dhd_suspend_resume_helper(dhd, 1, 0); ++} ++ ++static void dhd_late_resume(struct early_suspend *h) ++{ ++ struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); ++ DHD_TRACE_HW4(("%s: enter\n", __FUNCTION__)); ++ ++ if (dhd) ++ dhd_suspend_resume_helper(dhd, 0, 0); ++} ++#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ ++ ++/* ++ * Generalized timeout mechanism. Uses spin sleep with exponential back-off until ++ * the sleep time reaches one jiffy, then switches over to task delay. Usage: ++ * ++ * dhd_timeout_start(&tmo, usec); ++ * while (!dhd_timeout_expired(&tmo)) ++ * if (poll_something()) ++ * break; ++ * if (dhd_timeout_expired(&tmo)) ++ * fatal(); ++ */ ++ ++void ++dhd_timeout_start(dhd_timeout_t *tmo, uint usec) ++{ ++ tmo->limit = usec; ++ tmo->increment = 0; ++ tmo->elapsed = 0; ++ tmo->tick = jiffies_to_usecs(1); ++} ++ ++int ++dhd_timeout_expired(dhd_timeout_t *tmo) ++{ ++ /* Does nothing the first call */ ++ if (tmo->increment == 0) { ++ tmo->increment = 1; ++ return 0; ++ } ++ ++ if (tmo->elapsed >= tmo->limit) ++ return 1; ++ ++ /* Add the delay that's about to take place */ ++ tmo->elapsed += tmo->increment; ++ ++ if (tmo->increment < tmo->tick) { ++ OSL_DELAY(tmo->increment); ++ tmo->increment *= 2; ++ if (tmo->increment > tmo->tick) ++ tmo->increment = tmo->tick; ++ } else { ++ wait_queue_head_t delay_wait; ++ DECLARE_WAITQUEUE(wait, current); ++ init_waitqueue_head(&delay_wait); ++ add_wait_queue(&delay_wait, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ remove_wait_queue(&delay_wait, &wait); ++ set_current_state(TASK_RUNNING); ++ } ++ ++ return 0; ++} ++ ++int ++dhd_net2idx(dhd_info_t *dhd, struct net_device *net) ++{ ++ int i = 0; ++ ++ ASSERT(dhd); ++ while (i < DHD_MAX_IFS) { ++ if (dhd->iflist[i] && (dhd->iflist[i]->net == net)) ++ return i; ++ i++; ++ } ++ ++ return DHD_BAD_IF; ++} ++ ++struct net_device * dhd_idx2net(void *pub, int ifidx) ++{ ++ struct dhd_pub *dhd_pub = (struct dhd_pub *)pub; ++ struct dhd_info *dhd_info; ++ ++ if (!dhd_pub || ifidx < 0 || ifidx >= DHD_MAX_IFS) ++ return NULL; ++ dhd_info = dhd_pub->info; ++ if (dhd_info && dhd_info->iflist[ifidx]) ++ return dhd_info->iflist[ifidx]->net; ++ return NULL; ++} ++ ++int ++dhd_ifname2idx(dhd_info_t *dhd, char *name) ++{ ++ int i = DHD_MAX_IFS; ++ ++ ASSERT(dhd); ++ ++ if (name == NULL || *name == '\0') ++ return 0; ++ ++ while (--i > 0) ++ if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ)) ++ break; ++ ++ DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name)); ++ ++ return i; /* default - the primary interface */ ++} ++ ++char * ++dhd_ifname(dhd_pub_t *dhdp, int ifidx) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)dhdp->info; ++ ++ ASSERT(dhd); ++ ++ if (ifidx < 0 || ifidx >= DHD_MAX_IFS) { ++ DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx)); ++ return ""; ++ } ++ ++ if (dhd->iflist[ifidx] == NULL) { ++ DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx)); ++ return ""; ++ } ++ ++ if (dhd->iflist[ifidx]->net) ++ return dhd->iflist[ifidx]->net->name; ++ ++ return ""; ++} ++ ++uint8 * ++dhd_bssidx2bssid(dhd_pub_t *dhdp, int idx) ++{ ++ int i; ++ dhd_info_t *dhd = (dhd_info_t *)dhdp; ++ ++ ASSERT(dhd); ++ for (i = 0; i < DHD_MAX_IFS; i++) ++ if (dhd->iflist[i] && dhd->iflist[i]->bssidx == idx) ++ return dhd->iflist[i]->mac_addr; ++ ++ return NULL; ++} ++ ++ ++static void ++_dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) ++{ ++ struct net_device *dev; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) ++ struct netdev_hw_addr *ha; ++#else ++ struct dev_mc_list *mclist; ++#endif ++ uint32 allmulti, cnt; ++ ++ wl_ioctl_t ioc; ++ char *buf, *bufp; ++ uint buflen; ++ int ret; ++ ++ ASSERT(dhd && dhd->iflist[ifidx]); ++ dev = dhd->iflist[ifidx]->net; ++ if (!dev) ++ return; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) ++ netif_addr_lock_bh(dev); ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) ++ cnt = netdev_mc_count(dev); ++#else ++ cnt = dev->mc_count; ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) ++ netif_addr_unlock_bh(dev); ++#endif ++ ++ /* Determine initial value of allmulti flag */ ++ allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE; ++#ifdef PASS_ALL_MCAST_PKTS ++#ifdef PKT_FILTER_SUPPORT ++ if (!dhd->pub.early_suspended) ++#endif /* PKT_FILTER_SUPPORT */ ++ allmulti = TRUE; ++#endif /* PASS_ALL_MCAST_PKTS */ ++ ++ /* Send down the multicast list first. */ ++ ++ ++ buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN); ++ if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) { ++ DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n", ++ dhd_ifname(&dhd->pub, ifidx), cnt)); ++ return; ++ } ++ ++ strncpy(bufp, "mcast_list", buflen - 1); ++ bufp[buflen - 1] = '\0'; ++ bufp += strlen("mcast_list") + 1; ++ ++ cnt = htol32(cnt); ++ memcpy(bufp, &cnt, sizeof(cnt)); ++ bufp += sizeof(cnt); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) ++ netif_addr_lock_bh(dev); ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) ++ netdev_for_each_mc_addr(ha, dev) { ++ if (!cnt) ++ break; ++ memcpy(bufp, ha->addr, ETHER_ADDR_LEN); ++ bufp += ETHER_ADDR_LEN; ++ cnt--; ++ } ++#else ++ for (mclist = dev->mc_list; (mclist && (cnt > 0)); ++ cnt--, mclist = mclist->next) { ++ memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN); ++ bufp += ETHER_ADDR_LEN; ++ } ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) ++ netif_addr_unlock_bh(dev); ++#endif ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ioc.cmd = WLC_SET_VAR; ++ ioc.buf = buf; ++ ioc.len = buflen; ++ ioc.set = TRUE; ++ ++ ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); ++ if (ret < 0) { ++ DHD_ERROR(("%s: set mcast_list failed, cnt %d\n", ++ dhd_ifname(&dhd->pub, ifidx), cnt)); ++ allmulti = cnt ? TRUE : allmulti; ++ } ++ ++ MFREE(dhd->pub.osh, buf, buflen); ++ ++ /* Now send the allmulti setting. This is based on the setting in the ++ * net_device flags, but might be modified above to be turned on if we ++ * were trying to set some addresses and dongle rejected it... ++ */ ++ ++ buflen = sizeof("allmulti") + sizeof(allmulti); ++ if (!(buf = MALLOC(dhd->pub.osh, buflen))) { ++ DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx))); ++ return; ++ } ++ allmulti = htol32(allmulti); ++ ++ if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) { ++ DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n", ++ dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen)); ++ MFREE(dhd->pub.osh, buf, buflen); ++ return; ++ } ++ ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ioc.cmd = WLC_SET_VAR; ++ ioc.buf = buf; ++ ioc.len = buflen; ++ ioc.set = TRUE; ++ ++ ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); ++ if (ret < 0) { ++ DHD_ERROR(("%s: set allmulti %d failed\n", ++ dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti))); ++ } ++ ++ MFREE(dhd->pub.osh, buf, buflen); ++ ++ /* Finally, pick up the PROMISC flag as well, like the NIC driver does */ ++ ++ allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE; ++ allmulti = htol32(allmulti); ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ioc.cmd = WLC_SET_PROMISC; ++ ioc.buf = &allmulti; ++ ioc.len = sizeof(allmulti); ++ ioc.set = TRUE; ++ ++ ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); ++ if (ret < 0) { ++ DHD_ERROR(("%s: set promisc %d failed\n", ++ dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti))); ++ } ++} ++ ++int ++_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr) ++{ ++ char buf[32]; ++ wl_ioctl_t ioc; ++ int ret; ++ ++ if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) { ++ DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx))); ++ return -1; ++ } ++ memset(&ioc, 0, sizeof(ioc)); ++ ioc.cmd = WLC_SET_VAR; ++ ioc.buf = buf; ++ ioc.len = 32; ++ ioc.set = TRUE; ++ ++ ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); ++ if (ret < 0) { ++ DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx))); ++ } else { ++ memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN); ++ memcpy(dhd->pub.mac.octet, addr, ETHER_ADDR_LEN); ++ } ++ ++ return ret; ++} ++ ++#ifdef SOFTAP ++extern struct net_device *ap_net_dev; ++extern tsk_ctl_t ap_eth_ctl; /* ap netdev heper thread ctl */ ++#endif ++ ++static void ++dhd_op_if(dhd_if_t *ifp) ++{ ++ dhd_info_t *dhd; ++ int ret = 0, err = 0; ++#ifdef SOFTAP ++ unsigned long flags; ++#endif ++ ++ if (!ifp || !ifp->info || !ifp->idx) ++ return; ++ ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */ ++ dhd = ifp->info; ++ ++ DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state)); ++ ++#ifdef WL_CFG80211 ++ if (wl_cfg80211_is_progress_ifchange()) ++ return; ++ ++#endif ++ switch (ifp->state) { ++ case DHD_IF_ADD: ++ /* ++ * Delete the existing interface before overwriting it ++ * in case we missed the WLC_E_IF_DEL event. ++ */ ++ if (ifp->net != NULL) { ++ DHD_ERROR(("%s: ERROR: netdev:%s already exists, try free & unregister \n", ++ __FUNCTION__, ifp->net->name)); ++ netif_stop_queue(ifp->net); ++ unregister_netdev(ifp->net); ++ free_netdev(ifp->net); ++ } ++ /* Allocate etherdev, including space for private structure */ ++ if (!(ifp->net = alloc_etherdev(sizeof(dhd)))) { ++ DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__)); ++ ret = -ENOMEM; ++ } ++ if (ret == 0) { ++ strncpy(ifp->net->name, ifp->name, IFNAMSIZ); ++ ifp->net->name[IFNAMSIZ - 1] = '\0'; ++ memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd)); ++#ifdef WL_CFG80211 ++ if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) ++ if (!wl_cfg80211_notify_ifadd(ifp->net, ifp->idx, ifp->bssidx, ++ (void*)dhd_net_attach)) { ++ ifp->state = DHD_IF_NONE; ++ ifp->event2cfg80211 = TRUE; ++ return; ++ } ++#endif ++ if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) { ++ DHD_ERROR(("%s: dhd_net_attach failed, err %d\n", ++ __FUNCTION__, err)); ++ ret = -EOPNOTSUPP; ++ } else { ++#if defined(SOFTAP) ++ if (ap_fw_loaded && !(dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) { ++ /* semaphore that the soft AP CODE waits on */ ++ flags = dhd_os_spin_lock(&dhd->pub); ++ ++ /* save ptr to wl0.1 netdev for use in wl_iw.c */ ++ ap_net_dev = ifp->net; ++ /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */ ++ up(&ap_eth_ctl.sema); ++ dhd_os_spin_unlock(&dhd->pub, flags); ++ } ++#endif ++ DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n", ++ current->pid, ifp->net->name)); ++ ifp->state = DHD_IF_NONE; ++ } ++ } ++ break; ++ case DHD_IF_DEL: ++ /* Make sure that we don't enter again here if .. */ ++ /* dhd_op_if is called again from some other context */ ++ ifp->state = DHD_IF_DELETING; ++ if (ifp->net != NULL) { ++ DHD_TRACE(("\n%s: got 'DHD_IF_DEL' state\n", __FUNCTION__)); ++ netif_stop_queue(ifp->net); ++#ifdef WL_CFG80211 ++ if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { ++ wl_cfg80211_ifdel_ops(ifp->net); ++ } ++#endif ++ unregister_netdev(ifp->net); ++ ret = DHD_DEL_IF; /* Make sure the free_netdev() is called */ ++#ifdef WL_CFG80211 ++ if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { ++ wl_cfg80211_notify_ifdel(); ++ } ++#endif ++ } ++ break; ++ case DHD_IF_DELETING: ++ break; ++ default: ++ DHD_ERROR(("%s: bad op %d\n", __FUNCTION__, ifp->state)); ++ ASSERT(!ifp->state); ++ break; ++ } ++ ++ if (ret < 0) { ++ ifp->set_multicast = FALSE; ++ if (ifp->net) { ++ free_netdev(ifp->net); ++ ifp->net = NULL; ++ } ++ dhd->iflist[ifp->idx] = NULL; ++#ifdef SOFTAP ++ flags = dhd_os_spin_lock(&dhd->pub); ++ if (ifp->net == ap_net_dev) ++ ap_net_dev = NULL; /* NULL SOFTAP global wl0.1 as well */ ++ dhd_os_spin_unlock(&dhd->pub, flags); ++#endif /* SOFTAP */ ++ MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); ++ } ++} ++ ++static int ++_dhd_sysioc_thread(void *data) ++{ ++ tsk_ctl_t *tsk = (tsk_ctl_t *)data; ++ dhd_info_t *dhd = (dhd_info_t *)tsk->parent; ++ ++ ++ int i; ++#ifdef SOFTAP ++ bool in_ap = FALSE; ++ unsigned long flags; ++#endif ++ ++ DAEMONIZE("dhd_sysioc"); ++ ++ complete(&tsk->completed); ++ ++ while (down_interruptible(&tsk->sema) == 0) { ++ ++ SMP_RD_BARRIER_DEPENDS(); ++ if (tsk->terminated) { ++ break; ++ } ++ ++ dhd_net_if_lock_local(dhd); ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ ++ for (i = 0; i < DHD_MAX_IFS; i++) { ++ if (dhd->iflist[i]) { ++ DHD_TRACE(("%s: interface %d\n", __FUNCTION__, i)); ++#ifdef SOFTAP ++ flags = dhd_os_spin_lock(&dhd->pub); ++ in_ap = (ap_net_dev != NULL); ++ dhd_os_spin_unlock(&dhd->pub, flags); ++#endif /* SOFTAP */ ++ if (dhd->iflist[i] && dhd->iflist[i]->state) ++ dhd_op_if(dhd->iflist[i]); ++ ++ if (dhd->iflist[i] == NULL) { ++ DHD_TRACE(("\n\n %s: interface %d just been removed," ++ "!\n\n", __FUNCTION__, i)); ++ continue; ++ } ++#ifdef SOFTAP ++ if (in_ap && dhd->set_macaddress == i+1) { ++ DHD_TRACE(("attempt to set MAC for %s in AP Mode," ++ "blocked. \n", dhd->iflist[i]->net->name)); ++ dhd->set_macaddress = 0; ++ continue; ++ } ++ ++ if (in_ap && dhd->iflist[i]->set_multicast) { ++ DHD_TRACE(("attempt to set MULTICAST list for %s" ++ "in AP Mode, blocked. \n", dhd->iflist[i]->net->name)); ++ dhd->iflist[i]->set_multicast = FALSE; ++ continue; ++ } ++#endif /* SOFTAP */ ++ if (dhd->pub.up == 0) ++ continue; ++ if (dhd->iflist[i]->set_multicast) { ++ dhd->iflist[i]->set_multicast = FALSE; ++ _dhd_set_multicast_list(dhd, i); ++ } ++ if (dhd->set_macaddress == i+1) { ++ dhd->set_macaddress = 0; ++ if (_dhd_set_mac_address(dhd, i, &dhd->macvalue) == 0) { ++ DHD_INFO(( ++ "dhd_sysioc_thread: MACID is overwritten\n")); ++ } else { ++ DHD_ERROR(( ++ "dhd_sysioc_thread: _dhd_set_mac_address() failed\n")); ++ } ++ } ++ } ++ } ++ ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ dhd_net_if_unlock_local(dhd); ++ } ++ DHD_TRACE(("%s: stopped\n", __FUNCTION__)); ++ complete_and_exit(&tsk->completed, 0); ++} ++ ++static int ++dhd_set_mac_address(struct net_device *dev, void *addr) ++{ ++ int ret = 0; ++ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ struct sockaddr *sa = (struct sockaddr *)addr; ++ int ifidx; ++ ++ ifidx = dhd_net2idx(dhd, dev); ++ if (ifidx == DHD_BAD_IF) ++ return -1; ++ ++ ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); ++ memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN); ++ dhd->set_macaddress = ifidx+1; ++ up(&dhd->thr_sysioc_ctl.sema); ++ ++ return ret; ++} ++ ++static void ++dhd_set_multicast_list(struct net_device *dev) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ int ifidx; ++ ++ ifidx = dhd_net2idx(dhd, dev); ++ if (ifidx == DHD_BAD_IF) ++ return; ++ ++ ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); ++ dhd->iflist[ifidx]->set_multicast = TRUE; ++ up(&dhd->thr_sysioc_ctl.sema); ++} ++ ++#ifdef PROP_TXSTATUS ++int ++dhd_os_wlfc_block(dhd_pub_t *pub) ++{ ++ dhd_info_t *di = (dhd_info_t *)(pub->info); ++ ASSERT(di != NULL); ++ spin_lock_bh(&di->wlfc_spinlock); ++ return 1; ++} ++ ++int ++dhd_os_wlfc_unblock(dhd_pub_t *pub) ++{ ++ dhd_info_t *di = (dhd_info_t *)(pub->info); ++ ++ ASSERT(di != NULL); ++ spin_unlock_bh(&di->wlfc_spinlock); ++ return 1; ++} ++ ++const uint8 wme_fifo2ac[] = { 0, 1, 2, 3, 1, 1 }; ++uint8 prio2fifo[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; ++#define WME_PRIO2AC(prio) wme_fifo2ac[prio2fifo[(prio)]] ++ ++#endif /* PROP_TXSTATUS */ ++int ++dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) ++{ ++ int ret; ++ dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); ++ struct ether_header *eh = NULL; ++ ++ /* Reject if down */ ++ if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) { ++ /* free the packet here since the caller won't */ ++ PKTFREE(dhdp->osh, pktbuf, TRUE); ++ return -ENODEV; ++ } ++ ++ /* Update multicast statistic */ ++ if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_HDR_LEN) { ++ uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf); ++ eh = (struct ether_header *)pktdata; ++ ++ if (ETHER_ISMULTI(eh->ether_dhost)) ++ dhdp->tx_multicast++; ++ if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X) ++ atomic_inc(&dhd->pend_8021x_cnt); ++ } else { ++ PKTFREE(dhd->pub.osh, pktbuf, TRUE); ++ return BCME_ERROR; ++ } ++ ++ /* Look into the packet and update the packet priority */ ++#ifndef PKTPRIO_OVERRIDE ++ if (PKTPRIO(pktbuf) == 0) ++#endif ++ pktsetprio(pktbuf, FALSE); ++ ++#ifdef PROP_TXSTATUS ++ if (dhdp->wlfc_state) { ++ /* store the interface ID */ ++ DHD_PKTTAG_SETIF(PKTTAG(pktbuf), ifidx); ++ ++ /* store destination MAC in the tag as well */ ++ DHD_PKTTAG_SETDSTN(PKTTAG(pktbuf), eh->ether_dhost); ++ ++ /* decide which FIFO this packet belongs to */ ++ if (ETHER_ISMULTI(eh->ether_dhost)) ++ /* one additional queue index (highest AC + 1) is used for bc/mc queue */ ++ DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), AC_COUNT); ++ else ++ DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), WME_PRIO2AC(PKTPRIO(pktbuf))); ++ } else ++#endif /* PROP_TXSTATUS */ ++ /* If the protocol uses a data header, apply it */ ++ dhd_prot_hdrpush(dhdp, ifidx, pktbuf); ++ ++ /* Use bus module to send data frame */ ++#ifdef WLMEDIA_HTSF ++ dhd_htsf_addtxts(dhdp, pktbuf); ++#endif ++#ifdef PROP_TXSTATUS ++ dhd_os_wlfc_block(dhdp); ++ if (dhdp->wlfc_state && ((athost_wl_status_info_t*)dhdp->wlfc_state)->proptxstatus_mode ++ != WLFC_FCMODE_NONE) { ++ ret = dhd_wlfc_enque_sendq(dhdp->wlfc_state, DHD_PKTTAG_FIFO(PKTTAG(pktbuf)), ++ pktbuf); ++ dhd_wlfc_commit_packets(dhdp->wlfc_state, (f_commitpkt_t)dhd_bus_txdata, ++ dhdp->bus); ++ if (((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if) { ++ ((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if = 0; ++ } ++ dhd_os_wlfc_unblock(dhdp); ++ } ++ else { ++ dhd_os_wlfc_unblock(dhdp); ++ /* non-proptxstatus way */ ++ ret = dhd_bus_txdata(dhdp->bus, pktbuf); ++ } ++#else ++ ret = dhd_bus_txdata(dhdp->bus, pktbuf); ++#endif /* PROP_TXSTATUS */ ++ ++ return ret; ++} ++ ++int ++dhd_start_xmit(struct sk_buff *skb, struct net_device *net) ++{ ++ int ret; ++ void *pktbuf; ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); ++ int ifidx; ++#ifdef WLMEDIA_HTSF ++ uint8 htsfdlystat_sz = dhd->pub.htsfdlystat_sz; ++#else ++ uint8 htsfdlystat_sz = 0; ++#endif ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ ++ /* Reject if down */ ++ if (dhd->pub.busstate == DHD_BUS_DOWN || dhd->pub.hang_was_sent) { ++ DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d \n", ++ __FUNCTION__, dhd->pub.up, dhd->pub.busstate)); ++ netif_stop_queue(net); ++ /* Send Event when bus down detected during data session */ ++ if (dhd->pub.up) { ++ DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__)); ++ net_os_send_hang_message(net); ++ } ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) ++ return -ENODEV; ++#else ++ return NETDEV_TX_BUSY; ++#endif ++ } ++ ++ ifidx = dhd_net2idx(dhd, net); ++ if (ifidx == DHD_BAD_IF) { ++ DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx)); ++ netif_stop_queue(net); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) ++ return -ENODEV; ++#else ++ return NETDEV_TX_BUSY; ++#endif ++ } ++ ++ /* Make sure there's enough room for any header */ ++ ++ if (skb_headroom(skb) < dhd->pub.hdrlen + htsfdlystat_sz) { ++ struct sk_buff *skb2; ++ ++ DHD_INFO(("%s: insufficient headroom\n", ++ dhd_ifname(&dhd->pub, ifidx))); ++ dhd->pub.tx_realloc++; ++ ++ skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen + htsfdlystat_sz); ++ ++ dev_kfree_skb(skb); ++ if ((skb = skb2) == NULL) { ++ DHD_ERROR(("%s: skb_realloc_headroom failed\n", ++ dhd_ifname(&dhd->pub, ifidx))); ++ ret = -ENOMEM; ++ goto done; ++ } ++ } ++ ++ /* Convert to packet */ ++ if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) { ++ DHD_ERROR(("%s: PKTFRMNATIVE failed\n", ++ dhd_ifname(&dhd->pub, ifidx))); ++ dev_kfree_skb_any(skb); ++ ret = -ENOMEM; ++ goto done; ++ } ++#ifdef WLMEDIA_HTSF ++ if (htsfdlystat_sz && PKTLEN(dhd->pub.osh, pktbuf) >= ETHER_ADDR_LEN) { ++ uint8 *pktdata = (uint8 *)PKTDATA(dhd->pub.osh, pktbuf); ++ struct ether_header *eh = (struct ether_header *)pktdata; ++ ++ if (!ETHER_ISMULTI(eh->ether_dhost) && ++ (ntoh16(eh->ether_type) == ETHER_TYPE_IP)) { ++ eh->ether_type = hton16(ETHER_TYPE_BRCM_PKTDLYSTATS); ++ } ++ } ++#endif ++ ++ ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf); ++ ++ ++done: ++ if (ret) ++ dhd->pub.dstats.tx_dropped++; ++ else ++ dhd->pub.tx_packets++; ++ ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ ++ /* Return ok: we always eat the packet */ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) ++ return 0; ++#else ++ return NETDEV_TX_OK; ++#endif ++} ++ ++void ++dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state) ++{ ++ struct net_device *net; ++ dhd_info_t *dhd = dhdp->info; ++ int i; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ ASSERT(dhd); ++ ++ if (ifidx == ALL_INTERFACES) { ++ /* Flow control on all active interfaces */ ++ dhdp->txoff = state; ++ for (i = 0; i < DHD_MAX_IFS; i++) { ++ if (dhd->iflist[i]) { ++ net = dhd->iflist[i]->net; ++ if (state == ON) ++ netif_stop_queue(net); ++ else ++ netif_wake_queue(net); ++ } ++ } ++ } ++ else { ++ if (dhd->iflist[ifidx]) { ++ net = dhd->iflist[ifidx]->net; ++ if (state == ON) ++ netif_stop_queue(net); ++ else ++ netif_wake_queue(net); ++ } ++ } ++} ++ ++#ifdef DHD_RX_DUMP ++typedef struct { ++ uint16 type; ++ const char *str; ++} PKTTYPE_INFO; ++ ++static const PKTTYPE_INFO packet_type_info[] = ++{ ++ { ETHER_TYPE_IP, "IP" }, ++ { ETHER_TYPE_ARP, "ARP" }, ++ { ETHER_TYPE_BRCM, "BRCM" }, ++ { ETHER_TYPE_802_1X, "802.1X" }, ++ { ETHER_TYPE_WAI, "WAPI" }, ++ { 0, ""} ++}; ++ ++static const char *_get_packet_type_str(uint16 type) ++{ ++ int i; ++ int n = sizeof(packet_type_info)/sizeof(packet_type_info[1]) - 1; ++ ++ for (i = 0; i < n; i++) { ++ if (packet_type_info[i].type == type) ++ return packet_type_info[i].str; ++ } ++ ++ return packet_type_info[n].str; ++} ++#endif /* DHD_RX_DUMP */ ++ ++void ++dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)dhdp->info; ++ struct sk_buff *skb; ++ uchar *eth; ++ uint len; ++ void *data, *pnext = NULL; ++ int i; ++ dhd_if_t *ifp; ++ wl_event_msg_t event; ++ int tout_rx = 0; ++ int tout_ctrl = 0; ++ ++#ifdef DHD_RX_DUMP ++#ifdef DHD_RX_FULL_DUMP ++ int k; ++#endif /* DHD_RX_FULL_DUMP */ ++ char *dump_data; ++ uint16 protocol; ++#endif /* DHD_RX_DUMP */ ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) { ++#ifdef WLBTAMP ++ struct ether_header *eh; ++ struct dot11_llc_snap_header *lsh; ++#endif ++ ++ ifp = dhd->iflist[ifidx]; ++ if (ifp == NULL) { ++ DHD_ERROR(("%s: ifp is NULL. drop packet\n", ++ __FUNCTION__)); ++ pnext = PKTNEXT(dhdp->osh, pktbuf); ++ PKTSETNEXT(wl->sh.osh, pktbuf, NULL); ++ PKTFREE(dhdp->osh, pktbuf, TRUE); ++ continue; ++ } ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ++ /* Dropping packets before registering net device to avoid kernel panic */ ++#ifndef PROP_TXSTATUS_VSDB ++ if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) { ++#else ++ if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED || !dhd->pub.up) { ++#endif /* PROP_TXSTATUS_VSDB */ ++ DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n", ++ __FUNCTION__)); ++ pnext = PKTNEXT(dhdp->osh, pktbuf); ++ PKTSETNEXT(wl->sh.osh, pktbuf, NULL); ++ PKTFREE(dhdp->osh, pktbuf, TRUE); ++ continue; ++ } ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ ++ ++ pnext = PKTNEXT(dhdp->osh, pktbuf); ++ PKTSETNEXT(wl->sh.osh, pktbuf, NULL); ++ ++#ifdef WLBTAMP ++ eh = (struct ether_header *)PKTDATA(wl->sh.osh, pktbuf); ++ lsh = (struct dot11_llc_snap_header *)&eh[1]; ++ ++ if ((ntoh16(eh->ether_type) < ETHER_TYPE_MIN) && ++ (PKTLEN(wl->sh.osh, pktbuf) >= RFC1042_HDR_LEN) && ++ bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && ++ lsh->type == HTON16(BTA_PROT_L2CAP)) { ++ amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *) ++ ((uint8 *)eh + RFC1042_HDR_LEN); ++ ACL_data = NULL; ++ } ++#endif /* WLBTAMP */ ++ ++#ifdef PROP_TXSTATUS ++ if (dhdp->wlfc_state && PKTLEN(wl->sh.osh, pktbuf) == 0) { ++ /* WLFC may send header only packet when ++ there is an urgent message but no packet to ++ piggy-back on ++ */ ++ ((athost_wl_status_info_t*)dhdp->wlfc_state)->stats.wlfc_header_only_pkt++; ++ PKTFREE(dhdp->osh, pktbuf, TRUE); ++ continue; ++ } ++#endif ++ ++ skb = PKTTONATIVE(dhdp->osh, pktbuf); ++ ++ /* Get the protocol, maintain skb around eth_type_trans() ++ * The main reason for this hack is for the limitation of ++ * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len' ++ * to perform skb_pull inside vs ETH_HLEN. Since to avoid ++ * coping of the packet coming from the network stack to add ++ * BDC, Hardware header etc, during network interface registration ++ * we set the 'net->hard_header_len' to ETH_HLEN + extra space required ++ * for BDC, Hardware header etc. and not just the ETH_HLEN ++ */ ++ eth = skb->data; ++ len = skb->len; ++ ++#ifdef DHD_RX_DUMP ++ dump_data = skb->data; ++ protocol = (dump_data[12] << 8) | dump_data[13]; ++ DHD_ERROR(("RX DUMP - %s\n", _get_packet_type_str(protocol))); ++ ++#ifdef DHD_RX_FULL_DUMP ++ if (protocol != ETHER_TYPE_BRCM) { ++ for (k = 0; k < skb->len; k++) { ++ DHD_ERROR(("%02X ", dump_data[k])); ++ if ((k & 15) == 15) ++ DHD_ERROR(("\n")); ++ } ++ DHD_ERROR(("\n")); ++ } ++#endif /* DHD_RX_FULL_DUMP */ ++ ++ if (protocol != ETHER_TYPE_BRCM) { ++ if (dump_data[0] == 0xFF) { ++ DHD_ERROR(("%s: BROADCAST\n", __FUNCTION__)); ++ ++ if ((dump_data[12] == 8) && ++ (dump_data[13] == 6)) { ++ DHD_ERROR(("%s: ARP %d\n", ++ __FUNCTION__, dump_data[0x15])); ++ } ++ } else if (dump_data[0] & 1) { ++ DHD_ERROR(("%s: MULTICAST: " MACDBG "\n", ++ __FUNCTION__, MAC2STRDBG(dump_data))); ++ } ++ ++ if (protocol == ETHER_TYPE_802_1X) { ++ DHD_ERROR(("ETHER_TYPE_802_1X: " ++ "ver %d, type %d, replay %d\n", ++ dump_data[14], dump_data[15], ++ dump_data[30])); ++ } ++ } ++ ++#endif /* DHD_RX_DUMP */ ++ ++ ifp = dhd->iflist[ifidx]; ++ if (ifp == NULL) ++ ifp = dhd->iflist[0]; ++ ++ ASSERT(ifp); ++ skb->dev = ifp->net; ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ ++ if (skb->pkt_type == PACKET_MULTICAST) { ++ dhd->pub.rx_multicast++; ++ } ++ ++ skb->data = eth; ++ skb->len = len; ++ ++#ifdef WLMEDIA_HTSF ++ dhd_htsf_addrxts(dhdp, pktbuf); ++#endif ++ /* Strip header, count, deliver upward */ ++ skb_pull(skb, ETH_HLEN); ++ ++ /* Process special event packets and then discard them */ ++ if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) { ++ dhd_wl_host_event(dhd, &ifidx, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) ++ skb->mac_header, ++#else ++ skb->mac.raw, ++#endif ++ &event, ++ &data); ++ ++ wl_event_to_host_order(&event); ++ if (!tout_ctrl) ++ tout_ctrl = DHD_PACKET_TIMEOUT_MS; ++#ifdef WLBTAMP ++ if (event.event_type == WLC_E_BTA_HCI_EVENT) { ++ dhd_bta_doevt(dhdp, data, event.datalen); ++ } ++#endif /* WLBTAMP */ ++ ++#if defined(PNO_SUPPORT) ++ if (event.event_type == WLC_E_PFN_NET_FOUND) { ++ /* enforce custom wake lock to garantee that Kernel not suspended */ ++ tout_ctrl = CUSTOM_PNO_EVENT_LOCK_xTIME * DHD_PACKET_TIMEOUT_MS; ++ } ++#endif /* PNO_SUPPORT */ ++ ++#ifdef DHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT ++ PKTFREE(dhdp->osh, pktbuf, TRUE); ++ continue; ++#endif ++ } else { ++ tout_rx = DHD_PACKET_TIMEOUT_MS; ++ } ++ ++ ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]); ++ if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state) ++ ifp = dhd->iflist[ifidx]; ++ ++ if (ifp->net) ++ ifp->net->last_rx = jiffies; ++ ++ dhdp->dstats.rx_bytes += skb->len; ++ dhdp->rx_packets++; /* Local count */ ++ ++ if (in_interrupt()) { ++ netif_rx(skb); ++ } else { ++ /* If the receive is not processed inside an ISR, ++ * the softirqd must be woken explicitly to service ++ * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled ++ * by netif_rx_ni(), but in earlier kernels, we need ++ * to do it manually. ++ */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ++ netif_rx_ni(skb); ++#else ++ ulong flags; ++ netif_rx(skb); ++ local_irq_save(flags); ++ RAISE_RX_SOFTIRQ(); ++ local_irq_restore(flags); ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ ++ } ++ } ++ ++ DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(dhdp, tout_rx); ++ DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhdp, tout_ctrl); ++} ++ ++void ++dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx) ++{ ++ /* Linux version has nothing to do */ ++ return; ++} ++ ++void ++dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); ++ struct ether_header *eh; ++ uint16 type; ++#ifdef WLBTAMP ++ uint len; ++#endif ++ ++ dhd_prot_hdrpull(dhdp, NULL, txp, NULL, NULL); ++ ++ eh = (struct ether_header *)PKTDATA(dhdp->osh, txp); ++ type = ntoh16(eh->ether_type); ++ ++ if (type == ETHER_TYPE_802_1X) ++ atomic_dec(&dhd->pend_8021x_cnt); ++ ++#ifdef WLBTAMP ++ /* Crack open the packet and check to see if it is BT HCI ACL data packet. ++ * If yes generate packet completion event. ++ */ ++ len = PKTLEN(dhdp->osh, txp); ++ ++ /* Generate ACL data tx completion event locally to avoid SDIO bus transaction */ ++ if ((type < ETHER_TYPE_MIN) && (len >= RFC1042_HDR_LEN)) { ++ struct dot11_llc_snap_header *lsh = (struct dot11_llc_snap_header *)&eh[1]; ++ ++ if (bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && ++ ntoh16(lsh->type) == BTA_PROT_L2CAP) { ++ ++ dhd_bta_tx_hcidata_complete(dhdp, txp, success); ++ } ++ } ++#endif /* WLBTAMP */ ++} ++ ++static struct net_device_stats * ++dhd_get_stats(struct net_device *net) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); ++ dhd_if_t *ifp; ++ int ifidx; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ ifidx = dhd_net2idx(dhd, net); ++ if (ifidx == DHD_BAD_IF) { ++ DHD_ERROR(("%s: BAD_IF\n", __FUNCTION__)); ++ return NULL; ++ } ++ ++ ifp = dhd->iflist[ifidx]; ++ ASSERT(dhd && ifp); ++ ++ if (dhd->pub.up) { ++ /* Use the protocol to get dongle stats */ ++ dhd_prot_dstats(&dhd->pub); ++ } ++ ++ /* Copy dongle stats to net device stats */ ++ ifp->stats.rx_packets = dhd->pub.dstats.rx_packets; ++ ifp->stats.tx_packets = dhd->pub.dstats.tx_packets; ++ ifp->stats.rx_bytes = dhd->pub.dstats.rx_bytes; ++ ifp->stats.tx_bytes = dhd->pub.dstats.tx_bytes; ++ ifp->stats.rx_errors = dhd->pub.dstats.rx_errors; ++ ifp->stats.tx_errors = dhd->pub.dstats.tx_errors; ++ ifp->stats.rx_dropped = dhd->pub.dstats.rx_dropped; ++ ifp->stats.tx_dropped = dhd->pub.dstats.tx_dropped; ++ ifp->stats.multicast = dhd->pub.dstats.multicast; ++ ++ return &ifp->stats; ++} ++ ++#ifdef DHDTHREAD ++static int ++dhd_watchdog_thread(void *data) ++{ ++ tsk_ctl_t *tsk = (tsk_ctl_t *)data; ++ dhd_info_t *dhd = (dhd_info_t *)tsk->parent; ++ /* This thread doesn't need any user-level access, ++ * so get rid of all our resources ++ */ ++ if (dhd_watchdog_prio > 0) { ++ struct sched_param param; ++ param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)? ++ dhd_watchdog_prio:(MAX_RT_PRIO-1); ++ setScheduler(current, SCHED_FIFO, ¶m); ++ } ++ ++ DAEMONIZE("dhd_watchdog"); ++ ++ /* Run until signal received */ ++ complete(&tsk->completed); ++ ++ while (1) ++ if (down_interruptible (&tsk->sema) == 0) { ++ unsigned long flags; ++ unsigned long jiffies_at_start = jiffies; ++ unsigned long time_lapse; ++ ++ SMP_RD_BARRIER_DEPENDS(); ++ if (tsk->terminated) { ++ break; ++ } ++ ++ dhd_os_sdlock(&dhd->pub); ++ if (dhd->pub.dongle_reset == FALSE) { ++ DHD_TIMER(("%s:\n", __FUNCTION__)); ++ ++ /* Call the bus module watchdog */ ++ dhd_bus_watchdog(&dhd->pub); ++ ++ flags = dhd_os_spin_lock(&dhd->pub); ++ /* Count the tick for reference */ ++ dhd->pub.tickcnt++; ++ time_lapse = jiffies - jiffies_at_start; ++ ++ /* Reschedule the watchdog */ ++ if (dhd->wd_timer_valid) ++ mod_timer(&dhd->timer, ++ jiffies + ++ msecs_to_jiffies(dhd_watchdog_ms) - ++ min(msecs_to_jiffies(dhd_watchdog_ms), time_lapse)); ++ dhd_os_spin_unlock(&dhd->pub, flags); ++ } ++ dhd_os_sdunlock(&dhd->pub); ++ } else { ++ break; ++ } ++ ++ complete_and_exit(&tsk->completed, 0); ++} ++#endif /* DHDTHREAD */ ++ ++static void dhd_watchdog(ulong data) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)data; ++ unsigned long flags; ++ ++ if (dhd->pub.dongle_reset) { ++ return; ++ } ++ ++#ifdef DHDTHREAD ++ if (dhd->thr_wdt_ctl.thr_pid >= 0) { ++ up(&dhd->thr_wdt_ctl.sema); ++ return; ++ } ++#endif /* DHDTHREAD */ ++ ++ dhd_os_sdlock(&dhd->pub); ++ /* Call the bus module watchdog */ ++ dhd_bus_watchdog(&dhd->pub); ++ ++ flags = dhd_os_spin_lock(&dhd->pub); ++ /* Count the tick for reference */ ++ dhd->pub.tickcnt++; ++ ++ /* Reschedule the watchdog */ ++ if (dhd->wd_timer_valid) ++ mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); ++ dhd_os_spin_unlock(&dhd->pub, flags); ++ dhd_os_sdunlock(&dhd->pub); ++} ++ ++#ifdef DHDTHREAD ++static int ++dhd_dpc_thread(void *data) ++{ ++ tsk_ctl_t *tsk = (tsk_ctl_t *)data; ++ dhd_info_t *dhd = (dhd_info_t *)tsk->parent; ++ ++ /* This thread doesn't need any user-level access, ++ * so get rid of all our resources ++ */ ++ if (dhd_dpc_prio > 0) ++ { ++ struct sched_param param; ++ param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1); ++ setScheduler(current, SCHED_FIFO, ¶m); ++ } ++ ++ DAEMONIZE("dhd_dpc"); ++ /* DHD_OS_WAKE_LOCK is called in dhd_sched_dpc[dhd_linux.c] down below */ ++ ++ /* signal: thread has started */ ++ complete(&tsk->completed); ++ ++ /* Run until signal received */ ++ while (1) { ++ if (down_interruptible(&tsk->sema) == 0) { ++ ++ SMP_RD_BARRIER_DEPENDS(); ++ if (tsk->terminated) { ++ break; ++ } ++ ++ /* Call bus dpc unless it indicated down (then clean stop) */ ++ if (dhd->pub.busstate != DHD_BUS_DOWN) { ++ if (dhd_bus_dpc(dhd->pub.bus)) { ++ up(&tsk->sema); ++ } ++ else { ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ } ++ } else { ++ if (dhd->pub.up) ++ dhd_bus_stop(dhd->pub.bus, TRUE); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ } ++ } ++ else ++ break; ++ } ++ ++ complete_and_exit(&tsk->completed, 0); ++} ++#endif /* DHDTHREAD */ ++ ++static void ++dhd_dpc(ulong data) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)data; ++ ++ /* this (tasklet) can be scheduled in dhd_sched_dpc[dhd_linux.c] ++ * down below , wake lock is set, ++ * the tasklet is initialized in dhd_attach() ++ */ ++ /* Call bus dpc unless it indicated down (then clean stop) */ ++ if (dhd->pub.busstate != DHD_BUS_DOWN) { ++ if (dhd_bus_dpc(dhd->pub.bus)) ++ tasklet_schedule(&dhd->tasklet); ++ else ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ } else { ++ dhd_bus_stop(dhd->pub.bus, TRUE); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ } ++} ++ ++void ++dhd_sched_dpc(dhd_pub_t *dhdp) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)dhdp->info; ++ ++ DHD_OS_WAKE_LOCK(dhdp); ++#ifdef DHDTHREAD ++ if (dhd->thr_dpc_ctl.thr_pid >= 0) { ++ up(&dhd->thr_dpc_ctl.sema); ++ return; ++ } ++#endif /* DHDTHREAD */ ++ ++ if (dhd->dhd_tasklet_create) ++ tasklet_schedule(&dhd->tasklet); ++} ++ ++#ifdef TOE ++/* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */ ++static int ++dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol) ++{ ++ wl_ioctl_t ioc; ++ char buf[32]; ++ int ret; ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ++ ioc.cmd = WLC_GET_VAR; ++ ioc.buf = buf; ++ ioc.len = (uint)sizeof(buf); ++ ioc.set = FALSE; ++ ++ strncpy(buf, "toe_ol", sizeof(buf) - 1); ++ buf[sizeof(buf) - 1] = '\0'; ++ if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { ++ /* Check for older dongle image that doesn't support toe_ol */ ++ if (ret == -EIO) { ++ DHD_ERROR(("%s: toe not supported by device\n", ++ dhd_ifname(&dhd->pub, ifidx))); ++ return -EOPNOTSUPP; ++ } ++ ++ DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret)); ++ return ret; ++ } ++ ++ memcpy(toe_ol, buf, sizeof(uint32)); ++ return 0; ++} ++ ++/* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */ ++static int ++dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol) ++{ ++ wl_ioctl_t ioc; ++ char buf[32]; ++ int toe, ret; ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ++ ioc.cmd = WLC_SET_VAR; ++ ioc.buf = buf; ++ ioc.len = (uint)sizeof(buf); ++ ioc.set = TRUE; ++ ++ /* Set toe_ol as requested */ ++ ++ strncpy(buf, "toe_ol", sizeof(buf) - 1); ++ buf[sizeof(buf) - 1] = '\0'; ++ memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32)); ++ ++ if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { ++ DHD_ERROR(("%s: could not set toe_ol: ret=%d\n", ++ dhd_ifname(&dhd->pub, ifidx), ret)); ++ return ret; ++ } ++ ++ /* Enable toe globally only if any components are enabled. */ ++ ++ toe = (toe_ol != 0); ++ ++ strcpy(buf, "toe"); ++ memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32)); ++ ++ if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { ++ DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret)); ++ return ret; ++ } ++ ++ return 0; ++} ++#endif /* TOE */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) ++static void ++dhd_ethtool_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); ++ ++ snprintf(info->driver, sizeof(info->driver), "wl"); ++ snprintf(info->version, sizeof(info->version), "%lu", dhd->pub.drv_version); ++} ++ ++struct ethtool_ops dhd_ethtool_ops = { ++ .get_drvinfo = dhd_ethtool_get_drvinfo ++}; ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ ++ ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) ++static int ++dhd_ethtool(dhd_info_t *dhd, void *uaddr) ++{ ++ struct ethtool_drvinfo info; ++ char drvname[sizeof(info.driver)]; ++ uint32 cmd; ++#ifdef TOE ++ struct ethtool_value edata; ++ uint32 toe_cmpnt, csum_dir; ++ int ret; ++#endif ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ /* all ethtool calls start with a cmd word */ ++ if (copy_from_user(&cmd, uaddr, sizeof (uint32))) ++ return -EFAULT; ++ ++ switch (cmd) { ++ case ETHTOOL_GDRVINFO: ++ /* Copy out any request driver name */ ++ if (copy_from_user(&info, uaddr, sizeof(info))) ++ return -EFAULT; ++ strncpy(drvname, info.driver, sizeof(info.driver)); ++ drvname[sizeof(info.driver)-1] = '\0'; ++ ++ /* clear struct for return */ ++ memset(&info, 0, sizeof(info)); ++ info.cmd = cmd; ++ ++ /* if dhd requested, identify ourselves */ ++ if (strcmp(drvname, "?dhd") == 0) { ++ snprintf(info.driver, sizeof(info.driver), "dhd"); ++ strncpy(info.version, EPI_VERSION_STR, sizeof(info.version) - 1); ++ info.version[sizeof(info.version) - 1] = '\0'; ++ } ++ ++ /* otherwise, require dongle to be up */ ++ else if (!dhd->pub.up) { ++ DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__)); ++ return -ENODEV; ++ } ++ ++ /* finally, report dongle driver type */ ++ else if (dhd->pub.iswl) ++ snprintf(info.driver, sizeof(info.driver), "wl"); ++ else ++ snprintf(info.driver, sizeof(info.driver), "xx"); ++ ++ snprintf(info.version, sizeof(info.version), "%lu", dhd->pub.drv_version); ++ if (copy_to_user(uaddr, &info, sizeof(info))) ++ return -EFAULT; ++ DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__, ++ (int)sizeof(drvname), drvname, info.driver)); ++ break; ++ ++#ifdef TOE ++ /* Get toe offload components from dongle */ ++ case ETHTOOL_GRXCSUM: ++ case ETHTOOL_GTXCSUM: ++ if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0) ++ return ret; ++ ++ csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; ++ ++ edata.cmd = cmd; ++ edata.data = (toe_cmpnt & csum_dir) ? 1 : 0; ++ ++ if (copy_to_user(uaddr, &edata, sizeof(edata))) ++ return -EFAULT; ++ break; ++ ++ /* Set toe offload components in dongle */ ++ case ETHTOOL_SRXCSUM: ++ case ETHTOOL_STXCSUM: ++ if (copy_from_user(&edata, uaddr, sizeof(edata))) ++ return -EFAULT; ++ ++ /* Read the current settings, update and write back */ ++ if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0) ++ return ret; ++ ++ csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; ++ ++ if (edata.data != 0) ++ toe_cmpnt |= csum_dir; ++ else ++ toe_cmpnt &= ~csum_dir; ++ ++ if ((ret = dhd_toe_set(dhd, 0, toe_cmpnt)) < 0) ++ return ret; ++ ++ /* If setting TX checksum mode, tell Linux the new mode */ ++ if (cmd == ETHTOOL_STXCSUM) { ++ if (edata.data) ++ dhd->iflist[0]->net->features |= NETIF_F_IP_CSUM; ++ else ++ dhd->iflist[0]->net->features &= ~NETIF_F_IP_CSUM; ++ } ++ ++ break; ++#endif /* TOE */ ++ ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */ ++ ++static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) ++{ ++ dhd_info_t * dhd; ++ ++ if (!dhdp) ++ return FALSE; ++ ++ dhd = (dhd_info_t *)dhdp->info; ++ if (dhd->thr_sysioc_ctl.thr_pid < 0) { ++ DHD_ERROR(("%s : skipped due to negative pid - unloading?\n", __FUNCTION__)); ++ return FALSE; ++ } ++ ++ if ((error == -ETIMEDOUT) || (error == -EREMOTEIO) || ++ ((dhdp->busstate == DHD_BUS_DOWN) && (!dhdp->dongle_reset))) { ++ DHD_ERROR(("%s: Event HANG send up due to re=%d te=%d e=%d s=%d\n", __FUNCTION__, ++ dhdp->rxcnt_timeout, dhdp->txcnt_timeout, error, dhdp->busstate)); ++ net_os_send_hang_message(net); ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++static int ++dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); ++ dhd_ioctl_t ioc; ++ int bcmerror = 0; ++ int buflen = 0; ++ void *buf = NULL; ++ uint driver = 0; ++ int ifidx; ++ int ret; ++ ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ ++ /* send to dongle only if we are not waiting for reload already */ ++ if (dhd->pub.hang_was_sent) { ++ DHD_ERROR(("%s: HANG was sent up earlier\n", __FUNCTION__)); ++ DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(&dhd->pub, DHD_EVENT_TIMEOUT_MS); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return OSL_ERROR(BCME_DONGLE_DOWN); ++ } ++ ++ ifidx = dhd_net2idx(dhd, net); ++ DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd)); ++ ++ if (ifidx == DHD_BAD_IF) { ++ DHD_ERROR(("%s: BAD IF\n", __FUNCTION__)); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return -1; ++ } ++ ++#if defined(CONFIG_WIRELESS_EXT) ++ /* linux wireless extensions */ ++ if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) { ++ /* may recurse, do NOT lock */ ++ ret = wl_iw_ioctl(net, ifr, cmd); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return ret; ++ } ++#endif /* defined(CONFIG_WIRELESS_EXT) */ ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) ++ if (cmd == SIOCETHTOOL) { ++ ret = dhd_ethtool(dhd, (void*)ifr->ifr_data); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return ret; ++ } ++#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */ ++ ++ if (cmd == SIOCDEVPRIVATE+1) { ++ ret = wl_android_priv_cmd(net, ifr, cmd); ++ dhd_check_hang(net, &dhd->pub, ret); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return ret; ++ } ++ ++ if (cmd != SIOCDEVPRIVATE) { ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return -EOPNOTSUPP; ++ } ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ++ /* Copy the ioc control structure part of ioctl request */ ++ if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) { ++ bcmerror = BCME_BADADDR; ++ goto done; ++ } ++ ++ /* Copy out any buffer passed */ ++ if (ioc.buf) { ++ if (ioc.len == 0) { ++ DHD_TRACE(("%s: ioc.len=0, returns BCME_BADARG \n", __FUNCTION__)); ++ bcmerror = BCME_BADARG; ++ goto done; ++ } ++ buflen = MIN(ioc.len, DHD_IOCTL_MAXLEN); ++ /* optimization for direct ioctl calls from kernel */ ++ /* ++ if (segment_eq(get_fs(), KERNEL_DS)) { ++ buf = ioc.buf; ++ } else { ++ */ ++ { ++ if (!(buf = (char*)MALLOC(dhd->pub.osh, buflen))) { ++ bcmerror = BCME_NOMEM; ++ goto done; ++ } ++ if (copy_from_user(buf, ioc.buf, buflen)) { ++ bcmerror = BCME_BADADDR; ++ goto done; ++ } ++ } ++ } ++ ++ /* To differentiate between wl and dhd read 4 more byes */ ++ if ((copy_from_user(&driver, (char *)ifr->ifr_data + sizeof(wl_ioctl_t), ++ sizeof(uint)) != 0)) { ++ bcmerror = BCME_BADADDR; ++ goto done; ++ } ++ ++ if (!capable(CAP_NET_ADMIN)) { ++ bcmerror = BCME_EPERM; ++ goto done; ++ } ++ ++ /* check for local dhd ioctl and handle it */ ++ if (driver == DHD_IOCTL_MAGIC) { ++ bcmerror = dhd_ioctl((void *)&dhd->pub, &ioc, buf, buflen); ++ if (bcmerror) ++ dhd->pub.bcmerror = bcmerror; ++ goto done; ++ } ++ ++ /* send to dongle (must be up, and wl). */ ++ if (dhd->pub.busstate != DHD_BUS_DATA) { ++ bcmerror = BCME_DONGLE_DOWN; ++ goto done; ++ } ++ ++ if (!dhd->pub.iswl) { ++ bcmerror = BCME_DONGLE_DOWN; ++ goto done; ++ } ++ ++ /* ++ * Flush the TX queue if required for proper message serialization: ++ * Intercept WLC_SET_KEY IOCTL - serialize M4 send and set key IOCTL to ++ * prevent M4 encryption and ++ * intercept WLC_DISASSOC IOCTL - serialize WPS-DONE and WLC_DISASSOC IOCTL to ++ * prevent disassoc frame being sent before WPS-DONE frame. ++ */ ++ if (ioc.cmd == WLC_SET_KEY || ++ (ioc.cmd == WLC_SET_VAR && ioc.buf != NULL && ++ strncmp("wsec_key", ioc.buf, 9) == 0) || ++ (ioc.cmd == WLC_SET_VAR && ioc.buf != NULL && ++ strncmp("bsscfg:wsec_key", ioc.buf, 15) == 0) || ++ ioc.cmd == WLC_DISASSOC) ++ dhd_wait_pend8021x(net); ++ ++#ifdef WLMEDIA_HTSF ++ if (ioc.buf) { ++ /* short cut wl ioctl calls here */ ++ if (strcmp("htsf", ioc.buf) == 0) { ++ dhd_ioctl_htsf_get(dhd, 0); ++ return BCME_OK; ++ } ++ ++ if (strcmp("htsflate", ioc.buf) == 0) { ++ if (ioc.set) { ++ memset(ts, 0, sizeof(tstamp_t)*TSMAX); ++ memset(&maxdelayts, 0, sizeof(tstamp_t)); ++ maxdelay = 0; ++ tspktcnt = 0; ++ maxdelaypktno = 0; ++ memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN); ++ memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN); ++ memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN); ++ memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN); ++ } else { ++ dhd_dump_latency(); ++ } ++ return BCME_OK; ++ } ++ if (strcmp("htsfclear", ioc.buf) == 0) { ++ memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN); ++ memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN); ++ memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN); ++ memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN); ++ htsf_seqnum = 0; ++ return BCME_OK; ++ } ++ if (strcmp("htsfhis", ioc.buf) == 0) { ++ dhd_dump_htsfhisto(&vi_d1, "H to D"); ++ dhd_dump_htsfhisto(&vi_d2, "D to D"); ++ dhd_dump_htsfhisto(&vi_d3, "D to H"); ++ dhd_dump_htsfhisto(&vi_d4, "H to H"); ++ return BCME_OK; ++ } ++ if (strcmp("tsport", ioc.buf) == 0) { ++ if (ioc.set) { ++ memcpy(&tsport, ioc.buf + 7, 4); ++ } else { ++ DHD_ERROR(("current timestamp port: %d \n", tsport)); ++ } ++ return BCME_OK; ++ } ++ } ++#endif /* WLMEDIA_HTSF */ ++ ++ if ((ioc.cmd == WLC_SET_VAR || ioc.cmd == WLC_GET_VAR) && ++ ioc.buf != NULL && strncmp("rpc_", ioc.buf, 4) == 0) { ++#ifdef BCM_FD_AGGR ++ bcmerror = dhd_fdaggr_ioctl(&dhd->pub, ifidx, (wl_ioctl_t *)&ioc, buf, buflen); ++#else ++ bcmerror = BCME_UNSUPPORTED; ++#endif ++ goto done; ++ } ++ bcmerror = dhd_wl_ioctl(&dhd->pub, ifidx, (wl_ioctl_t *)&ioc, buf, buflen); ++ ++done: ++ dhd_check_hang(net, &dhd->pub, bcmerror); ++ ++ if (!bcmerror && buf && ioc.buf) { ++ if (copy_to_user(ioc.buf, buf, buflen)) ++ bcmerror = -EFAULT; ++ } ++ ++ if (buf) ++ MFREE(dhd->pub.osh, buf, buflen); ++ ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ ++ return OSL_ERROR(bcmerror); ++} ++ ++#ifdef WL_CFG80211 ++static int ++dhd_cleanup_virt_ifaces(dhd_info_t *dhd) ++{ ++ int i = 1; /* Leave ifidx 0 [Primary Interface] */ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ int rollback_lock = FALSE; ++#endif ++ ++ DHD_TRACE(("%s: Enter \n", __func__)); ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ /* release lock for unregister_netdev */ ++ if (rtnl_is_locked()) { ++ rtnl_unlock(); ++ rollback_lock = TRUE; ++ } ++#endif ++ ++ for (i = 1; i < DHD_MAX_IFS; i++) { ++ dhd_net_if_lock_local(dhd); ++ if (dhd->iflist[i]) { ++ DHD_TRACE(("Deleting IF: %d \n", i)); ++ if ((dhd->iflist[i]->state != DHD_IF_DEL) && ++ (dhd->iflist[i]->state != DHD_IF_DELETING)) { ++ dhd->iflist[i]->state = DHD_IF_DEL; ++ dhd->iflist[i]->idx = i; ++ dhd_op_if(dhd->iflist[i]); ++ } ++ } ++ dhd_net_if_unlock_local(dhd); ++ } ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ if (rollback_lock) ++ rtnl_lock(); ++#endif ++ ++ return 0; ++} ++#endif /* WL_CFG80211 */ ++ ++ ++static int ++dhd_stop(struct net_device *net) ++{ ++ int ifidx = 0; ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ DHD_TRACE(("%s: Enter %p\n", __FUNCTION__, net)); ++ if (dhd->pub.up == 0) { ++ goto exit; ++ } ++ ifidx = dhd_net2idx(dhd, net); ++ BCM_REFERENCE(ifidx); ++ ++ /* Set state and stop OS transmissions */ ++ netif_stop_queue(net); ++ dhd->pub.up = 0; ++ ++#ifdef WL_CFG80211 ++ if (ifidx == 0) { ++ wl_cfg80211_down(NULL); ++ ++ /* ++ * For CFG80211: Clean up all the left over virtual interfaces ++ * when the primary Interface is brought down. [ifconfig wlan0 down] ++ */ ++ if ((dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) && ++ (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) { ++ dhd_cleanup_virt_ifaces(dhd); ++ } ++ } ++#endif ++ ++#ifdef PROP_TXSTATUS ++ dhd_os_wlfc_block(&dhd->pub); ++ dhd_wlfc_cleanup(&dhd->pub); ++ dhd_os_wlfc_unblock(&dhd->pub); ++#endif ++ /* Stop the protocol module */ ++ dhd_prot_stop(&dhd->pub); ++ ++ OLD_MOD_DEC_USE_COUNT; ++exit: ++#if defined(WL_CFG80211) ++ if (ifidx == 0) { ++ if (!dhd_download_fw_on_driverload) ++ wl_android_wifi_off(net); ++ } ++#endif ++ dhd->pub.rxcnt_timeout = 0; ++ dhd->pub.txcnt_timeout = 0; ++ ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return 0; ++} ++ ++static int ++dhd_open(struct net_device *net) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); ++#ifdef TOE ++ uint32 toe_ol; ++#endif ++ int ifidx; ++ int32 ret = 0; ++ ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ /* Update FW path if it was changed */ ++ if (strlen(firmware_path) != 0) { ++ if (firmware_path[strlen(firmware_path)-1] == '\n') ++ firmware_path[strlen(firmware_path)-1] = '\0'; ++ strncpy(fw_path, firmware_path, sizeof(fw_path)-1); ++ fw_path[sizeof(fw_path)-1] = '\0'; ++ firmware_path[0] = '\0'; ++ } ++ ++ ++ dhd->pub.dongle_trap_occured = 0; ++ dhd->pub.hang_was_sent = 0; ++#if !defined(WL_CFG80211) ++ /* ++ * Force start if ifconfig_up gets called before START command ++ * We keep WEXT's wl_control_wl_start to provide backward compatibility ++ * This should be removed in the future ++ */ ++ ret = wl_control_wl_start(net); ++ if (ret != 0) { ++ DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); ++ ret = -1; ++ goto exit; ++ } ++#endif ++ ++ ifidx = dhd_net2idx(dhd, net); ++ DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); ++ ++ if (ifidx < 0) { ++ DHD_ERROR(("%s: Error: called with invalid IF\n", __FUNCTION__)); ++ ret = -1; ++ goto exit; ++ } ++ ++ if (!dhd->iflist[ifidx] || dhd->iflist[ifidx]->state == DHD_IF_DEL) { ++ DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__)); ++ ret = -1; ++ goto exit; ++ } ++ ++ if (ifidx == 0) { ++ atomic_set(&dhd->pend_8021x_cnt, 0); ++#if defined(WL_CFG80211) ++ DHD_ERROR(("\n%s\n", dhd_version)); ++ if (!dhd_download_fw_on_driverload) { ++ ret = wl_android_wifi_on(net); ++ if (ret != 0) { ++ DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); ++ ret = -1; ++ goto exit; ++ } ++ } else { ++ } ++#endif ++ ++ if (dhd->pub.busstate != DHD_BUS_DATA) { ++ ++ /* try to bring up bus */ ++ if ((ret = dhd_bus_start(&dhd->pub)) != 0) { ++ DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); ++ ret = -1; ++ goto exit; ++ } ++ ++ } ++ ++ /* dhd_prot_init has been called in dhd_bus_start or wl_android_wifi_on */ ++ memcpy(net->dev_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN); ++ ++#ifdef TOE ++ /* Get current TOE mode from dongle */ ++ if (dhd_toe_get(dhd, ifidx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0) ++ dhd->iflist[ifidx]->net->features |= NETIF_F_IP_CSUM; ++ else ++ dhd->iflist[ifidx]->net->features &= ~NETIF_F_IP_CSUM; ++#endif /* TOE */ ++ ++#if defined(WL_CFG80211) ++ if (unlikely(wl_cfg80211_up(NULL))) { ++ DHD_ERROR(("%s: failed to bring up cfg80211\n", __FUNCTION__)); ++ ret = -1; ++ goto exit; ++ } ++#endif /* WL_CFG80211 */ ++ } ++ ++ /* Allow transmit calls */ ++ netif_start_queue(net); ++ dhd->pub.up = 1; ++ ++#ifdef BCMDBGFS ++ dhd_dbg_init(&dhd->pub); ++#endif ++ ++ OLD_MOD_INC_USE_COUNT; ++exit: ++ if (ret) ++ dhd_stop(net); ++ ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return ret; ++} ++ ++int dhd_do_driver_init(struct net_device *net) ++{ ++ dhd_info_t *dhd = NULL; ++ ++ if (!net) { ++ DHD_ERROR(("Primary Interface not initialized \n")); ++ return -EINVAL; ++ } ++ ++ dhd = *(dhd_info_t **)netdev_priv(net); ++ ++ /* If driver is already initialized, do nothing ++ */ ++ if (dhd->pub.busstate == DHD_BUS_DATA) { ++ DHD_TRACE(("Driver already Inititalized. Nothing to do")); ++ return 0; ++ } ++ ++ if (dhd_open(net) < 0) { ++ DHD_ERROR(("Driver Init Failed \n")); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++osl_t * ++dhd_osl_attach(void *pdev, uint bustype) ++{ ++ return osl_attach(pdev, bustype, TRUE); ++} ++ ++void ++dhd_osl_detach(osl_t *osh) ++{ ++ if (MALLOCED(osh)) { ++ DHD_ERROR(("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh))); ++ } ++ osl_detach(osh); ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ dhd_registration_check = FALSE; ++ up(&dhd_registration_sem); ++#if defined(BCMLXSDMMC) ++ up(&dhd_chipup_sem); ++#endif ++#endif ++} ++ ++int ++dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name, ++ uint8 *mac_addr, uint32 flags, uint8 bssidx) ++{ ++ dhd_if_t *ifp; ++ ++ DHD_TRACE(("%s: idx %d, handle->%p\n", __FUNCTION__, ifidx, handle)); ++ ++ ASSERT(dhd && (ifidx < DHD_MAX_IFS)); ++ ++ ifp = dhd->iflist[ifidx]; ++ if (ifp != NULL) { ++ if (ifp->net != NULL) { ++ netif_stop_queue(ifp->net); ++ unregister_netdev(ifp->net); ++ free_netdev(ifp->net); ++ } ++ } else ++ if ((ifp = MALLOC(dhd->pub.osh, sizeof(dhd_if_t))) == NULL) { ++ DHD_ERROR(("%s: OOM - dhd_if_t\n", __FUNCTION__)); ++ return -ENOMEM; ++ } ++ ++ memset(ifp, 0, sizeof(dhd_if_t)); ++ ifp->event2cfg80211 = FALSE; ++ ifp->info = dhd; ++ dhd->iflist[ifidx] = ifp; ++ strncpy(ifp->name, name, IFNAMSIZ); ++ ifp->name[IFNAMSIZ] = '\0'; ++ if (mac_addr != NULL) ++ memcpy(&ifp->mac_addr, mac_addr, ETHER_ADDR_LEN); ++ ++ if (handle == NULL) { ++ ifp->state = DHD_IF_ADD; ++ ifp->idx = ifidx; ++ ifp->bssidx = bssidx; ++ ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); ++ up(&dhd->thr_sysioc_ctl.sema); ++ } else ++ ifp->net = (struct net_device *)handle; ++ ++ if (ifidx == 0) { ++ ifp->event2cfg80211 = TRUE; ++ } ++ ++ return 0; ++} ++ ++void ++dhd_del_if(dhd_info_t *dhd, int ifidx) ++{ ++ dhd_if_t *ifp; ++ ++ DHD_TRACE(("%s: idx %d\n", __FUNCTION__, ifidx)); ++ ++ ASSERT(dhd && ifidx && (ifidx < DHD_MAX_IFS)); ++ ifp = dhd->iflist[ifidx]; ++ if (!ifp) { ++ DHD_ERROR(("%s: Null interface\n", __FUNCTION__)); ++ return; ++ } ++ ++ ifp->state = DHD_IF_DEL; ++ ifp->idx = ifidx; ++ ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); ++ up(&dhd->thr_sysioc_ctl.sema); ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) ++static struct net_device_ops dhd_ops_pri = { ++ .ndo_open = dhd_open, ++ .ndo_stop = dhd_stop, ++ .ndo_get_stats = dhd_get_stats, ++ .ndo_do_ioctl = dhd_ioctl_entry, ++ .ndo_start_xmit = dhd_start_xmit, ++ .ndo_set_mac_address = dhd_set_mac_address, ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) ++ .ndo_set_rx_mode = dhd_set_multicast_list, ++#else ++ .ndo_set_multicast_list = dhd_set_multicast_list, ++#endif ++}; ++ ++static struct net_device_ops dhd_ops_virt = { ++ .ndo_get_stats = dhd_get_stats, ++ .ndo_do_ioctl = dhd_ioctl_entry, ++ .ndo_start_xmit = dhd_start_xmit, ++ .ndo_set_mac_address = dhd_set_mac_address, ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) ++ .ndo_set_rx_mode = dhd_set_multicast_list, ++#else ++ .ndo_set_multicast_list = dhd_set_multicast_list, ++#endif ++}; ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) */ ++ ++dhd_pub_t * ++dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) ++{ ++ dhd_info_t *dhd = NULL; ++ struct net_device *net = NULL; ++ ++ dhd_attach_states_t dhd_state = DHD_ATTACH_STATE_INIT; ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ /* updates firmware nvram path if it was provided as module parameters */ ++ if (strlen(firmware_path) != 0) { ++ strncpy(fw_path, firmware_path, sizeof(fw_path) - 1); ++ fw_path[sizeof(fw_path) - 1] = '\0'; ++ } ++ if (strlen(nvram_path) != 0) { ++ strncpy(nv_path, nvram_path, sizeof(nv_path) -1); ++ nv_path[sizeof(nv_path) -1] = '\0'; ++ } ++ ++ /* Allocate etherdev, including space for private structure */ ++ if (!(net = alloc_etherdev(sizeof(dhd)))) { ++ DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__)); ++ goto fail; ++ } ++ dhd_state |= DHD_ATTACH_STATE_NET_ALLOC; ++ ++ /* Allocate primary dhd_info */ ++ if (!(dhd = MALLOC(osh, sizeof(dhd_info_t)))) { ++ DHD_ERROR(("%s: OOM - alloc dhd_info\n", __FUNCTION__)); ++ goto fail; ++ } ++ memset(dhd, 0, sizeof(dhd_info_t)); ++ ++#ifdef DHDTHREAD ++ dhd->thr_dpc_ctl.thr_pid = DHD_PID_KT_TL_INVALID; ++ dhd->thr_wdt_ctl.thr_pid = DHD_PID_KT_INVALID; ++#endif /* DHDTHREAD */ ++ dhd->dhd_tasklet_create = FALSE; ++ dhd->thr_sysioc_ctl.thr_pid = DHD_PID_KT_INVALID; ++ dhd_state |= DHD_ATTACH_STATE_DHD_ALLOC; ++ ++ /* ++ * Save the dhd_info into the priv ++ */ ++ memcpy((void *)netdev_priv(net), &dhd, sizeof(dhd)); ++ dhd->pub.osh = osh; ++ ++ /* Link to info module */ ++ dhd->pub.info = dhd; ++ /* Link to bus module */ ++ dhd->pub.bus = bus; ++ dhd->pub.hdrlen = bus_hdrlen; ++ ++ /* Set network interface name if it was provided as module parameter */ ++ if (iface_name[0]) { ++ int len; ++ char ch; ++ strncpy(net->name, iface_name, IFNAMSIZ); ++ net->name[IFNAMSIZ - 1] = 0; ++ len = strlen(net->name); ++ ch = net->name[len - 1]; ++ if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2)) ++ strcat(net->name, "%d"); ++ } ++ ++ if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF) ++ goto fail; ++ dhd_state |= DHD_ATTACH_STATE_ADD_IF; ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) ++ net->open = NULL; ++#else ++ net->netdev_ops = NULL; ++#endif ++ ++ sema_init(&dhd->proto_sem, 1); ++ ++#ifdef PROP_TXSTATUS ++ spin_lock_init(&dhd->wlfc_spinlock); ++#ifdef PROP_TXSTATUS_VSDB ++ dhd->pub.wlfc_enabled = FALSE; ++#else ++ dhd->pub.wlfc_enabled = TRUE; ++#endif /* PROP_TXSTATUS_VSDB */ ++#endif /* PROP_TXSTATUS */ ++ ++ /* Initialize other structure content */ ++ init_waitqueue_head(&dhd->ioctl_resp_wait); ++ init_waitqueue_head(&dhd->ctrl_wait); ++ ++ /* Initialize the spinlocks */ ++ spin_lock_init(&dhd->sdlock); ++ spin_lock_init(&dhd->txqlock); ++ spin_lock_init(&dhd->dhd_lock); ++ ++ /* Initialize Wakelock stuff */ ++ spin_lock_init(&dhd->wakelock_spinlock); ++ dhd->wakelock_counter = 0; ++ dhd->wakelock_wd_counter = 0; ++ dhd->wakelock_rx_timeout_enable = 0; ++ dhd->wakelock_ctrl_timeout_enable = 0; ++#ifdef CONFIG_HAS_WAKELOCK ++ wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake"); ++ wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake"); ++ wake_lock_init(&dhd->wl_ctrlwake, WAKE_LOCK_SUSPEND, "wlan_ctrl_wake"); ++ wake_lock_init(&dhd->wl_wdwake, WAKE_LOCK_SUSPEND, "wlan_wd_wake"); ++#endif ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ mutex_init(&dhd->dhd_net_if_mutex); ++ mutex_init(&dhd->dhd_suspend_mutex); ++#endif ++ dhd_state |= DHD_ATTACH_STATE_WAKELOCKS_INIT; ++ ++ /* Attach and link in the protocol */ ++ if (dhd_prot_attach(&dhd->pub) != 0) { ++ DHD_ERROR(("dhd_prot_attach failed\n")); ++ goto fail; ++ } ++ dhd_state |= DHD_ATTACH_STATE_PROT_ATTACH; ++ ++#ifdef WL_CFG80211 ++ /* Attach and link in the cfg80211 */ ++ if (unlikely(wl_cfg80211_attach(net, &dhd->pub))) { ++ DHD_ERROR(("wl_cfg80211_attach failed\n")); ++ goto fail; ++ } ++ ++ dhd_monitor_init(&dhd->pub); ++ dhd_state |= DHD_ATTACH_STATE_CFG80211; ++#endif ++#if defined(CONFIG_WIRELESS_EXT) ++ /* Attach and link in the iw */ ++ if (!(dhd_state & DHD_ATTACH_STATE_CFG80211)) { ++ if (wl_iw_attach(net, (void *)&dhd->pub) != 0) { ++ DHD_ERROR(("wl_iw_attach failed\n")); ++ goto fail; ++ } ++ dhd_state |= DHD_ATTACH_STATE_WL_ATTACH; ++ } ++#endif /* defined(CONFIG_WIRELESS_EXT) */ ++ ++ ++ /* Set up the watchdog timer */ ++ init_timer(&dhd->timer); ++ dhd->timer.data = (ulong)dhd; ++ dhd->timer.function = dhd_watchdog; ++ ++#ifdef DHDTHREAD ++ /* Initialize thread based operation and lock */ ++ sema_init(&dhd->sdsem, 1); ++ if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) { ++ dhd->threads_only = TRUE; ++ } ++ else { ++ dhd->threads_only = FALSE; ++ } ++ ++ if (dhd_watchdog_prio >= 0) { ++ /* Initialize watchdog thread */ ++ PROC_START(dhd_watchdog_thread, dhd, &dhd->thr_wdt_ctl, 0); ++ } else { ++ dhd->thr_wdt_ctl.thr_pid = -1; ++ } ++ ++ /* Set up the bottom half handler */ ++ if (dhd_dpc_prio >= 0) { ++ /* Initialize DPC thread */ ++ PROC_START(dhd_dpc_thread, dhd, &dhd->thr_dpc_ctl, 0); ++ } else { ++ /* use tasklet for dpc */ ++ tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd); ++ dhd->thr_dpc_ctl.thr_pid = -1; ++ } ++#else ++ /* Set up the bottom half handler */ ++ tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd); ++ dhd->dhd_tasklet_create = TRUE; ++#endif /* DHDTHREAD */ ++ ++ if (dhd_sysioc) { ++ PROC_START(_dhd_sysioc_thread, dhd, &dhd->thr_sysioc_ctl, 0); ++ } else { ++ dhd->thr_sysioc_ctl.thr_pid = -1; ++ } ++ dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED; ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ INIT_WORK(&dhd->work_hang, dhd_hang_process); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++ /* ++ * Save the dhd_info into the priv ++ */ ++ memcpy(netdev_priv(net), &dhd, sizeof(dhd)); ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) ++ register_pm_notifier(&dhd_sleep_pm_notifier); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ ++ ++#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ++ dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20; ++ dhd->early_suspend.suspend = dhd_early_suspend; ++ dhd->early_suspend.resume = dhd_late_resume; ++ register_early_suspend(&dhd->early_suspend); ++ dhd_state |= DHD_ATTACH_STATE_EARLYSUSPEND_DONE; ++#endif ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++ dhd->pend_ipaddr = 0; ++ register_inetaddr_notifier(&dhd_notifier); ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++ dhd_state |= DHD_ATTACH_STATE_DONE; ++ dhd->dhd_state = dhd_state; ++ return &dhd->pub; ++ ++fail: ++ if (dhd_state < DHD_ATTACH_STATE_DHD_ALLOC) { ++ if (net) free_netdev(net); ++ } else { ++ DHD_TRACE(("%s: Calling dhd_detach dhd_state 0x%x &dhd->pub %p\n", ++ __FUNCTION__, dhd_state, &dhd->pub)); ++ dhd->dhd_state = dhd_state; ++ dhd_detach(&dhd->pub); ++ dhd_free(&dhd->pub); ++ } ++ ++ return NULL; ++} ++ ++int ++dhd_bus_start(dhd_pub_t *dhdp) ++{ ++ int ret = -1; ++ dhd_info_t *dhd = (dhd_info_t*)dhdp->info; ++ unsigned long flags; ++ ++ ASSERT(dhd); ++ ++ DHD_TRACE(("Enter %s:\n", __FUNCTION__)); ++ ++#ifdef DHDTHREAD ++ if (dhd->threads_only) ++ dhd_os_sdlock(dhdp); ++#endif /* DHDTHREAD */ ++ ++ ++ /* try to download image and nvram to the dongle */ ++ if ((dhd->pub.busstate == DHD_BUS_DOWN) && ++ (fw_path != NULL) && (fw_path[0] != '\0') && ++ (nv_path != NULL) && (nv_path[0] != '\0')) { ++ /* wake lock moved to dhdsdio_download_firmware */ ++ if (!(dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh, ++ fw_path, nv_path))) { ++ DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n", ++ __FUNCTION__, fw_path, nv_path)); ++#ifdef DHDTHREAD ++ if (dhd->threads_only) ++ dhd_os_sdunlock(dhdp); ++#endif /* DHDTHREAD */ ++ return -1; ++ } ++ } ++ if (dhd->pub.busstate != DHD_BUS_LOAD) { ++#ifdef DHDTHREAD ++ if (dhd->threads_only) ++ dhd_os_sdunlock(dhdp); ++#endif /* DHDTHREAD */ ++ return -ENETDOWN; ++ } ++ ++ /* Start the watchdog timer */ ++ dhd->pub.tickcnt = 0; ++ dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms); ++ ++ /* Bring up the bus */ ++ if ((ret = dhd_bus_init(&dhd->pub, FALSE)) != 0) { ++ ++ DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret)); ++#ifdef DHDTHREAD ++ if (dhd->threads_only) ++ dhd_os_sdunlock(dhdp); ++#endif /* DHDTHREAD */ ++ return ret; ++ } ++#if defined(OOB_INTR_ONLY) ++ /* Host registration for OOB interrupt */ ++ if (bcmsdh_register_oob_intr(dhdp)) { ++ /* deactivate timer and wait for the handler to finish */ ++ ++ flags = dhd_os_spin_lock(&dhd->pub); ++ dhd->wd_timer_valid = FALSE; ++ dhd_os_spin_unlock(&dhd->pub, flags); ++ del_timer_sync(&dhd->timer); ++ DHD_ERROR(("%s Host failed to register for OOB\n", __FUNCTION__)); ++#ifdef DHDTHREAD ++ if (dhd->threads_only) ++ dhd_os_sdunlock(dhdp); ++#endif /* DHDTHREAD */ ++ DHD_OS_WD_WAKE_UNLOCK(&dhd->pub); ++ return -ENODEV; ++ } ++ ++ /* Enable oob at firmware */ ++ dhd_enable_oob_intr(dhd->pub.bus, TRUE); ++#endif /* defined(OOB_INTR_ONLY) */ ++ ++ /* If bus is not ready, can't come up */ ++ if (dhd->pub.busstate != DHD_BUS_DATA) { ++ flags = dhd_os_spin_lock(&dhd->pub); ++ dhd->wd_timer_valid = FALSE; ++ dhd_os_spin_unlock(&dhd->pub, flags); ++ del_timer_sync(&dhd->timer); ++ DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__)); ++#ifdef DHDTHREAD ++ if (dhd->threads_only) ++ dhd_os_sdunlock(dhdp); ++#endif /* DHDTHREAD */ ++ DHD_OS_WD_WAKE_UNLOCK(&dhd->pub); ++ return -ENODEV; ++ } ++ ++#ifdef DHDTHREAD ++ if (dhd->threads_only) ++ dhd_os_sdunlock(dhdp); ++#endif /* DHDTHREAD */ ++ ++#ifdef BCMSDIOH_TXGLOM ++ if ((dhd->pub.busstate == DHD_BUS_DATA) && bcmsdh_glom_enabled()) { ++ dhd_txglom_enable(dhdp, TRUE); ++ } ++#endif ++ ++#ifdef READ_MACADDR ++ dhd_read_macaddr(dhd); ++#endif ++ ++ /* Bus is ready, do any protocol initialization */ ++ if ((ret = dhd_prot_init(&dhd->pub)) < 0) ++ return ret; ++ ++#ifdef WRITE_MACADDR ++ dhd_write_macaddr(dhd->pub.mac.octet); ++#endif ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++ if (dhd->pend_ipaddr) { ++#ifdef AOE_IP_ALIAS_SUPPORT ++ aoe_update_host_ipv4_table(&dhd->pub, dhd->pend_ipaddr, TRUE, 0); ++#endif /* AOE_IP_ALIAS_SUPPORT */ ++ dhd->pend_ipaddr = 0; ++ } ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++ return 0; ++} ++ ++bool dhd_is_concurrent_mode(dhd_pub_t *dhd) ++{ ++ if (!dhd) ++ return FALSE; ++ ++ if (dhd->op_mode & DHD_FLAG_CONCURR_MULTI_CHAN_MODE) ++ return TRUE; ++ else if ((dhd->op_mode & DHD_FLAG_CONCURR_SINGLE_CHAN_MODE) == ++ DHD_FLAG_CONCURR_SINGLE_CHAN_MODE) ++ return TRUE; ++ else ++ return FALSE; ++} ++ ++#if !defined(AP) && defined(WLP2P) ++/* From Android JerryBean release, the concurrent mode is enabled by default and the firmware ++ * name would be fw_bcmdhd.bin. So we need to determine whether P2P is enabled in the STA ++ * firmware and accordingly enable concurrent mode (Apply P2P settings). SoftAP firmware ++ * would still be named as fw_bcmdhd_apsta. ++ */ ++uint32 ++dhd_get_concurrent_capabilites(dhd_pub_t *dhd) ++{ ++ int32 ret = 0; ++ char buf[WLC_IOCTL_SMLEN]; ++ bool mchan_supported = FALSE; ++ /* if dhd->op_mode is already set for HOSTAP, ++ * that means we only will use the mode as it is ++ */ ++ if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) ++ return 0; ++ memset(buf, 0, sizeof(buf)); ++ bcm_mkiovar("cap", 0, 0, buf, sizeof(buf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), ++ FALSE, 0)) < 0) { ++ DHD_ERROR(("%s: Get Capability failed (error=%d)\n", ++ __FUNCTION__, ret)); ++ return 0; ++ } ++ if (strstr(buf, "vsdb")) { ++ mchan_supported = TRUE; ++ } ++ if (strstr(buf, "p2p") == NULL) { ++ DHD_TRACE(("Chip does not support p2p\n")); ++ return 0; ++ } ++ else { ++ /* Chip supports p2p but ensure that p2p is really implemented in firmware or not */ ++ memset(buf, 0, sizeof(buf)); ++ bcm_mkiovar("p2p", 0, 0, buf, sizeof(buf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), ++ FALSE, 0)) < 0) { ++ DHD_ERROR(("%s: Get P2P failed (error=%d)\n", __FUNCTION__, ret)); ++ return 0; ++ } ++ else { ++ if (buf[0] == 1) { ++ /* By default, chip supports single chan concurrency, ++ * now lets check for mchan ++ */ ++ ret = DHD_FLAG_CONCURR_SINGLE_CHAN_MODE; ++ if (mchan_supported) ++ ret |= DHD_FLAG_CONCURR_MULTI_CHAN_MODE; ++#if defined(WL_ENABLE_P2P_IF) ++ /* For customer_hw4, although ICS, ++ * we still support concurrent mode ++ */ ++ return ret; ++#else ++ return 0; ++#endif ++ } ++ } ++ } ++ return 0; ++} ++#endif ++int ++dhd_preinit_ioctls(dhd_pub_t *dhd) ++{ ++ int ret = 0; ++ char eventmask[WL_EVENTING_MASK_LEN]; ++ char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ ++ ++#if !defined(WL_CFG80211) ++ uint up = 0; ++#endif /* !defined(WL_CFG80211) */ ++ uint power_mode = PM_FAST; ++ uint32 dongle_align = DHD_SDALIGN; ++ uint32 glom = CUSTOM_GLOM_SETTING; ++#if defined(VSDB) || defined(ROAM_ENABLE) ++ uint bcn_timeout = 8; ++#else ++ uint bcn_timeout = 4; ++#endif ++#ifdef ENABLE_BCN_LI_BCN_WAKEUP ++ uint32 bcn_li_bcn = 1; ++#endif /* ENABLE_BCN_LI_BCN_WAKEUP */ ++ uint retry_max = 3; ++#if defined(ARP_OFFLOAD_SUPPORT) ++ int arpoe = 1; ++#endif ++ int scan_assoc_time = DHD_SCAN_ASSOC_ACTIVE_TIME; ++ int scan_unassoc_time = DHD_SCAN_UNASSOC_ACTIVE_TIME; ++ int scan_passive_time = DHD_SCAN_PASSIVE_TIME; ++ char buf[WLC_IOCTL_SMLEN]; ++ char *ptr; ++ uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ ++#ifdef ROAM_ENABLE ++ uint roamvar = 0; ++ int roam_trigger[2] = {CUSTOM_ROAM_TRIGGER_SETTING, WLC_BAND_ALL}; ++ int roam_scan_period[2] = {10, WLC_BAND_ALL}; ++ int roam_delta[2] = {CUSTOM_ROAM_DELTA_SETTING, WLC_BAND_ALL}; ++#ifdef FULL_ROAMING_SCAN_PERIOD_60_SEC ++ int roam_fullscan_period = 60; ++#else /* FULL_ROAMING_SCAN_PERIOD_60_SEC */ ++ int roam_fullscan_period = 120; ++#endif /* FULL_ROAMING_SCAN_PERIOD_60_SEC */ ++#else ++#ifdef DISABLE_BUILTIN_ROAM ++ uint roamvar = 1; ++#endif /* DISABLE_BUILTIN_ROAM */ ++#endif /* ROAM_ENABLE */ ++ ++#if defined(SOFTAP) ++ uint dtim = 1; ++#endif ++#if (defined(AP) && !defined(WLP2P)) || (!defined(AP) && defined(WL_CFG80211)) ++ uint32 mpc = 0; /* Turn MPC off for AP/APSTA mode */ ++ struct ether_addr p2p_ea; ++#endif ++ ++#if defined(AP) || defined(WLP2P) ++ uint32 apsta = 1; /* Enable APSTA mode */ ++#endif /* defined(AP) || defined(WLP2P) */ ++#ifdef GET_CUSTOM_MAC_ENABLE ++ struct ether_addr ea_addr; ++#endif /* GET_CUSTOM_MAC_ENABLE */ ++#ifdef DISABLE_11N ++ uint32 nmode = 0; ++#else ++#ifdef AMPDU_HOSTREORDER ++ uint32 hostreorder = 1; ++#endif ++#endif /* DISABLE_11N */ ++ dhd->suspend_bcn_li_dtim = CUSTOM_SUSPEND_BCN_LI_DTIM; ++#ifdef PROP_TXSTATUS ++#ifdef PROP_TXSTATUS_VSDB ++ dhd->wlfc_enabled = FALSE; ++ /* enable WLFC only if the firmware is VSDB */ ++#else ++ dhd->wlfc_enabled = TRUE; ++#endif /* PROP_TXSTATUS_VSDB */ ++#endif /* PROP_TXSTATUS */ ++ DHD_TRACE(("Enter %s\n", __FUNCTION__)); ++ dhd->op_mode = 0; ++#ifdef GET_CUSTOM_MAC_ENABLE ++ ret = dhd_custom_get_mac_address(ea_addr.octet); ++ if (!ret) { ++ memset(buf, 0, sizeof(buf)); ++ bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf)); ++ ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); ++ if (ret < 0) { ++ DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); ++ return BCME_NOTUP; ++ } ++ memcpy(dhd->mac.octet, ea_addr.octet, ETHER_ADDR_LEN); ++ } else { ++#endif /* GET_CUSTOM_MAC_ENABLE */ ++ /* Get the default device MAC address directly from firmware */ ++ memset(buf, 0, sizeof(buf)); ++ bcm_mkiovar("cur_etheraddr", 0, 0, buf, sizeof(buf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), ++ FALSE, 0)) < 0) { ++ DHD_ERROR(("%s: can't get MAC address , error=%d\n", __FUNCTION__, ret)); ++ return BCME_NOTUP; ++ } ++ /* Update public MAC address after reading from Firmware */ ++ memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN); ++ ++#ifdef GET_CUSTOM_MAC_ENABLE ++ } ++#endif /* GET_CUSTOM_MAC_ENABLE */ ++ ++ DHD_TRACE(("Firmware = %s\n", fw_path)); ++ ++ if ((!op_mode && strstr(fw_path, "_apsta") != NULL) || ++ (op_mode == DHD_FLAG_HOSTAP_MODE)) { ++#ifdef SET_RANDOM_MAC_SOFTAP ++ uint rand_mac; ++#endif ++ dhd->op_mode = DHD_FLAG_HOSTAP_MODE; ++#if defined(ARP_OFFLOAD_SUPPORT) ++ arpoe = 0; ++#endif ++#ifdef PKT_FILTER_SUPPORT ++ dhd_pkt_filter_enable = FALSE; ++#endif ++#ifdef SET_RANDOM_MAC_SOFTAP ++ srandom32((uint)jiffies); ++ rand_mac = random32(); ++ iovbuf[0] = 0x02; /* locally administered bit */ ++ iovbuf[1] = 0x1A; ++ iovbuf[2] = 0x11; ++ iovbuf[3] = (unsigned char)(rand_mac & 0x0F) | 0xF0; ++ iovbuf[4] = (unsigned char)(rand_mac >> 8); ++ iovbuf[5] = (unsigned char)(rand_mac >> 16); ++ ++ bcm_mkiovar("cur_etheraddr", (void *)iovbuf, ETHER_ADDR_LEN, buf, sizeof(buf)); ++ ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); ++ if (ret < 0) { ++ DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); ++ } else ++ memcpy(dhd->mac.octet, iovbuf, ETHER_ADDR_LEN); ++#endif /* SET_RANDOM_MAC_SOFTAP */ ++#if !defined(AP) && defined(WL_CFG80211) ++ /* Turn off MPC in AP mode */ ++ bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s mpc for HostAPD failed %d\n", __FUNCTION__, ret)); ++ } ++#endif ++ ++ } ++ else { ++ uint32 concurrent_mode = 0; ++ if ((!op_mode && strstr(fw_path, "_p2p") != NULL) || ++ (op_mode == DHD_FLAG_P2P_MODE)) { ++#if defined(ARP_OFFLOAD_SUPPORT) ++ arpoe = 0; ++#endif ++#ifdef PKT_FILTER_SUPPORT ++ dhd_pkt_filter_enable = FALSE; ++#endif ++ dhd->op_mode = DHD_FLAG_P2P_MODE; ++ } ++ else ++ dhd->op_mode = DHD_FLAG_STA_MODE; ++#if !defined(AP) && defined(WLP2P) ++ if ((concurrent_mode = dhd_get_concurrent_capabilites(dhd))) { ++#if defined(ARP_OFFLOAD_SUPPORT) ++ arpoe = 1; ++#endif ++ dhd->op_mode |= concurrent_mode; ++ } ++ ++ /* Check if we are enabling p2p */ ++ if (dhd->op_mode & DHD_FLAG_P2P_MODE) { ++ bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, ++ iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s APSTA for P2P failed ret= %d\n", __FUNCTION__, ret)); ++ } ++ ++ memcpy(&p2p_ea, &dhd->mac, ETHER_ADDR_LEN); ++ ETHER_SET_LOCALADDR(&p2p_ea); ++ bcm_mkiovar("p2p_da_override", (char *)&p2p_ea, ++ ETHER_ADDR_LEN, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, ++ iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s p2p_da_override ret= %d\n", __FUNCTION__, ret)); ++ } else { ++ DHD_INFO(("dhd_preinit_ioctls: p2p_da_override succeeded\n")); ++ } ++ } ++#else ++ (void)concurrent_mode; ++#endif ++ } ++ ++ DHD_ERROR(("Firmware up: op_mode=0x%04x, " ++ "Broadcom Dongle Host Driver mac="MACDBG"\n", ++ dhd->op_mode, ++ MAC2STRDBG(dhd->mac.octet))); ++ /* Set Country code */ ++ if (dhd->dhd_cspec.ccode[0] != 0) { ++ bcm_mkiovar("country", (char *)&dhd->dhd_cspec, ++ sizeof(wl_country_t), iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__)); ++ } ++ ++ /* Set Listen Interval */ ++ bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret)); ++ ++#if defined(ROAM_ENABLE) || defined(DISABLE_BUILTIN_ROAM) ++ /* Disable built-in roaming to allowed ext supplicant to take care of roaming */ ++ bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#endif /* ROAM_ENABLE || DISABLE_BUILTIN_ROAM */ ++#ifdef ROAM_ENABLE ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_TRIGGER, roam_trigger, ++ sizeof(roam_trigger), TRUE, 0)) < 0) ++ DHD_ERROR(("%s: roam trigger set failed %d\n", __FUNCTION__, ret)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_SCAN_PERIOD, roam_scan_period, ++ sizeof(roam_scan_period), TRUE, 0)) < 0) ++ DHD_ERROR(("%s: roam scan period set failed %d\n", __FUNCTION__, ret)); ++ if ((dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_DELTA, roam_delta, ++ sizeof(roam_delta), TRUE, 0)) < 0) ++ DHD_ERROR(("%s: roam delta set failed %d\n", __FUNCTION__, ret)); ++ bcm_mkiovar("fullroamperiod", (char *)&roam_fullscan_period, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("%s: roam fullscan period set failed %d\n", __FUNCTION__, ret)); ++#endif /* ROAM_ENABLE */ ++ ++ /* Set PowerSave mode */ ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode), TRUE, 0); ++ ++ /* Match Host and Dongle rx alignment */ ++ bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ ++ if (glom != DEFAULT_GLOM_VALUE) { ++ DHD_INFO(("%s set glom=0x%X\n", __FUNCTION__, glom)); ++ bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ } ++ ++ /* Setup timeout if Beacons are lost and roam is off to report link down */ ++ bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ /* Setup assoc_retry_max count to reconnect target AP in dongle */ ++ bcm_mkiovar("assoc_retry_max", (char *)&retry_max, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#if defined(AP) && !defined(WLP2P) ++ /* Turn off MPC in AP mode */ ++ bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#endif /* defined(AP) && !defined(WLP2P) */ ++ ++#if defined(SOFTAP) ++ if (ap_fw_loaded == TRUE) { ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_DTIMPRD, (char *)&dtim, sizeof(dtim), TRUE, 0); ++ } ++#endif ++ ++#if defined(KEEP_ALIVE) ++ { ++ /* Set Keep Alive : be sure to use FW with -keepalive */ ++ int res; ++ ++#if defined(SOFTAP) ++ if (ap_fw_loaded == FALSE) ++#endif ++ if (!(dhd->op_mode & DHD_FLAG_HOSTAP_MODE)) { ++ if ((res = dhd_keep_alive_onoff(dhd)) < 0) ++ DHD_ERROR(("%s set keeplive failed %d\n", ++ __FUNCTION__, res)); ++ } ++ } ++#endif /* defined(KEEP_ALIVE) */ ++ ++ /* Read event_msgs mask */ ++ bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0)) < 0) { ++ DHD_ERROR(("%s read Event mask failed %d\n", __FUNCTION__, ret)); ++ goto done; ++ } ++ bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN); ++ ++ /* Setup event_msgs */ ++ setbit(eventmask, WLC_E_SET_SSID); ++ setbit(eventmask, WLC_E_PRUNE); ++ setbit(eventmask, WLC_E_AUTH); ++ setbit(eventmask, WLC_E_ASSOC); ++ setbit(eventmask, WLC_E_REASSOC); ++ setbit(eventmask, WLC_E_REASSOC_IND); ++ setbit(eventmask, WLC_E_DEAUTH); ++ setbit(eventmask, WLC_E_DEAUTH_IND); ++ setbit(eventmask, WLC_E_DISASSOC_IND); ++ setbit(eventmask, WLC_E_DISASSOC); ++ setbit(eventmask, WLC_E_JOIN); ++ setbit(eventmask, WLC_E_ASSOC_IND); ++ setbit(eventmask, WLC_E_PSK_SUP); ++ setbit(eventmask, WLC_E_LINK); ++ setbit(eventmask, WLC_E_NDIS_LINK); ++ setbit(eventmask, WLC_E_MIC_ERROR); ++ setbit(eventmask, WLC_E_ASSOC_REQ_IE); ++ setbit(eventmask, WLC_E_ASSOC_RESP_IE); ++#ifndef WL_CFG80211 ++ setbit(eventmask, WLC_E_PMKID_CACHE); ++ setbit(eventmask, WLC_E_TXFAIL); ++#endif ++ setbit(eventmask, WLC_E_JOIN_START); ++ setbit(eventmask, WLC_E_SCAN_COMPLETE); ++#ifdef WLMEDIA_HTSF ++ setbit(eventmask, WLC_E_HTSFSYNC); ++#endif /* WLMEDIA_HTSF */ ++#ifdef PNO_SUPPORT ++ setbit(eventmask, WLC_E_PFN_NET_FOUND); ++#endif /* PNO_SUPPORT */ ++ /* enable dongle roaming event */ ++ setbit(eventmask, WLC_E_ROAM); ++#ifdef WL_CFG80211 ++ setbit(eventmask, WLC_E_ESCAN_RESULT); ++ if (dhd->op_mode & DHD_FLAG_P2P_MODE) { ++ setbit(eventmask, WLC_E_ACTION_FRAME_RX); ++ setbit(eventmask, WLC_E_P2P_DISC_LISTEN_COMPLETE); ++ } ++#endif /* WL_CFG80211 */ ++ ++ /* Write updated Event mask */ ++ bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set Event mask failed %d\n", __FUNCTION__, ret)); ++ goto done; ++ } ++ ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time, ++ sizeof(scan_assoc_time), TRUE, 0); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time, ++ sizeof(scan_unassoc_time), TRUE, 0); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_PASSIVE_TIME, (char *)&scan_passive_time, ++ sizeof(scan_passive_time), TRUE, 0); ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++ /* Set and enable ARP offload feature for STA only */ ++#if defined(SOFTAP) ++ if (arpoe && !ap_fw_loaded) { ++#else ++ if (arpoe) { ++#endif ++ dhd_arp_offload_enable(dhd, TRUE); ++ dhd_arp_offload_set(dhd, dhd_arp_mode); ++ } else { ++ dhd_arp_offload_enable(dhd, FALSE); ++ dhd_arp_offload_set(dhd, 0); ++ } ++ dhd_arp_enable = arpoe; ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++#ifdef PKT_FILTER_SUPPORT ++ /* Setup default defintions for pktfilter , enable in suspend */ ++ dhd->pktfilter_count = 5; ++ /* Setup filter to allow only unicast */ ++ dhd->pktfilter[0] = "100 0 0 0 0x01 0x00"; ++ dhd->pktfilter[1] = NULL; ++ dhd->pktfilter[2] = NULL; ++ dhd->pktfilter[3] = NULL; ++ /* Add filter to pass multicastDNS packet and NOT filter out as Broadcast */ ++ dhd->pktfilter[4] = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; ++ dhd_set_packet_filter(dhd); ++#if defined(SOFTAP) ++ if (ap_fw_loaded) { ++ dhd_enable_packet_filter(0, dhd); ++ } ++#endif /* defined(SOFTAP) */ ++#endif /* PKT_FILTER_SUPPORT */ ++#ifdef DISABLE_11N ++ bcm_mkiovar("nmode", (char *)&nmode, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("%s wl nmode 0 failed %d\n", __FUNCTION__, ret)); ++#else ++#ifdef AMPDU_HOSTREORDER ++ bcm_mkiovar("ampdu_hostreorder", (char *)&hostreorder, 4, buf, sizeof(buf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); ++#endif /* AMPDU_HOSTREORDER */ ++#endif /* DISABLE_11N */ ++ ++#if !defined(WL_CFG80211) ++ /* Force STA UP */ ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Setting WL UP failed %d\n", __FUNCTION__, ret)); ++ goto done; ++ } ++#endif ++ ++#ifdef ENABLE_BCN_LI_BCN_WAKEUP ++ bcm_mkiovar("bcn_li_bcn", (char *)&bcn_li_bcn, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#endif /* ENABLE_BCN_LI_BCN_WAKEUP */ ++ ++ /* query for 'ver' to get version info from firmware */ ++ memset(buf, 0, sizeof(buf)); ++ ptr = buf; ++ bcm_mkiovar("ver", (char *)&buf, 4, buf, sizeof(buf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), FALSE, 0)) < 0) ++ DHD_ERROR(("%s failed %d\n", __FUNCTION__, ret)); ++ else { ++ bcmstrtok(&ptr, "\n", 0); ++ /* Print fw version info */ ++ DHD_ERROR(("Firmware version = %s\n", buf)); ++ ++ dhd_set_version_info(dhd, buf); ++ ++ DHD_BLOG(buf, strlen(buf) + 1); ++ DHD_BLOG(dhd_version, strlen(dhd_version) + 1); ++ ++ /* Check and adjust IOCTL response timeout for Manufactring firmware */ ++ if (strstr(buf, MANUFACTRING_FW) != NULL) { ++ dhd_os_set_ioctl_resp_timeout(IOCTL_RESP_TIMEOUT * 10); ++ DHD_ERROR(("%s : adjust IOCTL response time for Manufactring Firmware\n", ++ __FUNCTION__)); ++ } ++ } ++ ++done: ++ return ret; ++} ++ ++ ++int ++dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, int set) ++{ ++ char buf[strlen(name) + 1 + cmd_len]; ++ int len = sizeof(buf); ++ wl_ioctl_t ioc; ++ int ret; ++ ++ len = bcm_mkiovar(name, cmd_buf, cmd_len, buf, len); ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ++ ioc.cmd = set? WLC_SET_VAR : WLC_GET_VAR; ++ ioc.buf = buf; ++ ioc.len = len; ++ ioc.set = TRUE; ++ ++ ret = dhd_wl_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len); ++ if (!set && ret >= 0) ++ memcpy(cmd_buf, buf, cmd_len); ++ ++ return ret; ++} ++ ++int dhd_change_mtu(dhd_pub_t *dhdp, int new_mtu, int ifidx) ++{ ++ struct dhd_info *dhd = dhdp->info; ++ struct net_device *dev = NULL; ++ ++ ASSERT(dhd && dhd->iflist[ifidx]); ++ dev = dhd->iflist[ifidx]->net; ++ ASSERT(dev); ++ ++ if (netif_running(dev)) { ++ DHD_ERROR(("%s: Must be down to change its MTU", dev->name)); ++ return BCME_NOTDOWN; ++ } ++ ++#define DHD_MIN_MTU 1500 ++#define DHD_MAX_MTU 1752 ++ ++ if ((new_mtu < DHD_MIN_MTU) || (new_mtu > DHD_MAX_MTU)) { ++ DHD_ERROR(("%s: MTU size %d is invalid.\n", __FUNCTION__, new_mtu)); ++ return BCME_BADARG; ++ } ++ ++ dev->mtu = new_mtu; ++ return 0; ++} ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++/* add or remove AOE host ip(s) (up to 8 IPs on the interface) */ ++void ++aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add, int idx) ++{ ++ u32 ipv4_buf[MAX_IPV4_ENTRIES]; /* temp save for AOE host_ip table */ ++ int i; ++ int ret; ++ ++ bzero(ipv4_buf, sizeof(ipv4_buf)); ++ ++ /* display what we've got */ ++ ret = dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf), idx); ++ DHD_ARPOE(("%s: hostip table read from Dongle:\n", __FUNCTION__)); ++#ifdef AOE_DBG ++ dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */ ++#endif ++ /* now we saved hoste_ip table, clr it in the dongle AOE */ ++ dhd_aoe_hostip_clr(dhd_pub, idx); ++ ++ if (ret) { ++ DHD_ERROR(("%s failed\n", __FUNCTION__)); ++ return; ++ } ++ ++ for (i = 0; i < MAX_IPV4_ENTRIES; i++) { ++ if (add && (ipv4_buf[i] == 0)) { ++ ipv4_buf[i] = ipa; ++ add = FALSE; /* added ipa to local table */ ++ DHD_ARPOE(("%s: Saved new IP in temp arp_hostip[%d]\n", ++ __FUNCTION__, i)); ++ } else if (ipv4_buf[i] == ipa) { ++ ipv4_buf[i] = 0; ++ DHD_ARPOE(("%s: removed IP:%x from temp table %d\n", ++ __FUNCTION__, ipa, i)); ++ } ++ ++ if (ipv4_buf[i] != 0) { ++ /* add back host_ip entries from our local cache */ ++ dhd_arp_offload_add_ip(dhd_pub, ipv4_buf[i], idx); ++ DHD_ARPOE(("%s: added IP:%x to dongle arp_hostip[%d]\n\n", ++ __FUNCTION__, ipv4_buf[i], i)); ++ } ++ } ++#ifdef AOE_DBG ++ /* see the resulting hostip table */ ++ dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf), idx); ++ DHD_ARPOE(("%s: read back arp_hostip table:\n", __FUNCTION__)); ++ dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */ ++#endif ++} ++ ++/* ++ * Notification mechanism from kernel to our driver. This function is called by the Linux kernel ++ * whenever there is an event related to an IP address. ++ * ptr : kernel provided pointer to IP address that has changed ++ */ ++static int dhd_device_event(struct notifier_block *this, ++ unsigned long event, ++ void *ptr) ++{ ++ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; ++ ++ dhd_info_t *dhd; ++ dhd_pub_t *dhd_pub; ++ int idx; ++ ++ if (!dhd_arp_enable) ++ return NOTIFY_DONE; ++ if (!ifa || !(ifa->ifa_dev->dev)) ++ return NOTIFY_DONE; ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) ++ /* Filter notifications meant for non Broadcom devices */ ++ if ((ifa->ifa_dev->dev->netdev_ops != &dhd_ops_pri) && ++ (ifa->ifa_dev->dev->netdev_ops != &dhd_ops_virt)) { ++#ifdef WLP2P ++ if (!wl_cfgp2p_is_ifops(ifa->ifa_dev->dev->netdev_ops)) ++#endif ++ return NOTIFY_DONE; ++ } ++#endif /* LINUX_VERSION_CODE */ ++ ++ dhd = *(dhd_info_t **)netdev_priv(ifa->ifa_dev->dev); ++ if (!dhd) ++ return NOTIFY_DONE; ++ ++ dhd_pub = &dhd->pub; ++ ++ if (dhd_pub->arp_version == 1) { ++ idx = 0; ++ } ++ else { ++ for (idx = 0; idx < DHD_MAX_IFS; idx++) { ++ if (dhd->iflist[idx] && dhd->iflist[idx]->net == ifa->ifa_dev->dev) ++ break; ++ } ++ if (idx < DHD_MAX_IFS) ++ DHD_TRACE(("ifidx : %p %s %d\n", dhd->iflist[idx]->net, ++ dhd->iflist[idx]->name, dhd->iflist[idx]->idx)); ++ else { ++ DHD_ERROR(("Cannot find ifidx for(%s) set to 0\n", ifa->ifa_label)); ++ idx = 0; ++ } ++ } ++ ++ switch (event) { ++ case NETDEV_UP: ++ DHD_ARPOE(("%s: [%s] Up IP: 0x%x\n", ++ __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); ++ ++ if (dhd->pub.busstate != DHD_BUS_DATA) { ++ DHD_ERROR(("%s: bus not ready, exit\n", __FUNCTION__)); ++ if (dhd->pend_ipaddr) { ++ DHD_ERROR(("%s: overwrite pending ipaddr: 0x%x\n", ++ __FUNCTION__, dhd->pend_ipaddr)); ++ } ++ dhd->pend_ipaddr = ifa->ifa_address; ++ break; ++ } ++ ++#ifdef AOE_IP_ALIAS_SUPPORT ++ DHD_ARPOE(("%s:add aliased IP to AOE hostip cache\n", ++ __FUNCTION__)); ++ aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, TRUE, idx); ++#endif ++ break; ++ ++ case NETDEV_DOWN: ++ DHD_ARPOE(("%s: [%s] Down IP: 0x%x\n", ++ __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); ++ dhd->pend_ipaddr = 0; ++#ifdef AOE_IP_ALIAS_SUPPORT ++ DHD_ARPOE(("%s:interface is down, AOE clr all for this if\n", ++ __FUNCTION__)); ++ aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, FALSE, idx); ++#else ++ dhd_aoe_hostip_clr(&dhd->pub, idx); ++ dhd_aoe_arp_clr(&dhd->pub, idx); ++#endif /* AOE_IP_ALIAS_SUPPORT */ ++ break; ++ ++ default: ++ DHD_ARPOE(("%s: do noting for [%s] Event: %lu\n", ++ __func__, ifa->ifa_label, event)); ++ break; ++ } ++ return NOTIFY_DONE; ++} ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++int ++dhd_net_attach(dhd_pub_t *dhdp, int ifidx) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)dhdp->info; ++ struct net_device *net = NULL; ++ int err = 0; ++ uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x11, 0x22, 0x33 }; ++ ++ DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); ++ ++ ASSERT(dhd && dhd->iflist[ifidx]); ++ ++ net = dhd->iflist[ifidx]->net; ++ ASSERT(net); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) ++ ASSERT(!net->open); ++ net->get_stats = dhd_get_stats; ++ net->do_ioctl = dhd_ioctl_entry; ++ net->hard_start_xmit = dhd_start_xmit; ++ net->set_mac_address = dhd_set_mac_address; ++ net->set_multicast_list = dhd_set_multicast_list; ++ net->open = net->stop = NULL; ++#else ++ ASSERT(!net->netdev_ops); ++ net->netdev_ops = &dhd_ops_virt; ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ ++ ++ /* Ok, link into the network layer... */ ++ if (ifidx == 0) { ++ /* ++ * device functions for the primary interface only ++ */ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) ++ net->open = dhd_open; ++ net->stop = dhd_stop; ++#else ++ net->netdev_ops = &dhd_ops_pri; ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ ++ if (!ETHER_ISNULLADDR(dhd->pub.mac.octet)) ++ memcpy(temp_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN); ++ } else { ++ /* ++ * We have to use the primary MAC for virtual interfaces ++ */ ++ memcpy(temp_addr, dhd->iflist[ifidx]->mac_addr, ETHER_ADDR_LEN); ++ /* ++ * Android sets the locally administered bit to indicate that this is a ++ * portable hotspot. This will not work in simultaneous AP/STA mode, ++ * nor with P2P. Need to set the Donlge's MAC address, and then use that. ++ */ ++ if (!memcmp(temp_addr, dhd->iflist[0]->mac_addr, ++ ETHER_ADDR_LEN)) { ++ DHD_ERROR(("%s interface [%s]: set locally administered bit in MAC\n", ++ __func__, net->name)); ++ temp_addr[0] |= 0x02; ++ } ++ } ++ ++ net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) ++ net->ethtool_ops = &dhd_ethtool_ops; ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ ++ ++#if defined(CONFIG_WIRELESS_EXT) ++#if WIRELESS_EXT < 19 ++ net->get_wireless_stats = dhd_get_wireless_stats; ++#endif /* WIRELESS_EXT < 19 */ ++#if WIRELESS_EXT > 12 ++ net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def; ++#endif /* WIRELESS_EXT > 12 */ ++#endif /* defined(CONFIG_WIRELESS_EXT) */ ++ ++ dhd->pub.rxsz = DBUS_RX_BUFFER_SIZE_DHD(net); ++ ++ memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); ++ ++ if ((err = register_netdev(net)) != 0) { ++ DHD_ERROR(("couldn't register the net device, err %d\n", err)); ++ goto fail; ++ } ++ printf("Broadcom Dongle Host Driver: register interface [%s]" ++ " MAC: "MACDBG"\n", ++ net->name, ++ MAC2STRDBG(net->dev_addr)); ++ ++#if defined(SOFTAP) && defined(CONFIG_WIRELESS_EXT) && !defined(WL_CFG80211) ++ wl_iw_iscan_set_scan_broadcast_prep(net, 1); ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ if (ifidx == 0) { ++ dhd_registration_check = TRUE; ++ up(&dhd_registration_sem); ++ } ++#endif ++ return 0; ++ ++fail: ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) ++ net->open = NULL; ++#else ++ net->netdev_ops = NULL; ++#endif ++ return err; ++} ++ ++void ++dhd_bus_detach(dhd_pub_t *dhdp) ++{ ++ dhd_info_t *dhd; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (dhdp) { ++ dhd = (dhd_info_t *)dhdp->info; ++ if (dhd) { ++ ++ /* ++ * In case of Android cfg80211 driver, the bus is down in dhd_stop, ++ * calling stop again will cuase SD read/write errors. ++ */ ++ if (dhd->pub.busstate != DHD_BUS_DOWN) { ++ /* Stop the protocol module */ ++ dhd_prot_stop(&dhd->pub); ++ ++ /* Stop the bus module */ ++ dhd_bus_stop(dhd->pub.bus, TRUE); ++ } ++ ++#if defined(OOB_INTR_ONLY) ++ bcmsdh_unregister_oob_intr(); ++#endif /* defined(OOB_INTR_ONLY) */ ++ } ++ } ++} ++ ++ ++void dhd_detach(dhd_pub_t *dhdp) ++{ ++ dhd_info_t *dhd; ++ unsigned long flags; ++ int timer_valid = FALSE; ++ ++ if (!dhdp) ++ return; ++ ++ dhd = (dhd_info_t *)dhdp->info; ++ if (!dhd) ++ return; ++ ++ DHD_TRACE(("%s: Enter state 0x%x\n", __FUNCTION__, dhd->dhd_state)); ++ ++ dhd->pub.up = 0; ++ if (!(dhd->dhd_state & DHD_ATTACH_STATE_DONE)) { ++ /* Give sufficient time for threads to start running in case ++ * dhd_attach() has failed ++ */ ++ osl_delay(1000*100); ++ } ++ ++ if (dhd->dhd_state & DHD_ATTACH_STATE_PROT_ATTACH) { ++ dhd_bus_detach(dhdp); ++ ++ if (dhdp->prot) ++ dhd_prot_detach(dhdp); ++ } ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++ unregister_inetaddr_notifier(&dhd_notifier); ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ++ if (dhd->dhd_state & DHD_ATTACH_STATE_EARLYSUSPEND_DONE) { ++ if (dhd->early_suspend.suspend) ++ unregister_early_suspend(&dhd->early_suspend); ++ } ++#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ cancel_work_sync(&dhd->work_hang); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++ ++#if defined(CONFIG_WIRELESS_EXT) ++ if (dhd->dhd_state & DHD_ATTACH_STATE_WL_ATTACH) { ++ /* Detatch and unlink in the iw */ ++ wl_iw_detach(); ++ } ++#endif /* defined(CONFIG_WIRELESS_EXT) */ ++ ++ if (dhd->thr_sysioc_ctl.thr_pid >= 0) { ++ PROC_STOP(&dhd->thr_sysioc_ctl); ++ } ++ ++ /* delete all interfaces, start with virtual */ ++ if (dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) { ++ int i = 1; ++ dhd_if_t *ifp; ++ ++ /* Cleanup virtual interfaces */ ++ for (i = 1; i < DHD_MAX_IFS; i++) { ++ dhd_net_if_lock_local(dhd); ++ if (dhd->iflist[i]) { ++ dhd->iflist[i]->state = DHD_IF_DEL; ++ dhd->iflist[i]->idx = i; ++ dhd_op_if(dhd->iflist[i]); ++ } ++ ++ dhd_net_if_unlock_local(dhd); ++ } ++ /* delete primary interface 0 */ ++ ifp = dhd->iflist[0]; ++ ASSERT(ifp); ++ ASSERT(ifp->net); ++ if (ifp && ifp->net) { ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) ++ if (ifp->net->open) ++#else ++ if (ifp->net->netdev_ops == &dhd_ops_pri) ++#endif ++ { ++ unregister_netdev(ifp->net); ++ free_netdev(ifp->net); ++ ifp->net = NULL; ++ MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); ++ dhd->iflist[0] = NULL; ++ } ++ } ++ } ++ ++ /* Clear the watchdog timer */ ++ flags = dhd_os_spin_lock(&dhd->pub); ++ timer_valid = dhd->wd_timer_valid; ++ dhd->wd_timer_valid = FALSE; ++ dhd_os_spin_unlock(&dhd->pub, flags); ++ if (timer_valid) ++ del_timer_sync(&dhd->timer); ++ ++ if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) { ++#ifdef DHDTHREAD ++ if (dhd->thr_wdt_ctl.thr_pid >= 0) { ++ PROC_STOP(&dhd->thr_wdt_ctl); ++ } ++ ++ if (dhd->thr_dpc_ctl.thr_pid >= 0) { ++ PROC_STOP(&dhd->thr_dpc_ctl); ++ } ++ else ++#endif /* DHDTHREAD */ ++ tasklet_kill(&dhd->tasklet); ++ } ++ ++#ifdef WL_CFG80211 ++ if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { ++ wl_cfg80211_detach(NULL); ++ dhd_monitor_uninit(); ++ } ++#endif ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) ++ unregister_pm_notifier(&dhd_sleep_pm_notifier); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ ++ /* && defined(CONFIG_PM_SLEEP) */ ++ ++ if (dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT) { ++#ifdef CONFIG_HAS_WAKELOCK ++ dhd->wakelock_counter = 0; ++ dhd->wakelock_wd_counter = 0; ++ dhd->wakelock_rx_timeout_enable = 0; ++ dhd->wakelock_ctrl_timeout_enable = 0; ++ wake_lock_destroy(&dhd->wl_wifi); ++ wake_lock_destroy(&dhd->wl_rxwake); ++ wake_lock_destroy(&dhd->wl_ctrlwake); ++ wake_lock_destroy(&dhd->wl_wdwake); ++#endif /* CONFIG_HAS_WAKELOCK */ ++ } ++} ++ ++ ++void ++dhd_free(dhd_pub_t *dhdp) ++{ ++ dhd_info_t *dhd; ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (dhdp) { ++ int i; ++ for (i = 0; i < ARRAYSIZE(dhdp->reorder_bufs); i++) { ++ if (dhdp->reorder_bufs[i]) { ++ reorder_info_t *ptr; ++ uint32 buf_size = sizeof(struct reorder_info); ++ ++ ptr = dhdp->reorder_bufs[i]; ++ ++ buf_size += ((ptr->max_idx + 1) * sizeof(void*)); ++ DHD_REORDER(("free flow id buf %d, maxidx is %d, buf_size %d\n", ++ i, ptr->max_idx, buf_size)); ++ ++ MFREE(dhdp->osh, dhdp->reorder_bufs[i], buf_size); ++ dhdp->reorder_bufs[i] = NULL; ++ } ++ } ++ dhd = (dhd_info_t *)dhdp->info; ++ if (dhd) ++ MFREE(dhd->pub.osh, dhd, sizeof(*dhd)); ++ } ++} ++ ++static void __exit ++dhd_module_cleanup(void) ++{ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ dhd_bus_unregister(); ++ ++#if defined(CONFIG_WIFI_CONTROL_FUNC) ++ wl_android_wifictrl_func_del(); ++#endif /* CONFIG_WIFI_CONTROL_FUNC */ ++ wl_android_exit(); ++ ++ /* Call customer gpio to turn off power with WL_REG_ON signal */ ++ dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); ++} ++ ++ ++static int __init ++dhd_module_init(void) ++{ ++ int error = 0; ++ ++#if defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ int retry = POWERUP_MAX_RETRY; ++ int chip_up = 0; ++#endif ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ wl_android_init(); ++ ++#if defined(DHDTHREAD) ++ /* Sanity check on the module parameters */ ++ do { ++ /* Both watchdog and DPC as tasklets are ok */ ++ if ((dhd_watchdog_prio < 0) && (dhd_dpc_prio < 0)) ++ break; ++ ++ /* If both watchdog and DPC are threads, TX must be deferred */ ++ if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0) && dhd_deferred_tx) ++ break; ++ ++ DHD_ERROR(("Invalid module parameters.\n")); ++ return -EINVAL; ++ } while (0); ++#endif ++ ++#if defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ do { ++ sema_init(&dhd_chipup_sem, 0); ++ dhd_bus_reg_sdio_notify(&dhd_chipup_sem); ++ dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON); ++#if defined(CONFIG_WIFI_CONTROL_FUNC) ++ if (wl_android_wifictrl_func_add() < 0) { ++ dhd_bus_unreg_sdio_notify(); ++ goto fail_1; ++ } ++#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ ++ if (down_timeout(&dhd_chipup_sem, ++ msecs_to_jiffies(POWERUP_WAIT_MS)) == 0) { ++ dhd_bus_unreg_sdio_notify(); ++ chip_up = 1; ++ break; ++ } ++ DHD_ERROR(("\nfailed to power up wifi chip, retry again (%d left) **\n\n", ++ retry+1)); ++ dhd_bus_unreg_sdio_notify(); ++#if defined(CONFIG_WIFI_CONTROL_FUNC) ++ wl_android_wifictrl_func_del(); ++#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ ++ dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); ++ } while (retry-- > 0); ++ ++ if (!chip_up) { ++ DHD_ERROR(("\nfailed to power up wifi chip, max retry reached, exits **\n\n")); ++ return -ENODEV; ++ } ++#else ++ dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON); ++#if defined(CONFIG_WIFI_CONTROL_FUNC) ++ if (wl_android_wifictrl_func_add() < 0) ++ goto fail_1; ++#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ ++ ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ sema_init(&dhd_registration_sem, 0); ++#endif ++ ++ ++ error = dhd_bus_register(); ++ ++ if (!error) ++ printf("\n%s\n", dhd_version); ++ else { ++ DHD_ERROR(("%s: sdio_register_driver failed\n", __FUNCTION__)); ++ goto fail_1; ++ } ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ /* ++ * Wait till MMC sdio_register_driver callback called and made driver attach. ++ * It's needed to make sync up exit from dhd insmod and ++ * Kernel MMC sdio device callback registration ++ */ ++ if ((down_timeout(&dhd_registration_sem, ++ msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) || ++ (dhd_registration_check != TRUE)) { ++ error = -ENODEV; ++ DHD_ERROR(("%s: sdio_register_driver timeout or error \n", __FUNCTION__)); ++ goto fail_2; ++ } ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++#if defined(WL_CFG80211) ++ wl_android_post_init(); ++#endif /* defined(WL_CFG80211) */ ++ ++ return error; ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++fail_2: ++ dhd_bus_unregister(); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++ ++fail_1: ++ ++#if defined(CONFIG_WIFI_CONTROL_FUNC) ++ wl_android_wifictrl_func_del(); ++#endif ++ ++ /* Call customer gpio to turn off power with WL_REG_ON signal */ ++ dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); ++ ++ return error; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ++late_initcall(dhd_module_init); ++#else ++module_init(dhd_module_init); ++#endif ++ ++module_exit(dhd_module_cleanup); ++ ++/* ++ * OS specific functions required to implement DHD driver in OS independent way ++ */ ++int ++dhd_os_proto_block(dhd_pub_t *pub) ++{ ++ dhd_info_t * dhd = (dhd_info_t *)(pub->info); ++ ++ if (dhd) { ++ down(&dhd->proto_sem); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++int ++dhd_os_proto_unblock(dhd_pub_t *pub) ++{ ++ dhd_info_t * dhd = (dhd_info_t *)(pub->info); ++ ++ if (dhd) { ++ up(&dhd->proto_sem); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++unsigned int ++dhd_os_get_ioctl_resp_timeout(void) ++{ ++ return ((unsigned int)dhd_ioctl_timeout_msec); ++} ++ ++void ++dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec) ++{ ++ dhd_ioctl_timeout_msec = (int)timeout_msec; ++} ++ ++int ++dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) ++{ ++ dhd_info_t * dhd = (dhd_info_t *)(pub->info); ++ int timeout; ++ ++ /* Convert timeout in millsecond to jiffies */ ++ timeout = msecs_to_jiffies(dhd_ioctl_timeout_msec); ++ ++ timeout = wait_event_timeout(dhd->ioctl_resp_wait, (*condition), timeout); ++ return timeout; ++} ++ ++int ++dhd_os_ioctl_resp_wake(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ ++ if (waitqueue_active(&dhd->ioctl_resp_wait)) { ++ wake_up(&dhd->ioctl_resp_wait); ++ } ++ ++ return 0; ++} ++ ++void ++dhd_os_wd_timer(void *bus, uint wdtick) ++{ ++ dhd_pub_t *pub = bus; ++ dhd_info_t *dhd = (dhd_info_t *)pub->info; ++ unsigned long flags; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (!dhd) ++ return; ++ ++ flags = dhd_os_spin_lock(pub); ++ ++ /* don't start the wd until fw is loaded */ ++ if (pub->busstate == DHD_BUS_DOWN) { ++ dhd_os_spin_unlock(pub, flags); ++ if (!wdtick) ++ DHD_OS_WD_WAKE_UNLOCK(pub); ++ return; ++ } ++ ++ /* totally stop the timer */ ++ if (!wdtick && dhd->wd_timer_valid == TRUE) { ++ dhd->wd_timer_valid = FALSE; ++ dhd_os_spin_unlock(pub, flags); ++#ifdef DHDTHREAD ++ del_timer_sync(&dhd->timer); ++#else ++ del_timer(&dhd->timer); ++#endif /* DHDTHREAD */ ++ DHD_OS_WD_WAKE_UNLOCK(pub); ++ return; ++ } ++ ++ if (wdtick) { ++ DHD_OS_WD_WAKE_LOCK(pub); ++ dhd_watchdog_ms = (uint)wdtick; ++ /* Re arm the timer, at last watchdog period */ ++ mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); ++ dhd->wd_timer_valid = TRUE; ++ } ++ dhd_os_spin_unlock(pub, flags); ++} ++ ++void * ++dhd_os_open_image(char *filename) ++{ ++ struct file *fp; ++ ++ fp = filp_open(filename, O_RDONLY, 0); ++ /* ++ * 2.6.11 (FC4) supports filp_open() but later revs don't? ++ * Alternative: ++ * fp = open_namei(AT_FDCWD, filename, O_RD, 0); ++ * ??? ++ */ ++ if (IS_ERR(fp)) ++ fp = NULL; ++ ++ return fp; ++} ++ ++int ++dhd_os_get_image_block(char *buf, int len, void *image) ++{ ++ struct file *fp = (struct file *)image; ++ int rdlen; ++ ++ if (!image) ++ return 0; ++ ++ rdlen = kernel_read(fp, fp->f_pos, buf, len); ++ if (rdlen > 0) ++ fp->f_pos += rdlen; ++ ++ return rdlen; ++} ++ ++void ++dhd_os_close_image(void *image) ++{ ++ if (image) ++ filp_close((struct file *)image, NULL); ++} ++ ++ ++void ++dhd_os_sdlock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)(pub->info); ++ ++#ifdef DHDTHREAD ++ if (dhd->threads_only) ++ down(&dhd->sdsem); ++ else ++#endif /* DHDTHREAD */ ++ spin_lock_bh(&dhd->sdlock); ++} ++ ++void ++dhd_os_sdunlock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)(pub->info); ++ ++#ifdef DHDTHREAD ++ if (dhd->threads_only) ++ up(&dhd->sdsem); ++ else ++#endif /* DHDTHREAD */ ++ spin_unlock_bh(&dhd->sdlock); ++} ++ ++void ++dhd_os_sdlock_txq(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)(pub->info); ++ spin_lock_bh(&dhd->txqlock); ++} ++ ++void ++dhd_os_sdunlock_txq(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)(pub->info); ++ spin_unlock_bh(&dhd->txqlock); ++} ++ ++void ++dhd_os_sdlock_rxq(dhd_pub_t *pub) ++{ ++} ++ ++void ++dhd_os_sdunlock_rxq(dhd_pub_t *pub) ++{ ++} ++ ++void ++dhd_os_sdtxlock(dhd_pub_t *pub) ++{ ++ dhd_os_sdlock(pub); ++} ++ ++void ++dhd_os_sdtxunlock(dhd_pub_t *pub) ++{ ++ dhd_os_sdunlock(pub); ++} ++ ++#if defined(CONFIG_DHD_USE_STATIC_BUF) ++uint8* dhd_os_prealloc(void *osh, int section, uint size) ++{ ++ return (uint8*)wl_android_prealloc(section, size); ++} ++ ++void dhd_os_prefree(void *osh, void *addr, uint size) ++{ ++} ++#endif /* defined(CONFIG_DHD_USE_STATIC_BUF) */ ++ ++#if defined(CONFIG_WIRELESS_EXT) ++struct iw_statistics * ++dhd_get_wireless_stats(struct net_device *dev) ++{ ++ int res = 0; ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ ++ if (!dhd->pub.up) { ++ return NULL; ++ } ++ ++ res = wl_iw_get_wireless_stats(dev, &dhd->iw.wstats); ++ ++ if (res == 0) ++ return &dhd->iw.wstats; ++ else ++ return NULL; ++} ++#endif /* defined(CONFIG_WIRELESS_EXT) */ ++ ++static int ++dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, ++ wl_event_msg_t *event, void **data) ++{ ++ int bcmerror = 0; ++ ASSERT(dhd != NULL); ++ ++ bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, event, data); ++ if (bcmerror != BCME_OK) ++ return (bcmerror); ++ ++#if defined(CONFIG_WIRELESS_EXT) ++ if (event->bsscfgidx == 0) { ++ /* ++ * Wireless ext is on primary interface only ++ */ ++ ++ ASSERT(dhd->iflist[*ifidx] != NULL); ++ ASSERT(dhd->iflist[*ifidx]->net != NULL); ++ ++ if (dhd->iflist[*ifidx]->net) { ++ wl_iw_event(dhd->iflist[*ifidx]->net, event, *data); ++ } ++ } ++#endif /* defined(CONFIG_WIRELESS_EXT) */ ++ ++#ifdef WL_CFG80211 ++ if ((ntoh32(event->event_type) == WLC_E_IF) && ++ (((dhd_if_event_t *)*data)->action == WLC_E_IF_ADD)) ++ /* If ADD_IF has been called directly by wl utility then we ++ * should not report this. In case if ADD_IF was called from ++ * CFG stack, then too this event need not be reported back ++ */ ++ return (BCME_OK); ++ if ((wl_cfg80211_is_progress_ifchange() || ++ wl_cfg80211_is_progress_ifadd()) && (*ifidx != 0)) { ++ /* ++ * If IF_ADD/CHANGE operation is going on, ++ * discard any event received on the virtual I/F ++ */ ++ return (BCME_OK); ++ } ++ ++ ASSERT(dhd->iflist[*ifidx] != NULL); ++ ASSERT(dhd->iflist[*ifidx]->net != NULL); ++ if (dhd->iflist[*ifidx]->event2cfg80211 && dhd->iflist[*ifidx]->net) { ++ wl_cfg80211_event(dhd->iflist[*ifidx]->net, event, *data); ++ } ++#endif /* defined(WL_CFG80211) */ ++ ++ return (bcmerror); ++} ++ ++/* send up locally generated event */ ++void ++dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data) ++{ ++ switch (ntoh32(event->event_type)) { ++#ifdef WLBTAMP ++ /* Send up locally generated AMP HCI Events */ ++ case WLC_E_BTA_HCI_EVENT: { ++ struct sk_buff *p, *skb; ++ bcm_event_t *msg; ++ wl_event_msg_t *p_bcm_event; ++ char *ptr; ++ uint32 len; ++ uint32 pktlen; ++ dhd_if_t *ifp; ++ dhd_info_t *dhd; ++ uchar *eth; ++ int ifidx; ++ ++ len = ntoh32(event->datalen); ++ pktlen = sizeof(bcm_event_t) + len + 2; ++ dhd = dhdp->info; ++ ifidx = dhd_ifname2idx(dhd, event->ifname); ++ ++ if ((p = PKTGET(dhdp->osh, pktlen, FALSE))) { ++ ASSERT(ISALIGNED((uintptr)PKTDATA(dhdp->osh, p), sizeof(uint32))); ++ ++ msg = (bcm_event_t *) PKTDATA(dhdp->osh, p); ++ ++ bcopy(&dhdp->mac, &msg->eth.ether_dhost, ETHER_ADDR_LEN); ++ bcopy(&dhdp->mac, &msg->eth.ether_shost, ETHER_ADDR_LEN); ++ ETHER_TOGGLE_LOCALADDR(&msg->eth.ether_shost); ++ ++ msg->eth.ether_type = hton16(ETHER_TYPE_BRCM); ++ ++ /* BCM Vendor specific header... */ ++ msg->bcm_hdr.subtype = hton16(BCMILCP_SUBTYPE_VENDOR_LONG); ++ msg->bcm_hdr.version = BCMILCP_BCM_SUBTYPEHDR_VERSION; ++ bcopy(BRCM_OUI, &msg->bcm_hdr.oui[0], DOT11_OUI_LEN); ++ ++ /* vendor spec header length + pvt data length (private indication ++ * hdr + actual message itself) ++ */ ++ msg->bcm_hdr.length = hton16(BCMILCP_BCM_SUBTYPEHDR_MINLENGTH + ++ BCM_MSG_LEN + sizeof(wl_event_msg_t) + (uint16)len); ++ msg->bcm_hdr.usr_subtype = hton16(BCMILCP_BCM_SUBTYPE_EVENT); ++ ++ PKTSETLEN(dhdp->osh, p, (sizeof(bcm_event_t) + len + 2)); ++ ++ /* copy wl_event_msg_t into sk_buf */ ++ ++ /* pointer to wl_event_msg_t in sk_buf */ ++ p_bcm_event = &msg->event; ++ bcopy(event, p_bcm_event, sizeof(wl_event_msg_t)); ++ ++ /* copy hci event into sk_buf */ ++ bcopy(data, (p_bcm_event + 1), len); ++ ++ msg->bcm_hdr.length = hton16(sizeof(wl_event_msg_t) + ++ ntoh16(msg->bcm_hdr.length)); ++ PKTSETLEN(dhdp->osh, p, (sizeof(bcm_event_t) + len + 2)); ++ ++ ptr = (char *)(msg + 1); ++ /* Last 2 bytes of the message are 0x00 0x00 to signal that there ++ * are no ethertypes which are following this ++ */ ++ ptr[len+0] = 0x00; ++ ptr[len+1] = 0x00; ++ ++ skb = PKTTONATIVE(dhdp->osh, p); ++ eth = skb->data; ++ len = skb->len; ++ ++ ifp = dhd->iflist[ifidx]; ++ if (ifp == NULL) ++ ifp = dhd->iflist[0]; ++ ++ ASSERT(ifp); ++ skb->dev = ifp->net; ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ ++ skb->data = eth; ++ skb->len = len; ++ ++ /* Strip header, count, deliver upward */ ++ skb_pull(skb, ETH_HLEN); ++ ++ /* Send the packet */ ++ if (in_interrupt()) { ++ netif_rx(skb); ++ } else { ++ netif_rx_ni(skb); ++ } ++ } ++ else { ++ /* Could not allocate a sk_buf */ ++ DHD_ERROR(("%s: unable to alloc sk_buf", __FUNCTION__)); ++ } ++ break; ++ } /* case WLC_E_BTA_HCI_EVENT */ ++#endif /* WLBTAMP */ ++ ++ default: ++ break; ++ } ++} ++ ++void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) ++ struct dhd_info *dhdinfo = dhd->info; ++ int timeout = msecs_to_jiffies(IOCTL_RESP_TIMEOUT); ++ dhd_os_sdunlock(dhd); ++ wait_event_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), timeout); ++ dhd_os_sdlock(dhd); ++#endif ++ return; ++} ++ ++void dhd_wait_event_wakeup(dhd_pub_t *dhd) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) ++ struct dhd_info *dhdinfo = dhd->info; ++ if (waitqueue_active(&dhdinfo->ctrl_wait)) ++ wake_up(&dhdinfo->ctrl_wait); ++#endif ++ return; ++} ++ ++int ++dhd_dev_reset(struct net_device *dev, uint8 flag) ++{ ++ int ret; ++ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ ++ if (flag == TRUE) { ++ /* Issue wl down command before resetting the chip */ ++ if (dhd_wl_ioctl_cmd(&dhd->pub, WLC_DOWN, NULL, 0, TRUE, 0) < 0) { ++ DHD_TRACE(("%s: wl down failed\n", __FUNCTION__)); ++ } ++ } ++ ++ ret = dhd_bus_devreset(&dhd->pub, flag); ++ if (ret) { ++ DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret)); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++int net_os_set_suspend_disable(struct net_device *dev, int val) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ int ret = 0; ++ ++ if (dhd) { ++ ret = dhd->pub.suspend_disable_flag; ++ dhd->pub.suspend_disable_flag = val; ++ } ++ return ret; ++} ++ ++int net_os_set_suspend(struct net_device *dev, int val, int force) ++{ ++ int ret = 0; ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ ++ if (dhd) { ++#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ++ ret = dhd_set_suspend(val, &dhd->pub); ++#else ++ ret = dhd_suspend_resume_helper(dhd, val, force); ++#endif ++#ifdef WL_CFG80211 ++ wl_cfg80211_update_power_mode(dev); ++#endif ++ } ++ return ret; ++} ++ ++int net_os_set_suspend_bcn_li_dtim(struct net_device *dev, int val) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ ++ if (dhd) ++ dhd->pub.suspend_bcn_li_dtim = val; ++ ++ return 0; ++} ++ ++#ifdef PKT_FILTER_SUPPORT ++int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num) ++{ ++#ifndef GAN_LITE_NAT_KEEPALIVE_FILTER ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ char *filterp = NULL; ++ int ret = 0; ++ ++ if (!dhd || (num == DHD_UNICAST_FILTER_NUM) || ++ (num == DHD_MDNS_FILTER_NUM)) ++ return ret; ++ if (num >= dhd->pub.pktfilter_count) ++ return -EINVAL; ++ if (add_remove) { ++ switch (num) { ++ case DHD_BROADCAST_FILTER_NUM: ++ filterp = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF"; ++ break; ++ case DHD_MULTICAST4_FILTER_NUM: ++ filterp = "102 0 0 0 0xFFFFFF 0x01005E"; ++ break; ++ case DHD_MULTICAST6_FILTER_NUM: ++ filterp = "103 0 0 0 0xFFFF 0x3333"; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ dhd->pub.pktfilter[num] = filterp; ++ dhd_pktfilter_offload_set(&dhd->pub, dhd->pub.pktfilter[num]); ++ return ret; ++#else ++ return 0; ++#endif ++} ++ ++int dhd_os_enable_packet_filter(dhd_pub_t *dhdp, int val) ++{ ++ int ret = 0; ++ ++ /* Packet filtering is set only if we still in early-suspend and ++ * we need either to turn it ON or turn it OFF ++ * We can always turn it OFF in case of early-suspend, but we turn it ++ * back ON only if suspend_disable_flag was not set ++ */ ++ if (dhdp && dhdp->up) { ++ if (dhdp->in_suspend) { ++ if (!val || (val && !dhdp->suspend_disable_flag)) ++ dhd_enable_packet_filter(val, dhdp); ++ } ++ } ++ return ret; ++ ++} ++ ++int net_os_enable_packet_filter(struct net_device *dev, int val) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ ++ return dhd_os_enable_packet_filter(&dhd->pub, val); ++} ++#endif /* PKT_FILTER_SUPPORT */ ++ ++int ++dhd_dev_init_ioctl(struct net_device *dev) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ ++ return dhd_preinit_ioctls(&dhd->pub); ++} ++ ++#ifdef PNO_SUPPORT ++/* Linux wrapper to call common dhd_pno_clean */ ++int ++dhd_dev_pno_reset(struct net_device *dev) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ ++ return (dhd_pno_clean(&dhd->pub)); ++} ++ ++ ++/* Linux wrapper to call common dhd_pno_enable */ ++int ++dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ ++ return (dhd_pno_enable(&dhd->pub, pfn_enabled)); ++} ++ ++ ++/* Linux wrapper to call common dhd_pno_set */ ++int ++dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, ++ ushort scan_fr, int pno_repeat, int pno_freq_expo_max) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ ++ return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr, pno_repeat, pno_freq_expo_max)); ++} ++ ++/* Linux wrapper to get pno status */ ++int ++dhd_dev_get_pno_status(struct net_device *dev) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ ++ return (dhd_pno_get_status(&dhd->pub)); ++} ++ ++#endif /* PNO_SUPPORT */ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++static void dhd_hang_process(struct work_struct *work) ++{ ++ dhd_info_t *dhd; ++ struct net_device *dev; ++ ++ dhd = (dhd_info_t *)container_of(work, dhd_info_t, work_hang); ++ dev = dhd->iflist[0]->net; ++ ++ if (dev) { ++ rtnl_lock(); ++ dev_close(dev); ++ rtnl_unlock(); ++#if defined(WL_WIRELESS_EXT) ++ wl_iw_send_priv_event(dev, "HANG"); ++#endif ++#if defined(WL_CFG80211) ++ wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); ++#endif ++ } ++} ++ ++int dhd_os_send_hang_message(dhd_pub_t *dhdp) ++{ ++ int ret = 0; ++ if (dhdp) { ++ if (!dhdp->hang_was_sent) { ++ dhdp->hang_was_sent = 1; ++ schedule_work(&dhdp->info->work_hang); ++ } ++ } ++ return ret; ++} ++ ++int net_os_send_hang_message(struct net_device *dev) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ int ret = 0; ++ ++ if (dhd) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ ret = dhd_os_send_hang_message(&dhd->pub); ++#else ++ ret = wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); ++#endif ++ return ret; ++} ++#endif ++ ++void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec, bool notify) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ ++ if (dhd && dhd->pub.up) { ++ memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); ++#ifdef WL_CFG80211 ++ wl_update_wiphybands(NULL, notify); ++#endif ++ } ++} ++ ++void dhd_bus_band_set(struct net_device *dev, uint band) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ if (dhd && dhd->pub.up) { ++#ifdef WL_CFG80211 ++ wl_update_wiphybands(NULL, true); ++#endif ++ } ++} ++ ++void dhd_net_if_lock(struct net_device *dev) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ dhd_net_if_lock_local(dhd); ++} ++ ++void dhd_net_if_unlock(struct net_device *dev) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ dhd_net_if_unlock_local(dhd); ++} ++ ++static void dhd_net_if_lock_local(dhd_info_t *dhd) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ if (dhd) ++ mutex_lock(&dhd->dhd_net_if_mutex); ++#endif ++} ++ ++static void dhd_net_if_unlock_local(dhd_info_t *dhd) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ if (dhd) ++ mutex_unlock(&dhd->dhd_net_if_mutex); ++#endif ++} ++ ++static void dhd_suspend_lock(dhd_pub_t *pub) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ if (dhd) ++ mutex_lock(&dhd->dhd_suspend_mutex); ++#endif ++} ++ ++static void dhd_suspend_unlock(dhd_pub_t *pub) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ if (dhd) ++ mutex_unlock(&dhd->dhd_suspend_mutex); ++#endif ++} ++ ++unsigned long dhd_os_spin_lock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags = 0; ++ ++ if (dhd) ++ spin_lock_irqsave(&dhd->dhd_lock, flags); ++ ++ return flags; ++} ++ ++void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ ++ if (dhd) ++ spin_unlock_irqrestore(&dhd->dhd_lock, flags); ++} ++ ++static int ++dhd_get_pend_8021x_cnt(dhd_info_t *dhd) ++{ ++ return (atomic_read(&dhd->pend_8021x_cnt)); ++} ++ ++#define MAX_WAIT_FOR_8021X_TX 25 ++ ++int ++dhd_wait_pend8021x(struct net_device *dev) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ int timeout = msecs_to_jiffies(10); ++ int ntimes = MAX_WAIT_FOR_8021X_TX; ++ int pend = dhd_get_pend_8021x_cnt(dhd); ++ ++ while (ntimes && pend) { ++ if (pend) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(timeout); ++ set_current_state(TASK_RUNNING); ++ ntimes--; ++ } ++ pend = dhd_get_pend_8021x_cnt(dhd); ++ } ++ if (ntimes == 0) ++ DHD_ERROR(("%s: TIMEOUT\n", __FUNCTION__)); ++ return pend; ++} ++ ++#ifdef DHD_DEBUG ++int ++write_to_file(dhd_pub_t *dhd, uint8 *buf, int size) ++{ ++ int ret = 0; ++ struct file *fp; ++ mm_segment_t old_fs; ++ loff_t pos = 0; ++ ++ /* change to KERNEL_DS address limit */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ /* open file to write */ ++ fp = filp_open("/tmp/mem_dump", O_WRONLY|O_CREAT, 0640); ++ if (!fp) { ++ printf("%s: open file error\n", __FUNCTION__); ++ ret = -1; ++ goto exit; ++ } ++ ++ /* Write buf to file */ ++ fp->f_op->write(fp, buf, size, &pos); ++ ++exit: ++ /* free buf before return */ ++ MFREE(dhd->osh, buf, size); ++ /* close file before return */ ++ if (fp) ++ filp_close(fp, current->files); ++ /* restore previous address limit */ ++ set_fs(old_fs); ++ ++ return ret; ++} ++#endif /* DHD_DEBUG */ ++ ++int dhd_os_wake_lock_timeout(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ int ret = 0; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ ret = dhd->wakelock_rx_timeout_enable > dhd->wakelock_ctrl_timeout_enable ? ++ dhd->wakelock_rx_timeout_enable : dhd->wakelock_ctrl_timeout_enable; ++#ifdef CONFIG_HAS_WAKELOCK ++ if (dhd->wakelock_rx_timeout_enable) ++ wake_lock_timeout(&dhd->wl_rxwake, ++ msecs_to_jiffies(dhd->wakelock_rx_timeout_enable)); ++ if (dhd->wakelock_ctrl_timeout_enable) ++ wake_lock_timeout(&dhd->wl_ctrlwake, ++ msecs_to_jiffies(dhd->wakelock_ctrl_timeout_enable)); ++#endif ++ dhd->wakelock_rx_timeout_enable = 0; ++ dhd->wakelock_ctrl_timeout_enable = 0; ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return ret; ++} ++ ++int net_os_wake_lock_timeout(struct net_device *dev) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ int ret = 0; ++ ++ if (dhd) ++ ret = dhd_os_wake_lock_timeout(&dhd->pub); ++ return ret; ++} ++ ++int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ if (val > dhd->wakelock_rx_timeout_enable) ++ dhd->wakelock_rx_timeout_enable = val; ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return 0; ++} ++ ++int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ if (val > dhd->wakelock_ctrl_timeout_enable) ++ dhd->wakelock_ctrl_timeout_enable = val; ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return 0; ++} ++ ++int net_os_wake_lock_rx_timeout_enable(struct net_device *dev, int val) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ int ret = 0; ++ ++ if (dhd) ++ ret = dhd_os_wake_lock_rx_timeout_enable(&dhd->pub, val); ++ return ret; ++} ++ ++int net_os_wake_lock_ctrl_timeout_enable(struct net_device *dev, int val) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ int ret = 0; ++ ++ if (dhd) ++ ret = dhd_os_wake_lock_ctrl_timeout_enable(&dhd->pub, val); ++ return ret; ++} ++ ++int dhd_os_wake_lock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ int ret = 0; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++#ifdef CONFIG_HAS_WAKELOCK ++ if (!dhd->wakelock_counter) ++ wake_lock(&dhd->wl_wifi); ++#endif ++ dhd->wakelock_counter++; ++ ret = dhd->wakelock_counter; ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return ret; ++} ++ ++int net_os_wake_lock(struct net_device *dev) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ int ret = 0; ++ ++ if (dhd) ++ ret = dhd_os_wake_lock(&dhd->pub); ++ return ret; ++} ++ ++int dhd_os_wake_unlock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ int ret = 0; ++ ++ dhd_os_wake_lock_timeout(pub); ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ if (dhd->wakelock_counter) { ++ dhd->wakelock_counter--; ++#ifdef CONFIG_HAS_WAKELOCK ++ if (!dhd->wakelock_counter) ++ wake_unlock(&dhd->wl_wifi); ++#endif ++ ret = dhd->wakelock_counter; ++ } ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return ret; ++} ++ ++int dhd_os_check_wakelock(void *dhdp) ++{ ++#ifdef CONFIG_HAS_WAKELOCK ++ dhd_pub_t *pub = (dhd_pub_t *)dhdp; ++ dhd_info_t *dhd; ++ ++ if (!pub) ++ return 0; ++ dhd = (dhd_info_t *)(pub->info); ++ ++ if (dhd && (wake_lock_active(&dhd->wl_wifi) || ++ wake_lock_active(&dhd->wl_wdwake))) ++ return 1; ++#endif ++ return 0; ++} ++ ++int net_os_wake_unlock(struct net_device *dev) ++{ ++ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); ++ int ret = 0; ++ ++ if (dhd) ++ ret = dhd_os_wake_unlock(&dhd->pub); ++ return ret; ++} ++ ++int dhd_os_wd_wake_lock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ int ret = 0; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++#ifdef CONFIG_HAS_WAKELOCK ++ if (!dhd->wakelock_wd_counter) ++ wake_lock(&dhd->wl_wdwake); ++#endif ++ dhd->wakelock_wd_counter++; ++ ret = dhd->wakelock_wd_counter; ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return ret; ++} ++ ++int dhd_os_wd_wake_unlock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ int ret = 0; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ if (dhd->wakelock_wd_counter) { ++ dhd->wakelock_wd_counter = 0; ++#ifdef CONFIG_HAS_WAKELOCK ++ wake_unlock(&dhd->wl_wdwake); ++#endif ++ } ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return ret; ++} ++ ++int dhd_os_check_if_up(void *dhdp) ++{ ++ dhd_pub_t *pub = (dhd_pub_t *)dhdp; ++ ++ if (!pub) ++ return 0; ++ return pub->up; ++} ++ ++/* function to collect firmware, chip id and chip version info */ ++void dhd_set_version_info(dhd_pub_t *dhdp, char *fw) ++{ ++ int i; ++ ++ i = snprintf(info_string, sizeof(info_string), ++ " Driver: %s\n Firmware: %s ", EPI_VERSION_STR, fw); ++ ++ if (!dhdp) ++ return; ++ ++ i = snprintf(&info_string[i], sizeof(info_string) - i, ++ "\n Chip: %x Rev %x Pkg %x", dhd_bus_chip_id(dhdp), ++ dhd_bus_chiprev_id(dhdp), dhd_bus_chippkg_id(dhdp)); ++} ++ ++int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd) ++{ ++ int ifidx; ++ int ret = 0; ++ dhd_info_t *dhd = NULL; ++ ++ if (!net || !netdev_priv(net)) { ++ DHD_ERROR(("%s invalid parameter\n", __FUNCTION__)); ++ return -EINVAL; ++ } ++ ++ dhd = *(dhd_info_t **)netdev_priv(net); ++ ifidx = dhd_net2idx(dhd, net); ++ if (ifidx == DHD_BAD_IF) { ++ DHD_ERROR(("%s bad ifidx\n", __FUNCTION__)); ++ return -ENODEV; ++ } ++ ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ ret = dhd_wl_ioctl(&dhd->pub, ifidx, ioc, ioc->buf, ioc->len); ++ dhd_check_hang(net, &dhd->pub, ret); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ ++ return ret; ++} ++ ++bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret) ++{ ++ struct net_device *net; ++ ++ net = dhd_idx2net(dhdp, ifidx); ++ return dhd_check_hang(net, dhdp, ret); ++} ++ ++ ++#ifdef PROP_TXSTATUS ++extern int dhd_wlfc_interface_entry_update(void* state, ewlfc_mac_entry_action_t action, uint8 ifid, ++ uint8 iftype, uint8* ea); ++extern int dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits); ++ ++int dhd_wlfc_interface_event(struct dhd_info *dhd, ++ ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) ++{ ++ int ret = BCME_OK; ++ ++ dhd_os_wlfc_block(&dhd->pub); ++ if (dhd->pub.wlfc_state != NULL) ++ ret = dhd_wlfc_interface_entry_update(dhd->pub.wlfc_state, action, ifid, iftype, ea); ++ dhd_os_wlfc_unblock(&dhd->pub); ++ return ret; ++} ++ ++int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data) ++{ ++ int ret = BCME_OK; ++ ++ dhd_os_wlfc_block(&dhd->pub); ++ if (dhd->pub.wlfc_state != NULL) ++ ret = dhd_wlfc_FIFOcreditmap_update(dhd->pub.wlfc_state, event_data); ++ dhd_os_wlfc_unblock(&dhd->pub); ++ return ret; ++} ++ ++int dhd_wlfc_event(struct dhd_info *dhd) ++{ ++ int ret; ++ ++ dhd_os_wlfc_block(&dhd->pub); ++ ret = dhd_wlfc_enable(&dhd->pub); ++ dhd_os_wlfc_unblock(&dhd->pub); ++ return ret; ++} ++#endif /* PROP_TXSTATUS */ ++ ++#ifdef BCMDBGFS ++ ++#include ++ ++extern uint32 dhd_readregl(void *bp, uint32 addr); ++extern uint32 dhd_writeregl(void *bp, uint32 addr, uint32 data); ++ ++typedef struct dhd_dbgfs { ++ struct dentry *debugfs_dir; ++ struct dentry *debugfs_mem; ++ dhd_pub_t *dhdp; ++ uint32 size; ++} dhd_dbgfs_t; ++ ++dhd_dbgfs_t g_dbgfs; ++ ++static int ++dhd_dbg_state_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = inode->i_private; ++ return 0; ++} ++ ++static ssize_t ++dhd_dbg_state_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ ssize_t rval; ++ uint32 tmp; ++ loff_t pos = *ppos; ++ size_t ret; ++ ++ if (pos < 0) ++ return -EINVAL; ++ if (pos >= g_dbgfs.size || !count) ++ return 0; ++ if (count > g_dbgfs.size - pos) ++ count = g_dbgfs.size - pos; ++ ++ /* Basically enforce aligned 4 byte reads. It's up to the user to work out the details */ ++ tmp = dhd_readregl(g_dbgfs.dhdp->bus, file->f_pos & (~3)); ++ ++ ret = copy_to_user(ubuf, &tmp, 4); ++ if (ret == count) ++ return -EFAULT; ++ ++ count -= ret; ++ *ppos = pos + count; ++ rval = count; ++ ++ return rval; ++} ++ ++ ++static ssize_t ++dhd_debugfs_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) ++{ ++ loff_t pos = *ppos; ++ size_t ret; ++ uint32 buf; ++ ++ if (pos < 0) ++ return -EINVAL; ++ if (pos >= g_dbgfs.size || !count) ++ return 0; ++ if (count > g_dbgfs.size - pos) ++ count = g_dbgfs.size - pos; ++ ++ ret = copy_from_user(&buf, ubuf, sizeof(uint32)); ++ if (ret == count) ++ return -EFAULT; ++ ++ /* Basically enforce aligned 4 byte writes. It's up to the user to work out the details */ ++ dhd_writeregl(g_dbgfs.dhdp->bus, file->f_pos & (~3), buf); ++ ++ return count; ++} ++ ++ ++loff_t ++dhd_debugfs_lseek(struct file *file, loff_t off, int whence) ++{ ++ loff_t pos = -1; ++ ++ switch (whence) { ++ case 0: ++ pos = off; ++ break; ++ case 1: ++ pos = file->f_pos + off; ++ break; ++ case 2: ++ pos = g_dbgfs.size - off; ++ } ++ return (pos < 0 || pos > g_dbgfs.size) ? -EINVAL : (file->f_pos = pos); ++} ++ ++static const struct file_operations dhd_dbg_state_ops = { ++ .read = dhd_dbg_state_read, ++ .write = dhd_debugfs_write, ++ .open = dhd_dbg_state_open, ++ .llseek = dhd_debugfs_lseek ++}; ++ ++static void dhd_dbg_create(void) ++{ ++ if (g_dbgfs.debugfs_dir) { ++ g_dbgfs.debugfs_mem = debugfs_create_file("mem", 0644, g_dbgfs.debugfs_dir, ++ NULL, &dhd_dbg_state_ops); ++ } ++} ++ ++void dhd_dbg_init(dhd_pub_t *dhdp) ++{ ++ int err; ++ ++ g_dbgfs.dhdp = dhdp; ++ g_dbgfs.size = 0x20000000; /* Allow access to various cores regs */ ++ ++ g_dbgfs.debugfs_dir = debugfs_create_dir("dhd", 0); ++ if (IS_ERR(g_dbgfs.debugfs_dir)) { ++ err = PTR_ERR(g_dbgfs.debugfs_dir); ++ g_dbgfs.debugfs_dir = NULL; ++ return; ++ } ++ ++ dhd_dbg_create(); ++ ++ return; ++} ++ ++void dhd_dbg_remove(void) ++{ ++ debugfs_remove(g_dbgfs.debugfs_mem); ++ debugfs_remove(g_dbgfs.debugfs_dir); ++ ++ bzero((unsigned char *) &g_dbgfs, sizeof(g_dbgfs)); ++ ++} ++#endif /* ifdef BCMDBGFS */ ++ ++#ifdef WLMEDIA_HTSF ++ ++static ++void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); ++ struct sk_buff *skb; ++ uint32 htsf = 0; ++ uint16 dport = 0, oldmagic = 0xACAC; ++ char *p1; ++ htsfts_t ts; ++ ++ /* timestamp packet */ ++ ++ p1 = (char*) PKTDATA(dhdp->osh, pktbuf); ++ ++ if (PKTLEN(dhdp->osh, pktbuf) > HTSF_MINLEN) { ++/* memcpy(&proto, p1+26, 4); */ ++ memcpy(&dport, p1+40, 2); ++/* proto = ((ntoh32(proto))>> 16) & 0xFF; */ ++ dport = ntoh16(dport); ++ } ++ ++ /* timestamp only if icmp or udb iperf with port 5555 */ ++/* if (proto == 17 && dport == tsport) { */ ++ if (dport >= tsport && dport <= tsport + 20) { ++ ++ skb = (struct sk_buff *) pktbuf; ++ ++ htsf = dhd_get_htsf(dhd, 0); ++ memset(skb->data + 44, 0, 2); /* clear checksum */ ++ memcpy(skb->data+82, &oldmagic, 2); ++ memcpy(skb->data+84, &htsf, 4); ++ ++ memset(&ts, 0, sizeof(htsfts_t)); ++ ts.magic = HTSFMAGIC; ++ ts.prio = PKTPRIO(pktbuf); ++ ts.seqnum = htsf_seqnum++; ++ ts.c10 = get_cycles(); ++ ts.t10 = htsf; ++ ts.endmagic = HTSFENDMAGIC; ++ ++ memcpy(skb->data + HTSF_HOSTOFFSET, &ts, sizeof(ts)); ++ } ++} ++ ++static void dhd_dump_htsfhisto(histo_t *his, char *s) ++{ ++ int pktcnt = 0, curval = 0, i; ++ for (i = 0; i < (NUMBIN-2); i++) { ++ curval += 500; ++ printf("%d ", his->bin[i]); ++ pktcnt += his->bin[i]; ++ } ++ printf(" max: %d TotPkt: %d neg: %d [%s]\n", his->bin[NUMBIN-2], pktcnt, ++ his->bin[NUMBIN-1], s); ++} ++ ++static ++void sorttobin(int value, histo_t *histo) ++{ ++ int i, binval = 0; ++ ++ if (value < 0) { ++ histo->bin[NUMBIN-1]++; ++ return; ++ } ++ if (value > histo->bin[NUMBIN-2]) /* store the max value */ ++ histo->bin[NUMBIN-2] = value; ++ ++ for (i = 0; i < (NUMBIN-2); i++) { ++ binval += 500; /* 500m s bins */ ++ if (value <= binval) { ++ histo->bin[i]++; ++ return; ++ } ++ } ++ histo->bin[NUMBIN-3]++; ++} ++ ++static ++void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)dhdp->info; ++ struct sk_buff *skb; ++ char *p1; ++ uint16 old_magic; ++ int d1, d2, d3, end2end; ++ htsfts_t *htsf_ts; ++ uint32 htsf; ++ ++ skb = PKTTONATIVE(dhdp->osh, pktbuf); ++ p1 = (char*)PKTDATA(dhdp->osh, pktbuf); ++ ++ if (PKTLEN(osh, pktbuf) > HTSF_MINLEN) { ++ memcpy(&old_magic, p1+78, 2); ++ htsf_ts = (htsfts_t*) (p1 + HTSF_HOSTOFFSET - 4); ++ } ++ else ++ return; ++ ++ if (htsf_ts->magic == HTSFMAGIC) { ++ htsf_ts->tE0 = dhd_get_htsf(dhd, 0); ++ htsf_ts->cE0 = get_cycles(); ++ } ++ ++ if (old_magic == 0xACAC) { ++ ++ tspktcnt++; ++ htsf = dhd_get_htsf(dhd, 0); ++ memcpy(skb->data+92, &htsf, sizeof(uint32)); ++ ++ memcpy(&ts[tsidx].t1, skb->data+80, 16); ++ ++ d1 = ts[tsidx].t2 - ts[tsidx].t1; ++ d2 = ts[tsidx].t3 - ts[tsidx].t2; ++ d3 = ts[tsidx].t4 - ts[tsidx].t3; ++ end2end = ts[tsidx].t4 - ts[tsidx].t1; ++ ++ sorttobin(d1, &vi_d1); ++ sorttobin(d2, &vi_d2); ++ sorttobin(d3, &vi_d3); ++ sorttobin(end2end, &vi_d4); ++ ++ if (end2end > 0 && end2end > maxdelay) { ++ maxdelay = end2end; ++ maxdelaypktno = tspktcnt; ++ memcpy(&maxdelayts, &ts[tsidx], 16); ++ } ++ if (++tsidx >= TSMAX) ++ tsidx = 0; ++ } ++} ++ ++uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx) ++{ ++ uint32 htsf = 0, cur_cycle, delta, delta_us; ++ uint32 factor, baseval, baseval2; ++ cycles_t t; ++ ++ t = get_cycles(); ++ cur_cycle = t; ++ ++ if (cur_cycle > dhd->htsf.last_cycle) ++ delta = cur_cycle - dhd->htsf.last_cycle; ++ else { ++ delta = cur_cycle + (0xFFFFFFFF - dhd->htsf.last_cycle); ++ } ++ ++ delta = delta >> 4; ++ ++ if (dhd->htsf.coef) { ++ /* times ten to get the first digit */ ++ factor = (dhd->htsf.coef*10 + dhd->htsf.coefdec1); ++ baseval = (delta*10)/factor; ++ baseval2 = (delta*10)/(factor+1); ++ delta_us = (baseval - (((baseval - baseval2) * dhd->htsf.coefdec2)) / 10); ++ htsf = (delta_us << 4) + dhd->htsf.last_tsf + HTSF_BUS_DELAY; ++ } ++ else { ++ DHD_ERROR(("-------dhd->htsf.coef = 0 -------\n")); ++ } ++ ++ return htsf; ++} ++ ++static void dhd_dump_latency(void) ++{ ++ int i, max = 0; ++ int d1, d2, d3, d4, d5; ++ ++ printf("T1 T2 T3 T4 d1 d2 t4-t1 i \n"); ++ for (i = 0; i < TSMAX; i++) { ++ d1 = ts[i].t2 - ts[i].t1; ++ d2 = ts[i].t3 - ts[i].t2; ++ d3 = ts[i].t4 - ts[i].t3; ++ d4 = ts[i].t4 - ts[i].t1; ++ d5 = ts[max].t4-ts[max].t1; ++ if (d4 > d5 && d4 > 0) { ++ max = i; ++ } ++ printf("%08X %08X %08X %08X \t%d %d %d %d i=%d\n", ++ ts[i].t1, ts[i].t2, ts[i].t3, ts[i].t4, ++ d1, d2, d3, d4, i); ++ } ++ ++ printf("current idx = %d \n", tsidx); ++ ++ printf("Highest latency %d pkt no.%d total=%d\n", maxdelay, maxdelaypktno, tspktcnt); ++ printf("%08X %08X %08X %08X \t%d %d %d %d\n", ++ maxdelayts.t1, maxdelayts.t2, maxdelayts.t3, maxdelayts.t4, ++ maxdelayts.t2 - maxdelayts.t1, ++ maxdelayts.t3 - maxdelayts.t2, ++ maxdelayts.t4 - maxdelayts.t3, ++ maxdelayts.t4 - maxdelayts.t1); ++} ++ ++ ++static int ++dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx) ++{ ++ wl_ioctl_t ioc; ++ char buf[32]; ++ int ret; ++ uint32 s1, s2; ++ ++ struct tsf { ++ uint32 low; ++ uint32 high; ++ } tsf_buf; ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ memset(&tsf_buf, 0, sizeof(tsf_buf)); ++ ++ ioc.cmd = WLC_GET_VAR; ++ ioc.buf = buf; ++ ioc.len = (uint)sizeof(buf); ++ ioc.set = FALSE; ++ ++ strncpy(buf, "tsf", sizeof(buf) - 1); ++ buf[sizeof(buf) - 1] = '\0'; ++ s1 = dhd_get_htsf(dhd, 0); ++ if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { ++ if (ret == -EIO) { ++ DHD_ERROR(("%s: tsf is not supported by device\n", ++ dhd_ifname(&dhd->pub, ifidx))); ++ return -EOPNOTSUPP; ++ } ++ return ret; ++ } ++ s2 = dhd_get_htsf(dhd, 0); ++ ++ memcpy(&tsf_buf, buf, sizeof(tsf_buf)); ++ printf(" TSF_h=%04X lo=%08X Calc:htsf=%08X, coef=%d.%d%d delta=%d ", ++ tsf_buf.high, tsf_buf.low, s2, dhd->htsf.coef, dhd->htsf.coefdec1, ++ dhd->htsf.coefdec2, s2-tsf_buf.low); ++ printf("lasttsf=%08X lastcycle=%08X\n", dhd->htsf.last_tsf, dhd->htsf.last_cycle); ++ return 0; ++} ++ ++void htsf_update(dhd_info_t *dhd, void *data) ++{ ++ static ulong cur_cycle = 0, prev_cycle = 0; ++ uint32 htsf, tsf_delta = 0; ++ uint32 hfactor = 0, cyc_delta, dec1 = 0, dec2, dec3, tmp; ++ ulong b, a; ++ cycles_t t; ++ ++ /* cycles_t in inlcude/mips/timex.h */ ++ ++ t = get_cycles(); ++ ++ prev_cycle = cur_cycle; ++ cur_cycle = t; ++ ++ if (cur_cycle > prev_cycle) ++ cyc_delta = cur_cycle - prev_cycle; ++ else { ++ b = cur_cycle; ++ a = prev_cycle; ++ cyc_delta = cur_cycle + (0xFFFFFFFF - prev_cycle); ++ } ++ ++ if (data == NULL) ++ printf(" tsf update ata point er is null \n"); ++ ++ memcpy(&prev_tsf, &cur_tsf, sizeof(tsf_t)); ++ memcpy(&cur_tsf, data, sizeof(tsf_t)); ++ ++ if (cur_tsf.low == 0) { ++ DHD_INFO((" ---- 0 TSF, do not update, return\n")); ++ return; ++ } ++ ++ if (cur_tsf.low > prev_tsf.low) ++ tsf_delta = (cur_tsf.low - prev_tsf.low); ++ else { ++ DHD_INFO((" ---- tsf low is smaller cur_tsf= %08X, prev_tsf=%08X, \n", ++ cur_tsf.low, prev_tsf.low)); ++ if (cur_tsf.high > prev_tsf.high) { ++ tsf_delta = cur_tsf.low + (0xFFFFFFFF - prev_tsf.low); ++ DHD_INFO((" ---- Wrap around tsf coutner adjusted TSF=%08X\n", tsf_delta)); ++ } ++ else ++ return; /* do not update */ ++ } ++ ++ if (tsf_delta) { ++ hfactor = cyc_delta / tsf_delta; ++ tmp = (cyc_delta - (hfactor * tsf_delta))*10; ++ dec1 = tmp/tsf_delta; ++ dec2 = ((tmp - dec1*tsf_delta)*10) / tsf_delta; ++ tmp = (tmp - (dec1*tsf_delta))*10; ++ dec3 = ((tmp - dec2*tsf_delta)*10) / tsf_delta; ++ ++ if (dec3 > 4) { ++ if (dec2 == 9) { ++ dec2 = 0; ++ if (dec1 == 9) { ++ dec1 = 0; ++ hfactor++; ++ } ++ else { ++ dec1++; ++ } ++ } ++ else ++ dec2++; ++ } ++ } ++ ++ if (hfactor) { ++ htsf = ((cyc_delta * 10) / (hfactor*10+dec1)) + prev_tsf.low; ++ dhd->htsf.coef = hfactor; ++ dhd->htsf.last_cycle = cur_cycle; ++ dhd->htsf.last_tsf = cur_tsf.low; ++ dhd->htsf.coefdec1 = dec1; ++ dhd->htsf.coefdec2 = dec2; ++ } ++ else { ++ htsf = prev_tsf.low; ++ } ++} ++ ++#endif /* WLMEDIA_HTSF */ +diff --git a/drivers/net/wireless/bcmdhd/dhd_linux_sched.c b/drivers/net/wireless/bcmdhd/dhd_linux_sched.c +new file mode 100644 +index 00000000..290caf7e +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_linux_sched.c +@@ -0,0 +1,39 @@ ++/* ++ * Expose some of the kernel scheduler routines ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhd_linux_sched.c 291086 2011-10-21 01:17:24Z $ ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++int setScheduler(struct task_struct *p, int policy, struct sched_param *param) ++{ ++ int rc = 0; ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) ++ rc = sched_setscheduler(p, policy, param); ++#endif /* LinuxVer */ ++ return rc; ++} +diff --git a/drivers/net/wireless/bcmdhd/dhd_proto.h b/drivers/net/wireless/bcmdhd/dhd_proto.h +new file mode 100644 +index 00000000..09d54680 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_proto.h +@@ -0,0 +1,113 @@ ++/* ++ * Header file describing the internal (inter-module) DHD interfaces. ++ * ++ * Provides type definitions and function prototypes used to link the ++ * DHD OS, bus, and protocol modules. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhd_proto.h 343390 2012-07-06 22:34:19Z $ ++ */ ++ ++#ifndef _dhd_proto_h_ ++#define _dhd_proto_h_ ++ ++#include ++#include ++ ++#ifndef IOCTL_RESP_TIMEOUT ++#define IOCTL_RESP_TIMEOUT 2000 /* In milli second default value for Production FW */ ++#endif /* IOCTL_RESP_TIMEOUT */ ++ ++/* ++ * Exported from the dhd protocol module (dhd_cdc, dhd_rndis) ++ */ ++ ++/* Linkage, sets prot link and updates hdrlen in pub */ ++extern int dhd_prot_attach(dhd_pub_t *dhdp); ++ ++/* Unlink, frees allocated protocol memory (including dhd_prot) */ ++extern void dhd_prot_detach(dhd_pub_t *dhdp); ++ ++/* Initialize protocol: sync w/dongle state. ++ * Sets dongle media info (iswl, drv_version, mac address). ++ */ ++extern int dhd_prot_init(dhd_pub_t *dhdp); ++ ++/* Stop protocol: sync w/dongle state. */ ++extern void dhd_prot_stop(dhd_pub_t *dhdp); ++#ifdef PROP_TXSTATUS ++extern int dhd_wlfc_init(dhd_pub_t *dhd); ++extern void dhd_wlfc_deinit(dhd_pub_t *dhd); ++#endif /* PROP_TXSTATUS */ ++ ++/* Add any protocol-specific data header. ++ * Caller must reserve prot_hdrlen prepend space. ++ */ ++extern void dhd_prot_hdrpush(dhd_pub_t *, int ifidx, void *txp); ++ ++/* Remove any protocol-specific data header. */ ++extern int dhd_prot_hdrpull(dhd_pub_t *, int *ifidx, void *rxp, uchar *buf, uint *len); ++ ++/* Use protocol to issue ioctl to dongle */ ++extern int dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len); ++ ++/* Handles a protocol control response asynchronously */ ++extern int dhd_prot_ctl_complete(dhd_pub_t *dhd); ++ ++/* Check for and handle local prot-specific iovar commands */ ++extern int dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name, ++ void *params, int plen, void *arg, int len, bool set); ++ ++/* Add prot dump output to a buffer */ ++extern void dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf); ++ ++/* Update local copy of dongle statistics */ ++extern void dhd_prot_dstats(dhd_pub_t *dhdp); ++ ++extern int dhd_ioctl(dhd_pub_t * dhd_pub, dhd_ioctl_t *ioc, void * buf, uint buflen); ++ ++extern int dhd_preinit_ioctls(dhd_pub_t *dhd); ++ ++#ifdef PROP_TXSTATUS ++extern int dhd_wlfc_enque_sendq(void* state, int prec, void* p); ++extern int dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx); ++extern void dhd_wlfc_cleanup(dhd_pub_t *dhd); ++#endif /* PROP_TXSTATUS */ ++ ++extern int dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf, ++ uint reorder_info_len, void **pkt, uint32 *free_buf_count); ++ ++ ++/******************************** ++ * For version-string expansion * ++ */ ++#if defined(BDC) ++#define DHD_PROTOCOL "bdc" ++#elif defined(CDC) ++#define DHD_PROTOCOL "cdc" ++#elif defined(RNDIS) ++#define DHD_PROTOCOL "rndis" ++#else ++#define DHD_PROTOCOL "unknown" ++#endif /* proto */ ++ ++#endif /* _dhd_proto_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c +new file mode 100644 +index 00000000..c6342934 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c +@@ -0,0 +1,7694 @@ ++/* ++ * DHD Bus Module for SDIO ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhd_sdio.c 378263 2013-01-11 03:03:05Z $ ++ */ ++ ++#include ++#include ++#include ++ ++#ifdef BCMEMBEDIMAGE ++#include BCMEMBEDIMAGE ++#endif /* BCMEMBEDIMAGE */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#if defined(DHD_DEBUG) ++#include ++#include ++#endif /* defined(DHD_DEBUG) */ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef DHDSDIO_MEM_DUMP_FNAME ++#define DHDSDIO_MEM_DUMP_FNAME "mem_dump" ++#endif ++ ++#define QLEN 256 /* bulk rx and tx queue lengths */ ++#define FCHI (QLEN - 10) ++#define FCLOW (FCHI / 2) ++#define PRIOMASK 7 ++ ++#define TXRETRIES 2 /* # of retries for tx frames */ ++ ++#define DHD_RXBOUND 50 /* Default for max rx frames in one scheduling */ ++ ++#define DHD_TXBOUND 20 /* Default for max tx frames in one scheduling */ ++ ++#define DHD_TXMINMAX 1 /* Max tx frames if rx still pending */ ++ ++#define MEMBLOCK 2048 /* Block size used for downloading of dongle image */ ++#define MAX_NVRAMBUF_SIZE 4096 /* max nvram buf size */ ++#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold biggest possible glom */ ++ ++#ifndef DHD_FIRSTREAD ++#define DHD_FIRSTREAD 32 ++#endif ++#if !ISPOWEROF2(DHD_FIRSTREAD) ++#error DHD_FIRSTREAD is not a power of 2! ++#endif ++ ++#ifdef BCMSDIOH_TXGLOM ++/* Total length of TX frame header for dongle protocol */ ++#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN + SDPCM_SWHEADER_LEN) ++/* Total length of RX frame for dongle protocol */ ++#else ++/* Total length of TX frame header for dongle protocol */ ++#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN) ++#endif ++ ++#define SDPCM_HDRLEN_RX (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN) ++ ++#ifdef SDTEST ++#define SDPCM_RESERVE (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN) ++#else ++#define SDPCM_RESERVE (SDPCM_HDRLEN + DHD_SDALIGN) ++#endif ++ ++/* Space for header read, limit for data packets */ ++#ifndef MAX_HDR_READ ++#define MAX_HDR_READ 32 ++#endif ++#if !ISPOWEROF2(MAX_HDR_READ) ++#error MAX_HDR_READ is not a power of 2! ++#endif ++ ++#define MAX_RX_DATASZ 2048 ++ ++/* Maximum milliseconds to wait for F2 to come up */ ++#define DHD_WAIT_F2RDY 3000 ++ ++/* Bump up limit on waiting for HT to account for first startup; ++ * if the image is doing a CRC calculation before programming the PMU ++ * for HT availability, it could take a couple hundred ms more, so ++ * max out at a 1 second (1000000us). ++ */ ++#if (PMU_MAX_TRANSITION_DLY <= 1000000) ++#undef PMU_MAX_TRANSITION_DLY ++#define PMU_MAX_TRANSITION_DLY 1000000 ++#endif ++ ++/* Value for ChipClockCSR during initial setup */ ++#define DHD_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ) ++#define DHD_INIT_CLKCTL2 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP) ++ ++/* Flags for SDH calls */ ++#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) ++ ++/* Packet free applicable unconditionally for sdio and sdspi. Conditional if ++ * bufpool was present for gspi bus. ++ */ ++#define PKTFREE2() if ((bus->bus != SPI_BUS) || bus->usebufpool) \ ++ PKTFREE(bus->dhd->osh, pkt, FALSE); ++DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep); ++#if defined(OOB_INTR_ONLY) ++extern void bcmsdh_set_irq(int flag); ++#endif /* defined(OOB_INTR_ONLY) */ ++#ifdef PROP_TXSTATUS ++extern void dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success); ++extern void dhd_wlfc_trigger_pktcommit(dhd_pub_t *dhd); ++#endif ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++DEFINE_MUTEX(_dhd_sdio_mutex_lock_); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ ++ ++#ifdef DHD_DEBUG ++/* Device console log buffer state */ ++#define CONSOLE_LINE_MAX 192 ++#define CONSOLE_BUFFER_MAX 2024 ++typedef struct dhd_console { ++ uint count; /* Poll interval msec counter */ ++ uint log_addr; /* Log struct address (fixed) */ ++ hndrte_log_t log; /* Log struct (host copy) */ ++ uint bufsize; /* Size of log buffer */ ++ uint8 *buf; /* Log buffer (host copy) */ ++ uint last; /* Last buffer read index */ ++} dhd_console_t; ++#endif /* DHD_DEBUG */ ++ ++#define REMAP_ENAB(bus) ((bus)->remap) ++#define REMAP_ISADDR(bus, a) (((a) >= ((bus)->orig_ramsize)) && ((a) < ((bus)->ramsize))) ++#define KSO_ENAB(bus) ((bus)->kso) ++#define SR_ENAB(bus) ((bus)->_srenab) ++#define SLPAUTO_ENAB(bus) ((SR_ENAB(bus)) && ((bus)->_slpauto)) ++#define MIN_RSRC_ADDR (SI_ENUM_BASE + 0x618) ++#define MIN_RSRC_SR 0x3 ++#define CORE_CAPEXT_ADDR (SI_ENUM_BASE + 0x64c) ++#define CORE_CAPEXT_SR_SUPPORTED_MASK (1 << 1) ++#define RCTL_MACPHY_DISABLE_MASK (1 << 26) ++#define RCTL_LOGIC_DISABLE_MASK (1 << 27) ++ ++#define OOB_WAKEUP_ENAB(bus) ((bus)->_oobwakeup) ++#define GPIO_DEV_SRSTATE 16 /* Host gpio17 mapped to device gpio0 SR state */ ++#define GPIO_DEV_SRSTATE_TIMEOUT 320000 /* 320ms */ ++#define GPIO_DEV_WAKEUP 17 /* Host gpio17 mapped to device gpio1 wakeup */ ++#define CC_CHIPCTRL2_GPIO1_WAKEUP (1 << 0) ++ ++#define CC_PMUCC3 (0x3) ++/* Private data for SDIO bus interaction */ ++typedef struct dhd_bus { ++ dhd_pub_t *dhd; ++ ++ bcmsdh_info_t *sdh; /* Handle for BCMSDH calls */ ++ si_t *sih; /* Handle for SI calls */ ++ char *vars; /* Variables (from CIS and/or other) */ ++ uint varsz; /* Size of variables buffer */ ++ uint32 sbaddr; /* Current SB window pointer (-1, invalid) */ ++ ++ sdpcmd_regs_t *regs; /* Registers for SDIO core */ ++ uint sdpcmrev; /* SDIO core revision */ ++ uint armrev; /* CPU core revision */ ++ uint ramrev; /* SOCRAM core revision */ ++ uint32 ramsize; /* Size of RAM in SOCRAM (bytes) */ ++ uint32 orig_ramsize; /* Size of RAM in SOCRAM (bytes) */ ++ uint32 srmemsize; /* Size of SRMEM */ ++ ++ uint32 bus; /* gSPI or SDIO bus */ ++ uint32 hostintmask; /* Copy of Host Interrupt Mask */ ++ uint32 intstatus; /* Intstatus bits (events) pending */ ++ bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */ ++ bool fcstate; /* State of dongle flow-control */ ++ ++ uint16 cl_devid; /* cached devid for dhdsdio_probe_attach() */ ++ char *fw_path; /* module_param: path to firmware image */ ++ char *nv_path; /* module_param: path to nvram vars file */ ++ const char *nvram_params; /* user specified nvram params. */ ++ ++ uint blocksize; /* Block size of SDIO transfers */ ++ uint roundup; /* Max roundup limit */ ++ ++ struct pktq txq; /* Queue length used for flow-control */ ++ uint8 flowcontrol; /* per prio flow control bitmask */ ++ uint8 tx_seq; /* Transmit sequence number (next) */ ++ uint8 tx_max; /* Maximum transmit sequence allowed */ ++ ++ uint8 hdrbuf[MAX_HDR_READ + DHD_SDALIGN]; ++ uint8 *rxhdr; /* Header of current rx frame (in hdrbuf) */ ++ uint16 nextlen; /* Next Read Len from last header */ ++ uint8 rx_seq; /* Receive sequence number (expected) */ ++ bool rxskip; /* Skip receive (awaiting NAK ACK) */ ++ ++ void *glomd; /* Packet containing glomming descriptor */ ++ void *glom; /* Packet chain for glommed superframe */ ++ uint glomerr; /* Glom packet read errors */ ++ ++ uint8 *rxbuf; /* Buffer for receiving control packets */ ++ uint rxblen; /* Allocated length of rxbuf */ ++ uint8 *rxctl; /* Aligned pointer into rxbuf */ ++ uint8 *databuf; /* Buffer for receiving big glom packet */ ++ uint8 *dataptr; /* Aligned pointer into databuf */ ++ uint rxlen; /* Length of valid data in buffer */ ++ ++ uint8 sdpcm_ver; /* Bus protocol reported by dongle */ ++ ++ bool intr; /* Use interrupts */ ++ bool poll; /* Use polling */ ++ bool ipend; /* Device interrupt is pending */ ++ bool intdis; /* Interrupts disabled by isr */ ++ uint intrcount; /* Count of device interrupt callbacks */ ++ uint lastintrs; /* Count as of last watchdog timer */ ++ uint spurious; /* Count of spurious interrupts */ ++ uint pollrate; /* Ticks between device polls */ ++ uint polltick; /* Tick counter */ ++ uint pollcnt; /* Count of active polls */ ++ ++#ifdef DHD_DEBUG ++ dhd_console_t console; /* Console output polling support */ ++ uint console_addr; /* Console address from shared struct */ ++#endif /* DHD_DEBUG */ ++ ++ uint regfails; /* Count of R_REG/W_REG failures */ ++ ++ uint clkstate; /* State of sd and backplane clock(s) */ ++ bool activity; /* Activity flag for clock down */ ++ int32 idletime; /* Control for activity timeout */ ++ int32 idlecount; /* Activity timeout counter */ ++ int32 idleclock; /* How to set bus driver when idle */ ++ int32 sd_divisor; /* Speed control to bus driver */ ++ int32 sd_mode; /* Mode control to bus driver */ ++ int32 sd_rxchain; /* If bcmsdh api accepts PKT chains */ ++ bool use_rxchain; /* If dhd should use PKT chains */ ++ bool sleeping; /* Is SDIO bus sleeping? */ ++ uint rxflow_mode; /* Rx flow control mode */ ++ bool rxflow; /* Is rx flow control on */ ++ uint prev_rxlim_hit; /* Is prev rx limit exceeded (per dpc schedule) */ ++ bool alp_only; /* Don't use HT clock (ALP only) */ ++ /* Field to decide if rx of control frames happen in rxbuf or lb-pool */ ++ bool usebufpool; ++ ++#ifdef SDTEST ++ /* external loopback */ ++ bool ext_loop; ++ uint8 loopid; ++ ++ /* pktgen configuration */ ++ uint pktgen_freq; /* Ticks between bursts */ ++ uint pktgen_count; /* Packets to send each burst */ ++ uint pktgen_print; /* Bursts between count displays */ ++ uint pktgen_total; /* Stop after this many */ ++ uint pktgen_minlen; /* Minimum packet data len */ ++ uint pktgen_maxlen; /* Maximum packet data len */ ++ uint pktgen_mode; /* Configured mode: tx, rx, or echo */ ++ uint pktgen_stop; /* Number of tx failures causing stop */ ++ ++ /* active pktgen fields */ ++ uint pktgen_tick; /* Tick counter for bursts */ ++ uint pktgen_ptick; /* Burst counter for printing */ ++ uint pktgen_sent; /* Number of test packets generated */ ++ uint pktgen_rcvd; /* Number of test packets received */ ++ uint pktgen_prev_time; /* Time at which previous stats where printed */ ++ uint pktgen_prev_sent; /* Number of test packets generated when ++ * previous stats were printed ++ */ ++ uint pktgen_prev_rcvd; /* Number of test packets received when ++ * previous stats were printed ++ */ ++ uint pktgen_fail; /* Number of failed send attempts */ ++ uint16 pktgen_len; /* Length of next packet to send */ ++#define PKTGEN_RCV_IDLE (0) ++#define PKTGEN_RCV_ONGOING (1) ++ uint16 pktgen_rcv_state; /* receive state */ ++ uint pktgen_rcvd_rcvsession; /* test pkts rcvd per rcv session. */ ++#endif /* SDTEST */ ++ ++ /* Some additional counters */ ++ uint tx_sderrs; /* Count of tx attempts with sd errors */ ++ uint fcqueued; /* Tx packets that got queued */ ++ uint rxrtx; /* Count of rtx requests (NAK to dongle) */ ++ uint rx_toolong; /* Receive frames too long to receive */ ++ uint rxc_errors; /* SDIO errors when reading control frames */ ++ uint rx_hdrfail; /* SDIO errors on header reads */ ++ uint rx_badhdr; /* Bad received headers (roosync?) */ ++ uint rx_badseq; /* Mismatched rx sequence number */ ++ uint fc_rcvd; /* Number of flow-control events received */ ++ uint fc_xoff; /* Number which turned on flow-control */ ++ uint fc_xon; /* Number which turned off flow-control */ ++ uint rxglomfail; /* Failed deglom attempts */ ++ uint rxglomframes; /* Number of glom frames (superframes) */ ++ uint rxglompkts; /* Number of packets from glom frames */ ++ uint f2rxhdrs; /* Number of header reads */ ++ uint f2rxdata; /* Number of frame data reads */ ++ uint f2txdata; /* Number of f2 frame writes */ ++ uint f1regdata; /* Number of f1 register accesses */ ++ ++ uint8 *ctrl_frame_buf; ++ uint32 ctrl_frame_len; ++ bool ctrl_frame_stat; ++ uint32 rxint_mode; /* rx interrupt mode */ ++ bool remap; /* Contiguous 1MB RAM: 512K socram + 512K devram ++ * Available with socram rev 16 ++ * Remap region not DMA-able ++ */ ++ bool kso; ++ bool _slpauto; ++ bool _oobwakeup; ++ bool _srenab; ++ bool readframes; ++ bool reqbussleep; ++ uint32 resetinstr; ++ uint32 dongle_ram_base; ++#ifdef BCMSDIOH_TXGLOM ++ void *glom_pkt_arr[SDPCM_MAXGLOM_SIZE]; /* Array of pkts for glomming */ ++ uint16 glom_cnt; /* Number of pkts in the glom array */ ++ uint16 glom_total_len; /* Total length of pkts in glom array */ ++ bool glom_enable; /* Flag to indicate whether tx glom is enabled/disabled */ ++ uint8 glom_mode; /* Glom mode - 0-copy mode, 1 - Multi-descriptor mode */ ++ uint32 glomsize; /* Glom size limitation */ ++#endif ++} dhd_bus_t; ++ ++/* clkstate */ ++#define CLK_NONE 0 ++#define CLK_SDONLY 1 ++#define CLK_PENDING 2 /* Not used yet */ ++#define CLK_AVAIL 3 ++ ++#define DHD_NOPMU(dhd) (FALSE) ++ ++#ifdef DHD_DEBUG ++static int qcount[NUMPRIO]; ++static int tx_packets[NUMPRIO]; ++#endif /* DHD_DEBUG */ ++ ++/* Deferred transmit */ ++const uint dhd_deferred_tx = 1; ++ ++extern uint dhd_watchdog_ms; ++extern void dhd_os_wd_timer(void *bus, uint wdtick); ++ ++/* Tx/Rx bounds */ ++uint dhd_txbound; ++uint dhd_rxbound; ++uint dhd_txminmax = DHD_TXMINMAX; ++ ++/* override the RAM size if possible */ ++#define DONGLE_MIN_MEMSIZE (128 *1024) ++int dhd_dongle_memsize; ++ ++uint dhd_doflow = TRUE; ++uint dhd_dpcpoll = FALSE; ++static bool dhd_alignctl; ++ ++static bool sd1idle; ++ ++static bool retrydata; ++#define RETRYCHAN(chan) (((chan) == SDPCM_EVENT_CHANNEL) || retrydata) ++ ++#if defined(SDIO_CRC_ERROR_FIX) ++static uint watermark = 48; ++static uint mesbusyctrl = 80; ++#else ++static const uint watermark = 8; ++static const uint mesbusyctrl = 0; ++#endif ++static const uint firstread = DHD_FIRSTREAD; ++ ++#define HDATLEN (firstread - (SDPCM_HDRLEN)) ++ ++/* Retry count for register access failures */ ++static const uint retry_limit = 2; ++ ++/* Force even SD lengths (some host controllers mess up on odd bytes) */ ++static bool forcealign; ++ ++#define ALIGNMENT 4 ++ ++#if defined(OOB_INTR_ONLY) && defined(HW_OOB) ++extern void bcmsdh_enable_hw_oob_intr(void *sdh, bool enable); ++#endif ++ ++#if defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) ++#error OOB_INTR_ONLY is NOT working with SDIO_ISR_THREAD ++#endif /* defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) */ ++#define PKTALIGN(osh, p, len, align) \ ++ do { \ ++ uint datalign; \ ++ datalign = (uintptr)PKTDATA((osh), (p)); \ ++ datalign = ROUNDUP(datalign, (align)) - datalign; \ ++ ASSERT(datalign < (align)); \ ++ ASSERT(PKTLEN((osh), (p)) >= ((len) + datalign)); \ ++ if (datalign) \ ++ PKTPULL((osh), (p), datalign); \ ++ PKTSETLEN((osh), (p), (len)); \ ++ } while (0) ++ ++/* Limit on rounding up frames */ ++static const uint max_roundup = 512; ++ ++/* Try doing readahead */ ++static bool dhd_readahead; ++ ++ ++/* To check if there's window offered */ ++#define DATAOK(bus) \ ++ (((uint8)(bus->tx_max - bus->tx_seq) > 1) && \ ++ (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0)) ++ ++/* To check if there's window offered for ctrl frame */ ++#define TXCTLOK(bus) \ ++ (((uint8)(bus->tx_max - bus->tx_seq) != 0) && \ ++ (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0)) ++ ++/* Number of pkts available in dongle for data RX */ ++#define DATABUFCNT(bus) \ ++ ((uint8)(bus->tx_max - bus->tx_seq) - 1) ++ ++/* Macros to get register read/write status */ ++/* NOTE: these assume a local dhdsdio_bus_t *bus! */ ++#define R_SDREG(regvar, regaddr, retryvar) \ ++do { \ ++ retryvar = 0; \ ++ do { \ ++ regvar = R_REG(bus->dhd->osh, regaddr); \ ++ } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \ ++ if (retryvar) { \ ++ bus->regfails += (retryvar-1); \ ++ if (retryvar > retry_limit) { \ ++ DHD_ERROR(("%s: FAILED" #regvar "READ, LINE %d\n", \ ++ __FUNCTION__, __LINE__)); \ ++ regvar = 0; \ ++ } \ ++ } \ ++} while (0) ++ ++#define W_SDREG(regval, regaddr, retryvar) \ ++do { \ ++ retryvar = 0; \ ++ do { \ ++ W_REG(bus->dhd->osh, regaddr, regval); \ ++ } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \ ++ if (retryvar) { \ ++ bus->regfails += (retryvar-1); \ ++ if (retryvar > retry_limit) \ ++ DHD_ERROR(("%s: FAILED REGISTER WRITE, LINE %d\n", \ ++ __FUNCTION__, __LINE__)); \ ++ } \ ++} while (0) ++ ++#define BUS_WAKE(bus) \ ++ do { \ ++ bus->idlecount = 0; \ ++ if ((bus)->sleeping) \ ++ dhdsdio_bussleep((bus), FALSE); \ ++ } while (0); ++ ++/* ++ * pktavail interrupts from dongle to host can be managed in 3 different ways ++ * whenever there is a packet available in dongle to transmit to host. ++ * ++ * Mode 0: Dongle writes the software host mailbox and host is interrupted. ++ * Mode 1: (sdiod core rev >= 4) ++ * Device sets a new bit in the intstatus whenever there is a packet ++ * available in fifo. Host can't clear this specific status bit until all the ++ * packets are read from the FIFO. No need to ack dongle intstatus. ++ * Mode 2: (sdiod core rev >= 4) ++ * Device sets a bit in the intstatus, and host acks this by writing ++ * one to this bit. Dongle won't generate anymore packet interrupts ++ * until host reads all the packets from the dongle and reads a zero to ++ * figure that there are no more packets. No need to disable host ints. ++ * Need to ack the intstatus. ++ */ ++ ++#define SDIO_DEVICE_HMB_RXINT 0 /* default old way */ ++#define SDIO_DEVICE_RXDATAINT_MODE_0 1 /* from sdiod rev 4 */ ++#define SDIO_DEVICE_RXDATAINT_MODE_1 2 /* from sdiod rev 4 */ ++ ++ ++#define FRAME_AVAIL_MASK(bus) \ ++ ((bus->rxint_mode == SDIO_DEVICE_HMB_RXINT) ? I_HMB_FRAME_IND : I_XMTDATA_AVAIL) ++ ++#define DHD_BUS SDIO_BUS ++ ++#define PKT_AVAILABLE(bus, intstatus) ((intstatus) & (FRAME_AVAIL_MASK(bus))) ++ ++#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) ++ ++#define GSPI_PR55150_BAILOUT ++ ++#ifdef SDTEST ++static void dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq); ++static void dhdsdio_sdtest_set(dhd_bus_t *bus, uint count); ++#endif ++ ++#ifdef DHD_DEBUG ++static int dhdsdio_checkdied(dhd_bus_t *bus, char *data, uint size); ++static int dhd_serialconsole(dhd_bus_t *bus, bool get, bool enable, int *bcmerror); ++#endif /* DHD_DEBUG */ ++ ++static int dhdsdio_devcap_set(dhd_bus_t *bus, uint8 cap); ++static int dhdsdio_download_state(dhd_bus_t *bus, bool enter); ++ ++static void dhdsdio_release(dhd_bus_t *bus, osl_t *osh); ++static void dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh); ++static void dhdsdio_disconnect(void *ptr); ++static bool dhdsdio_chipmatch(uint16 chipid); ++static bool dhdsdio_probe_attach(dhd_bus_t *bus, osl_t *osh, void *sdh, ++ void * regsva, uint16 devid); ++static bool dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh); ++static bool dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh); ++static void dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation, ++ bool reset_flag); ++ ++static void dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size); ++static int dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, ++ uint8 *buf, uint nbytes, ++ void *pkt, bcmsdh_cmplt_fn_t complete, void *handle); ++static int dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, ++ uint8 *buf, uint nbytes, ++ void *pkt, bcmsdh_cmplt_fn_t complete, void *handle); ++#ifdef BCMSDIOH_TXGLOM ++static void dhd_bcmsdh_glom_post(dhd_bus_t *bus, uint8 *frame, uint len); ++static void dhd_bcmsdh_glom_clear(dhd_bus_t *bus); ++#endif ++ ++static bool dhdsdio_download_firmware(dhd_bus_t *bus, osl_t *osh, void *sdh); ++static int _dhdsdio_download_firmware(dhd_bus_t *bus); ++ ++static int dhdsdio_download_code_file(dhd_bus_t *bus, char *image_path); ++static int dhdsdio_download_nvram(dhd_bus_t *bus); ++#ifdef BCMEMBEDIMAGE ++static int dhdsdio_download_code_array(dhd_bus_t *bus); ++#endif ++static int dhdsdio_bussleep(dhd_bus_t *bus, bool sleep); ++static int dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok); ++static uint8 dhdsdio_sleepcsr_get(dhd_bus_t *bus); ++ ++#ifdef WLMEDIA_HTSF ++#include ++extern uint32 dhd_get_htsf(void *dhd, int ifidx); ++#endif /* WLMEDIA_HTSF */ ++ ++static void ++dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size) ++{ ++ int32 min_size = DONGLE_MIN_MEMSIZE; ++ /* Restrict the memsize to user specified limit */ ++ DHD_ERROR(("user: Restrict the dongle ram size to %d, min accepted %d\n", ++ dhd_dongle_memsize, min_size)); ++ if ((dhd_dongle_memsize > min_size) && ++ (dhd_dongle_memsize < (int32)bus->orig_ramsize)) ++ bus->ramsize = dhd_dongle_memsize; ++} ++ ++static int ++dhdsdio_set_siaddr_window(dhd_bus_t *bus, uint32 address) ++{ ++ int err = 0; ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, ++ (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err); ++ if (!err) ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID, ++ (address >> 16) & SBSDIO_SBADDRMID_MASK, &err); ++ if (!err) ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH, ++ (address >> 24) & SBSDIO_SBADDRHIGH_MASK, &err); ++ return err; ++} ++ ++ ++#ifdef USE_OOB_GPIO1 ++static int ++dhdsdio_oobwakeup_init(dhd_bus_t *bus) ++{ ++ uint32 val, addr, data; ++ ++ bcmsdh_gpioouten(bus->sdh, GPIO_DEV_WAKEUP); ++ ++ addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr); ++ data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data); ++ ++ /* Set device for gpio1 wakeup */ ++ bcmsdh_reg_write(bus->sdh, addr, 4, 2); ++ val = bcmsdh_reg_read(bus->sdh, data, 4); ++ val |= CC_CHIPCTRL2_GPIO1_WAKEUP; ++ bcmsdh_reg_write(bus->sdh, data, 4, val); ++ ++ bus->_oobwakeup = TRUE; ++ ++ return 0; ++} ++#endif /* USE_OOB_GPIO1 */ ++ ++/* ++ * Query if FW is in SR mode ++ */ ++static bool ++dhdsdio_sr_cap(dhd_bus_t *bus) ++{ ++ bool cap = FALSE; ++ uint32 min = 0, core_capext, addr, data; ++ if (bus->sih->chip == BCM4324_CHIP_ID) { ++ addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr); ++ data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data); ++ bcmsdh_reg_write(bus->sdh, addr, 4, 3); ++ core_capext = bcmsdh_reg_read(bus->sdh, data, 4); ++ } else if (bus->sih->chip == BCM4330_CHIP_ID) { ++ core_capext = FALSE; ++ } else if (bus->sih->chip == BCM4335_CHIP_ID) { ++ core_capext = TRUE; ++ } else { ++ core_capext = bcmsdh_reg_read(bus->sdh, CORE_CAPEXT_ADDR, 4); ++ core_capext = (core_capext & CORE_CAPEXT_SR_SUPPORTED_MASK); ++ } ++ if (!(core_capext)) ++ return FALSE; ++ ++ if (bus->sih->chip == BCM4324_CHIP_ID) { ++ /* FIX: Should change to query SR control register instead */ ++ min = bcmsdh_reg_read(bus->sdh, MIN_RSRC_ADDR, 4); ++ if (min == MIN_RSRC_SR) ++ cap = TRUE; ++ } else if (bus->sih->chip == BCM4335_CHIP_ID) { ++ uint32 enabval = 0; ++ addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr); ++ data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data); ++ bcmsdh_reg_write(bus->sdh, addr, 4, CC_PMUCC3); ++ enabval = bcmsdh_reg_read(bus->sdh, data, 4); ++ ++ if (enabval) ++ cap = TRUE; ++ } else { ++ data = bcmsdh_reg_read(bus->sdh, ++ SI_ENUM_BASE + OFFSETOF(chipcregs_t, retention_ctl), 4); ++ if ((data & (RCTL_MACPHY_DISABLE_MASK | RCTL_LOGIC_DISABLE_MASK)) == 0) ++ cap = TRUE; ++ } ++ ++ return cap; ++} ++ ++static int ++dhdsdio_srwar_init(dhd_bus_t *bus) ++{ ++ ++ bcmsdh_gpio_init(bus->sdh); ++ ++#ifdef USE_OOB_GPIO1 ++ dhdsdio_oobwakeup_init(bus); ++#endif ++ ++ ++ return 0; ++} ++ ++static int ++dhdsdio_sr_init(dhd_bus_t *bus) ++{ ++ uint8 val; ++ int err = 0; ++ ++ if ((bus->sih->chip == BCM4334_CHIP_ID) && (bus->sih->chiprev == 2)) ++ dhdsdio_srwar_init(bus); ++ ++ val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_WAKEUPCTRL, NULL); ++ val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT; ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_WAKEUPCTRL, ++ 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT, &err); ++ val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_WAKEUPCTRL, NULL); ++ ++ /* Add CMD14 Support */ ++ dhdsdio_devcap_set(bus, ++ (SDIOD_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | SDIOD_CCCR_BRCM_CARDCAP_CMD14_EXT)); ++ ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_CHIPCLKCSR, SBSDIO_FORCE_HT, &err); ++ ++ bus->_slpauto = dhd_slpauto ? TRUE : FALSE; ++ ++ bus->_srenab = TRUE; ++ ++ return 0; ++} ++ ++/* ++ * FIX: Be sure KSO bit is enabled ++ * Currently, it's defaulting to 0 which should be 1. ++ */ ++static int ++dhdsdio_clk_kso_init(dhd_bus_t *bus) ++{ ++ uint8 val; ++ int err = 0; ++ ++ /* set flag */ ++ bus->kso = TRUE; ++ ++ /* ++ * Enable KeepSdioOn (KSO) bit for normal operation ++ * Default is 0 (4334A0) so set it. Fixed in B0. ++ */ ++ val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, NULL); ++ if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) { ++ val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, val, &err); ++ if (err) ++ DHD_ERROR(("%s: SBSDIO_FUNC1_SLEEPCSR err: 0x%x\n", __FUNCTION__, err)); ++ } ++ ++ return 0; ++} ++ ++#define KSO_DBG(x) ++#define MAX_KSO_ATTEMPTS 64 ++static int ++dhdsdio_clk_kso_enab(dhd_bus_t *bus, bool on) ++{ ++ uint8 wr_val = 0, rd_val, cmp_val, bmask; ++ int err = 0; ++ int try_cnt = 0; ++ ++ KSO_DBG(("%s> op:%s\n", __FUNCTION__, (on ? "KSO_SET" : "KSO_CLR"))); ++ ++ wr_val |= (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); ++ ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); ++ ++ if (on) { ++ cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK | SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK; ++ bmask = cmp_val; ++ ++ msleep(3); ++ ++ } else { ++ /* Put device to sleep, turn off KSO */ ++ cmp_val = 0; ++ bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK; ++ } ++ ++ do { ++ rd_val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, &err); ++ if (((rd_val & bmask) == cmp_val) && !err) ++ break; ++ ++ KSO_DBG(("%s> KSO wr/rd retry:%d, ERR:%x \n", __FUNCTION__, try_cnt, err)); ++ OSL_DELAY(50); ++ ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); ++ ++ } while (try_cnt++ < MAX_KSO_ATTEMPTS); ++ ++ ++ if (try_cnt > 1) { ++ KSO_DBG(("%s> op:%s, try_cnt:%d, rd_val:%x, ERR:%x \n", ++ __FUNCTION__, (on ? "KSO_SET" : "KSO_CLR"), try_cnt, rd_val, err)); ++ } ++ ++ if (try_cnt > MAX_KSO_ATTEMPTS) { ++ DHD_ERROR(("%s> op:%s, ERROR: try_cnt:%d, rd_val:%x, ERR:%x \n", ++ __FUNCTION__, (on ? "KSO_SET" : "KSO_CLR"), try_cnt, rd_val, err)); ++ } ++ return err; ++} ++ ++static int ++dhdsdio_clk_kso_iovar(dhd_bus_t *bus, bool on) ++{ ++ int err = 0; ++ ++ if (on == FALSE) { ++ ++ BUS_WAKE(bus); ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ ++ DHD_ERROR(("%s: KSO disable clk: 0x%x\n", __FUNCTION__, ++ bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_CHIPCLKCSR, &err))); ++ dhdsdio_clk_kso_enab(bus, FALSE); ++ } else { ++ DHD_ERROR(("%s: KSO enable\n", __FUNCTION__)); ++ ++ /* Make sure we have SD bus access */ ++ if (bus->clkstate == CLK_NONE) { ++ DHD_ERROR(("%s: Request SD clk\n", __FUNCTION__)); ++ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); ++ } ++ ++ /* Double-write to be safe in case transition of AOS */ ++ dhdsdio_clk_kso_enab(bus, TRUE); ++ dhdsdio_clk_kso_enab(bus, TRUE); ++ OSL_DELAY(4000); ++ ++ /* Wait for device ready during transition to wake-up */ ++ SPINWAIT(((dhdsdio_sleepcsr_get(bus)) != ++ (SBSDIO_FUNC1_SLEEPCSR_KSO_MASK | ++ SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)), ++ (10000)); ++ ++ DHD_ERROR(("%s: sleepcsr: 0x%x\n", __FUNCTION__, ++ dhdsdio_sleepcsr_get(bus))); ++ } ++ ++ bus->kso = on; ++ BCM_REFERENCE(err); ++ ++ return 0; ++} ++ ++static uint8 ++dhdsdio_sleepcsr_get(dhd_bus_t *bus) ++{ ++ int err = 0; ++ uint8 val = 0; ++ ++ val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, &err); ++ if (err) ++ DHD_TRACE(("Failed to read SLEEPCSR: %d\n", err)); ++ ++ return val; ++} ++ ++uint8 ++dhdsdio_devcap_get(dhd_bus_t *bus) ++{ ++ return bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_BRCM_CARDCAP, NULL); ++} ++ ++static int ++dhdsdio_devcap_set(dhd_bus_t *bus, uint8 cap) ++{ ++ int err = 0; ++ ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_BRCM_CARDCAP, cap, &err); ++ if (err) ++ DHD_ERROR(("%s: devcap set err: 0x%x\n", __FUNCTION__, err)); ++ ++ return 0; ++} ++ ++static int ++dhdsdio_clk_devsleep_iovar(dhd_bus_t *bus, bool on) ++{ ++ int err = 0, retry; ++ uint8 val; ++ ++ retry = 0; ++ if (on == TRUE) { ++ /* Enter Sleep */ ++ ++ /* Be sure we request clk before going to sleep ++ * so we can wake-up with clk request already set ++ * else device can go back to sleep immediately ++ */ ++ if (!SLPAUTO_ENAB(bus)) ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ else { ++ val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); ++ if ((val & SBSDIO_CSR_MASK) == 0) { ++ DHD_ERROR(("%s: No clock before enter sleep:0x%x\n", ++ __FUNCTION__, val)); ++ ++ /* Reset clock request */ ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, ++ SBSDIO_ALP_AVAIL_REQ, &err); ++ DHD_ERROR(("%s: clock before sleep:0x%x\n", __FUNCTION__, ++ bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_CHIPCLKCSR, &err))); ++ } ++ } ++ ++ DHD_TRACE(("%s: clk before sleep: 0x%x\n", __FUNCTION__, ++ bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_CHIPCLKCSR, &err))); ++#ifdef USE_CMD14 ++ err = bcmsdh_sleep(bus->sdh, TRUE); ++#else ++ err = dhdsdio_clk_kso_enab(bus, FALSE); ++ if (OOB_WAKEUP_ENAB(bus)) ++ err = bcmsdh_gpioout(bus->sdh, GPIO_DEV_WAKEUP, FALSE); /* GPIO_1 is off */ ++#endif ++ } else { ++ /* Exit Sleep */ ++ /* Make sure we have SD bus access */ ++ if (bus->clkstate == CLK_NONE) { ++ DHD_TRACE(("%s: Request SD clk\n", __FUNCTION__)); ++ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); ++ } ++ ++ if ((bus->sih->chip == BCM4334_CHIP_ID) && (bus->sih->chiprev == 2)) { ++ SPINWAIT((bcmsdh_gpioin(bus->sdh, GPIO_DEV_SRSTATE) != TRUE), ++ GPIO_DEV_SRSTATE_TIMEOUT); ++ ++ if (bcmsdh_gpioin(bus->sdh, GPIO_DEV_SRSTATE) == FALSE) { ++ DHD_ERROR(("ERROR: GPIO_DEV_SRSTATE still low!\n")); ++ } ++ } ++#ifdef USE_CMD14 ++ err = bcmsdh_sleep(bus->sdh, FALSE); ++ if (SLPAUTO_ENAB(bus) && (err != 0)) { ++ OSL_DELAY(10000); ++ DHD_TRACE(("%s: Resync device sleep\n", __FUNCTION__)); ++ ++ /* Toggle sleep to resync with host and device */ ++ err = bcmsdh_sleep(bus->sdh, TRUE); ++ OSL_DELAY(10000); ++ err = bcmsdh_sleep(bus->sdh, FALSE); ++ ++ if (err) { ++ OSL_DELAY(10000); ++ DHD_ERROR(("%s: CMD14 exit failed again!\n", __FUNCTION__)); ++ ++ /* Toggle sleep to resync with host and device */ ++ err = bcmsdh_sleep(bus->sdh, TRUE); ++ OSL_DELAY(10000); ++ err = bcmsdh_sleep(bus->sdh, FALSE); ++ if (err) { ++ DHD_ERROR(("%s: CMD14 exit failed twice!\n", __FUNCTION__)); ++ DHD_ERROR(("%s: FATAL: Device non-response!\n", ++ __FUNCTION__)); ++ err = 0; ++ } ++ } ++ } ++#else ++ if (OOB_WAKEUP_ENAB(bus)) ++ err = bcmsdh_gpioout(bus->sdh, GPIO_DEV_WAKEUP, TRUE); /* GPIO_1 is on */ ++ ++ do { ++ err = dhdsdio_clk_kso_enab(bus, TRUE); ++ if (err) ++ OSL_DELAY(10000); ++ } while ((err != 0) && (++retry < 3)); ++ ++ if (err != 0) { ++ DHD_ERROR(("ERROR: kso set failed retry: %d\n", retry)); ++ err = 0; /* continue anyway */ ++ } ++#endif /* !USE_CMD14 */ ++ ++ if (err == 0) { ++ uint8 csr; ++ ++ /* Wait for device ready during transition to wake-up */ ++ SPINWAIT((((csr = dhdsdio_sleepcsr_get(bus)) & ++ SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK) != ++ (SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)), (20000)); ++ ++ DHD_TRACE(("%s: ExitSleep sleepcsr: 0x%x\n", __FUNCTION__, csr)); ++ ++ if (!(csr & SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)) { ++ DHD_ERROR(("%s:ERROR: ExitSleep device NOT Ready! 0x%x\n", ++ __FUNCTION__, csr)); ++ err = BCME_NODEVICE; ++ } ++ ++ SPINWAIT((((csr = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_CHIPCLKCSR, &err)) & SBSDIO_HT_AVAIL) != ++ (SBSDIO_HT_AVAIL)), (10000)); ++ ++ } ++ } ++ ++ /* Update if successful */ ++ if (err == 0) ++ bus->kso = on ? FALSE : TRUE; ++ else { ++ DHD_ERROR(("%s: Sleep request failed: on:%d err:%d\n", __FUNCTION__, on, err)); ++ if (!on && retry > 2) ++ bus->kso = TRUE; ++ } ++ ++ return err; ++} ++ ++/* Turn backplane clock on or off */ ++static int ++dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok) ++{ ++#define HT_AVAIL_ERROR_MAX 10 ++ static int ht_avail_error = 0; ++ int err; ++ uint8 clkctl, clkreq, devctl; ++ bcmsdh_info_t *sdh; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++#if defined(OOB_INTR_ONLY) ++ pendok = FALSE; ++#endif /* defined(OOB_INTR_ONLY) */ ++ clkctl = 0; ++ sdh = bus->sdh; ++ ++ ++ if (!KSO_ENAB(bus)) ++ return BCME_OK; ++ ++ if (SLPAUTO_ENAB(bus)) { ++ bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY); ++ return BCME_OK; ++ } ++ ++ if (on) { ++ /* Request HT Avail */ ++ clkreq = bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; ++ ++ ++ ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err); ++ if (err) { ++ ht_avail_error++; ++ if (ht_avail_error < HT_AVAIL_ERROR_MAX) { ++ DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err)); ++ } ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) ++ else if (ht_avail_error == HT_AVAIL_ERROR_MAX) { ++ dhd_os_send_hang_message(bus->dhd); ++ } ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) */ ++ return BCME_ERROR; ++ } else { ++ ht_avail_error = 0; ++ } ++ ++ if (pendok && ++ ((bus->sih->buscoretype == PCMCIA_CORE_ID) && (bus->sih->buscorerev == 9))) { ++ uint32 dummy, retries; ++ R_SDREG(dummy, &bus->regs->clockctlstatus, retries); ++ BCM_REFERENCE(dummy); ++ } ++ ++ /* Check current status */ ++ clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); ++ if (err) { ++ DHD_ERROR(("%s: HT Avail read error: %d\n", __FUNCTION__, err)); ++ return BCME_ERROR; ++ } ++ ++ /* Go to pending and await interrupt if appropriate */ ++ if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) { ++ /* Allow only clock-available interrupt */ ++ devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); ++ if (err) { ++ DHD_ERROR(("%s: Devctl access error setting CA: %d\n", ++ __FUNCTION__, err)); ++ return BCME_ERROR; ++ } ++ ++ devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); ++ DHD_INFO(("CLKCTL: set PENDING\n")); ++ bus->clkstate = CLK_PENDING; ++ return BCME_OK; ++ } else if (bus->clkstate == CLK_PENDING) { ++ /* Cancel CA-only interrupt filter */ ++ devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); ++ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); ++ } ++ ++ /* Otherwise, wait here (polling) for HT Avail */ ++ if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { ++ SPINWAIT_SLEEP(sdioh_spinwait_sleep, ++ ((clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_CHIPCLKCSR, &err)), ++ !SBSDIO_CLKAV(clkctl, bus->alp_only)), PMU_MAX_TRANSITION_DLY); ++ } ++ if (err) { ++ DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err)); ++ return BCME_ERROR; ++ } ++ if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { ++ DHD_ERROR(("%s: HT Avail timeout (%d): clkctl 0x%02x\n", ++ __FUNCTION__, PMU_MAX_TRANSITION_DLY, clkctl)); ++ return BCME_ERROR; ++ } ++ ++ /* Mark clock available */ ++ bus->clkstate = CLK_AVAIL; ++ DHD_INFO(("CLKCTL: turned ON\n")); ++ ++#if defined(DHD_DEBUG) ++ if (bus->alp_only == TRUE) { ++#if !defined(BCMLXSDMMC) ++ if (!SBSDIO_ALPONLY(clkctl)) { ++ DHD_ERROR(("%s: HT Clock, when ALP Only\n", __FUNCTION__)); ++ } ++#endif /* !defined(BCMLXSDMMC) */ ++ } else { ++ if (SBSDIO_ALPONLY(clkctl)) { ++ DHD_ERROR(("%s: HT Clock should be on.\n", __FUNCTION__)); ++ } ++ } ++#endif /* defined (DHD_DEBUG) */ ++ ++ bus->activity = TRUE; ++#ifdef DHD_USE_IDLECOUNT ++ bus->idlecount = 0; ++#endif /* DHD_USE_IDLECOUNT */ ++ } else { ++ clkreq = 0; ++ if (bus->clkstate == CLK_PENDING) { ++ /* Cancel CA-only interrupt filter */ ++ devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); ++ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); ++ } ++ ++ bus->clkstate = CLK_SDONLY; ++ if (!SR_ENAB(bus)) { ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err); ++ DHD_INFO(("CLKCTL: turned OFF\n")); ++ if (err) { ++ DHD_ERROR(("%s: Failed access turning clock off: %d\n", ++ __FUNCTION__, err)); ++ return BCME_ERROR; ++ } ++ } ++ } ++ return BCME_OK; ++} ++ ++/* Change idle/active SD state */ ++static int ++dhdsdio_sdclk(dhd_bus_t *bus, bool on) ++{ ++ int err; ++ int32 iovalue; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (on) { ++ if (bus->idleclock == DHD_IDLE_STOP) { ++ /* Turn on clock and restore mode */ ++ iovalue = 1; ++ err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0, ++ &iovalue, sizeof(iovalue), TRUE); ++ if (err) { ++ DHD_ERROR(("%s: error enabling sd_clock: %d\n", ++ __FUNCTION__, err)); ++ return BCME_ERROR; ++ } ++ ++ iovalue = bus->sd_mode; ++ err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0, ++ &iovalue, sizeof(iovalue), TRUE); ++ if (err) { ++ DHD_ERROR(("%s: error changing sd_mode: %d\n", ++ __FUNCTION__, err)); ++ return BCME_ERROR; ++ } ++ } else if (bus->idleclock != DHD_IDLE_ACTIVE) { ++ /* Restore clock speed */ ++ iovalue = bus->sd_divisor; ++ err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0, ++ &iovalue, sizeof(iovalue), TRUE); ++ if (err) { ++ DHD_ERROR(("%s: error restoring sd_divisor: %d\n", ++ __FUNCTION__, err)); ++ return BCME_ERROR; ++ } ++ } ++ bus->clkstate = CLK_SDONLY; ++ } else { ++ /* Stop or slow the SD clock itself */ ++ if ((bus->sd_divisor == -1) || (bus->sd_mode == -1)) { ++ DHD_TRACE(("%s: can't idle clock, divisor %d mode %d\n", ++ __FUNCTION__, bus->sd_divisor, bus->sd_mode)); ++ return BCME_ERROR; ++ } ++ if (bus->idleclock == DHD_IDLE_STOP) { ++ if (sd1idle) { ++ /* Change to SD1 mode and turn off clock */ ++ iovalue = 1; ++ err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0, ++ &iovalue, sizeof(iovalue), TRUE); ++ if (err) { ++ DHD_ERROR(("%s: error changing sd_clock: %d\n", ++ __FUNCTION__, err)); ++ return BCME_ERROR; ++ } ++ } ++ ++ iovalue = 0; ++ err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0, ++ &iovalue, sizeof(iovalue), TRUE); ++ if (err) { ++ DHD_ERROR(("%s: error disabling sd_clock: %d\n", ++ __FUNCTION__, err)); ++ return BCME_ERROR; ++ } ++ } else if (bus->idleclock != DHD_IDLE_ACTIVE) { ++ /* Set divisor to idle value */ ++ iovalue = bus->idleclock; ++ err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0, ++ &iovalue, sizeof(iovalue), TRUE); ++ if (err) { ++ DHD_ERROR(("%s: error changing sd_divisor: %d\n", ++ __FUNCTION__, err)); ++ return BCME_ERROR; ++ } ++ } ++ bus->clkstate = CLK_NONE; ++ } ++ ++ return BCME_OK; ++} ++ ++/* Transition SD and backplane clock readiness */ ++static int ++dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok) ++{ ++ int ret = BCME_OK; ++#ifdef DHD_DEBUG ++ uint oldstate = bus->clkstate; ++#endif /* DHD_DEBUG */ ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ /* Early exit if we're already there */ ++ if (bus->clkstate == target) { ++ if (target == CLK_AVAIL) { ++ dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); ++ bus->activity = TRUE; ++#ifdef DHD_USE_IDLECOUNT ++ bus->idlecount = 0; ++#endif /* DHD_USE_IDLECOUNT */ ++ } ++ return ret; ++ } ++ ++ switch (target) { ++ case CLK_AVAIL: ++ /* Make sure SD clock is available */ ++ if (bus->clkstate == CLK_NONE) ++ dhdsdio_sdclk(bus, TRUE); ++ /* Now request HT Avail on the backplane */ ++ ret = dhdsdio_htclk(bus, TRUE, pendok); ++ if (ret == BCME_OK) { ++ dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); ++ bus->activity = TRUE; ++#ifdef DHD_USE_IDLECOUNT ++ bus->idlecount = 0; ++#endif /* DHD_USE_IDLECOUNT */ ++ } ++ break; ++ ++ case CLK_SDONLY: ++ /* Remove HT request, or bring up SD clock */ ++ if (bus->clkstate == CLK_NONE) ++ ret = dhdsdio_sdclk(bus, TRUE); ++ else if (bus->clkstate == CLK_AVAIL) ++ ret = dhdsdio_htclk(bus, FALSE, FALSE); ++ else ++ DHD_ERROR(("dhdsdio_clkctl: request for %d -> %d\n", ++ bus->clkstate, target)); ++ if (ret == BCME_OK) { ++ dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); ++ } ++ break; ++ ++ case CLK_NONE: ++ /* Make sure to remove HT request */ ++ if (bus->clkstate == CLK_AVAIL) ++ ret = dhdsdio_htclk(bus, FALSE, FALSE); ++ /* Now remove the SD clock */ ++ ret = dhdsdio_sdclk(bus, FALSE); ++#ifdef DHD_DEBUG ++ if (dhd_console_ms == 0) ++#endif /* DHD_DEBUG */ ++ if (bus->poll == 0) ++ dhd_os_wd_timer(bus->dhd, 0); ++ break; ++ } ++#ifdef DHD_DEBUG ++ DHD_INFO(("dhdsdio_clkctl: %d -> %d\n", oldstate, bus->clkstate)); ++#endif /* DHD_DEBUG */ ++ ++ return ret; ++} ++ ++static int ++dhdsdio_bussleep(dhd_bus_t *bus, bool sleep) ++{ ++ int err = 0; ++ bcmsdh_info_t *sdh = bus->sdh; ++ sdpcmd_regs_t *regs = bus->regs; ++ uint retries = 0; ++ ++ DHD_INFO(("dhdsdio_bussleep: request %s (currently %s)\n", ++ (sleep ? "SLEEP" : "WAKE"), ++ (bus->sleeping ? "SLEEP" : "WAKE"))); ++ ++ /* Done if we're already in the requested state */ ++ if (sleep == bus->sleeping) ++ return BCME_OK; ++ ++ /* Going to sleep: set the alarm and turn off the lights... */ ++ if (sleep) { ++ /* Don't sleep if something is pending */ ++ if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq)) ++ return BCME_BUSY; ++ ++ ++ if (!SLPAUTO_ENAB(bus)) { ++ /* Disable SDIO interrupts (no longer interested) */ ++ bcmsdh_intr_disable(bus->sdh); ++ ++ /* Make sure the controller has the bus up */ ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ ++ /* Tell device to start using OOB wakeup */ ++ W_SDREG(SMB_USE_OOB, ®s->tosbmailbox, retries); ++ if (retries > retry_limit) ++ DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n")); ++ ++ /* Turn off our contribution to the HT clock request */ ++ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); ++ ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, ++ SBSDIO_FORCE_HW_CLKREQ_OFF, NULL); ++ ++ /* Isolate the bus */ ++ if (bus->sih->chip != BCM4329_CHIP_ID && ++ bus->sih->chip != BCM4319_CHIP_ID) { ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, ++ SBSDIO_DEVCTL_PADS_ISO, NULL); ++ } ++ } else { ++ /* Leave interrupts enabled since device can exit sleep and ++ * interrupt host ++ */ ++ err = dhdsdio_clk_devsleep_iovar(bus, TRUE /* sleep */); ++ } ++ ++ /* Change state */ ++ bus->sleeping = TRUE; ++ ++ } else { ++ /* Waking up: bus power up is ok, set local state */ ++ ++ if (!SLPAUTO_ENAB(bus)) { ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0, &err); ++ ++ /* Force pad isolation off if possible (in case power never toggled) */ ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, 0, NULL); ++ ++ ++ /* Make sure the controller has the bus up */ ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ ++ /* Send misc interrupt to indicate OOB not needed */ ++ W_SDREG(0, ®s->tosbmailboxdata, retries); ++ if (retries <= retry_limit) ++ W_SDREG(SMB_DEV_INT, ®s->tosbmailbox, retries); ++ ++ if (retries > retry_limit) ++ DHD_ERROR(("CANNOT SIGNAL CHIP TO CLEAR OOB!!\n")); ++ ++ /* Make sure we have SD bus access */ ++ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); ++ ++ /* Enable interrupts again */ ++ if (bus->intr && (bus->dhd->busstate == DHD_BUS_DATA)) { ++ bus->intdis = FALSE; ++ bcmsdh_intr_enable(bus->sdh); ++ } ++ } else { ++ err = dhdsdio_clk_devsleep_iovar(bus, FALSE /* wake */); ++ } ++ ++ if (err == 0) { ++ /* Change state */ ++ bus->sleeping = FALSE; ++ } ++ } ++ ++ return err; ++} ++ ++#if defined(OOB_INTR_ONLY) ++void ++dhd_enable_oob_intr(struct dhd_bus *bus, bool enable) ++{ ++#if defined(HW_OOB) ++ bcmsdh_enable_hw_oob_intr(bus->sdh, enable); ++#else ++ sdpcmd_regs_t *regs = bus->regs; ++ uint retries = 0; ++ ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ if (enable == TRUE) { ++ ++ /* Tell device to start using OOB wakeup */ ++ W_SDREG(SMB_USE_OOB, ®s->tosbmailbox, retries); ++ if (retries > retry_limit) ++ DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n")); ++ ++ } else { ++ /* Send misc interrupt to indicate OOB not needed */ ++ W_SDREG(0, ®s->tosbmailboxdata, retries); ++ if (retries <= retry_limit) ++ W_SDREG(SMB_DEV_INT, ®s->tosbmailbox, retries); ++ } ++ ++ /* Turn off our contribution to the HT clock request */ ++ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); ++#endif /* !defined(HW_OOB) */ ++} ++#endif /* defined(OOB_INTR_ONLY) */ ++ ++/* Writes a HW/SW header into the packet and sends it. */ ++/* Assumes: (a) header space already there, (b) caller holds lock */ ++static int ++dhdsdio_txpkt(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt, bool queue_only) ++{ ++ int ret; ++ osl_t *osh; ++ uint8 *frame; ++ uint16 len, pad1 = 0; ++ uint32 swheader; ++ uint retries = 0; ++ bcmsdh_info_t *sdh; ++ void *new; ++ int i; ++ int pkt_cnt; ++#ifdef BCMSDIOH_TXGLOM ++ uint8 *frame_tmp; ++#endif ++#ifdef WLMEDIA_HTSF ++ char *p; ++ htsfts_t *htsf_ts; ++#endif ++ ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ sdh = bus->sdh; ++ osh = bus->dhd->osh; ++ ++ if (bus->dhd->dongle_reset) { ++ ret = BCME_NOTREADY; ++ goto done; ++ } ++ ++ frame = (uint8*)PKTDATA(osh, pkt); ++ ++#ifdef WLMEDIA_HTSF ++ if (PKTLEN(osh, pkt) >= 100) { ++ p = PKTDATA(osh, pkt); ++ htsf_ts = (htsfts_t*) (p + HTSF_HOSTOFFSET + 12); ++ if (htsf_ts->magic == HTSFMAGIC) { ++ htsf_ts->c20 = get_cycles(); ++ htsf_ts->t20 = dhd_get_htsf(bus->dhd->info, 0); ++ } ++ } ++#endif /* WLMEDIA_HTSF */ ++ ++ /* Add alignment padding, allocate new packet if needed */ ++ if (!((uintptr)frame & 1) && (pad1 = ((uintptr)frame % DHD_SDALIGN))) { ++ if (PKTHEADROOM(osh, pkt) < pad1) { ++ DHD_INFO(("%s: insufficient headroom %d for %d pad1\n", ++ __FUNCTION__, (int)PKTHEADROOM(osh, pkt), pad1)); ++ bus->dhd->tx_realloc++; ++ new = PKTGET(osh, (PKTLEN(osh, pkt) + DHD_SDALIGN), TRUE); ++ if (!new) { ++ DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n", ++ __FUNCTION__, PKTLEN(osh, pkt) + DHD_SDALIGN)); ++ ret = BCME_NOMEM; ++ goto done; ++ } ++ ++ PKTALIGN(osh, new, PKTLEN(osh, pkt), DHD_SDALIGN); ++ bcopy(PKTDATA(osh, pkt), PKTDATA(osh, new), PKTLEN(osh, pkt)); ++ if (free_pkt) ++ PKTFREE(osh, pkt, TRUE); ++ /* free the pkt if canned one is not used */ ++ free_pkt = TRUE; ++ pkt = new; ++ frame = (uint8*)PKTDATA(osh, pkt); ++ ASSERT(((uintptr)frame % DHD_SDALIGN) == 0); ++ pad1 = 0; ++ } else { ++ PKTPUSH(osh, pkt, pad1); ++ frame = (uint8*)PKTDATA(osh, pkt); ++ ++ ASSERT((pad1 + SDPCM_HDRLEN) <= (int) PKTLEN(osh, pkt)); ++ bzero(frame, pad1 + SDPCM_HDRLEN); ++ } ++ } ++ ASSERT(pad1 < DHD_SDALIGN); ++ ++ /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ ++ len = (uint16)PKTLEN(osh, pkt); ++ *(uint16*)frame = htol16(len); ++ *(((uint16*)frame) + 1) = htol16(~len); ++ ++#ifdef BCMSDIOH_TXGLOM ++ if (bus->glom_enable) { ++ uint32 hwheader1 = 0, hwheader2 = 0, act_len = len; ++ ++ /* Software tag: channel, sequence number, data offset */ ++ swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | ++ ((bus->tx_seq + bus->glom_cnt) % SDPCM_SEQUENCE_WRAP) | ++ (((pad1 + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); ++ htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN); ++ htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN + sizeof(swheader)); ++ ++ if (queue_only) { ++ if (forcealign && (len & (ALIGNMENT - 1))) ++ len = ROUNDUP(len, ALIGNMENT); ++ /* Hardware extention tag */ ++ /* 2byte frame length, 1byte-, 1byte frame flag, ++ * 2byte-hdrlength, 2byte padlenght ++ */ ++ hwheader1 = (act_len - SDPCM_FRAMETAG_LEN) | (0 << 24); ++ hwheader2 = (len - act_len) << 16; ++ htol32_ua_store(hwheader1, frame + SDPCM_FRAMETAG_LEN); ++ htol32_ua_store(hwheader2, frame + SDPCM_FRAMETAG_LEN + 4); ++ /* Post the frame pointer to sdio glom array */ ++ dhd_bcmsdh_glom_post(bus, frame, len); ++ /* Save the pkt pointer in bus glom array */ ++ bus->glom_pkt_arr[bus->glom_cnt] = pkt; ++ bus->glom_total_len += len; ++ bus->glom_cnt++; ++ return BCME_OK; ++ } else { ++ /* Raise len to next SDIO block to eliminate tail command */ ++ if (bus->roundup && bus->blocksize && ++ ((bus->glom_total_len + len) > bus->blocksize)) { ++ uint16 pad2 = bus->blocksize - ++ ((bus->glom_total_len + len) % bus->blocksize); ++ if ((pad2 <= bus->roundup) && (pad2 < bus->blocksize)) { ++ len += pad2; ++ } else { ++ } ++ } else if ((bus->glom_total_len + len) % DHD_SDALIGN) { ++ len += DHD_SDALIGN ++ - ((bus->glom_total_len + len) % DHD_SDALIGN); ++ } ++ if (forcealign && (len & (ALIGNMENT - 1))) { ++ len = ROUNDUP(len, ALIGNMENT); ++ } ++ ++ /* Hardware extention tag */ ++ /* 2byte frame length, 1byte-, 1byte frame flag, ++ * 2byte-hdrlength, 2byte padlenght ++ */ ++ hwheader1 = (act_len - SDPCM_FRAMETAG_LEN) | (1 << 24); ++ hwheader2 = (len - act_len) << 16; ++ htol32_ua_store(hwheader1, frame + SDPCM_FRAMETAG_LEN); ++ htol32_ua_store(hwheader2, frame + SDPCM_FRAMETAG_LEN + 4); ++ ++ /* Post the frame pointer to sdio glom array */ ++ dhd_bcmsdh_glom_post(bus, frame, len); ++ /* Save the pkt pointer in bus glom array */ ++ bus->glom_pkt_arr[bus->glom_cnt] = pkt; ++ bus->glom_cnt++; ++ bus->glom_total_len += len; ++ ++ /* Update the total length on the first pkt */ ++ frame_tmp = (uint8*)PKTDATA(osh, bus->glom_pkt_arr[0]); ++ *(uint16*)frame_tmp = htol16(bus->glom_total_len); ++ *(((uint16*)frame_tmp) + 1) = htol16(~bus->glom_total_len); ++ } ++ } else ++#endif /* BCMSDIOH_TXGLOM */ ++ { ++ /* Software tag: channel, sequence number, data offset */ ++ swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq | ++ (((pad1 + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); ++ htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN); ++ htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); ++ ++#ifdef DHD_DEBUG ++ if (PKTPRIO(pkt) < ARRAYSIZE(tx_packets)) { ++ tx_packets[PKTPRIO(pkt)]++; ++ } ++ if (DHD_BYTES_ON() && ++ (((DHD_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) || ++ (DHD_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) { ++ prhex("Tx Frame", frame, len); ++ } else if (DHD_HDRS_ON()) { ++ prhex("TxHdr", frame, MIN(len, 16)); ++ } ++#endif ++ ++ /* Raise len to next SDIO block to eliminate tail command */ ++ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { ++ uint16 pad2 = bus->blocksize - (len % bus->blocksize); ++ if ((pad2 <= bus->roundup) && (pad2 < bus->blocksize)) ++#ifdef NOTUSED ++ if (pad2 <= PKTTAILROOM(osh, pkt)) ++#endif /* NOTUSED */ ++ len += pad2; ++ } else if (len % DHD_SDALIGN) { ++ len += DHD_SDALIGN - (len % DHD_SDALIGN); ++ } ++ ++ /* Some controllers have trouble with odd bytes -- round to even */ ++ if (forcealign && (len & (ALIGNMENT - 1))) { ++#ifdef NOTUSED ++ if (PKTTAILROOM(osh, pkt)) ++#endif ++ len = ROUNDUP(len, ALIGNMENT); ++#ifdef NOTUSED ++ else ++ DHD_ERROR(("%s: sending unrounded %d-byte packet\n", __FUNCTION__, len)); ++#endif ++ } ++ } ++ ++ do { ++ ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, ++ frame, len, pkt, NULL, NULL); ++ bus->f2txdata++; ++ ASSERT(ret != BCME_PENDING); ++ ++ if (ret == BCME_NODEVICE) { ++ DHD_ERROR(("%s: Device asleep already\n", __FUNCTION__)); ++ } else if (ret < 0) { ++ /* On failure, abort the command and terminate the frame */ ++ DHD_ERROR(("%s: sdio error %d, abort command and terminate frame.\n", ++ __FUNCTION__, ret)); ++ bus->tx_sderrs++; ++ ++ bcmsdh_abort(sdh, SDIO_FUNC_2); ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, ++ SFC_WF_TERM, NULL); ++ bus->f1regdata++; ++ ++ for (i = 0; i < 3; i++) { ++ uint8 hi, lo; ++ hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_WFRAMEBCHI, NULL); ++ lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_WFRAMEBCLO, NULL); ++ bus->f1regdata += 2; ++ if ((hi == 0) && (lo == 0)) ++ break; ++ } ++ } ++ if (ret == 0) { ++#ifdef BCMSDIOH_TXGLOM ++ if (bus->glom_enable) { ++ bus->tx_seq = (bus->tx_seq + bus->glom_cnt) % SDPCM_SEQUENCE_WRAP; ++ } else ++#endif ++ { ++ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; ++ } ++ } ++ } while ((ret < 0) && retrydata && retries++ < TXRETRIES); ++ ++done: ++ ++#ifdef BCMSDIOH_TXGLOM ++ if (bus->glom_enable) { ++ dhd_bcmsdh_glom_clear(bus); ++ pkt_cnt = bus->glom_cnt; ++ } else ++#endif ++ { ++ pkt_cnt = 1; ++ } ++ /* restore pkt buffer pointer before calling tx complete routine */ ++ while (pkt_cnt) { ++#ifdef BCMSDIOH_TXGLOM ++ uint32 doff; ++ if (bus->glom_enable) { ++ pkt = bus->glom_pkt_arr[bus->glom_cnt - pkt_cnt]; ++ frame = (uint8*)PKTDATA(osh, pkt); ++ doff = ltoh32_ua(frame + SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN); ++ doff = (doff & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT; ++ PKTPULL(osh, pkt, doff); ++ } else ++#endif ++ { ++ PKTPULL(osh, pkt, SDPCM_HDRLEN + pad1); ++ } ++#ifdef PROP_TXSTATUS ++ if (bus->dhd->wlfc_state) { ++ dhd_os_sdunlock(bus->dhd); ++ dhd_wlfc_txcomplete(bus->dhd, pkt, ret == 0); ++ dhd_os_sdlock(bus->dhd); ++ } else { ++#endif /* PROP_TXSTATUS */ ++#ifdef SDTEST ++ if (chan != SDPCM_TEST_CHANNEL) { ++ dhd_txcomplete(bus->dhd, pkt, ret != 0); ++ } ++#else /* SDTEST */ ++ dhd_txcomplete(bus->dhd, pkt, ret != 0); ++#endif /* SDTEST */ ++ if (free_pkt) ++ PKTFREE(osh, pkt, TRUE); ++ ++#ifdef PROP_TXSTATUS ++ } ++#endif ++ pkt_cnt--; ++ } ++ ++#ifdef BCMSDIOH_TXGLOM ++ /* Reset the glom array */ ++ if (bus->glom_enable) { ++ bus->glom_cnt = 0; ++ bus->glom_total_len = 0; ++ } ++#endif ++ return ret; ++} ++ ++int ++dhd_bus_txdata(struct dhd_bus *bus, void *pkt) ++{ ++ int ret = BCME_ERROR; ++ osl_t *osh; ++ uint datalen, prec; ++#ifdef DHD_TX_DUMP ++ uint8 *dump_data; ++ uint16 protocol; ++#ifdef DHD_TX_FULL_DUMP ++ int i; ++#endif /* DHD_TX_FULL_DUMP */ ++#endif /* DHD_TX_DUMP */ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ osh = bus->dhd->osh; ++ datalen = PKTLEN(osh, pkt); ++ ++#ifdef SDTEST ++ /* Push the test header if doing loopback */ ++ if (bus->ext_loop) { ++ uint8* data; ++ PKTPUSH(osh, pkt, SDPCM_TEST_HDRLEN); ++ data = PKTDATA(osh, pkt); ++ *data++ = SDPCM_TEST_ECHOREQ; ++ *data++ = (uint8)bus->loopid++; ++ *data++ = (datalen >> 0); ++ *data++ = (datalen >> 8); ++ datalen += SDPCM_TEST_HDRLEN; ++ } ++#endif /* SDTEST */ ++ ++#ifdef DHD_TX_DUMP ++ dump_data = PKTDATA(osh, pkt); ++ dump_data += 4; /* skip 4 bytes header */ ++ protocol = (dump_data[12] << 8) | dump_data[13]; ++#ifdef DHD_TX_FULL_DUMP ++ DHD_ERROR(("TX DUMP\n")); ++ ++ for (i = 0; i < (datalen - 4); i++) { ++ DHD_ERROR(("%02X ", dump_data[i])); ++ if ((i & 15) == 15) ++ printk("\n"); ++ } ++ DHD_ERROR(("\n")); ++ ++#endif /* DHD_TX_FULL_DUMP */ ++ if (protocol == ETHER_TYPE_802_1X) { ++ DHD_ERROR(("ETHER_TYPE_802_1X: ver %d, type %d, replay %d\n", ++ dump_data[14], dump_data[15], dump_data[30])); ++ } ++#endif /* DHD_TX_DUMP */ ++ ++ /* Add space for the header */ ++ PKTPUSH(osh, pkt, SDPCM_HDRLEN); ++ ASSERT(ISALIGNED((uintptr)PKTDATA(osh, pkt), 2)); ++ ++ prec = PRIO2PREC((PKTPRIO(pkt) & PRIOMASK)); ++#ifndef DHDTHREAD ++ /* Lock: we're about to use shared data/code (and SDIO) */ ++ dhd_os_sdlock(bus->dhd); ++#endif /* DHDTHREAD */ ++ ++ /* Check for existing queue, current flow-control, pending event, or pending clock */ ++ if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq) || bus->dpc_sched || ++ (!DATAOK(bus)) || (bus->flowcontrol & NBITVAL(prec)) || ++ (bus->clkstate != CLK_AVAIL)) { ++ DHD_TRACE(("%s: deferring pktq len %d\n", __FUNCTION__, ++ pktq_len(&bus->txq))); ++ bus->fcqueued++; ++ ++ /* Priority based enq */ ++ dhd_os_sdlock_txq(bus->dhd); ++ if (dhd_prec_enq(bus->dhd, &bus->txq, pkt, prec) == FALSE) { ++ PKTPULL(osh, pkt, SDPCM_HDRLEN); ++#ifndef DHDTHREAD ++ /* Need to also release txqlock before releasing sdlock. ++ * This thread still has txqlock and releases sdlock. ++ * Deadlock happens when dpc() grabs sdlock first then ++ * attempts to grab txqlock. ++ */ ++ dhd_os_sdunlock_txq(bus->dhd); ++ dhd_os_sdunlock(bus->dhd); ++#endif ++#ifdef PROP_TXSTATUS ++ if (bus->dhd->wlfc_state) ++ dhd_wlfc_txcomplete(bus->dhd, pkt, FALSE); ++ else ++#endif ++ dhd_txcomplete(bus->dhd, pkt, FALSE); ++#ifndef DHDTHREAD ++ dhd_os_sdlock(bus->dhd); ++ dhd_os_sdlock_txq(bus->dhd); ++#endif ++#ifdef PROP_TXSTATUS ++ /* let the caller decide whether to free the packet */ ++ if (!bus->dhd->wlfc_state) ++#endif ++ PKTFREE(osh, pkt, TRUE); ++ ret = BCME_NORESOURCE; ++ } ++ else ++ ret = BCME_OK; ++ dhd_os_sdunlock_txq(bus->dhd); ++ ++ if ((pktq_len(&bus->txq) >= FCHI) && dhd_doflow) ++ dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, ON); ++ ++#ifdef DHD_DEBUG ++ if (pktq_plen(&bus->txq, prec) > qcount[prec]) ++ qcount[prec] = pktq_plen(&bus->txq, prec); ++#endif ++ /* Schedule DPC if needed to send queued packet(s) */ ++ if (dhd_deferred_tx && !bus->dpc_sched) { ++ bus->dpc_sched = TRUE; ++ dhd_sched_dpc(bus->dhd); ++ } ++ } else { ++#ifdef DHDTHREAD ++ /* Lock: we're about to use shared data/code (and SDIO) */ ++ dhd_os_sdlock(bus->dhd); ++#endif /* DHDTHREAD */ ++ ++ /* Otherwise, send it now */ ++ BUS_WAKE(bus); ++ /* Make sure back plane ht clk is on, no pending allowed */ ++ dhdsdio_clkctl(bus, CLK_AVAIL, TRUE); ++#ifndef SDTEST ++ ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE, FALSE); ++#else ++ ret = dhdsdio_txpkt(bus, pkt, ++ (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), TRUE, FALSE); ++#endif ++ if (ret) ++ bus->dhd->tx_errors++; ++ else ++ bus->dhd->dstats.tx_bytes += datalen; ++ ++ if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { ++ bus->activity = FALSE; ++ dhdsdio_clkctl(bus, CLK_NONE, TRUE); ++ } ++ ++#ifdef DHDTHREAD ++ dhd_os_sdunlock(bus->dhd); ++#endif /* DHDTHREAD */ ++ } ++ ++#ifndef DHDTHREAD ++ dhd_os_sdunlock(bus->dhd); ++#endif /* DHDTHREAD */ ++ ++ return ret; ++} ++ ++static uint ++dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes) ++{ ++ void *pkt; ++ uint32 intstatus = 0; ++ uint retries = 0; ++ int ret = 0, prec_out; ++ uint cnt = 0; ++ uint datalen; ++ uint8 tx_prec_map; ++#ifdef BCMSDIOH_TXGLOM ++ uint i; ++ uint8 glom_cnt; ++#endif ++ ++ dhd_pub_t *dhd = bus->dhd; ++ sdpcmd_regs_t *regs = bus->regs; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (!KSO_ENAB(bus)) { ++ DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); ++ return BCME_NODEVICE; ++ } ++ ++ tx_prec_map = ~bus->flowcontrol; ++ ++ /* Send frames until the limit or some other event */ ++ for (cnt = 0; (cnt < maxframes) && DATAOK(bus); cnt++) { ++#ifdef BCMSDIOH_TXGLOM ++ if (bus->glom_enable) { ++ glom_cnt = MIN(DATABUFCNT(bus), bus->glomsize); ++ glom_cnt = MIN(glom_cnt, pktq_mlen(&bus->txq, tx_prec_map)); ++ glom_cnt = MIN(glom_cnt, maxframes-cnt); ++ ++ /* Limiting the size to 2pkts in case of copy */ ++ if (bus->glom_mode == SDPCM_TXGLOM_CPY) ++ glom_cnt = MIN(glom_cnt, 5); ++ ++ if (glom_cnt == 0) ++ break; ++ datalen = 0; ++ for (i = 0; i < glom_cnt; i++) { ++ dhd_os_sdlock_txq(bus->dhd); ++ if ((pkt = pktq_mdeq(&bus->txq, tx_prec_map, &prec_out)) == NULL) { ++ /* This case should not happen */ ++ DHD_ERROR(("No pkts in the queue for glomming\n")); ++ dhd_os_sdunlock_txq(bus->dhd); ++ break; ++ } ++ dhd_os_sdunlock_txq(bus->dhd); ++ ++ datalen += (PKTLEN(bus->dhd->osh, pkt) - SDPCM_HDRLEN); ++#ifndef SDTEST ++ ret = dhdsdio_txpkt(bus, ++ pkt, ++ SDPCM_DATA_CHANNEL, ++ TRUE, ++ (i == (glom_cnt-1))? FALSE: TRUE); ++#else ++ ret = dhdsdio_txpkt(bus, ++ pkt, ++ (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), ++ TRUE, ++ (i == (glom_cnt-1))? FALSE: TRUE); ++#endif ++ } ++ cnt += i-1; ++ } else ++#endif /* BCMSDIOH_TXGLOM */ ++ { ++ dhd_os_sdlock_txq(bus->dhd); ++ if ((pkt = pktq_mdeq(&bus->txq, tx_prec_map, &prec_out)) == NULL) { ++ dhd_os_sdunlock_txq(bus->dhd); ++ break; ++ } ++ dhd_os_sdunlock_txq(bus->dhd); ++ datalen = PKTLEN(bus->dhd->osh, pkt) - SDPCM_HDRLEN; ++ ++#ifndef SDTEST ++ ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE, FALSE); ++#else ++ ret = dhdsdio_txpkt(bus, ++ pkt, ++ (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), ++ TRUE, ++ FALSE); ++#endif ++ } ++ ++ if (ret) ++ bus->dhd->tx_errors++; ++ else ++ bus->dhd->dstats.tx_bytes += datalen; ++ ++ /* In poll mode, need to check for other events */ ++ if (!bus->intr && cnt) ++ { ++ /* Check device status, signal pending interrupt */ ++ R_SDREG(intstatus, ®s->intstatus, retries); ++ bus->f2txdata++; ++ if (bcmsdh_regfail(bus->sdh)) ++ break; ++ if (intstatus & bus->hostintmask) ++ bus->ipend = TRUE; ++ } ++ } ++ ++ /* Deflow-control stack if needed */ ++ if (dhd_doflow && dhd->up && (dhd->busstate == DHD_BUS_DATA) && ++ dhd->txoff && (pktq_len(&bus->txq) < FCLOW)) ++ dhd_txflowcontrol(dhd, ALL_INTERFACES, OFF); ++ ++ return cnt; ++} ++ ++int ++dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) ++{ ++ uint8 *frame; ++ uint16 len; ++ uint32 swheader; ++ uint retries = 0; ++ bcmsdh_info_t *sdh = bus->sdh; ++ uint8 doff = 0; ++ int ret = -1; ++ int i; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (bus->dhd->dongle_reset) ++ return -EIO; ++ ++ /* Back the pointer to make a room for bus header */ ++ frame = msg - SDPCM_HDRLEN; ++ len = (msglen += SDPCM_HDRLEN); ++ ++ /* Add alignment padding (optional for ctl frames) */ ++ if (dhd_alignctl) { ++ if ((doff = ((uintptr)frame % DHD_SDALIGN))) { ++ frame -= doff; ++ len += doff; ++ msglen += doff; ++ bzero(frame, doff + SDPCM_HDRLEN); ++ } ++ ASSERT(doff < DHD_SDALIGN); ++ } ++ doff += SDPCM_HDRLEN; ++ ++ /* Round send length to next SDIO block */ ++ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { ++ uint16 pad = bus->blocksize - (len % bus->blocksize); ++ if ((pad <= bus->roundup) && (pad < bus->blocksize)) ++ len += pad; ++ } else if (len % DHD_SDALIGN) { ++ len += DHD_SDALIGN - (len % DHD_SDALIGN); ++ } ++ ++ /* Satisfy length-alignment requirements */ ++ if (forcealign && (len & (ALIGNMENT - 1))) ++ len = ROUNDUP(len, ALIGNMENT); ++ ++ ASSERT(ISALIGNED((uintptr)frame, 2)); ++ ++ ++ /* Need to lock here to protect txseq and SDIO tx calls */ ++ dhd_os_sdlock(bus->dhd); ++ ++ BUS_WAKE(bus); ++ ++ /* Make sure backplane clock is on */ ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ ++ /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ ++ *(uint16*)frame = htol16((uint16)msglen); ++ *(((uint16*)frame) + 1) = htol16(~msglen); ++ ++#ifdef BCMSDIOH_TXGLOM ++ if (bus->glom_enable) { ++ uint32 hwheader1, hwheader2; ++ /* Software tag: channel, sequence number, data offset */ ++ swheader = ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) ++ | bus->tx_seq ++ | ((doff << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); ++ htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN); ++ htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN ++ + SDPCM_HWEXT_LEN + sizeof(swheader)); ++ ++ hwheader1 = (msglen - SDPCM_FRAMETAG_LEN) | (1 << 24); ++ hwheader2 = (len - (msglen)) << 16; ++ htol32_ua_store(hwheader1, frame + SDPCM_FRAMETAG_LEN); ++ htol32_ua_store(hwheader2, frame + SDPCM_FRAMETAG_LEN + 4); ++ ++ *(uint16*)frame = htol16(len); ++ *(((uint16*)frame) + 1) = htol16(~(len)); ++ } else ++#endif /* BCMSDIOH_TXGLOM */ ++ { ++ /* Software tag: channel, sequence number, data offset */ ++ swheader = ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) ++ | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); ++ htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN); ++ htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); ++ } ++ if (!TXCTLOK(bus)) { ++ DHD_INFO(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d\n", ++ __FUNCTION__, bus->tx_max, bus->tx_seq)); ++ bus->ctrl_frame_stat = TRUE; ++ /* Send from dpc */ ++ bus->ctrl_frame_buf = frame; ++ bus->ctrl_frame_len = len; ++ ++ if (!bus->dpc_sched) { ++ bus->dpc_sched = TRUE; ++ dhd_sched_dpc(bus->dhd); ++ } ++ if (bus->ctrl_frame_stat) { ++ dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat); ++ } ++ ++ if (bus->ctrl_frame_stat == FALSE) { ++ DHD_INFO(("%s: ctrl_frame_stat == FALSE\n", __FUNCTION__)); ++ ret = 0; ++ } else { ++ bus->dhd->txcnt_timeout++; ++ if (!bus->dhd->hang_was_sent) { ++ DHD_ERROR(("%s: ctrl_frame_stat == TRUE txcnt_timeout=%d\n", ++ __FUNCTION__, bus->dhd->txcnt_timeout)); ++ } ++ ret = -1; ++ bus->ctrl_frame_stat = FALSE; ++ goto done; ++ } ++ } ++ ++ bus->dhd->txcnt_timeout = 0; ++ ++ if (ret == -1) { ++#ifdef DHD_DEBUG ++ if (DHD_BYTES_ON() && DHD_CTL_ON()) { ++ prhex("Tx Frame", frame, len); ++ } else if (DHD_HDRS_ON()) { ++ prhex("TxHdr", frame, MIN(len, 16)); ++ } ++#endif ++ ++ do { ++ ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, ++ frame, len, NULL, NULL, NULL); ++ ASSERT(ret != BCME_PENDING); ++ ++ if (ret == BCME_NODEVICE) { ++ DHD_ERROR(("%s: Device asleep already\n", __FUNCTION__)); ++ } else if (ret < 0) { ++ /* On failure, abort the command and terminate the frame */ ++ DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n", ++ __FUNCTION__, ret)); ++ bus->tx_sderrs++; ++ ++ bcmsdh_abort(sdh, SDIO_FUNC_2); ++ ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, ++ SFC_WF_TERM, NULL); ++ bus->f1regdata++; ++ ++ for (i = 0; i < 3; i++) { ++ uint8 hi, lo; ++ hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_WFRAMEBCHI, NULL); ++ lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_WFRAMEBCLO, NULL); ++ bus->f1regdata += 2; ++ if ((hi == 0) && (lo == 0)) ++ break; ++ } ++ } ++ if (ret == 0) { ++ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; ++ } ++ } while ((ret < 0) && retries++ < TXRETRIES); ++ } ++ ++done: ++ if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { ++ bus->activity = FALSE; ++ dhdsdio_clkctl(bus, CLK_NONE, TRUE); ++ } ++ ++ dhd_os_sdunlock(bus->dhd); ++ ++ if (ret) ++ bus->dhd->tx_ctlerrs++; ++ else ++ bus->dhd->tx_ctlpkts++; ++ ++ if (bus->dhd->txcnt_timeout >= MAX_CNTL_TIMEOUT) ++ return -ETIMEDOUT; ++ ++ return ret ? -EIO : 0; ++} ++ ++int ++dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen) ++{ ++ int timeleft; ++ uint rxlen = 0; ++ bool pending; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (bus->dhd->dongle_reset) ++ return -EIO; ++ ++ /* Wait until control frame is available */ ++ timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, &pending); ++ ++ dhd_os_sdlock(bus->dhd); ++ rxlen = bus->rxlen; ++ bcopy(bus->rxctl, msg, MIN(msglen, rxlen)); ++ bus->rxlen = 0; ++ dhd_os_sdunlock(bus->dhd); ++ ++ if (rxlen) { ++ DHD_CTL(("%s: resumed on rxctl frame, got %d expected %d\n", ++ __FUNCTION__, rxlen, msglen)); ++ } else if (timeleft == 0) { ++#ifdef DHD_DEBUG ++ uint32 status, retry = 0; ++ R_SDREG(status, &bus->regs->intstatus, retry); ++ DHD_ERROR(("%s: resumed on timeout, INT status=0x%08X\n", ++ __FUNCTION__, status)); ++#else ++ DHD_ERROR(("%s: resumed on timeout\n", __FUNCTION__)); ++#endif /* DHD_DEBUG */ ++#ifdef DHD_DEBUG ++ dhd_os_sdlock(bus->dhd); ++ dhdsdio_checkdied(bus, NULL, 0); ++ dhd_os_sdunlock(bus->dhd); ++#endif /* DHD_DEBUG */ ++ } else if (pending == TRUE) { ++ /* signal pending */ ++ DHD_ERROR(("%s: signal pending\n", __FUNCTION__)); ++ return -EINTR; ++ } else { ++ DHD_CTL(("%s: resumed for unknown reason?\n", __FUNCTION__)); ++#ifdef DHD_DEBUG ++ dhd_os_sdlock(bus->dhd); ++ dhdsdio_checkdied(bus, NULL, 0); ++ dhd_os_sdunlock(bus->dhd); ++#endif /* DHD_DEBUG */ ++ } ++ if (timeleft == 0) { ++ bus->dhd->rxcnt_timeout++; ++ DHD_ERROR(("%s: rxcnt_timeout=%d\n", __FUNCTION__, bus->dhd->rxcnt_timeout)); ++ } ++ else ++ bus->dhd->rxcnt_timeout = 0; ++ ++ if (rxlen) ++ bus->dhd->rx_ctlpkts++; ++ else ++ bus->dhd->rx_ctlerrs++; ++ ++ if (bus->dhd->rxcnt_timeout >= MAX_CNTL_TIMEOUT) ++ return -ETIMEDOUT; ++ ++ if (bus->dhd->dongle_trap_occured) ++ return -EREMOTEIO; ++ ++ return rxlen ? (int)rxlen : -EIO; ++} ++ ++/* IOVar table */ ++enum { ++ IOV_INTR = 1, ++ IOV_POLLRATE, ++ IOV_SDREG, ++ IOV_SBREG, ++ IOV_SDCIS, ++ IOV_MEMBYTES, ++ IOV_MEMSIZE, ++#ifdef DHD_DEBUG ++ IOV_CHECKDIED, ++ IOV_SERIALCONS, ++#endif /* DHD_DEBUG */ ++ IOV_SET_DOWNLOAD_STATE, ++ IOV_SOCRAM_STATE, ++ IOV_FORCEEVEN, ++ IOV_SDIOD_DRIVE, ++ IOV_READAHEAD, ++ IOV_SDRXCHAIN, ++ IOV_ALIGNCTL, ++ IOV_SDALIGN, ++ IOV_DEVRESET, ++ IOV_CPU, ++#if defined(SDIO_CRC_ERROR_FIX) ++ IOV_WATERMARK, ++ IOV_MESBUSYCTRL, ++#endif /* SDIO_CRC_ERROR_FIX */ ++#ifdef SDTEST ++ IOV_PKTGEN, ++ IOV_EXTLOOP, ++#endif /* SDTEST */ ++ IOV_SPROM, ++ IOV_TXBOUND, ++ IOV_RXBOUND, ++ IOV_TXMINMAX, ++ IOV_IDLETIME, ++ IOV_IDLECLOCK, ++ IOV_SD1IDLE, ++ IOV_SLEEP, ++ IOV_DONGLEISOLATION, ++ IOV_KSO, ++ IOV_DEVSLEEP, ++ IOV_DEVCAP, ++ IOV_VARS, ++#ifdef SOFTAP ++ IOV_FWPATH, ++#endif ++ IOV_TXGLOMSIZE, ++ IOV_TXGLOMMODE ++}; ++ ++const bcm_iovar_t dhdsdio_iovars[] = { ++ {"intr", IOV_INTR, 0, IOVT_BOOL, 0 }, ++ {"sleep", IOV_SLEEP, 0, IOVT_BOOL, 0 }, ++ {"pollrate", IOV_POLLRATE, 0, IOVT_UINT32, 0 }, ++ {"idletime", IOV_IDLETIME, 0, IOVT_INT32, 0 }, ++ {"idleclock", IOV_IDLECLOCK, 0, IOVT_INT32, 0 }, ++ {"sd1idle", IOV_SD1IDLE, 0, IOVT_BOOL, 0 }, ++ {"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int) }, ++ {"memsize", IOV_MEMSIZE, 0, IOVT_UINT32, 0 }, ++ {"dwnldstate", IOV_SET_DOWNLOAD_STATE, 0, IOVT_BOOL, 0 }, ++ {"socram_state", IOV_SOCRAM_STATE, 0, IOVT_BOOL, 0 }, ++ {"vars", IOV_VARS, 0, IOVT_BUFFER, 0 }, ++ {"sdiod_drive", IOV_SDIOD_DRIVE, 0, IOVT_UINT32, 0 }, ++ {"readahead", IOV_READAHEAD, 0, IOVT_BOOL, 0 }, ++ {"sdrxchain", IOV_SDRXCHAIN, 0, IOVT_BOOL, 0 }, ++ {"alignctl", IOV_ALIGNCTL, 0, IOVT_BOOL, 0 }, ++ {"sdalign", IOV_SDALIGN, 0, IOVT_BOOL, 0 }, ++ {"devreset", IOV_DEVRESET, 0, IOVT_BOOL, 0 }, ++#ifdef DHD_DEBUG ++ {"sdreg", IOV_SDREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, ++ {"sbreg", IOV_SBREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, ++ {"sd_cis", IOV_SDCIS, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN }, ++ {"forcealign", IOV_FORCEEVEN, 0, IOVT_BOOL, 0 }, ++ {"txbound", IOV_TXBOUND, 0, IOVT_UINT32, 0 }, ++ {"rxbound", IOV_RXBOUND, 0, IOVT_UINT32, 0 }, ++ {"txminmax", IOV_TXMINMAX, 0, IOVT_UINT32, 0 }, ++ {"cpu", IOV_CPU, 0, IOVT_BOOL, 0 }, ++#ifdef DHD_DEBUG ++ {"checkdied", IOV_CHECKDIED, 0, IOVT_BUFFER, 0 }, ++ {"serial", IOV_SERIALCONS, 0, IOVT_UINT32, 0 }, ++#endif /* DHD_DEBUG */ ++#endif /* DHD_DEBUG */ ++#ifdef SDTEST ++ {"extloop", IOV_EXTLOOP, 0, IOVT_BOOL, 0 }, ++ {"pktgen", IOV_PKTGEN, 0, IOVT_BUFFER, sizeof(dhd_pktgen_t) }, ++#endif /* SDTEST */ ++#if defined(SDIO_CRC_ERROR_FIX) ++ {"watermark", IOV_WATERMARK, 0, IOVT_UINT32, 0 }, ++ {"mesbusyctrl", IOV_MESBUSYCTRL, 0, IOVT_UINT32, 0 }, ++#endif /* SDIO_CRC_ERROR_FIX */ ++ {"devcap", IOV_DEVCAP, 0, IOVT_UINT32, 0 }, ++ {"dngl_isolation", IOV_DONGLEISOLATION, 0, IOVT_UINT32, 0 }, ++ {"kso", IOV_KSO, 0, IOVT_UINT32, 0 }, ++ {"devsleep", IOV_DEVSLEEP, 0, IOVT_UINT32, 0 }, ++#ifdef SOFTAP ++ {"fwpath", IOV_FWPATH, 0, IOVT_BUFFER, 0 }, ++#endif ++ {"txglomsize", IOV_TXGLOMSIZE, 0, IOVT_UINT32, 0 }, ++ {"txglommode", IOV_TXGLOMMODE, 0, IOVT_UINT32, 0 }, ++ {NULL, 0, 0, 0, 0 } ++}; ++ ++static void ++dhd_dump_pct(struct bcmstrbuf *strbuf, char *desc, uint num, uint div) ++{ ++ uint q1, q2; ++ ++ if (!div) { ++ bcm_bprintf(strbuf, "%s N/A", desc); ++ } else { ++ q1 = num / div; ++ q2 = (100 * (num - (q1 * div))) / div; ++ bcm_bprintf(strbuf, "%s %d.%02d", desc, q1, q2); ++ } ++} ++ ++void ++dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) ++{ ++ dhd_bus_t *bus = dhdp->bus; ++ ++ bcm_bprintf(strbuf, "Bus SDIO structure:\n"); ++ bcm_bprintf(strbuf, "hostintmask 0x%08x intstatus 0x%08x sdpcm_ver %d\n", ++ bus->hostintmask, bus->intstatus, bus->sdpcm_ver); ++ bcm_bprintf(strbuf, "fcstate %d qlen %d tx_seq %d, max %d, rxskip %d rxlen %d rx_seq %d\n", ++ bus->fcstate, pktq_len(&bus->txq), bus->tx_seq, bus->tx_max, bus->rxskip, ++ bus->rxlen, bus->rx_seq); ++ bcm_bprintf(strbuf, "intr %d intrcount %d lastintrs %d spurious %d\n", ++ bus->intr, bus->intrcount, bus->lastintrs, bus->spurious); ++ bcm_bprintf(strbuf, "pollrate %d pollcnt %d regfails %d\n", ++ bus->pollrate, bus->pollcnt, bus->regfails); ++ ++ bcm_bprintf(strbuf, "\nAdditional counters:\n"); ++ bcm_bprintf(strbuf, "tx_sderrs %d fcqueued %d rxrtx %d rx_toolong %d rxc_errors %d\n", ++ bus->tx_sderrs, bus->fcqueued, bus->rxrtx, bus->rx_toolong, ++ bus->rxc_errors); ++ bcm_bprintf(strbuf, "rx_hdrfail %d badhdr %d badseq %d\n", ++ bus->rx_hdrfail, bus->rx_badhdr, bus->rx_badseq); ++ bcm_bprintf(strbuf, "fc_rcvd %d, fc_xoff %d, fc_xon %d\n", ++ bus->fc_rcvd, bus->fc_xoff, bus->fc_xon); ++ bcm_bprintf(strbuf, "rxglomfail %d, rxglomframes %d, rxglompkts %d\n", ++ bus->rxglomfail, bus->rxglomframes, bus->rxglompkts); ++ bcm_bprintf(strbuf, "f2rx (hdrs/data) %d (%d/%d), f2tx %d f1regs %d\n", ++ (bus->f2rxhdrs + bus->f2rxdata), bus->f2rxhdrs, bus->f2rxdata, ++ bus->f2txdata, bus->f1regdata); ++ { ++ dhd_dump_pct(strbuf, "\nRx: pkts/f2rd", bus->dhd->rx_packets, ++ (bus->f2rxhdrs + bus->f2rxdata)); ++ dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->rx_packets, bus->f1regdata); ++ dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->rx_packets, ++ (bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata)); ++ dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->rx_packets, bus->intrcount); ++ bcm_bprintf(strbuf, "\n"); ++ ++ dhd_dump_pct(strbuf, "Rx: glom pct", (100 * bus->rxglompkts), ++ bus->dhd->rx_packets); ++ dhd_dump_pct(strbuf, ", pkts/glom", bus->rxglompkts, bus->rxglomframes); ++ bcm_bprintf(strbuf, "\n"); ++ ++ dhd_dump_pct(strbuf, "Tx: pkts/f2wr", bus->dhd->tx_packets, bus->f2txdata); ++ dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->tx_packets, bus->f1regdata); ++ dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->tx_packets, ++ (bus->f2txdata + bus->f1regdata)); ++ dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->tx_packets, bus->intrcount); ++ bcm_bprintf(strbuf, "\n"); ++ ++ dhd_dump_pct(strbuf, "Total: pkts/f2rw", ++ (bus->dhd->tx_packets + bus->dhd->rx_packets), ++ (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata)); ++ dhd_dump_pct(strbuf, ", pkts/f1sd", ++ (bus->dhd->tx_packets + bus->dhd->rx_packets), bus->f1regdata); ++ dhd_dump_pct(strbuf, ", pkts/sd", ++ (bus->dhd->tx_packets + bus->dhd->rx_packets), ++ (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata)); ++ dhd_dump_pct(strbuf, ", pkts/int", ++ (bus->dhd->tx_packets + bus->dhd->rx_packets), bus->intrcount); ++ bcm_bprintf(strbuf, "\n\n"); ++ } ++ ++#ifdef SDTEST ++ if (bus->pktgen_count) { ++ bcm_bprintf(strbuf, "pktgen config and count:\n"); ++ bcm_bprintf(strbuf, "freq %d count %d print %d total %d min %d len %d\n", ++ bus->pktgen_freq, bus->pktgen_count, bus->pktgen_print, ++ bus->pktgen_total, bus->pktgen_minlen, bus->pktgen_maxlen); ++ bcm_bprintf(strbuf, "send attempts %d rcvd %d fail %d\n", ++ bus->pktgen_sent, bus->pktgen_rcvd, bus->pktgen_fail); ++ } ++#endif /* SDTEST */ ++#ifdef DHD_DEBUG ++ bcm_bprintf(strbuf, "dpc_sched %d host interrupt%spending\n", ++ bus->dpc_sched, (bcmsdh_intr_pending(bus->sdh) ? " " : " not ")); ++ bcm_bprintf(strbuf, "blocksize %d roundup %d\n", bus->blocksize, bus->roundup); ++#endif /* DHD_DEBUG */ ++ bcm_bprintf(strbuf, "clkstate %d activity %d idletime %d idlecount %d sleeping %d\n", ++ bus->clkstate, bus->activity, bus->idletime, bus->idlecount, bus->sleeping); ++} ++ ++void ++dhd_bus_clearcounts(dhd_pub_t *dhdp) ++{ ++ dhd_bus_t *bus = (dhd_bus_t *)dhdp->bus; ++ ++ bus->intrcount = bus->lastintrs = bus->spurious = bus->regfails = 0; ++ bus->rxrtx = bus->rx_toolong = bus->rxc_errors = 0; ++ bus->rx_hdrfail = bus->rx_badhdr = bus->rx_badseq = 0; ++ bus->tx_sderrs = bus->fc_rcvd = bus->fc_xoff = bus->fc_xon = 0; ++ bus->rxglomfail = bus->rxglomframes = bus->rxglompkts = 0; ++ bus->f2rxhdrs = bus->f2rxdata = bus->f2txdata = bus->f1regdata = 0; ++} ++ ++#ifdef SDTEST ++static int ++dhdsdio_pktgen_get(dhd_bus_t *bus, uint8 *arg) ++{ ++ dhd_pktgen_t pktgen; ++ ++ pktgen.version = DHD_PKTGEN_VERSION; ++ pktgen.freq = bus->pktgen_freq; ++ pktgen.count = bus->pktgen_count; ++ pktgen.print = bus->pktgen_print; ++ pktgen.total = bus->pktgen_total; ++ pktgen.minlen = bus->pktgen_minlen; ++ pktgen.maxlen = bus->pktgen_maxlen; ++ pktgen.numsent = bus->pktgen_sent; ++ pktgen.numrcvd = bus->pktgen_rcvd; ++ pktgen.numfail = bus->pktgen_fail; ++ pktgen.mode = bus->pktgen_mode; ++ pktgen.stop = bus->pktgen_stop; ++ ++ bcopy(&pktgen, arg, sizeof(pktgen)); ++ ++ return 0; ++} ++ ++static int ++dhdsdio_pktgen_set(dhd_bus_t *bus, uint8 *arg) ++{ ++ dhd_pktgen_t pktgen; ++ uint oldcnt, oldmode; ++ ++ bcopy(arg, &pktgen, sizeof(pktgen)); ++ if (pktgen.version != DHD_PKTGEN_VERSION) ++ return BCME_BADARG; ++ ++ oldcnt = bus->pktgen_count; ++ oldmode = bus->pktgen_mode; ++ ++ bus->pktgen_freq = pktgen.freq; ++ bus->pktgen_count = pktgen.count; ++ bus->pktgen_print = pktgen.print; ++ bus->pktgen_total = pktgen.total; ++ bus->pktgen_minlen = pktgen.minlen; ++ bus->pktgen_maxlen = pktgen.maxlen; ++ bus->pktgen_mode = pktgen.mode; ++ bus->pktgen_stop = pktgen.stop; ++ ++ bus->pktgen_tick = bus->pktgen_ptick = 0; ++ bus->pktgen_prev_time = jiffies; ++ bus->pktgen_len = MAX(bus->pktgen_len, bus->pktgen_minlen); ++ bus->pktgen_len = MIN(bus->pktgen_len, bus->pktgen_maxlen); ++ ++ /* Clear counts for a new pktgen (mode change, or was stopped) */ ++ if (bus->pktgen_count && (!oldcnt || oldmode != bus->pktgen_mode)) { ++ bus->pktgen_sent = bus->pktgen_prev_sent = bus->pktgen_rcvd = 0; ++ bus->pktgen_prev_rcvd = bus->pktgen_fail = 0; ++ } ++ ++ return 0; ++} ++#endif /* SDTEST */ ++ ++static void ++dhdsdio_devram_remap(dhd_bus_t *bus, bool val) ++{ ++ uint8 enable, protect, remap; ++ ++ si_socdevram(bus->sih, FALSE, &enable, &protect, &remap); ++ remap = val ? TRUE : FALSE; ++ si_socdevram(bus->sih, TRUE, &enable, &protect, &remap); ++} ++ ++static int ++dhdsdio_membytes(dhd_bus_t *bus, bool write, uint32 address, uint8 *data, uint size) ++{ ++ int bcmerror = 0; ++ uint32 sdaddr; ++ uint dsize; ++ ++ /* In remap mode, adjust address beyond socram and redirect ++ * to devram at SOCDEVRAM_BP_ADDR since remap address > orig_ramsize ++ * is not backplane accessible ++ */ ++ if (REMAP_ENAB(bus) && REMAP_ISADDR(bus, address)) { ++ address -= bus->orig_ramsize; ++ address += SOCDEVRAM_BP_ADDR; ++ } ++ ++ /* Determine initial transfer parameters */ ++ sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK; ++ if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK) ++ dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr); ++ else ++ dsize = size; ++ ++ /* Set the backplane window to include the start address */ ++ if ((bcmerror = dhdsdio_set_siaddr_window(bus, address))) { ++ DHD_ERROR(("%s: window change failed\n", __FUNCTION__)); ++ goto xfer_done; ++ } ++ ++ /* Do the transfer(s) */ ++ while (size) { ++ DHD_INFO(("%s: %s %d bytes at offset 0x%08x in window 0x%08x\n", ++ __FUNCTION__, (write ? "write" : "read"), dsize, sdaddr, ++ (address & SBSDIO_SBWINDOW_MASK))); ++ if ((bcmerror = bcmsdh_rwdata(bus->sdh, write, sdaddr, data, dsize))) { ++ DHD_ERROR(("%s: membytes transfer failed\n", __FUNCTION__)); ++ break; ++ } ++ ++ /* Adjust for next transfer (if any) */ ++ if ((size -= dsize)) { ++ data += dsize; ++ address += dsize; ++ if ((bcmerror = dhdsdio_set_siaddr_window(bus, address))) { ++ DHD_ERROR(("%s: window change failed\n", __FUNCTION__)); ++ break; ++ } ++ sdaddr = 0; ++ dsize = MIN(SBSDIO_SB_OFT_ADDR_LIMIT, size); ++ } ++ ++ } ++ ++xfer_done: ++ /* Return the window to backplane enumeration space for core access */ ++ if (dhdsdio_set_siaddr_window(bus, bcmsdh_cur_sbwad(bus->sdh))) { ++ DHD_ERROR(("%s: FAILED to set window back to 0x%x\n", __FUNCTION__, ++ bcmsdh_cur_sbwad(bus->sdh))); ++ } ++ ++ return bcmerror; ++} ++ ++#ifdef DHD_DEBUG ++static int ++dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh) ++{ ++ uint32 addr; ++ int rv, i; ++ uint32 shaddr = 0; ++ ++ shaddr = bus->dongle_ram_base + bus->ramsize - 4; ++ i = 0; ++ do { ++ /* Read last word in memory to determine address of sdpcm_shared structure */ ++ if ((rv = dhdsdio_membytes(bus, FALSE, shaddr, (uint8 *)&addr, 4)) < 0) ++ return rv; ++ ++ addr = ltoh32(addr); ++ ++ DHD_INFO(("sdpcm_shared address 0x%08X\n", addr)); ++ ++ /* ++ * Check if addr is valid. ++ * NVRAM length at the end of memory should have been overwritten. ++ */ ++ if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) { ++ if ((bus->srmemsize > 0) && (i++ == 0)) { ++ shaddr -= bus->srmemsize; ++ } else { ++ DHD_ERROR(("%s: address (0x%08x) of sdpcm_shared invalid\n", ++ __FUNCTION__, addr)); ++ return BCME_ERROR; ++ } ++ } else ++ break; ++ } while (i < 2); ++ ++ /* Read hndrte_shared structure */ ++ if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)sh, sizeof(sdpcm_shared_t))) < 0) ++ return rv; ++ ++ /* Endianness */ ++ sh->flags = ltoh32(sh->flags); ++ sh->trap_addr = ltoh32(sh->trap_addr); ++ sh->assert_exp_addr = ltoh32(sh->assert_exp_addr); ++ sh->assert_file_addr = ltoh32(sh->assert_file_addr); ++ sh->assert_line = ltoh32(sh->assert_line); ++ sh->console_addr = ltoh32(sh->console_addr); ++ sh->msgtrace_addr = ltoh32(sh->msgtrace_addr); ++ ++ if ((sh->flags & SDPCM_SHARED_VERSION_MASK) == 3 && SDPCM_SHARED_VERSION == 1) ++ return BCME_OK; ++ ++ if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) { ++ DHD_ERROR(("%s: sdpcm_shared version %d in dhd " ++ "is different than sdpcm_shared version %d in dongle\n", ++ __FUNCTION__, SDPCM_SHARED_VERSION, ++ sh->flags & SDPCM_SHARED_VERSION_MASK)); ++ return BCME_ERROR; ++ } ++ ++ return BCME_OK; ++} ++ ++#define CONSOLE_LINE_MAX 192 ++ ++static int ++dhdsdio_readconsole(dhd_bus_t *bus) ++{ ++ dhd_console_t *c = &bus->console; ++ uint8 line[CONSOLE_LINE_MAX], ch; ++ uint32 n, idx, addr; ++ int rv; ++ ++ /* Don't do anything until FWREADY updates console address */ ++ if (bus->console_addr == 0) ++ return 0; ++ ++ if (!KSO_ENAB(bus)) ++ return 0; ++ ++ /* Read console log struct */ ++ addr = bus->console_addr + OFFSETOF(hndrte_cons_t, log); ++ if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&c->log, sizeof(c->log))) < 0) ++ return rv; ++ ++ /* Allocate console buffer (one time only) */ ++ if (c->buf == NULL) { ++ c->bufsize = ltoh32(c->log.buf_size); ++ if ((c->buf = MALLOC(bus->dhd->osh, c->bufsize)) == NULL) ++ return BCME_NOMEM; ++ } ++ ++ idx = ltoh32(c->log.idx); ++ ++ /* Protect against corrupt value */ ++ if (idx > c->bufsize) ++ return BCME_ERROR; ++ ++ /* Skip reading the console buffer if the index pointer has not moved */ ++ if (idx == c->last) ++ return BCME_OK; ++ ++ /* Read the console buffer */ ++ addr = ltoh32(c->log.buf); ++ if ((rv = dhdsdio_membytes(bus, FALSE, addr, c->buf, c->bufsize)) < 0) ++ return rv; ++ ++ while (c->last != idx) { ++ for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { ++ if (c->last == idx) { ++ /* This would output a partial line. Instead, back up ++ * the buffer pointer and output this line next time around. ++ */ ++ if (c->last >= n) ++ c->last -= n; ++ else ++ c->last = c->bufsize - n; ++ goto break2; ++ } ++ ch = c->buf[c->last]; ++ c->last = (c->last + 1) % c->bufsize; ++ if (ch == '\n') ++ break; ++ line[n] = ch; ++ } ++ ++ if (n > 0) { ++ if (line[n - 1] == '\r') ++ n--; ++ line[n] = 0; ++ printf("CONSOLE: %s\n", line); ++ } ++ } ++break2: ++ ++ return BCME_OK; ++} ++ ++static int ++dhdsdio_checkdied(dhd_bus_t *bus, char *data, uint size) ++{ ++ int bcmerror = 0; ++ uint msize = 512; ++ char *mbuffer = NULL; ++ char *console_buffer = NULL; ++ uint maxstrlen = 256; ++ char *str = NULL; ++ trap_t tr; ++ sdpcm_shared_t sdpcm_shared; ++ struct bcmstrbuf strbuf; ++ uint32 console_ptr, console_size, console_index; ++ uint8 line[CONSOLE_LINE_MAX], ch; ++ uint32 n, i, addr; ++ int rv; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (data == NULL) { ++ /* ++ * Called after a rx ctrl timeout. "data" is NULL. ++ * allocate memory to trace the trap or assert. ++ */ ++ size = msize; ++ mbuffer = data = MALLOC(bus->dhd->osh, msize); ++ if (mbuffer == NULL) { ++ DHD_ERROR(("%s: MALLOC(%d) failed \n", __FUNCTION__, msize)); ++ bcmerror = BCME_NOMEM; ++ goto done; ++ } ++ } ++ ++ if ((str = MALLOC(bus->dhd->osh, maxstrlen)) == NULL) { ++ DHD_ERROR(("%s: MALLOC(%d) failed \n", __FUNCTION__, maxstrlen)); ++ bcmerror = BCME_NOMEM; ++ goto done; ++ } ++ ++ if ((bcmerror = dhdsdio_readshared(bus, &sdpcm_shared)) < 0) ++ goto done; ++ ++ bcm_binit(&strbuf, data, size); ++ ++ bcm_bprintf(&strbuf, "msgtrace address : 0x%08X\nconsole address : 0x%08X\n", ++ sdpcm_shared.msgtrace_addr, sdpcm_shared.console_addr); ++ ++ if ((sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { ++ /* NOTE: Misspelled assert is intentional - DO NOT FIX. ++ * (Avoids conflict with real asserts for programmatic parsing of output.) ++ */ ++ bcm_bprintf(&strbuf, "Assrt not built in dongle\n"); ++ } ++ ++ if ((sdpcm_shared.flags & (SDPCM_SHARED_ASSERT|SDPCM_SHARED_TRAP)) == 0) { ++ /* NOTE: Misspelled assert is intentional - DO NOT FIX. ++ * (Avoids conflict with real asserts for programmatic parsing of output.) ++ */ ++ bcm_bprintf(&strbuf, "No trap%s in dongle", ++ (sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) ++ ?"/assrt" :""); ++ } else { ++ if (sdpcm_shared.flags & SDPCM_SHARED_ASSERT) { ++ /* Download assert */ ++ bcm_bprintf(&strbuf, "Dongle assert"); ++ if (sdpcm_shared.assert_exp_addr != 0) { ++ str[0] = '\0'; ++ if ((bcmerror = dhdsdio_membytes(bus, FALSE, ++ sdpcm_shared.assert_exp_addr, ++ (uint8 *)str, maxstrlen)) < 0) ++ goto done; ++ ++ str[maxstrlen - 1] = '\0'; ++ bcm_bprintf(&strbuf, " expr \"%s\"", str); ++ } ++ ++ if (sdpcm_shared.assert_file_addr != 0) { ++ str[0] = '\0'; ++ if ((bcmerror = dhdsdio_membytes(bus, FALSE, ++ sdpcm_shared.assert_file_addr, ++ (uint8 *)str, maxstrlen)) < 0) ++ goto done; ++ ++ str[maxstrlen - 1] = '\0'; ++ bcm_bprintf(&strbuf, " file \"%s\"", str); ++ } ++ ++ bcm_bprintf(&strbuf, " line %d ", sdpcm_shared.assert_line); ++ } ++ ++ if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) { ++ bus->dhd->dongle_trap_occured = TRUE; ++ if ((bcmerror = dhdsdio_membytes(bus, FALSE, ++ sdpcm_shared.trap_addr, ++ (uint8*)&tr, sizeof(trap_t))) < 0) ++ goto done; ++ ++ bcm_bprintf(&strbuf, ++ "Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x," ++ "lp 0x%x, rpc 0x%x Trap offset 0x%x, " ++ "r0 0x%x, r1 0x%x, r2 0x%x, r3 0x%x, " ++ "r4 0x%x, r5 0x%x, r6 0x%x, r7 0x%x\n\n", ++ ltoh32(tr.type), ltoh32(tr.epc), ltoh32(tr.cpsr), ltoh32(tr.spsr), ++ ltoh32(tr.r13), ltoh32(tr.r14), ltoh32(tr.pc), ++ ltoh32(sdpcm_shared.trap_addr), ++ ltoh32(tr.r0), ltoh32(tr.r1), ltoh32(tr.r2), ltoh32(tr.r3), ++ ltoh32(tr.r4), ltoh32(tr.r5), ltoh32(tr.r6), ltoh32(tr.r7)); ++ ++ addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log); ++ if ((rv = dhdsdio_membytes(bus, FALSE, addr, ++ (uint8 *)&console_ptr, sizeof(console_ptr))) < 0) ++ goto printbuf; ++ ++ addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log.buf_size); ++ if ((rv = dhdsdio_membytes(bus, FALSE, addr, ++ (uint8 *)&console_size, sizeof(console_size))) < 0) ++ goto printbuf; ++ ++ addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log.idx); ++ if ((rv = dhdsdio_membytes(bus, FALSE, addr, ++ (uint8 *)&console_index, sizeof(console_index))) < 0) ++ goto printbuf; ++ ++ console_ptr = ltoh32(console_ptr); ++ console_size = ltoh32(console_size); ++ console_index = ltoh32(console_index); ++ ++ if (console_size > CONSOLE_BUFFER_MAX || ++ !(console_buffer = MALLOC(bus->dhd->osh, console_size))) ++ goto printbuf; ++ ++ if ((rv = dhdsdio_membytes(bus, FALSE, console_ptr, ++ (uint8 *)console_buffer, console_size)) < 0) ++ goto printbuf; ++ ++ for (i = 0, n = 0; i < console_size; i += n + 1) { ++ for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { ++ ch = console_buffer[(console_index + i + n) % console_size]; ++ if (ch == '\n') ++ break; ++ line[n] = ch; ++ } ++ ++ ++ if (n > 0) { ++ if (line[n - 1] == '\r') ++ n--; ++ line[n] = 0; ++ /* Don't use DHD_ERROR macro since we print ++ * a lot of information quickly. The macro ++ * will truncate a lot of the printfs ++ */ ++ ++ if (dhd_msg_level & DHD_ERROR_VAL) ++ printf("CONSOLE: %s\n", line); ++ } ++ } ++ } ++ } ++ ++printbuf: ++ if (sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP)) { ++ DHD_ERROR(("%s: %s\n", __FUNCTION__, strbuf.origbuf)); ++ } ++ ++ ++done: ++ if (mbuffer) ++ MFREE(bus->dhd->osh, mbuffer, msize); ++ if (str) ++ MFREE(bus->dhd->osh, str, maxstrlen); ++ if (console_buffer) ++ MFREE(bus->dhd->osh, console_buffer, console_size); ++ ++ return bcmerror; ++} ++#endif /* #ifdef DHD_DEBUG */ ++ ++ ++int ++dhdsdio_downloadvars(dhd_bus_t *bus, void *arg, int len) ++{ ++ int bcmerror = BCME_OK; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ /* Basic sanity checks */ ++ if (bus->dhd->up) { ++ bcmerror = BCME_NOTDOWN; ++ goto err; ++ } ++ if (!len) { ++ bcmerror = BCME_BUFTOOSHORT; ++ goto err; ++ } ++ ++ /* Free the old ones and replace with passed variables */ ++ if (bus->vars) ++ MFREE(bus->dhd->osh, bus->vars, bus->varsz); ++ ++ bus->vars = MALLOC(bus->dhd->osh, len); ++ bus->varsz = bus->vars ? len : 0; ++ if (bus->vars == NULL) { ++ bcmerror = BCME_NOMEM; ++ goto err; ++ } ++ ++ /* Copy the passed variables, which should include the terminating double-null */ ++ bcopy(arg, bus->vars, bus->varsz); ++err: ++ return bcmerror; ++} ++ ++#ifdef DHD_DEBUG ++ ++#define CC_PLL_CHIPCTRL_SERIAL_ENAB (1 << 24) ++#define CC_CHIPCTRL_JTAG_SEL (1 << 3) ++#define CC_CHIPCTRL_GPIO_SEL (0x3) ++#define CC_PLL_CHIPCTRL_SERIAL_ENAB_4334 (1 << 28) ++ ++static int ++dhd_serialconsole(dhd_bus_t *bus, bool set, bool enable, int *bcmerror) ++{ ++ int int_val; ++ uint32 addr, data, uart_enab = 0; ++ uint32 jtag_sel = CC_CHIPCTRL_JTAG_SEL; ++ uint32 gpio_sel = CC_CHIPCTRL_GPIO_SEL; ++ ++ addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr); ++ data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data); ++ *bcmerror = 0; ++ ++ bcmsdh_reg_write(bus->sdh, addr, 4, 1); ++ if (bcmsdh_regfail(bus->sdh)) { ++ *bcmerror = BCME_SDIO_ERROR; ++ return -1; ++ } ++ int_val = bcmsdh_reg_read(bus->sdh, data, 4); ++ if (bcmsdh_regfail(bus->sdh)) { ++ *bcmerror = BCME_SDIO_ERROR; ++ return -1; ++ } ++ if (bus->sih->chip == BCM4330_CHIP_ID) { ++ uart_enab = CC_PLL_CHIPCTRL_SERIAL_ENAB; ++ } ++ else if (bus->sih->chip == BCM4334_CHIP_ID || ++ bus->sih->chip == BCM43341_CHIP_ID) { ++ if (enable) { ++ /* Moved to PMU chipcontrol 1 from 4330 */ ++ int_val &= ~gpio_sel; ++ int_val |= jtag_sel; ++ } else { ++ int_val |= gpio_sel; ++ int_val &= ~jtag_sel; ++ } ++ uart_enab = CC_PLL_CHIPCTRL_SERIAL_ENAB_4334; ++ } ++ ++ if (!set) ++ return (int_val & uart_enab); ++ if (enable) ++ int_val |= uart_enab; ++ else ++ int_val &= ~uart_enab; ++ bcmsdh_reg_write(bus->sdh, data, 4, int_val); ++ if (bcmsdh_regfail(bus->sdh)) { ++ *bcmerror = BCME_SDIO_ERROR; ++ return -1; ++ } ++ if (bus->sih->chip == BCM4330_CHIP_ID) { ++ uint32 chipcontrol; ++ addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol); ++ chipcontrol = bcmsdh_reg_read(bus->sdh, addr, 4); ++ chipcontrol &= ~jtag_sel; ++ if (enable) { ++ chipcontrol |= jtag_sel; ++ chipcontrol &= ~gpio_sel; ++ } ++ bcmsdh_reg_write(bus->sdh, addr, 4, chipcontrol); ++ } ++ ++ return (int_val & uart_enab); ++} ++#endif ++ ++static int ++dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const char *name, ++ void *params, int plen, void *arg, int len, int val_size) ++{ ++ int bcmerror = 0; ++ int32 int_val = 0; ++ bool bool_val = 0; ++ ++ DHD_TRACE(("%s: Enter, action %d name %s params %p plen %d arg %p len %d val_size %d\n", ++ __FUNCTION__, actionid, name, params, plen, arg, len, val_size)); ++ ++ if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0) ++ goto exit; ++ ++ if (plen >= (int)sizeof(int_val)) ++ bcopy(params, &int_val, sizeof(int_val)); ++ ++ bool_val = (int_val != 0) ? TRUE : FALSE; ++ ++ ++ /* Some ioctls use the bus */ ++ dhd_os_sdlock(bus->dhd); ++ ++ /* Check if dongle is in reset. If so, only allow DEVRESET iovars */ ++ if (bus->dhd->dongle_reset && !(actionid == IOV_SVAL(IOV_DEVRESET) || ++ actionid == IOV_GVAL(IOV_DEVRESET))) { ++ bcmerror = BCME_NOTREADY; ++ goto exit; ++ } ++ ++ /* ++ * Special handling for keepSdioOn: New SDIO Wake-up Mechanism ++ */ ++ if ((vi->varid == IOV_KSO) && (IOV_ISSET(actionid))) { ++ dhdsdio_clk_kso_iovar(bus, bool_val); ++ goto exit; ++ } else if ((vi->varid == IOV_DEVSLEEP) && (IOV_ISSET(actionid))) { ++ { ++ dhdsdio_clk_devsleep_iovar(bus, bool_val); ++ if (!SLPAUTO_ENAB(bus) && (bool_val == FALSE) && (bus->ipend)) { ++ DHD_ERROR(("INT pending in devsleep 1, dpc_sched: %d\n", ++ bus->dpc_sched)); ++ if (!bus->dpc_sched) { ++ bus->dpc_sched = TRUE; ++ dhd_sched_dpc(bus->dhd); ++ } ++ } ++ } ++ goto exit; ++ } ++ ++ /* Handle sleep stuff before any clock mucking */ ++ if (vi->varid == IOV_SLEEP) { ++ if (IOV_ISSET(actionid)) { ++ bcmerror = dhdsdio_bussleep(bus, bool_val); ++ } else { ++ int_val = (int32)bus->sleeping; ++ bcopy(&int_val, arg, val_size); ++ } ++ goto exit; ++ } ++ ++ /* Request clock to allow SDIO accesses */ ++ if (!bus->dhd->dongle_reset) { ++ BUS_WAKE(bus); ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ } ++ ++ switch (actionid) { ++ case IOV_GVAL(IOV_INTR): ++ int_val = (int32)bus->intr; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_INTR): ++ bus->intr = bool_val; ++ bus->intdis = FALSE; ++ if (bus->dhd->up) { ++ if (bus->intr) { ++ DHD_INTR(("%s: enable SDIO device interrupts\n", __FUNCTION__)); ++ bcmsdh_intr_enable(bus->sdh); ++ } else { ++ DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); ++ bcmsdh_intr_disable(bus->sdh); ++ } ++ } ++ break; ++ ++ case IOV_GVAL(IOV_POLLRATE): ++ int_val = (int32)bus->pollrate; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_POLLRATE): ++ bus->pollrate = (uint)int_val; ++ bus->poll = (bus->pollrate != 0); ++ break; ++ ++ case IOV_GVAL(IOV_IDLETIME): ++ int_val = bus->idletime; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_IDLETIME): ++ if ((int_val < 0) && (int_val != DHD_IDLE_IMMEDIATE)) { ++ bcmerror = BCME_BADARG; ++ } else { ++ bus->idletime = int_val; ++ } ++ break; ++ ++ case IOV_GVAL(IOV_IDLECLOCK): ++ int_val = (int32)bus->idleclock; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_IDLECLOCK): ++ bus->idleclock = int_val; ++ break; ++ ++ case IOV_GVAL(IOV_SD1IDLE): ++ int_val = (int32)sd1idle; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_SD1IDLE): ++ sd1idle = bool_val; ++ break; ++ ++ ++ case IOV_SVAL(IOV_MEMBYTES): ++ case IOV_GVAL(IOV_MEMBYTES): ++ { ++ uint32 address; ++ uint size, dsize; ++ uint8 *data; ++ ++ bool set = (actionid == IOV_SVAL(IOV_MEMBYTES)); ++ ++ ASSERT(plen >= 2*sizeof(int)); ++ ++ address = (uint32)int_val; ++ bcopy((char *)params + sizeof(int_val), &int_val, sizeof(int_val)); ++ size = (uint)int_val; ++ ++ /* Do some validation */ ++ dsize = set ? plen - (2 * sizeof(int)) : len; ++ if (dsize < size) { ++ DHD_ERROR(("%s: error on %s membytes, addr 0x%08x size %d dsize %d\n", ++ __FUNCTION__, (set ? "set" : "get"), address, size, dsize)); ++ bcmerror = BCME_BADARG; ++ break; ++ } ++ ++ DHD_INFO(("%s: Request to %s %d bytes at address 0x%08x\n", __FUNCTION__, ++ (set ? "write" : "read"), size, address)); ++ ++ /* If we know about SOCRAM, check for a fit */ ++ if ((bus->orig_ramsize) && ++ ((address > bus->orig_ramsize) || (address + size > bus->orig_ramsize))) ++ { ++ uint8 enable, protect, remap; ++ si_socdevram(bus->sih, FALSE, &enable, &protect, &remap); ++ if (!enable || protect) { ++ DHD_ERROR(("%s: ramsize 0x%08x doesn't have %d bytes at 0x%08x\n", ++ __FUNCTION__, bus->orig_ramsize, size, address)); ++ DHD_ERROR(("%s: socram enable %d, protect %d\n", ++ __FUNCTION__, enable, protect)); ++ bcmerror = BCME_BADARG; ++ break; ++ } ++ ++ if (!REMAP_ENAB(bus) && (address >= SOCDEVRAM_ARM_ADDR)) { ++ uint32 devramsize = si_socdevram_size(bus->sih); ++ if ((address < SOCDEVRAM_ARM_ADDR) || ++ (address + size > (SOCDEVRAM_ARM_ADDR + devramsize))) { ++ DHD_ERROR(("%s: bad address 0x%08x, size 0x%08x\n", ++ __FUNCTION__, address, size)); ++ DHD_ERROR(("%s: socram range 0x%08x,size 0x%08x\n", ++ __FUNCTION__, SOCDEVRAM_ARM_ADDR, devramsize)); ++ bcmerror = BCME_BADARG; ++ break; ++ } ++ /* move it such that address is real now */ ++ address -= SOCDEVRAM_ARM_ADDR; ++ address += SOCDEVRAM_BP_ADDR; ++ DHD_INFO(("%s: Request to %s %d bytes @ Mapped address 0x%08x\n", ++ __FUNCTION__, (set ? "write" : "read"), size, address)); ++ } else if (REMAP_ENAB(bus) && REMAP_ISADDR(bus, address) && remap) { ++ /* Can not access remap region while devram remap bit is set ++ * ROM content would be returned in this case ++ */ ++ DHD_ERROR(("%s: Need to disable remap for address 0x%08x\n", ++ __FUNCTION__, address)); ++ bcmerror = BCME_ERROR; ++ break; ++ } ++ } ++ ++ /* Generate the actual data pointer */ ++ data = set ? (uint8*)params + 2 * sizeof(int): (uint8*)arg; ++ ++ /* Call to do the transfer */ ++ bcmerror = dhdsdio_membytes(bus, set, address, data, size); ++ ++ break; ++ } ++ ++ case IOV_GVAL(IOV_MEMSIZE): ++ int_val = (int32)bus->ramsize; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_GVAL(IOV_SDIOD_DRIVE): ++ int_val = (int32)dhd_sdiod_drive_strength; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_SDIOD_DRIVE): ++ dhd_sdiod_drive_strength = int_val; ++ si_sdiod_drive_strength_init(bus->sih, bus->dhd->osh, dhd_sdiod_drive_strength); ++ break; ++ ++ case IOV_SVAL(IOV_SET_DOWNLOAD_STATE): ++ bcmerror = dhdsdio_download_state(bus, bool_val); ++ break; ++ ++ case IOV_SVAL(IOV_SOCRAM_STATE): ++ bcmerror = dhdsdio_download_state(bus, bool_val); ++ break; ++ ++ case IOV_SVAL(IOV_VARS): ++ bcmerror = dhdsdio_downloadvars(bus, arg, len); ++ break; ++ ++ case IOV_GVAL(IOV_READAHEAD): ++ int_val = (int32)dhd_readahead; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_READAHEAD): ++ if (bool_val && !dhd_readahead) ++ bus->nextlen = 0; ++ dhd_readahead = bool_val; ++ break; ++ ++ case IOV_GVAL(IOV_SDRXCHAIN): ++ int_val = (int32)bus->use_rxchain; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_SDRXCHAIN): ++ if (bool_val && !bus->sd_rxchain) ++ bcmerror = BCME_UNSUPPORTED; ++ else ++ bus->use_rxchain = bool_val; ++ break; ++ case IOV_GVAL(IOV_ALIGNCTL): ++ int_val = (int32)dhd_alignctl; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_ALIGNCTL): ++ dhd_alignctl = bool_val; ++ break; ++ ++ case IOV_GVAL(IOV_SDALIGN): ++ int_val = DHD_SDALIGN; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++#ifdef DHD_DEBUG ++ case IOV_GVAL(IOV_VARS): ++ if (bus->varsz < (uint)len) ++ bcopy(bus->vars, arg, bus->varsz); ++ else ++ bcmerror = BCME_BUFTOOSHORT; ++ break; ++#endif /* DHD_DEBUG */ ++ ++#ifdef DHD_DEBUG ++ case IOV_GVAL(IOV_SDREG): ++ { ++ sdreg_t *sd_ptr; ++ uint32 addr, size; ++ ++ sd_ptr = (sdreg_t *)params; ++ ++ addr = (uintptr)bus->regs + sd_ptr->offset; ++ size = sd_ptr->func; ++ int_val = (int32)bcmsdh_reg_read(bus->sdh, addr, size); ++ if (bcmsdh_regfail(bus->sdh)) ++ bcmerror = BCME_SDIO_ERROR; ++ bcopy(&int_val, arg, sizeof(int32)); ++ break; ++ } ++ ++ case IOV_SVAL(IOV_SDREG): ++ { ++ sdreg_t *sd_ptr; ++ uint32 addr, size; ++ ++ sd_ptr = (sdreg_t *)params; ++ ++ addr = (uintptr)bus->regs + sd_ptr->offset; ++ size = sd_ptr->func; ++ bcmsdh_reg_write(bus->sdh, addr, size, sd_ptr->value); ++ if (bcmsdh_regfail(bus->sdh)) ++ bcmerror = BCME_SDIO_ERROR; ++ break; ++ } ++ ++ /* Same as above, but offset is not backplane (not SDIO core) */ ++ case IOV_GVAL(IOV_SBREG): ++ { ++ sdreg_t sdreg; ++ uint32 addr, size; ++ ++ bcopy(params, &sdreg, sizeof(sdreg)); ++ ++ addr = SI_ENUM_BASE + sdreg.offset; ++ size = sdreg.func; ++ int_val = (int32)bcmsdh_reg_read(bus->sdh, addr, size); ++ if (bcmsdh_regfail(bus->sdh)) ++ bcmerror = BCME_SDIO_ERROR; ++ bcopy(&int_val, arg, sizeof(int32)); ++ break; ++ } ++ ++ case IOV_SVAL(IOV_SBREG): ++ { ++ sdreg_t sdreg; ++ uint32 addr, size; ++ ++ bcopy(params, &sdreg, sizeof(sdreg)); ++ ++ addr = SI_ENUM_BASE + sdreg.offset; ++ size = sdreg.func; ++ bcmsdh_reg_write(bus->sdh, addr, size, sdreg.value); ++ if (bcmsdh_regfail(bus->sdh)) ++ bcmerror = BCME_SDIO_ERROR; ++ break; ++ } ++ ++ case IOV_GVAL(IOV_SDCIS): ++ { ++ *(char *)arg = 0; ++ ++ bcmstrcat(arg, "\nFunc 0\n"); ++ bcmsdh_cis_read(bus->sdh, 0x10, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT); ++ bcmstrcat(arg, "\nFunc 1\n"); ++ bcmsdh_cis_read(bus->sdh, 0x11, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT); ++ bcmstrcat(arg, "\nFunc 2\n"); ++ bcmsdh_cis_read(bus->sdh, 0x12, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT); ++ break; ++ } ++ ++ case IOV_GVAL(IOV_FORCEEVEN): ++ int_val = (int32)forcealign; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_FORCEEVEN): ++ forcealign = bool_val; ++ break; ++ ++ case IOV_GVAL(IOV_TXBOUND): ++ int_val = (int32)dhd_txbound; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_TXBOUND): ++ dhd_txbound = (uint)int_val; ++ break; ++ ++ case IOV_GVAL(IOV_RXBOUND): ++ int_val = (int32)dhd_rxbound; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_RXBOUND): ++ dhd_rxbound = (uint)int_val; ++ break; ++ ++ case IOV_GVAL(IOV_TXMINMAX): ++ int_val = (int32)dhd_txminmax; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_TXMINMAX): ++ dhd_txminmax = (uint)int_val; ++ break; ++ ++ case IOV_GVAL(IOV_SERIALCONS): ++ int_val = dhd_serialconsole(bus, FALSE, 0, &bcmerror); ++ if (bcmerror != 0) ++ break; ++ ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_SERIALCONS): ++ dhd_serialconsole(bus, TRUE, bool_val, &bcmerror); ++ break; ++ ++ ++ ++#endif /* DHD_DEBUG */ ++ ++ ++#ifdef SDTEST ++ case IOV_GVAL(IOV_EXTLOOP): ++ int_val = (int32)bus->ext_loop; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_EXTLOOP): ++ bus->ext_loop = bool_val; ++ break; ++ ++ case IOV_GVAL(IOV_PKTGEN): ++ bcmerror = dhdsdio_pktgen_get(bus, arg); ++ break; ++ ++ case IOV_SVAL(IOV_PKTGEN): ++ bcmerror = dhdsdio_pktgen_set(bus, arg); ++ break; ++#endif /* SDTEST */ ++ ++#if defined(SDIO_CRC_ERROR_FIX) ++ case IOV_GVAL(IOV_WATERMARK): ++ int_val = (int32)watermark; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_WATERMARK): ++ watermark = (uint)int_val; ++ watermark = (watermark > SBSDIO_WATERMARK_MASK) ? SBSDIO_WATERMARK_MASK : watermark; ++ DHD_ERROR(("Setting watermark as 0x%x.\n", watermark)); ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK, (uint8)watermark, NULL); ++ break; ++ ++ case IOV_GVAL(IOV_MESBUSYCTRL): ++ int_val = (int32)mesbusyctrl; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_MESBUSYCTRL): ++ mesbusyctrl = (uint)int_val; ++ mesbusyctrl = (mesbusyctrl > SBSDIO_MESBUSYCTRL_MASK) ++ ? SBSDIO_MESBUSYCTRL_MASK : mesbusyctrl; ++ DHD_ERROR(("Setting mesbusyctrl as 0x%x.\n", mesbusyctrl)); ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_MESBUSYCTRL, ++ ((uint8)mesbusyctrl | 0x80), NULL); ++ break; ++#endif /* SDIO_CRC_ERROR_FIX */ ++ ++ case IOV_GVAL(IOV_DONGLEISOLATION): ++ int_val = bus->dhd->dongle_isolation; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_DONGLEISOLATION): ++ bus->dhd->dongle_isolation = bool_val; ++ break; ++ ++ case IOV_SVAL(IOV_DEVRESET): ++ DHD_TRACE(("%s: Called set IOV_DEVRESET=%d dongle_reset=%d busstate=%d\n", ++ __FUNCTION__, bool_val, bus->dhd->dongle_reset, ++ bus->dhd->busstate)); ++ ++ ASSERT(bus->dhd->osh); ++ /* ASSERT(bus->cl_devid); */ ++ ++ dhd_bus_devreset(bus->dhd, (uint8)bool_val); ++ ++ break; ++#ifdef SOFTAP ++ case IOV_GVAL(IOV_FWPATH): ++ { ++ uint32 fw_path_len; ++ ++ fw_path_len = strlen(bus->fw_path); ++ DHD_INFO(("[softap] get fwpath, l=%d\n", len)); ++ ++ if (fw_path_len > len-1) { ++ bcmerror = BCME_BUFTOOSHORT; ++ break; ++ } ++ ++ if (fw_path_len) { ++ bcopy(bus->fw_path, arg, fw_path_len); ++ ((uchar*)arg)[fw_path_len] = 0; ++ } ++ break; ++ } ++ ++ case IOV_SVAL(IOV_FWPATH): ++ DHD_INFO(("[softap] set fwpath, idx=%d\n", int_val)); ++ ++ switch (int_val) { ++ case 1: ++ bus->fw_path = fw_path; /* ordinary one */ ++ break; ++ case 2: ++ bus->fw_path = fw_path2; ++ break; ++ default: ++ bcmerror = BCME_BADARG; ++ break; ++ } ++ ++ DHD_INFO(("[softap] new fw path: %s\n", (bus->fw_path[0] ? bus->fw_path : "NULL"))); ++ break; ++ ++#endif /* SOFTAP */ ++ case IOV_GVAL(IOV_DEVRESET): ++ DHD_TRACE(("%s: Called get IOV_DEVRESET\n", __FUNCTION__)); ++ ++ /* Get its status */ ++ int_val = (bool) bus->dhd->dongle_reset; ++ bcopy(&int_val, arg, val_size); ++ ++ break; ++ ++ case IOV_GVAL(IOV_KSO): ++ int_val = dhdsdio_sleepcsr_get(bus); ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_GVAL(IOV_DEVCAP): ++ int_val = dhdsdio_devcap_get(bus); ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_DEVCAP): ++ dhdsdio_devcap_set(bus, (uint8) int_val); ++ break; ++ ++#ifdef BCMSDIOH_TXGLOM ++ case IOV_GVAL(IOV_TXGLOMSIZE): ++ int_val = (int32)bus->glomsize; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_TXGLOMSIZE): ++ if (int_val > SDPCM_MAXGLOM_SIZE) { ++ bcmerror = BCME_ERROR; ++ } else { ++ bus->glomsize = (uint)int_val; ++ } ++ break; ++ case IOV_GVAL(IOV_TXGLOMMODE): ++ int_val = (int32)bus->glom_mode; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_TXGLOMMODE): ++ if ((int_val != SDPCM_TXGLOM_CPY) && (int_val != SDPCM_TXGLOM_MDESC)) { ++ bcmerror = BCME_RANGE; ++ } else { ++ if ((bus->glom_mode = bcmsdh_set_mode(bus->sdh, (uint)int_val)) != int_val) ++ bcmerror = BCME_ERROR; ++ } ++ break; ++#endif /* BCMSDIOH_TXGLOM */ ++ default: ++ bcmerror = BCME_UNSUPPORTED; ++ break; ++ } ++ ++exit: ++ if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { ++ bus->activity = FALSE; ++ dhdsdio_clkctl(bus, CLK_NONE, TRUE); ++ } ++ ++ dhd_os_sdunlock(bus->dhd); ++ ++ return bcmerror; ++} ++ ++static int ++dhdsdio_write_vars(dhd_bus_t *bus) ++{ ++ int bcmerror = 0; ++ uint32 varsize, phys_size; ++ uint32 varaddr; ++ uint8 *vbuffer; ++ uint32 varsizew; ++#ifdef DHD_DEBUG ++ uint8 *nvram_ularray; ++#endif /* DHD_DEBUG */ ++ ++ /* Even if there are no vars are to be written, we still need to set the ramsize. */ ++ varsize = bus->varsz ? ROUNDUP(bus->varsz, 4) : 0; ++ varaddr = (bus->ramsize - 4) - varsize; ++ ++ varaddr += bus->dongle_ram_base; ++ ++ if (bus->vars) { ++ if ((bus->sih->buscoretype == SDIOD_CORE_ID) && (bus->sdpcmrev == 7)) { ++ if (((varaddr & 0x3C) == 0x3C) && (varsize > 4)) { ++ DHD_ERROR(("PR85623WAR in place\n")); ++ varsize += 4; ++ varaddr -= 4; ++ } ++ } ++ ++ vbuffer = (uint8 *)MALLOC(bus->dhd->osh, varsize); ++ if (!vbuffer) ++ return BCME_NOMEM; ++ ++ bzero(vbuffer, varsize); ++ bcopy(bus->vars, vbuffer, bus->varsz); ++ ++ /* Write the vars list */ ++ bcmerror = dhdsdio_membytes(bus, TRUE, varaddr, vbuffer, varsize); ++#ifdef DHD_DEBUG ++ /* Verify NVRAM bytes */ ++ DHD_INFO(("Compare NVRAM dl & ul; varsize=%d\n", varsize)); ++ nvram_ularray = (uint8*)MALLOC(bus->dhd->osh, varsize); ++ if (!nvram_ularray) ++ return BCME_NOMEM; ++ ++ /* Upload image to verify downloaded contents. */ ++ memset(nvram_ularray, 0xaa, varsize); ++ ++ /* Read the vars list to temp buffer for comparison */ ++ bcmerror = dhdsdio_membytes(bus, FALSE, varaddr, nvram_ularray, varsize); ++ if (bcmerror) { ++ DHD_ERROR(("%s: error %d on reading %d nvram bytes at 0x%08x\n", ++ __FUNCTION__, bcmerror, varsize, varaddr)); ++ } ++ /* Compare the org NVRAM with the one read from RAM */ ++ if (memcmp(vbuffer, nvram_ularray, varsize)) { ++ DHD_ERROR(("%s: Downloaded NVRAM image is corrupted.\n", __FUNCTION__)); ++ } else ++ DHD_ERROR(("%s: Download, Upload and compare of NVRAM succeeded.\n", ++ __FUNCTION__)); ++ ++ MFREE(bus->dhd->osh, nvram_ularray, varsize); ++#endif /* DHD_DEBUG */ ++ ++ MFREE(bus->dhd->osh, vbuffer, varsize); ++ } ++ ++ phys_size = REMAP_ENAB(bus) ? bus->ramsize : bus->orig_ramsize; ++ ++ phys_size += bus->dongle_ram_base; ++ ++ /* adjust to the user specified RAM */ ++ DHD_INFO(("Physical memory size: %d, usable memory size: %d\n", ++ phys_size, bus->ramsize)); ++ DHD_INFO(("Vars are at %d, orig varsize is %d\n", ++ varaddr, varsize)); ++ varsize = ((phys_size - 4) - varaddr); ++ ++ /* ++ * Determine the length token: ++ * Varsize, converted to words, in lower 16-bits, checksum in upper 16-bits. ++ */ ++ if (bcmerror) { ++ varsizew = 0; ++ } else { ++ varsizew = varsize / 4; ++ varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF); ++ varsizew = htol32(varsizew); ++ } ++ ++ DHD_INFO(("New varsize is %d, length token=0x%08x\n", varsize, varsizew)); ++ ++ /* Write the length token to the last word */ ++ bcmerror = dhdsdio_membytes(bus, TRUE, (phys_size - 4), ++ (uint8*)&varsizew, 4); ++ ++ return bcmerror; ++} ++ ++static int ++dhdsdio_download_state(dhd_bus_t *bus, bool enter) ++{ ++ uint retries; ++ int bcmerror = 0; ++ int foundcr4 = 0; ++ ++ /* To enter download state, disable ARM and reset SOCRAM. ++ * To exit download state, simply reset ARM (default is RAM boot). ++ */ ++ if (enter) { ++ bus->alp_only = TRUE; ++ ++ if (!(si_setcore(bus->sih, ARM7S_CORE_ID, 0)) && ++ !(si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) { ++ if (si_setcore(bus->sih, ARMCR4_CORE_ID, 0)) { ++ foundcr4 = 1; ++ } else { ++ DHD_ERROR(("%s: Failed to find ARM core!\n", __FUNCTION__)); ++ bcmerror = BCME_ERROR; ++ goto fail; ++ } ++ } ++ ++ if (!foundcr4) { ++ si_core_disable(bus->sih, 0); ++ if (bcmsdh_regfail(bus->sdh)) { ++ bcmerror = BCME_SDIO_ERROR; ++ goto fail; ++ } ++ ++ if (!(si_setcore(bus->sih, SOCRAM_CORE_ID, 0))) { ++ DHD_ERROR(("%s: Failed to find SOCRAM core!\n", __FUNCTION__)); ++ bcmerror = BCME_ERROR; ++ goto fail; ++ } ++ ++ si_core_reset(bus->sih, 0, 0); ++ if (bcmsdh_regfail(bus->sdh)) { ++ DHD_ERROR(("%s: Failure trying reset SOCRAM core?\n", __FUNCTION__)); ++ bcmerror = BCME_SDIO_ERROR; ++ goto fail; ++ } ++ ++ /* Disable remap for download */ ++ if (REMAP_ENAB(bus) && si_socdevram_remap_isenb(bus->sih)) ++ dhdsdio_devram_remap(bus, FALSE); ++ ++ /* Clear the top bit of memory */ ++ if (bus->ramsize) { ++ uint32 zeros = 0; ++ if (dhdsdio_membytes(bus, TRUE, bus->ramsize - 4, (uint8*)&zeros, 4) < 0) { ++ bcmerror = BCME_SDIO_ERROR; ++ goto fail; ++ } ++ } ++ } else { ++ /* For CR4, ++ * Halt ARM ++ * Remove ARM reset ++ * Read RAM base address [0x18_0000] ++ * [next] Download firmware ++ * [done at else] Populate the reset vector ++ * [done at else] Remove ARM halt ++ */ ++ /* Halt ARM & remove reset */ ++ si_core_reset(bus->sih, SICF_CPUHALT, SICF_CPUHALT); ++ } ++ } else { ++ if (!si_setcore(bus->sih, ARMCR4_CORE_ID, 0)) { ++ if (!(si_setcore(bus->sih, SOCRAM_CORE_ID, 0))) { ++ DHD_ERROR(("%s: Failed to find SOCRAM core!\n", __FUNCTION__)); ++ bcmerror = BCME_ERROR; ++ goto fail; ++ } ++ ++ if (!si_iscoreup(bus->sih)) { ++ DHD_ERROR(("%s: SOCRAM core is down after reset?\n", __FUNCTION__)); ++ bcmerror = BCME_ERROR; ++ goto fail; ++ } ++ ++ if ((bcmerror = dhdsdio_write_vars(bus))) { ++ DHD_ERROR(("%s: could not write vars to RAM\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ /* Enable remap before ARM reset but after vars. ++ * No backplane access in remap mode ++ */ ++ if (REMAP_ENAB(bus) && !si_socdevram_remap_isenb(bus->sih)) ++ dhdsdio_devram_remap(bus, TRUE); ++ ++ if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0) && ++ !si_setcore(bus->sih, SDIOD_CORE_ID, 0)) { ++ DHD_ERROR(("%s: Can't change back to SDIO core?\n", __FUNCTION__)); ++ bcmerror = BCME_ERROR; ++ goto fail; ++ } ++ W_SDREG(0xFFFFFFFF, &bus->regs->intstatus, retries); ++ ++ ++ if (!(si_setcore(bus->sih, ARM7S_CORE_ID, 0)) && ++ !(si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) { ++ DHD_ERROR(("%s: Failed to find ARM core!\n", __FUNCTION__)); ++ bcmerror = BCME_ERROR; ++ goto fail; ++ } ++ } else { ++ /* cr4 has no socram, but tcm's */ ++ /* write vars */ ++ if ((bcmerror = dhdsdio_write_vars(bus))) { ++ DHD_ERROR(("%s: could not write vars to RAM\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0) && ++ !si_setcore(bus->sih, SDIOD_CORE_ID, 0)) { ++ DHD_ERROR(("%s: Can't change back to SDIO core?\n", __FUNCTION__)); ++ bcmerror = BCME_ERROR; ++ goto fail; ++ } ++ W_SDREG(0xFFFFFFFF, &bus->regs->intstatus, retries); ++ ++ /* switch back to arm core again */ ++ if (!(si_setcore(bus->sih, ARMCR4_CORE_ID, 0))) { ++ DHD_ERROR(("%s: Failed to find ARM CR4 core!\n", __FUNCTION__)); ++ bcmerror = BCME_ERROR; ++ goto fail; ++ } ++ /* write address 0 with reset instruction */ ++ bcmerror = dhdsdio_membytes(bus, TRUE, 0, ++ (uint8 *)&bus->resetinstr, sizeof(bus->resetinstr)); ++ ++ /* now remove reset and halt and continue to run CR4 */ ++ } ++ ++ si_core_reset(bus->sih, 0, 0); ++ if (bcmsdh_regfail(bus->sdh)) { ++ DHD_ERROR(("%s: Failure trying to reset ARM core?\n", __FUNCTION__)); ++ bcmerror = BCME_SDIO_ERROR; ++ goto fail; ++ } ++ ++ /* Allow HT Clock now that the ARM is running. */ ++ bus->alp_only = FALSE; ++ ++ bus->dhd->busstate = DHD_BUS_LOAD; ++ } ++ ++fail: ++ /* Always return to SDIOD core */ ++ if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0)) ++ si_setcore(bus->sih, SDIOD_CORE_ID, 0); ++ ++ return bcmerror; ++} ++ ++int ++dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name, ++ void *params, int plen, void *arg, int len, bool set) ++{ ++ dhd_bus_t *bus = dhdp->bus; ++ const bcm_iovar_t *vi = NULL; ++ int bcmerror = 0; ++ int val_size; ++ uint32 actionid; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ ASSERT(name); ++ ASSERT(len >= 0); ++ ++ /* Get MUST have return space */ ++ ASSERT(set || (arg && len)); ++ ++ /* Set does NOT take qualifiers */ ++ ASSERT(!set || (!params && !plen)); ++ ++ /* Look up var locally; if not found pass to host driver */ ++ if ((vi = bcm_iovar_lookup(dhdsdio_iovars, name)) == NULL) { ++ dhd_os_sdlock(bus->dhd); ++ ++ BUS_WAKE(bus); ++ ++ /* Turn on clock in case SD command needs backplane */ ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ ++ bcmerror = bcmsdh_iovar_op(bus->sdh, name, params, plen, arg, len, set); ++ ++ /* Check for bus configuration changes of interest */ ++ ++ /* If it was divisor change, read the new one */ ++ if (set && strcmp(name, "sd_divisor") == 0) { ++ if (bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0, ++ &bus->sd_divisor, sizeof(int32), FALSE) != BCME_OK) { ++ bus->sd_divisor = -1; ++ DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, name)); ++ } else { ++ DHD_INFO(("%s: noted %s update, value now %d\n", ++ __FUNCTION__, name, bus->sd_divisor)); ++ } ++ } ++ /* If it was a mode change, read the new one */ ++ if (set && strcmp(name, "sd_mode") == 0) { ++ if (bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0, ++ &bus->sd_mode, sizeof(int32), FALSE) != BCME_OK) { ++ bus->sd_mode = -1; ++ DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, name)); ++ } else { ++ DHD_INFO(("%s: noted %s update, value now %d\n", ++ __FUNCTION__, name, bus->sd_mode)); ++ } ++ } ++ /* Similar check for blocksize change */ ++ if (set && strcmp(name, "sd_blocksize") == 0) { ++ int32 fnum = 2; ++ if (bcmsdh_iovar_op(bus->sdh, "sd_blocksize", &fnum, sizeof(int32), ++ &bus->blocksize, sizeof(int32), FALSE) != BCME_OK) { ++ bus->blocksize = 0; ++ DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_blocksize")); ++ } else { ++ DHD_INFO(("%s: noted %s update, value now %d\n", ++ __FUNCTION__, "sd_blocksize", bus->blocksize)); ++ } ++ } ++ bus->roundup = MIN(max_roundup, bus->blocksize); ++ ++ if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { ++ bus->activity = FALSE; ++ dhdsdio_clkctl(bus, CLK_NONE, TRUE); ++ } ++ ++ dhd_os_sdunlock(bus->dhd); ++ goto exit; ++ } ++ ++ DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__, ++ name, (set ? "set" : "get"), len, plen)); ++ ++ /* set up 'params' pointer in case this is a set command so that ++ * the convenience int and bool code can be common to set and get ++ */ ++ if (params == NULL) { ++ params = arg; ++ plen = len; ++ } ++ ++ if (vi->type == IOVT_VOID) ++ val_size = 0; ++ else if (vi->type == IOVT_BUFFER) ++ val_size = len; ++ else ++ /* all other types are integer sized */ ++ val_size = sizeof(int); ++ ++ actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); ++ bcmerror = dhdsdio_doiovar(bus, vi, actionid, name, params, plen, arg, len, val_size); ++ ++exit: ++ return bcmerror; ++} ++ ++void ++dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex) ++{ ++ osl_t *osh; ++ uint32 local_hostintmask; ++ uint8 saveclk, dat; ++ uint retries; ++ int err; ++ if (!bus->dhd) ++ return; ++ ++ osh = bus->dhd->osh; ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ bcmsdh_waitlockfree(NULL); ++ ++ if (enforce_mutex) ++ dhd_os_sdlock(bus->dhd); ++ ++ if ((bus->dhd->busstate == DHD_BUS_DOWN) || bus->dhd->hang_was_sent) { ++ bus->dhd->busstate = DHD_BUS_DOWN; ++ bus->hostintmask = 0; ++ bcmsdh_intr_disable(bus->sdh); ++ } else { ++ BUS_WAKE(bus); ++ ++ if (KSO_ENAB(bus)) { ++ /* Mask the interrupt */ ++ dat = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_INTEN, NULL); ++ dat &= ~(INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN); ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_INTEN, dat, NULL); ++ } ++ ++ /* Change our idea of bus state */ ++ bus->dhd->busstate = DHD_BUS_DOWN; ++ ++ if (KSO_ENAB(bus)) { ++ ++ /* Enable clock for device interrupts */ ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ ++ /* Disable and clear interrupts at the chip level also */ ++ W_SDREG(0, &bus->regs->hostintmask, retries); ++ local_hostintmask = bus->hostintmask; ++ bus->hostintmask = 0; ++ ++ /* Force clocks on backplane to be sure F2 interrupt propagates */ ++ saveclk = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); ++ if (!err) { ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, ++ (saveclk | SBSDIO_FORCE_HT), &err); ++ } ++ if (err) { ++ DHD_ERROR(("%s: Failed to force clock for F2: err %d\n", __FUNCTION__, err)); ++ } ++ ++ /* Turn off the bus (F2), free any pending packets */ ++ DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); ++ bcmsdh_intr_disable(bus->sdh); ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, SDIO_FUNC_ENABLE_1, NULL); ++ ++ /* Clear any pending interrupts now that F2 is disabled */ ++ W_SDREG(local_hostintmask, &bus->regs->intstatus, retries); ++ } ++ ++ /* Turn off the backplane clock (only) */ ++ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); ++ } ++ ++ /* Clear the data packet queues */ ++ pktq_flush(osh, &bus->txq, TRUE, NULL, 0); ++ ++ /* Clear any held glomming stuff */ ++ if (bus->glomd) ++ PKTFREE(osh, bus->glomd, FALSE); ++ ++ if (bus->glom) ++ PKTFREE(osh, bus->glom, FALSE); ++ ++ bus->glom = bus->glomd = NULL; ++ ++ /* Clear rx control and wake any waiters */ ++ bus->rxlen = 0; ++ dhd_os_ioctl_resp_wake(bus->dhd); ++ ++ /* Reset some F2 state stuff */ ++ bus->rxskip = FALSE; ++ bus->tx_seq = bus->rx_seq = 0; ++ ++ if (enforce_mutex) ++ dhd_os_sdunlock(bus->dhd); ++} ++ ++#ifdef BCMSDIOH_TXGLOM ++void ++dhd_txglom_enable(dhd_pub_t *dhdp, bool enable) ++{ ++ dhd_bus_t *bus = dhdp->bus; ++ ++ char buf[256]; ++ uint32 rxglom; ++ int32 ret; ++ ++ if (enable) { ++ rxglom = 1; ++ memset(buf, 0, sizeof(buf)); ++ bcm_mkiovar("bus:rxglom", ++ (void *)&rxglom, ++ 4, buf, sizeof(buf)); ++ ret = dhd_wl_ioctl_cmd(dhdp, ++ WLC_SET_VAR, buf, ++ sizeof(buf), TRUE, 0); ++ if (!(ret < 0)) { ++ bus->glom_enable = TRUE; ++ } ++ } else { ++ bus->glom_enable = FALSE; ++ } ++} ++#endif /* BCMSDIOH_TXGLOM */ ++ ++int ++dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) ++{ ++ dhd_bus_t *bus = dhdp->bus; ++ dhd_timeout_t tmo; ++ uint retries = 0; ++ uint8 ready, enable; ++ int err, ret = 0; ++ uint8 saveclk; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ ASSERT(bus->dhd); ++ if (!bus->dhd) ++ return 0; ++ ++ if (enforce_mutex) ++ dhd_os_sdlock(bus->dhd); ++ ++ /* Make sure backplane clock is on, needed to generate F2 interrupt */ ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ if (bus->clkstate != CLK_AVAIL) { ++ DHD_ERROR(("%s: clock state is wrong. state = %d\n", __FUNCTION__, bus->clkstate)); ++ ret = -1; ++ goto exit; ++ } ++ ++ ++ /* Force clocks on backplane to be sure F2 interrupt propagates */ ++ saveclk = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); ++ if (!err) { ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, ++ (saveclk | SBSDIO_FORCE_HT), &err); ++ } ++ if (err) { ++ DHD_ERROR(("%s: Failed to force clock for F2: err %d\n", __FUNCTION__, err)); ++ ret = -1; ++ goto exit; ++ } ++ ++ /* Enable function 2 (frame transfers) */ ++ W_SDREG((SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT), ++ &bus->regs->tosbmailboxdata, retries); ++ enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2); ++ ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable, NULL); ++ ++ /* Give the dongle some time to do its thing and set IOR2 */ ++ dhd_timeout_start(&tmo, DHD_WAIT_F2RDY * 1000); ++ ++ ready = 0; ++ while (ready != enable && !dhd_timeout_expired(&tmo)) ++ ready = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IORDY, NULL); ++ ++ DHD_ERROR(("%s: enable 0x%02x, ready 0x%02x (waited %uus)\n", ++ __FUNCTION__, enable, ready, tmo.elapsed)); ++ ++ ++ /* If F2 successfully enabled, set core and enable interrupts */ ++ if (ready == enable) { ++ /* Make sure we're talking to the core. */ ++ if (!(bus->regs = si_setcore(bus->sih, PCMCIA_CORE_ID, 0))) ++ bus->regs = si_setcore(bus->sih, SDIOD_CORE_ID, 0); ++ ASSERT(bus->regs != NULL); ++ ++ /* Set up the interrupt mask and enable interrupts */ ++ bus->hostintmask = HOSTINTMASK; ++ /* corerev 4 could use the newer interrupt logic to detect the frames */ ++ if ((bus->sih->buscoretype == SDIOD_CORE_ID) && (bus->sdpcmrev == 4) && ++ (bus->rxint_mode != SDIO_DEVICE_HMB_RXINT)) { ++ bus->hostintmask &= ~I_HMB_FRAME_IND; ++ bus->hostintmask |= I_XMTDATA_AVAIL; ++ } ++ W_SDREG(bus->hostintmask, &bus->regs->hostintmask, retries); ++#ifdef SDIO_CRC_ERROR_FIX ++ if (bus->blocksize < 512) { ++ mesbusyctrl = watermark = bus->blocksize / 4; ++ } ++#endif /* SDIO_CRC_ERROR_FIX */ ++ ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK, (uint8)watermark, &err); ++#ifdef SDIO_CRC_ERROR_FIX ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_MESBUSYCTRL, ++ (uint8)mesbusyctrl|0x80, &err); ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, ++ SBSDIO_DEVCTL_EN_F2_BLK_WATERMARK, NULL); ++#endif /* SDIO_CRC_ERROR_FIX */ ++ ++ /* Set bus state according to enable result */ ++ dhdp->busstate = DHD_BUS_DATA; ++ ++ /* bcmsdh_intr_unmask(bus->sdh); */ ++ ++ bus->intdis = FALSE; ++ if (bus->intr) { ++ DHD_INTR(("%s: enable SDIO device interrupts\n", __FUNCTION__)); ++ bcmsdh_intr_enable(bus->sdh); ++ } else { ++ DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); ++ bcmsdh_intr_disable(bus->sdh); ++ } ++ ++ } ++ ++ ++ else { ++ /* Disable F2 again */ ++ enable = SDIO_FUNC_ENABLE_1; ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable, NULL); ++ } ++ ++ if (dhdsdio_sr_cap(bus)) ++ dhdsdio_sr_init(bus); ++ else ++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err); ++ ++ /* If we didn't come up, turn off backplane clock */ ++ if (dhdp->busstate != DHD_BUS_DATA) ++ dhdsdio_clkctl(bus, CLK_NONE, FALSE); ++ ++exit: ++ if (enforce_mutex) ++ dhd_os_sdunlock(bus->dhd); ++ ++ return ret; ++} ++ ++static void ++dhdsdio_rxfail(dhd_bus_t *bus, bool abort, bool rtx) ++{ ++ bcmsdh_info_t *sdh = bus->sdh; ++ sdpcmd_regs_t *regs = bus->regs; ++ uint retries = 0; ++ uint16 lastrbc; ++ uint8 hi, lo; ++ int err; ++ ++ DHD_ERROR(("%s: %sterminate frame%s\n", __FUNCTION__, ++ (abort ? "abort command, " : ""), (rtx ? ", send NAK" : ""))); ++ ++ if (!KSO_ENAB(bus)) { ++ DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); ++ return; ++ } ++ ++ if (abort) { ++ bcmsdh_abort(sdh, SDIO_FUNC_2); ++ } ++ ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, SFC_RF_TERM, &err); ++ bus->f1regdata++; ++ ++ /* Wait until the packet has been flushed (device/FIFO stable) */ ++ for (lastrbc = retries = 0xffff; retries > 0; retries--) { ++ hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCHI, NULL); ++ lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCLO, NULL); ++ bus->f1regdata += 2; ++ ++ if ((hi == 0) && (lo == 0)) ++ break; ++ ++ if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) { ++ DHD_ERROR(("%s: count growing: last 0x%04x now 0x%04x\n", ++ __FUNCTION__, lastrbc, ((hi << 8) + lo))); ++ } ++ lastrbc = (hi << 8) + lo; ++ } ++ ++ if (!retries) { ++ DHD_ERROR(("%s: count never zeroed: last 0x%04x\n", __FUNCTION__, lastrbc)); ++ } else { ++ DHD_INFO(("%s: flush took %d iterations\n", __FUNCTION__, (0xffff - retries))); ++ } ++ ++ if (rtx) { ++ bus->rxrtx++; ++ W_SDREG(SMB_NAK, ®s->tosbmailbox, retries); ++ bus->f1regdata++; ++ if (retries <= retry_limit) { ++ bus->rxskip = TRUE; ++ } ++ } ++ ++ /* Clear partial in any case */ ++ bus->nextlen = 0; ++ ++ /* If we can't reach the device, signal failure */ ++ if (err || bcmsdh_regfail(sdh)) ++ bus->dhd->busstate = DHD_BUS_DOWN; ++} ++ ++static void ++dhdsdio_read_control(dhd_bus_t *bus, uint8 *hdr, uint len, uint doff) ++{ ++ bcmsdh_info_t *sdh = bus->sdh; ++ uint rdlen, pad; ++ ++ int sdret; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ /* Control data already received in aligned rxctl */ ++ if ((bus->bus == SPI_BUS) && (!bus->usebufpool)) ++ goto gotpkt; ++ ++ ASSERT(bus->rxbuf); ++ /* Set rxctl for frame (w/optional alignment) */ ++ bus->rxctl = bus->rxbuf; ++ if (dhd_alignctl) { ++ bus->rxctl += firstread; ++ if ((pad = ((uintptr)bus->rxctl % DHD_SDALIGN))) ++ bus->rxctl += (DHD_SDALIGN - pad); ++ bus->rxctl -= firstread; ++ } ++ ASSERT(bus->rxctl >= bus->rxbuf); ++ ++ /* Copy the already-read portion over */ ++ bcopy(hdr, bus->rxctl, firstread); ++ if (len <= firstread) ++ goto gotpkt; ++ ++ /* Copy the full data pkt in gSPI case and process ioctl. */ ++ if (bus->bus == SPI_BUS) { ++ bcopy(hdr, bus->rxctl, len); ++ goto gotpkt; ++ } ++ ++ /* Raise rdlen to next SDIO block to avoid tail command */ ++ rdlen = len - firstread; ++ if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { ++ pad = bus->blocksize - (rdlen % bus->blocksize); ++ if ((pad <= bus->roundup) && (pad < bus->blocksize) && ++ ((len + pad) < bus->dhd->maxctl)) ++ rdlen += pad; ++ } else if (rdlen % DHD_SDALIGN) { ++ rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN); ++ } ++ ++ /* Satisfy length-alignment requirements */ ++ if (forcealign && (rdlen & (ALIGNMENT - 1))) ++ rdlen = ROUNDUP(rdlen, ALIGNMENT); ++ ++ /* Drop if the read is too big or it exceeds our maximum */ ++ if ((rdlen + firstread) > bus->dhd->maxctl) { ++ DHD_ERROR(("%s: %d-byte control read exceeds %d-byte buffer\n", ++ __FUNCTION__, rdlen, bus->dhd->maxctl)); ++ bus->dhd->rx_errors++; ++ dhdsdio_rxfail(bus, FALSE, FALSE); ++ goto done; ++ } ++ ++ if ((len - doff) > bus->dhd->maxctl) { ++ DHD_ERROR(("%s: %d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n", ++ __FUNCTION__, len, (len - doff), bus->dhd->maxctl)); ++ bus->dhd->rx_errors++; bus->rx_toolong++; ++ dhdsdio_rxfail(bus, FALSE, FALSE); ++ goto done; ++ } ++ ++ ++ /* Read remainder of frame body into the rxctl buffer */ ++ sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, ++ (bus->rxctl + firstread), rdlen, NULL, NULL, NULL); ++ bus->f2rxdata++; ++ ASSERT(sdret != BCME_PENDING); ++ ++ /* Control frame failures need retransmission */ ++ if (sdret < 0) { ++ DHD_ERROR(("%s: read %d control bytes failed: %d\n", __FUNCTION__, rdlen, sdret)); ++ bus->rxc_errors++; /* dhd.rx_ctlerrs is higher level */ ++ dhdsdio_rxfail(bus, TRUE, TRUE); ++ goto done; ++ } ++ ++gotpkt: ++ ++#ifdef DHD_DEBUG ++ if (DHD_BYTES_ON() && DHD_CTL_ON()) { ++ prhex("RxCtrl", bus->rxctl, len); ++ } ++#endif ++ ++ /* Point to valid data and indicate its length */ ++ bus->rxctl += doff; ++ bus->rxlen = len - doff; ++ ++done: ++ /* Awake any waiters */ ++ dhd_os_ioctl_resp_wake(bus->dhd); ++} ++ ++static uint8 ++dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) ++{ ++ uint16 dlen, totlen; ++ uint8 *dptr, num = 0; ++ ++ uint16 sublen, check; ++ void *pfirst, *plast, *pnext; ++ void * list_tail[DHD_MAX_IFS] = { NULL }; ++ void * list_head[DHD_MAX_IFS] = { NULL }; ++ uint8 idx; ++ osl_t *osh = bus->dhd->osh; ++ ++ int errcode; ++ uint8 chan, seq, doff, sfdoff; ++ uint8 txmax; ++ uchar reorder_info_buf[WLHOST_REORDERDATA_TOTLEN]; ++ uint reorder_info_len; ++ ++ int ifidx = 0; ++ bool usechain = bus->use_rxchain; ++ ++ /* If packets, issue read(s) and send up packet chain */ ++ /* Return sequence numbers consumed? */ ++ ++ DHD_TRACE(("dhdsdio_rxglom: start: glomd %p glom %p\n", bus->glomd, bus->glom)); ++ ++ /* If there's a descriptor, generate the packet chain */ ++ if (bus->glomd) { ++ dhd_os_sdlock_rxq(bus->dhd); ++ ++ pfirst = plast = pnext = NULL; ++ dlen = (uint16)PKTLEN(osh, bus->glomd); ++ dptr = PKTDATA(osh, bus->glomd); ++ if (!dlen || (dlen & 1)) { ++ DHD_ERROR(("%s: bad glomd len (%d), ignore descriptor\n", ++ __FUNCTION__, dlen)); ++ dlen = 0; ++ } ++ ++ for (totlen = num = 0; dlen; num++) { ++ /* Get (and move past) next length */ ++ sublen = ltoh16_ua(dptr); ++ dlen -= sizeof(uint16); ++ dptr += sizeof(uint16); ++ if ((sublen < SDPCM_HDRLEN_RX) || ++ ((num == 0) && (sublen < (2 * SDPCM_HDRLEN_RX)))) { ++ DHD_ERROR(("%s: descriptor len %d bad: %d\n", ++ __FUNCTION__, num, sublen)); ++ pnext = NULL; ++ break; ++ } ++ if (sublen % DHD_SDALIGN) { ++ DHD_ERROR(("%s: sublen %d not a multiple of %d\n", ++ __FUNCTION__, sublen, DHD_SDALIGN)); ++ usechain = FALSE; ++ } ++ totlen += sublen; ++ ++ /* For last frame, adjust read len so total is a block multiple */ ++ if (!dlen) { ++ sublen += (ROUNDUP(totlen, bus->blocksize) - totlen); ++ totlen = ROUNDUP(totlen, bus->blocksize); ++ } ++ ++ /* Allocate/chain packet for next subframe */ ++ if ((pnext = PKTGET(osh, sublen + DHD_SDALIGN, FALSE)) == NULL) { ++ DHD_ERROR(("%s: PKTGET failed, num %d len %d\n", ++ __FUNCTION__, num, sublen)); ++ break; ++ } ++ ASSERT(!PKTLINK(pnext)); ++ if (!pfirst) { ++ ASSERT(!plast); ++ pfirst = plast = pnext; ++ } else { ++ ASSERT(plast); ++ PKTSETNEXT(osh, plast, pnext); ++ plast = pnext; ++ } ++ ++ /* Adhere to start alignment requirements */ ++ PKTALIGN(osh, pnext, sublen, DHD_SDALIGN); ++ } ++ ++ /* If all allocations succeeded, save packet chain in bus structure */ ++ if (pnext) { ++ DHD_GLOM(("%s: allocated %d-byte packet chain for %d subframes\n", ++ __FUNCTION__, totlen, num)); ++ if (DHD_GLOM_ON() && bus->nextlen) { ++ if (totlen != bus->nextlen) { ++ DHD_GLOM(("%s: glomdesc mismatch: nextlen %d glomdesc %d " ++ "rxseq %d\n", __FUNCTION__, bus->nextlen, ++ totlen, rxseq)); ++ } ++ } ++ bus->glom = pfirst; ++ pfirst = pnext = NULL; ++ } else { ++ if (pfirst) ++ PKTFREE(osh, pfirst, FALSE); ++ bus->glom = NULL; ++ num = 0; ++ } ++ ++ /* Done with descriptor packet */ ++ PKTFREE(osh, bus->glomd, FALSE); ++ bus->glomd = NULL; ++ bus->nextlen = 0; ++ ++ dhd_os_sdunlock_rxq(bus->dhd); ++ } ++ ++ /* Ok -- either we just generated a packet chain, or had one from before */ ++ if (bus->glom) { ++ if (DHD_GLOM_ON()) { ++ DHD_GLOM(("%s: attempt superframe read, packet chain:\n", __FUNCTION__)); ++ for (pnext = bus->glom; pnext; pnext = PKTNEXT(osh, pnext)) { ++ DHD_GLOM((" %p: %p len 0x%04x (%d)\n", ++ pnext, (uint8*)PKTDATA(osh, pnext), ++ PKTLEN(osh, pnext), PKTLEN(osh, pnext))); ++ } ++ } ++ ++ pfirst = bus->glom; ++ dlen = (uint16)pkttotlen(osh, pfirst); ++ ++ /* Do an SDIO read for the superframe. Configurable iovar to ++ * read directly into the chained packet, or allocate a large ++ * packet and and copy into the chain. ++ */ ++ if (usechain) { ++ errcode = dhd_bcmsdh_recv_buf(bus, ++ bcmsdh_cur_sbwad(bus->sdh), SDIO_FUNC_2, ++ F2SYNC, (uint8*)PKTDATA(osh, pfirst), ++ dlen, pfirst, NULL, NULL); ++ } else if (bus->dataptr) { ++ errcode = dhd_bcmsdh_recv_buf(bus, ++ bcmsdh_cur_sbwad(bus->sdh), SDIO_FUNC_2, ++ F2SYNC, bus->dataptr, ++ dlen, NULL, NULL, NULL); ++ sublen = (uint16)pktfrombuf(osh, pfirst, 0, dlen, bus->dataptr); ++ if (sublen != dlen) { ++ DHD_ERROR(("%s: FAILED TO COPY, dlen %d sublen %d\n", ++ __FUNCTION__, dlen, sublen)); ++ errcode = -1; ++ } ++ pnext = NULL; ++ } else { ++ DHD_ERROR(("COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n", dlen)); ++ errcode = -1; ++ } ++ bus->f2rxdata++; ++ ASSERT(errcode != BCME_PENDING); ++ ++ /* On failure, kill the superframe, allow a couple retries */ ++ if (errcode < 0) { ++ DHD_ERROR(("%s: glom read of %d bytes failed: %d\n", ++ __FUNCTION__, dlen, errcode)); ++ bus->dhd->rx_errors++; ++ ++ if (bus->glomerr++ < 3) { ++ dhdsdio_rxfail(bus, TRUE, TRUE); ++ } else { ++ bus->glomerr = 0; ++ dhdsdio_rxfail(bus, TRUE, FALSE); ++ dhd_os_sdlock_rxq(bus->dhd); ++ PKTFREE(osh, bus->glom, FALSE); ++ dhd_os_sdunlock_rxq(bus->dhd); ++ bus->rxglomfail++; ++ bus->glom = NULL; ++ } ++ return 0; ++ } ++ ++#ifdef DHD_DEBUG ++ if (DHD_GLOM_ON()) { ++ prhex("SUPERFRAME", PKTDATA(osh, pfirst), ++ MIN(PKTLEN(osh, pfirst), 48)); ++ } ++#endif ++ ++ ++ /* Validate the superframe header */ ++ dptr = (uint8 *)PKTDATA(osh, pfirst); ++ sublen = ltoh16_ua(dptr); ++ check = ltoh16_ua(dptr + sizeof(uint16)); ++ ++ chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); ++ seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]); ++ bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; ++ if ((bus->nextlen << 4) > MAX_RX_DATASZ) { ++ DHD_INFO(("%s: got frame w/nextlen too large (%d) seq %d\n", ++ __FUNCTION__, bus->nextlen, seq)); ++ bus->nextlen = 0; ++ } ++ doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); ++ txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); ++ ++ errcode = 0; ++ if ((uint16)~(sublen^check)) { ++ DHD_ERROR(("%s (superframe): HW hdr error: len/check 0x%04x/0x%04x\n", ++ __FUNCTION__, sublen, check)); ++ errcode = -1; ++ } else if (ROUNDUP(sublen, bus->blocksize) != dlen) { ++ DHD_ERROR(("%s (superframe): len 0x%04x, rounded 0x%04x, expect 0x%04x\n", ++ __FUNCTION__, sublen, ROUNDUP(sublen, bus->blocksize), dlen)); ++ errcode = -1; ++ } else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) != SDPCM_GLOM_CHANNEL) { ++ DHD_ERROR(("%s (superframe): bad channel %d\n", __FUNCTION__, ++ SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]))); ++ errcode = -1; ++ } else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) { ++ DHD_ERROR(("%s (superframe): got second descriptor?\n", __FUNCTION__)); ++ errcode = -1; ++ } else if ((doff < SDPCM_HDRLEN_RX) || ++ (doff > (PKTLEN(osh, pfirst) - SDPCM_HDRLEN_RX))) { ++ DHD_ERROR(("%s (superframe): Bad data offset %d: HW %d pkt %d min %d\n", ++ __FUNCTION__, doff, sublen, PKTLEN(osh, pfirst), ++ SDPCM_HDRLEN_RX)); ++ errcode = -1; ++ } ++ ++ /* Check sequence number of superframe SW header */ ++ if (rxseq != seq) { ++ DHD_INFO(("%s: (superframe) rx_seq %d, expected %d\n", ++ __FUNCTION__, seq, rxseq)); ++ bus->rx_badseq++; ++ rxseq = seq; ++ } ++ ++ /* Check window for sanity */ ++ if ((uint8)(txmax - bus->tx_seq) > 0x40) { ++ DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", ++ __FUNCTION__, txmax, bus->tx_seq)); ++ txmax = bus->tx_max; ++ } ++ bus->tx_max = txmax; ++ ++ /* Remove superframe header, remember offset */ ++ PKTPULL(osh, pfirst, doff); ++ sfdoff = doff; ++ ++ /* Validate all the subframe headers */ ++ for (num = 0, pnext = pfirst; pnext && !errcode; ++ num++, pnext = PKTNEXT(osh, pnext)) { ++ dptr = (uint8 *)PKTDATA(osh, pnext); ++ dlen = (uint16)PKTLEN(osh, pnext); ++ sublen = ltoh16_ua(dptr); ++ check = ltoh16_ua(dptr + sizeof(uint16)); ++ chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); ++ doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); ++#ifdef DHD_DEBUG ++ if (DHD_GLOM_ON()) { ++ prhex("subframe", dptr, 32); ++ } ++#endif ++ ++ if ((uint16)~(sublen^check)) { ++ DHD_ERROR(("%s (subframe %d): HW hdr error: " ++ "len/check 0x%04x/0x%04x\n", ++ __FUNCTION__, num, sublen, check)); ++ errcode = -1; ++ } else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN_RX)) { ++ DHD_ERROR(("%s (subframe %d): length mismatch: " ++ "len 0x%04x, expect 0x%04x\n", ++ __FUNCTION__, num, sublen, dlen)); ++ errcode = -1; ++ } else if ((chan != SDPCM_DATA_CHANNEL) && ++ (chan != SDPCM_EVENT_CHANNEL)) { ++ DHD_ERROR(("%s (subframe %d): bad channel %d\n", ++ __FUNCTION__, num, chan)); ++ errcode = -1; ++ } else if ((doff < SDPCM_HDRLEN_RX) || (doff > sublen)) { ++ DHD_ERROR(("%s (subframe %d): Bad data offset %d: HW %d min %d\n", ++ __FUNCTION__, num, doff, sublen, SDPCM_HDRLEN_RX)); ++ errcode = -1; ++ } ++ } ++ ++ if (errcode) { ++ /* Terminate frame on error, request a couple retries */ ++ if (bus->glomerr++ < 3) { ++ /* Restore superframe header space */ ++ PKTPUSH(osh, pfirst, sfdoff); ++ dhdsdio_rxfail(bus, TRUE, TRUE); ++ } else { ++ bus->glomerr = 0; ++ dhdsdio_rxfail(bus, TRUE, FALSE); ++ dhd_os_sdlock_rxq(bus->dhd); ++ PKTFREE(osh, bus->glom, FALSE); ++ dhd_os_sdunlock_rxq(bus->dhd); ++ bus->rxglomfail++; ++ bus->glom = NULL; ++ } ++ bus->nextlen = 0; ++ return 0; ++ } ++ ++ /* Basic SD framing looks ok - process each packet (header) */ ++ bus->glom = NULL; ++ plast = NULL; ++ ++ dhd_os_sdlock_rxq(bus->dhd); ++ for (num = 0; pfirst; rxseq++, pfirst = pnext) { ++ pnext = PKTNEXT(osh, pfirst); ++ PKTSETNEXT(osh, pfirst, NULL); ++ ++ dptr = (uint8 *)PKTDATA(osh, pfirst); ++ sublen = ltoh16_ua(dptr); ++ chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); ++ seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]); ++ doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); ++ ++ DHD_GLOM(("%s: Get subframe %d, %p(%p/%d), sublen %d chan %d seq %d\n", ++ __FUNCTION__, num, pfirst, PKTDATA(osh, pfirst), ++ PKTLEN(osh, pfirst), sublen, chan, seq)); ++ ++ ASSERT((chan == SDPCM_DATA_CHANNEL) || (chan == SDPCM_EVENT_CHANNEL)); ++ ++ if (rxseq != seq) { ++ DHD_GLOM(("%s: rx_seq %d, expected %d\n", ++ __FUNCTION__, seq, rxseq)); ++ bus->rx_badseq++; ++ rxseq = seq; ++ } ++ ++#ifdef DHD_DEBUG ++ if (DHD_BYTES_ON() && DHD_DATA_ON()) { ++ prhex("Rx Subframe Data", dptr, dlen); ++ } ++#endif ++ ++ PKTSETLEN(osh, pfirst, sublen); ++ PKTPULL(osh, pfirst, doff); ++ ++ reorder_info_len = sizeof(reorder_info_buf); ++ ++ if (PKTLEN(osh, pfirst) == 0) { ++ PKTFREE(bus->dhd->osh, pfirst, FALSE); ++ continue; ++ } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pfirst, reorder_info_buf, ++ &reorder_info_len) != 0) { ++ DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__)); ++ bus->dhd->rx_errors++; ++ PKTFREE(osh, pfirst, FALSE); ++ continue; ++ } ++ if (reorder_info_len) { ++ uint32 free_buf_count; ++ void *ppfirst; ++ ++ ppfirst = pfirst; ++ /* Reordering info from the firmware */ ++ dhd_process_pkt_reorder_info(bus->dhd, reorder_info_buf, ++ reorder_info_len, &ppfirst, &free_buf_count); ++ ++ if (free_buf_count == 0) { ++ continue; ++ } ++ else { ++ void *temp; ++ ++ /* go to the end of the chain and attach the pnext there */ ++ temp = ppfirst; ++ while (PKTNEXT(osh, temp) != NULL) { ++ temp = PKTNEXT(osh, temp); ++ } ++ pfirst = temp; ++ if (list_tail[ifidx] == NULL) { ++ list_head[ifidx] = ppfirst; ++ list_tail[ifidx] = pfirst; ++ } ++ else { ++ PKTSETNEXT(osh, list_tail[ifidx], ppfirst); ++ list_tail[ifidx] = pfirst; ++ } ++ } ++ ++ num += (uint8)free_buf_count; ++ } ++ else { ++ /* this packet will go up, link back into chain and count it */ ++ ++ if (list_tail[ifidx] == NULL) { ++ list_head[ifidx] = list_tail[ifidx] = pfirst; ++ } ++ else { ++ PKTSETNEXT(osh, list_tail[ifidx], pfirst); ++ list_tail[ifidx] = pfirst; ++ } ++ num++; ++ } ++#ifdef DHD_DEBUG ++ if (DHD_GLOM_ON()) { ++ DHD_GLOM(("%s subframe %d to stack, %p(%p/%d) nxt/lnk %p/%p\n", ++ __FUNCTION__, num, pfirst, ++ PKTDATA(osh, pfirst), PKTLEN(osh, pfirst), ++ PKTNEXT(osh, pfirst), PKTLINK(pfirst))); ++ prhex("", (uint8 *)PKTDATA(osh, pfirst), ++ MIN(PKTLEN(osh, pfirst), 32)); ++ } ++#endif /* DHD_DEBUG */ ++ } ++ dhd_os_sdunlock_rxq(bus->dhd); ++ ++ for (idx = 0; idx < DHD_MAX_IFS; idx++) { ++ if (list_head[idx]) { ++ void *temp; ++ uint8 cnt = 0; ++ temp = list_head[idx]; ++ do { ++ temp = PKTNEXT(osh, temp); ++ cnt++; ++ } while (temp); ++ if (cnt) { ++ dhd_os_sdunlock(bus->dhd); ++ dhd_rx_frame(bus->dhd, idx, list_head[idx], cnt, 0); ++ dhd_os_sdlock(bus->dhd); ++ } ++ } ++ } ++ bus->rxglomframes++; ++ bus->rxglompkts += num; ++ } ++ return num; ++} ++ ++ ++/* Return TRUE if there may be more frames to read */ ++static uint ++dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) ++{ ++ osl_t *osh = bus->dhd->osh; ++ bcmsdh_info_t *sdh = bus->sdh; ++ ++ uint16 len, check; /* Extracted hardware header fields */ ++ uint8 chan, seq, doff; /* Extracted software header fields */ ++ uint8 fcbits; /* Extracted fcbits from software header */ ++ uint8 delta; ++ ++ void *pkt; /* Packet for event or data frames */ ++ uint16 pad; /* Number of pad bytes to read */ ++ uint16 rdlen; /* Total number of bytes to read */ ++ uint8 rxseq; /* Next sequence number to expect */ ++ uint rxleft = 0; /* Remaining number of frames allowed */ ++ int sdret; /* Return code from bcmsdh calls */ ++ uint8 txmax; /* Maximum tx sequence offered */ ++ bool len_consistent; /* Result of comparing readahead len and len from hw-hdr */ ++ uint8 *rxbuf; ++ int ifidx = 0; ++ uint rxcount = 0; /* Total frames read */ ++ uchar reorder_info_buf[WLHOST_REORDERDATA_TOTLEN]; ++ uint reorder_info_len; ++ uint pkt_count; ++ ++#if defined(DHD_DEBUG) || defined(SDTEST) ++ bool sdtest = FALSE; /* To limit message spew from test mode */ ++#endif ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ bus->readframes = TRUE; ++ ++ if (!KSO_ENAB(bus)) { ++ DHD_ERROR(("%s: KSO off\n", __FUNCTION__)); ++ bus->readframes = FALSE; ++ return 0; ++ } ++ ++ ASSERT(maxframes); ++ ++#ifdef SDTEST ++ /* Allow pktgen to override maxframes */ ++ if (bus->pktgen_count && (bus->pktgen_mode == DHD_PKTGEN_RECV)) { ++ maxframes = bus->pktgen_count; ++ sdtest = TRUE; ++ } ++#endif ++ ++ /* Not finished unless we encounter no more frames indication */ ++ *finished = FALSE; ++ ++ ++ for (rxseq = bus->rx_seq, rxleft = maxframes; ++ !bus->rxskip && rxleft && bus->dhd->busstate != DHD_BUS_DOWN; ++ rxseq++, rxleft--) { ++ ++#ifdef DHDTHREAD ++ /* tx more to improve rx performance */ ++ if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && ++ pktq_mlen(&bus->txq, ~bus->flowcontrol) && DATAOK(bus)) { ++ dhdsdio_sendfromq(bus, dhd_txbound); ++ } ++#endif /* DHDTHREAD */ ++ ++ /* Handle glomming separately */ ++ if (bus->glom || bus->glomd) { ++ uint8 cnt; ++ DHD_GLOM(("%s: calling rxglom: glomd %p, glom %p\n", ++ __FUNCTION__, bus->glomd, bus->glom)); ++ cnt = dhdsdio_rxglom(bus, rxseq); ++ DHD_GLOM(("%s: rxglom returned %d\n", __FUNCTION__, cnt)); ++ rxseq += cnt - 1; ++ rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; ++ continue; ++ } ++ ++ /* Try doing single read if we can */ ++ if (dhd_readahead && bus->nextlen) { ++ uint16 nextlen = bus->nextlen; ++ bus->nextlen = 0; ++ ++ if (bus->bus == SPI_BUS) { ++ rdlen = len = nextlen; ++ } ++ else { ++ rdlen = len = nextlen << 4; ++ ++ /* Pad read to blocksize for efficiency */ ++ if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { ++ pad = bus->blocksize - (rdlen % bus->blocksize); ++ if ((pad <= bus->roundup) && (pad < bus->blocksize) && ++ ((rdlen + pad + firstread) < MAX_RX_DATASZ)) ++ rdlen += pad; ++ } else if (rdlen % DHD_SDALIGN) { ++ rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN); ++ } ++ } ++ ++ /* We use bus->rxctl buffer in WinXP for initial control pkt receives. ++ * Later we use buffer-poll for data as well as control packets. ++ * This is required because dhd receives full frame in gSPI unlike SDIO. ++ * After the frame is received we have to distinguish whether it is data ++ * or non-data frame. ++ */ ++ /* Allocate a packet buffer */ ++ dhd_os_sdlock_rxq(bus->dhd); ++ if (!(pkt = PKTGET(osh, rdlen + DHD_SDALIGN, FALSE))) { ++ if (bus->bus == SPI_BUS) { ++ bus->usebufpool = FALSE; ++ bus->rxctl = bus->rxbuf; ++ if (dhd_alignctl) { ++ bus->rxctl += firstread; ++ if ((pad = ((uintptr)bus->rxctl % DHD_SDALIGN))) ++ bus->rxctl += (DHD_SDALIGN - pad); ++ bus->rxctl -= firstread; ++ } ++ ASSERT(bus->rxctl >= bus->rxbuf); ++ rxbuf = bus->rxctl; ++ /* Read the entire frame */ ++ sdret = dhd_bcmsdh_recv_buf(bus, ++ bcmsdh_cur_sbwad(sdh), ++ SDIO_FUNC_2, ++ F2SYNC, rxbuf, rdlen, ++ NULL, NULL, NULL); ++ bus->f2rxdata++; ++ ASSERT(sdret != BCME_PENDING); ++ ++ ++ /* Control frame failures need retransmission */ ++ if (sdret < 0) { ++ DHD_ERROR(("%s: read %d control bytes failed: %d\n", ++ __FUNCTION__, rdlen, sdret)); ++ /* dhd.rx_ctlerrs is higher level */ ++ bus->rxc_errors++; ++ dhd_os_sdunlock_rxq(bus->dhd); ++ dhdsdio_rxfail(bus, TRUE, ++ (bus->bus == SPI_BUS) ? FALSE : TRUE); ++ continue; ++ } ++ } else { ++ /* Give up on data, request rtx of events */ ++ DHD_ERROR(("%s (nextlen): PKTGET failed: len %d rdlen %d " ++ "expected rxseq %d\n", ++ __FUNCTION__, len, rdlen, rxseq)); ++ /* Just go try again w/normal header read */ ++ dhd_os_sdunlock_rxq(bus->dhd); ++ continue; ++ } ++ } else { ++ if (bus->bus == SPI_BUS) ++ bus->usebufpool = TRUE; ++ ++ ASSERT(!PKTLINK(pkt)); ++ PKTALIGN(osh, pkt, rdlen, DHD_SDALIGN); ++ rxbuf = (uint8 *)PKTDATA(osh, pkt); ++ /* Read the entire frame */ ++ sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), ++ SDIO_FUNC_2, ++ F2SYNC, rxbuf, rdlen, ++ pkt, NULL, NULL); ++ bus->f2rxdata++; ++ ASSERT(sdret != BCME_PENDING); ++ ++ if (sdret < 0) { ++ DHD_ERROR(("%s (nextlen): read %d bytes failed: %d\n", ++ __FUNCTION__, rdlen, sdret)); ++ PKTFREE(bus->dhd->osh, pkt, FALSE); ++ bus->dhd->rx_errors++; ++ dhd_os_sdunlock_rxq(bus->dhd); ++ /* Force retry w/normal header read. Don't attempt NAK for ++ * gSPI ++ */ ++ dhdsdio_rxfail(bus, TRUE, ++ (bus->bus == SPI_BUS) ? FALSE : TRUE); ++ continue; ++ } ++ } ++ dhd_os_sdunlock_rxq(bus->dhd); ++ ++ /* Now check the header */ ++ bcopy(rxbuf, bus->rxhdr, SDPCM_HDRLEN_RX); ++ ++ /* Extract hardware header fields */ ++ len = ltoh16_ua(bus->rxhdr); ++ check = ltoh16_ua(bus->rxhdr + sizeof(uint16)); ++ ++ /* All zeros means readahead info was bad */ ++ if (!(len|check)) { ++ DHD_INFO(("%s (nextlen): read zeros in HW header???\n", ++ __FUNCTION__)); ++ dhd_os_sdlock_rxq(bus->dhd); ++ PKTFREE2(); ++ dhd_os_sdunlock_rxq(bus->dhd); ++ GSPI_PR55150_BAILOUT; ++ continue; ++ } ++ ++ /* Validate check bytes */ ++ if ((uint16)~(len^check)) { ++ DHD_ERROR(("%s (nextlen): HW hdr error: nextlen/len/check" ++ " 0x%04x/0x%04x/0x%04x\n", __FUNCTION__, nextlen, ++ len, check)); ++ dhd_os_sdlock_rxq(bus->dhd); ++ PKTFREE2(); ++ dhd_os_sdunlock_rxq(bus->dhd); ++ bus->rx_badhdr++; ++ dhdsdio_rxfail(bus, FALSE, FALSE); ++ GSPI_PR55150_BAILOUT; ++ continue; ++ } ++ ++ /* Validate frame length */ ++ if (len < SDPCM_HDRLEN_RX) { ++ DHD_ERROR(("%s (nextlen): HW hdr length invalid: %d\n", ++ __FUNCTION__, len)); ++ dhd_os_sdlock_rxq(bus->dhd); ++ PKTFREE2(); ++ dhd_os_sdunlock_rxq(bus->dhd); ++ GSPI_PR55150_BAILOUT; ++ continue; ++ } ++ ++ /* Check for consistency with readahead info */ ++ len_consistent = (nextlen != (ROUNDUP(len, 16) >> 4)); ++ if (len_consistent) { ++ /* Mismatch, force retry w/normal header (may be >4K) */ ++ DHD_ERROR(("%s (nextlen): mismatch, nextlen %d len %d rnd %d; " ++ "expected rxseq %d\n", ++ __FUNCTION__, nextlen, len, ROUNDUP(len, 16), rxseq)); ++ dhd_os_sdlock_rxq(bus->dhd); ++ PKTFREE2(); ++ dhd_os_sdunlock_rxq(bus->dhd); ++ dhdsdio_rxfail(bus, TRUE, (bus->bus == SPI_BUS) ? FALSE : TRUE); ++ GSPI_PR55150_BAILOUT; ++ continue; ++ } ++ ++ ++ /* Extract software header fields */ ++ chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); ++ seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); ++ doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); ++ txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); ++ ++ bus->nextlen = ++ bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; ++ if ((bus->nextlen << 4) > MAX_RX_DATASZ) { ++ DHD_INFO(("%s (nextlen): got frame w/nextlen too large" ++ " (%d), seq %d\n", __FUNCTION__, bus->nextlen, ++ seq)); ++ bus->nextlen = 0; ++ } ++ ++ bus->dhd->rx_readahead_cnt ++; ++ /* Handle Flow Control */ ++ fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); ++ ++ delta = 0; ++ if (~bus->flowcontrol & fcbits) { ++ bus->fc_xoff++; ++ delta = 1; ++ } ++ if (bus->flowcontrol & ~fcbits) { ++ bus->fc_xon++; ++ delta = 1; ++ } ++ ++ if (delta) { ++ bus->fc_rcvd++; ++ bus->flowcontrol = fcbits; ++ } ++ ++ /* Check and update sequence number */ ++ if (rxseq != seq) { ++ DHD_INFO(("%s (nextlen): rx_seq %d, expected %d\n", ++ __FUNCTION__, seq, rxseq)); ++ bus->rx_badseq++; ++ rxseq = seq; ++ } ++ ++ /* Check window for sanity */ ++ if ((uint8)(txmax - bus->tx_seq) > 0x40) { ++ DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", ++ __FUNCTION__, txmax, bus->tx_seq)); ++ txmax = bus->tx_max; ++ } ++ bus->tx_max = txmax; ++ ++#ifdef DHD_DEBUG ++ if (DHD_BYTES_ON() && DHD_DATA_ON()) { ++ prhex("Rx Data", rxbuf, len); ++ } else if (DHD_HDRS_ON()) { ++ prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN_RX); ++ } ++#endif ++ ++ if (chan == SDPCM_CONTROL_CHANNEL) { ++ if (bus->bus == SPI_BUS) { ++ dhdsdio_read_control(bus, rxbuf, len, doff); ++ if (bus->usebufpool) { ++ dhd_os_sdlock_rxq(bus->dhd); ++ PKTFREE(bus->dhd->osh, pkt, FALSE); ++ dhd_os_sdunlock_rxq(bus->dhd); ++ } ++ continue; ++ } else { ++ DHD_ERROR(("%s (nextlen): readahead on control" ++ " packet %d?\n", __FUNCTION__, seq)); ++ /* Force retry w/normal header read */ ++ bus->nextlen = 0; ++ dhdsdio_rxfail(bus, FALSE, TRUE); ++ dhd_os_sdlock_rxq(bus->dhd); ++ PKTFREE2(); ++ dhd_os_sdunlock_rxq(bus->dhd); ++ continue; ++ } ++ } ++ ++ if ((bus->bus == SPI_BUS) && !bus->usebufpool) { ++ DHD_ERROR(("Received %d bytes on %d channel. Running out of " ++ "rx pktbuf's or not yet malloced.\n", len, chan)); ++ continue; ++ } ++ ++ /* Validate data offset */ ++ if ((doff < SDPCM_HDRLEN_RX) || (doff > len)) { ++ DHD_ERROR(("%s (nextlen): bad data offset %d: HW len %d min %d\n", ++ __FUNCTION__, doff, len, SDPCM_HDRLEN_RX)); ++ dhd_os_sdlock_rxq(bus->dhd); ++ PKTFREE2(); ++ dhd_os_sdunlock_rxq(bus->dhd); ++ ASSERT(0); ++ dhdsdio_rxfail(bus, FALSE, FALSE); ++ continue; ++ } ++ ++ /* All done with this one -- now deliver the packet */ ++ goto deliver; ++ } ++ /* gSPI frames should not be handled in fractions */ ++ if (bus->bus == SPI_BUS) { ++ break; ++ } ++ ++ /* Read frame header (hardware and software) */ ++ sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, ++ bus->rxhdr, firstread, NULL, NULL, NULL); ++ bus->f2rxhdrs++; ++ ASSERT(sdret != BCME_PENDING); ++ ++ if (sdret < 0) { ++ DHD_ERROR(("%s: RXHEADER FAILED: %d\n", __FUNCTION__, sdret)); ++ bus->rx_hdrfail++; ++ dhdsdio_rxfail(bus, TRUE, TRUE); ++ continue; ++ } ++ ++#ifdef DHD_DEBUG ++ if (DHD_BYTES_ON() || DHD_HDRS_ON()) { ++ prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN_RX); ++ } ++#endif ++ ++ /* Extract hardware header fields */ ++ len = ltoh16_ua(bus->rxhdr); ++ check = ltoh16_ua(bus->rxhdr + sizeof(uint16)); ++ ++ /* All zeros means no more frames */ ++ if (!(len|check)) { ++ *finished = TRUE; ++ break; ++ } ++ ++ /* Validate check bytes */ ++ if ((uint16)~(len^check)) { ++ DHD_ERROR(("%s: HW hdr error: len/check 0x%04x/0x%04x\n", ++ __FUNCTION__, len, check)); ++ bus->rx_badhdr++; ++ dhdsdio_rxfail(bus, FALSE, FALSE); ++ continue; ++ } ++ ++ /* Validate frame length */ ++ if (len < SDPCM_HDRLEN_RX) { ++ DHD_ERROR(("%s: HW hdr length invalid: %d\n", __FUNCTION__, len)); ++ continue; ++ } ++ ++ /* Extract software header fields */ ++ chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); ++ seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); ++ doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); ++ txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); ++ ++ /* Validate data offset */ ++ if ((doff < SDPCM_HDRLEN_RX) || (doff > len)) { ++ DHD_ERROR(("%s: Bad data offset %d: HW len %d, min %d seq %d\n", ++ __FUNCTION__, doff, len, SDPCM_HDRLEN_RX, seq)); ++ bus->rx_badhdr++; ++ ASSERT(0); ++ dhdsdio_rxfail(bus, FALSE, FALSE); ++ continue; ++ } ++ ++ /* Save the readahead length if there is one */ ++ bus->nextlen = bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; ++ if ((bus->nextlen << 4) > MAX_RX_DATASZ) { ++ DHD_INFO(("%s (nextlen): got frame w/nextlen too large (%d), seq %d\n", ++ __FUNCTION__, bus->nextlen, seq)); ++ bus->nextlen = 0; ++ } ++ ++ /* Handle Flow Control */ ++ fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); ++ ++ delta = 0; ++ if (~bus->flowcontrol & fcbits) { ++ bus->fc_xoff++; ++ delta = 1; ++ } ++ if (bus->flowcontrol & ~fcbits) { ++ bus->fc_xon++; ++ delta = 1; ++ } ++ ++ if (delta) { ++ bus->fc_rcvd++; ++ bus->flowcontrol = fcbits; ++ } ++ ++ /* Check and update sequence number */ ++ if (rxseq != seq) { ++ DHD_INFO(("%s: rx_seq %d, expected %d\n", __FUNCTION__, seq, rxseq)); ++ bus->rx_badseq++; ++ rxseq = seq; ++ } ++ ++ /* Check window for sanity */ ++ if ((uint8)(txmax - bus->tx_seq) > 0x40) { ++ DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", ++ __FUNCTION__, txmax, bus->tx_seq)); ++ txmax = bus->tx_max; ++ } ++ bus->tx_max = txmax; ++ ++ /* Call a separate function for control frames */ ++ if (chan == SDPCM_CONTROL_CHANNEL) { ++ dhdsdio_read_control(bus, bus->rxhdr, len, doff); ++ continue; ++ } ++ ++ ASSERT((chan == SDPCM_DATA_CHANNEL) || (chan == SDPCM_EVENT_CHANNEL) || ++ (chan == SDPCM_TEST_CHANNEL) || (chan == SDPCM_GLOM_CHANNEL)); ++ ++ /* Length to read */ ++ rdlen = (len > firstread) ? (len - firstread) : 0; ++ ++ /* May pad read to blocksize for efficiency */ ++ if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { ++ pad = bus->blocksize - (rdlen % bus->blocksize); ++ if ((pad <= bus->roundup) && (pad < bus->blocksize) && ++ ((rdlen + pad + firstread) < MAX_RX_DATASZ)) ++ rdlen += pad; ++ } else if (rdlen % DHD_SDALIGN) { ++ rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN); ++ } ++ ++ /* Satisfy length-alignment requirements */ ++ if (forcealign && (rdlen & (ALIGNMENT - 1))) ++ rdlen = ROUNDUP(rdlen, ALIGNMENT); ++ ++ if ((rdlen + firstread) > MAX_RX_DATASZ) { ++ /* Too long -- skip this frame */ ++ DHD_ERROR(("%s: too long: len %d rdlen %d\n", __FUNCTION__, len, rdlen)); ++ bus->dhd->rx_errors++; bus->rx_toolong++; ++ dhdsdio_rxfail(bus, FALSE, FALSE); ++ continue; ++ } ++ ++ dhd_os_sdlock_rxq(bus->dhd); ++ if (!(pkt = PKTGET(osh, (rdlen + firstread + DHD_SDALIGN), FALSE))) { ++ /* Give up on data, request rtx of events */ ++ DHD_ERROR(("%s: PKTGET failed: rdlen %d chan %d\n", ++ __FUNCTION__, rdlen, chan)); ++ bus->dhd->rx_dropped++; ++ dhd_os_sdunlock_rxq(bus->dhd); ++ dhdsdio_rxfail(bus, FALSE, RETRYCHAN(chan)); ++ continue; ++ } ++ dhd_os_sdunlock_rxq(bus->dhd); ++ ++ ASSERT(!PKTLINK(pkt)); ++ ++ /* Leave room for what we already read, and align remainder */ ++ ASSERT(firstread < (PKTLEN(osh, pkt))); ++ PKTPULL(osh, pkt, firstread); ++ PKTALIGN(osh, pkt, rdlen, DHD_SDALIGN); ++ ++ /* Read the remaining frame data */ ++ sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, ++ ((uint8 *)PKTDATA(osh, pkt)), rdlen, pkt, NULL, NULL); ++ bus->f2rxdata++; ++ ASSERT(sdret != BCME_PENDING); ++ ++ if (sdret < 0) { ++ DHD_ERROR(("%s: read %d %s bytes failed: %d\n", __FUNCTION__, rdlen, ++ ((chan == SDPCM_EVENT_CHANNEL) ? "event" : ++ ((chan == SDPCM_DATA_CHANNEL) ? "data" : "test")), sdret)); ++ dhd_os_sdlock_rxq(bus->dhd); ++ PKTFREE(bus->dhd->osh, pkt, FALSE); ++ dhd_os_sdunlock_rxq(bus->dhd); ++ bus->dhd->rx_errors++; ++ dhdsdio_rxfail(bus, TRUE, RETRYCHAN(chan)); ++ continue; ++ } ++ ++ /* Copy the already-read portion */ ++ PKTPUSH(osh, pkt, firstread); ++ bcopy(bus->rxhdr, PKTDATA(osh, pkt), firstread); ++ ++#ifdef DHD_DEBUG ++ if (DHD_BYTES_ON() && DHD_DATA_ON()) { ++ prhex("Rx Data", PKTDATA(osh, pkt), len); ++ } ++#endif ++ ++deliver: ++ /* Save superframe descriptor and allocate packet frame */ ++ if (chan == SDPCM_GLOM_CHANNEL) { ++ if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) { ++ DHD_GLOM(("%s: got glom descriptor, %d bytes:\n", ++ __FUNCTION__, len)); ++#ifdef DHD_DEBUG ++ if (DHD_GLOM_ON()) { ++ prhex("Glom Data", PKTDATA(osh, pkt), len); ++ } ++#endif ++ PKTSETLEN(osh, pkt, len); ++ ASSERT(doff == SDPCM_HDRLEN_RX); ++ PKTPULL(osh, pkt, SDPCM_HDRLEN_RX); ++ bus->glomd = pkt; ++ } else { ++ DHD_ERROR(("%s: glom superframe w/o descriptor!\n", __FUNCTION__)); ++ dhdsdio_rxfail(bus, FALSE, FALSE); ++ } ++ continue; ++ } ++ ++ /* Fill in packet len and prio, deliver upward */ ++ PKTSETLEN(osh, pkt, len); ++ PKTPULL(osh, pkt, doff); ++ ++#ifdef SDTEST ++ /* Test channel packets are processed separately */ ++ if (chan == SDPCM_TEST_CHANNEL) { ++ dhdsdio_testrcv(bus, pkt, seq); ++ continue; ++ } ++#endif /* SDTEST */ ++ ++ if (PKTLEN(osh, pkt) == 0) { ++ dhd_os_sdlock_rxq(bus->dhd); ++ PKTFREE(bus->dhd->osh, pkt, FALSE); ++ dhd_os_sdunlock_rxq(bus->dhd); ++ continue; ++ } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pkt, reorder_info_buf, ++ &reorder_info_len) != 0) { ++ DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__)); ++ dhd_os_sdlock_rxq(bus->dhd); ++ PKTFREE(bus->dhd->osh, pkt, FALSE); ++ dhd_os_sdunlock_rxq(bus->dhd); ++ bus->dhd->rx_errors++; ++ continue; ++ } ++ if (reorder_info_len) { ++ /* Reordering info from the firmware */ ++ dhd_process_pkt_reorder_info(bus->dhd, reorder_info_buf, reorder_info_len, ++ &pkt, &pkt_count); ++ if (pkt_count == 0) ++ continue; ++ } ++ else ++ pkt_count = 1; ++ ++ ++ /* Unlock during rx call */ ++ dhd_os_sdunlock(bus->dhd); ++ dhd_rx_frame(bus->dhd, ifidx, pkt, pkt_count, chan); ++ dhd_os_sdlock(bus->dhd); ++ } ++ rxcount = maxframes - rxleft; ++#ifdef DHD_DEBUG ++ /* Message if we hit the limit */ ++ if (!rxleft && !sdtest) ++ DHD_DATA(("%s: hit rx limit of %d frames\n", __FUNCTION__, maxframes)); ++ else ++#endif /* DHD_DEBUG */ ++ DHD_DATA(("%s: processed %d frames\n", __FUNCTION__, rxcount)); ++ /* Back off rxseq if awaiting rtx, update rx_seq */ ++ if (bus->rxskip) ++ rxseq--; ++ bus->rx_seq = rxseq; ++ ++ if (bus->reqbussleep) ++ { ++ dhdsdio_bussleep(bus, TRUE); ++ bus->reqbussleep = FALSE; ++ } ++ bus->readframes = FALSE; ++ ++ return rxcount; ++} ++ ++static uint32 ++dhdsdio_hostmail(dhd_bus_t *bus) ++{ ++ sdpcmd_regs_t *regs = bus->regs; ++ uint32 intstatus = 0; ++ uint32 hmb_data; ++ uint8 fcbits; ++ uint retries = 0; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ /* Read mailbox data and ack that we did so */ ++ R_SDREG(hmb_data, ®s->tohostmailboxdata, retries); ++ if (retries <= retry_limit) ++ W_SDREG(SMB_INT_ACK, ®s->tosbmailbox, retries); ++ bus->f1regdata += 2; ++ ++ /* Dongle recomposed rx frames, accept them again */ ++ if (hmb_data & HMB_DATA_NAKHANDLED) { ++ DHD_INFO(("Dongle reports NAK handled, expect rtx of %d\n", bus->rx_seq)); ++ if (!bus->rxskip) { ++ DHD_ERROR(("%s: unexpected NAKHANDLED!\n", __FUNCTION__)); ++ } ++ bus->rxskip = FALSE; ++ intstatus |= FRAME_AVAIL_MASK(bus); ++ } ++ ++ /* ++ * DEVREADY does not occur with gSPI. ++ */ ++ if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) { ++ bus->sdpcm_ver = (hmb_data & HMB_DATA_VERSION_MASK) >> HMB_DATA_VERSION_SHIFT; ++ if (bus->sdpcm_ver != SDPCM_PROT_VERSION) ++ DHD_ERROR(("Version mismatch, dongle reports %d, expecting %d\n", ++ bus->sdpcm_ver, SDPCM_PROT_VERSION)); ++ else ++ DHD_INFO(("Dongle ready, protocol version %d\n", bus->sdpcm_ver)); ++ /* make sure for the SDIO_DEVICE_RXDATAINT_MODE_1 corecontrol is proper */ ++ if ((bus->sih->buscoretype == SDIOD_CORE_ID) && (bus->sdpcmrev >= 4) && ++ (bus->rxint_mode == SDIO_DEVICE_RXDATAINT_MODE_1)) { ++ uint32 val; ++ ++ val = R_REG(bus->dhd->osh, &bus->regs->corecontrol); ++ val &= ~CC_XMTDATAAVAIL_MODE; ++ val |= CC_XMTDATAAVAIL_CTRL; ++ W_REG(bus->dhd->osh, &bus->regs->corecontrol, val); ++ ++ val = R_REG(bus->dhd->osh, &bus->regs->corecontrol); ++ } ++ ++#ifdef DHD_DEBUG ++ /* Retrieve console state address now that firmware should have updated it */ ++ { ++ sdpcm_shared_t shared; ++ if (dhdsdio_readshared(bus, &shared) == 0) ++ bus->console_addr = shared.console_addr; ++ } ++#endif /* DHD_DEBUG */ ++ } ++ ++ /* ++ * Flow Control has been moved into the RX headers and this out of band ++ * method isn't used any more. Leave this here for possibly remaining backward ++ * compatible with older dongles ++ */ ++ if (hmb_data & HMB_DATA_FC) { ++ fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >> HMB_DATA_FCDATA_SHIFT; ++ ++ if (fcbits & ~bus->flowcontrol) ++ bus->fc_xoff++; ++ if (bus->flowcontrol & ~fcbits) ++ bus->fc_xon++; ++ ++ bus->fc_rcvd++; ++ bus->flowcontrol = fcbits; ++ } ++ ++#ifdef DHD_DEBUG ++ /* At least print a message if FW halted */ ++ if (hmb_data & HMB_DATA_FWHALT) { ++ DHD_ERROR(("INTERNAL ERROR: FIRMWARE HALTED : set BUS DOWN\n")); ++ dhdsdio_checkdied(bus, NULL, 0); ++ bus->dhd->busstate = DHD_BUS_DOWN; ++ } ++#endif /* DHD_DEBUG */ ++ ++ /* Shouldn't be any others */ ++ if (hmb_data & ~(HMB_DATA_DEVREADY | ++ HMB_DATA_FWHALT | ++ HMB_DATA_NAKHANDLED | ++ HMB_DATA_FC | ++ HMB_DATA_FWREADY | ++ HMB_DATA_FCDATA_MASK | ++ HMB_DATA_VERSION_MASK)) { ++ DHD_ERROR(("Unknown mailbox data content: 0x%02x\n", hmb_data)); ++ } ++ ++ return intstatus; ++} ++ ++static bool ++dhdsdio_dpc(dhd_bus_t *bus) ++{ ++ bcmsdh_info_t *sdh = bus->sdh; ++ sdpcmd_regs_t *regs = bus->regs; ++ uint32 intstatus, newstatus = 0; ++ uint retries = 0; ++ uint rxlimit = dhd_rxbound; /* Rx frames to read before resched */ ++ uint txlimit = dhd_txbound; /* Tx frames to send before resched */ ++ uint framecnt = 0; /* Temporary counter of tx/rx frames */ ++ bool rxdone = TRUE; /* Flag for no more read data */ ++ bool resched = FALSE; /* Flag indicating resched wanted */ ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (bus->dhd->busstate == DHD_BUS_DOWN) { ++ DHD_ERROR(("%s: Bus down, ret\n", __FUNCTION__)); ++ bus->intstatus = 0; ++ return 0; ++ } ++ ++ /* Start with leftover status bits */ ++ intstatus = bus->intstatus; ++ ++ dhd_os_sdlock(bus->dhd); ++ ++ if (!SLPAUTO_ENAB(bus) && !KSO_ENAB(bus)) { ++ DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); ++ goto exit; ++ } ++ ++ /* If waiting for HTAVAIL, check status */ ++ if (!SLPAUTO_ENAB(bus) && (bus->clkstate == CLK_PENDING)) { ++ int err; ++ uint8 clkctl, devctl = 0; ++ ++#ifdef DHD_DEBUG ++ /* Check for inconsistent device control */ ++ devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); ++ if (err) { ++ DHD_ERROR(("%s: error reading DEVCTL: %d\n", __FUNCTION__, err)); ++ bus->dhd->busstate = DHD_BUS_DOWN; ++ } else { ++ ASSERT(devctl & SBSDIO_DEVCTL_CA_INT_ONLY); ++ } ++#endif /* DHD_DEBUG */ ++ ++ /* Read CSR, if clock on switch to AVAIL, else ignore */ ++ clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); ++ if (err) { ++ DHD_ERROR(("%s: error reading CSR: %d\n", __FUNCTION__, err)); ++ bus->dhd->busstate = DHD_BUS_DOWN; ++ } ++ ++ DHD_INFO(("DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", devctl, clkctl)); ++ ++ if (SBSDIO_HTAV(clkctl)) { ++ devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); ++ if (err) { ++ DHD_ERROR(("%s: error reading DEVCTL: %d\n", ++ __FUNCTION__, err)); ++ bus->dhd->busstate = DHD_BUS_DOWN; ++ } ++ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); ++ if (err) { ++ DHD_ERROR(("%s: error writing DEVCTL: %d\n", ++ __FUNCTION__, err)); ++ bus->dhd->busstate = DHD_BUS_DOWN; ++ } ++ bus->clkstate = CLK_AVAIL; ++ } else { ++ goto clkwait; ++ } ++ } ++ ++ BUS_WAKE(bus); ++ ++ /* Make sure backplane clock is on */ ++ dhdsdio_clkctl(bus, CLK_AVAIL, TRUE); ++ if (bus->clkstate != CLK_AVAIL) ++ goto clkwait; ++ ++ /* Pending interrupt indicates new device status */ ++ if (bus->ipend) { ++ bus->ipend = FALSE; ++ R_SDREG(newstatus, ®s->intstatus, retries); ++ bus->f1regdata++; ++ if (bcmsdh_regfail(bus->sdh)) ++ newstatus = 0; ++ newstatus &= bus->hostintmask; ++ bus->fcstate = !!(newstatus & I_HMB_FC_STATE); ++ if (newstatus) { ++ bus->f1regdata++; ++ if ((bus->rxint_mode == SDIO_DEVICE_RXDATAINT_MODE_0) && ++ (newstatus == I_XMTDATA_AVAIL)) { ++ } ++ else ++ W_SDREG(newstatus, ®s->intstatus, retries); ++ } ++ } ++ ++ /* Merge new bits with previous */ ++ intstatus |= newstatus; ++ bus->intstatus = 0; ++ ++ /* Handle flow-control change: read new state in case our ack ++ * crossed another change interrupt. If change still set, assume ++ * FC ON for safety, let next loop through do the debounce. ++ */ ++ if (intstatus & I_HMB_FC_CHANGE) { ++ intstatus &= ~I_HMB_FC_CHANGE; ++ W_SDREG(I_HMB_FC_CHANGE, ®s->intstatus, retries); ++ R_SDREG(newstatus, ®s->intstatus, retries); ++ bus->f1regdata += 2; ++ bus->fcstate = !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)); ++ intstatus |= (newstatus & bus->hostintmask); ++ } ++ ++ /* Just being here means nothing more to do for chipactive */ ++ if (intstatus & I_CHIPACTIVE) { ++ /* ASSERT(bus->clkstate == CLK_AVAIL); */ ++ intstatus &= ~I_CHIPACTIVE; ++ } ++ ++ /* Handle host mailbox indication */ ++ if (intstatus & I_HMB_HOST_INT) { ++ intstatus &= ~I_HMB_HOST_INT; ++ intstatus |= dhdsdio_hostmail(bus); ++ } ++ ++ /* Generally don't ask for these, can get CRC errors... */ ++ if (intstatus & I_WR_OOSYNC) { ++ DHD_ERROR(("Dongle reports WR_OOSYNC\n")); ++ intstatus &= ~I_WR_OOSYNC; ++ } ++ ++ if (intstatus & I_RD_OOSYNC) { ++ DHD_ERROR(("Dongle reports RD_OOSYNC\n")); ++ intstatus &= ~I_RD_OOSYNC; ++ } ++ ++ if (intstatus & I_SBINT) { ++ DHD_ERROR(("Dongle reports SBINT\n")); ++ intstatus &= ~I_SBINT; ++ } ++ ++ /* Would be active due to wake-wlan in gSPI */ ++ if (intstatus & I_CHIPACTIVE) { ++ DHD_INFO(("Dongle reports CHIPACTIVE\n")); ++ intstatus &= ~I_CHIPACTIVE; ++ } ++ ++ /* Ignore frame indications if rxskip is set */ ++ if (bus->rxskip) { ++ intstatus &= ~FRAME_AVAIL_MASK(bus); ++ } ++ ++ /* On frame indication, read available frames */ ++ if (PKT_AVAILABLE(bus, intstatus)) { ++ framecnt = dhdsdio_readframes(bus, rxlimit, &rxdone); ++ if (rxdone || bus->rxskip) ++ intstatus &= ~FRAME_AVAIL_MASK(bus); ++ rxlimit -= MIN(framecnt, rxlimit); ++ } ++ ++ /* Keep still-pending events for next scheduling */ ++ bus->intstatus = intstatus; ++ ++clkwait: ++ /* Re-enable interrupts to detect new device events (mailbox, rx frame) ++ * or clock availability. (Allows tx loop to check ipend if desired.) ++ * (Unless register access seems hosed, as we may not be able to ACK...) ++ */ ++ if (bus->intr && bus->intdis && !bcmsdh_regfail(sdh)) { ++ DHD_INTR(("%s: enable SDIO interrupts, rxdone %d framecnt %d\n", ++ __FUNCTION__, rxdone, framecnt)); ++ bus->intdis = FALSE; ++#if defined(OOB_INTR_ONLY) ++ bcmsdh_oob_intr_set(1); ++#endif /* defined(OOB_INTR_ONLY) */ ++ bcmsdh_intr_enable(sdh); ++ } ++ ++#if defined(OOB_INTR_ONLY) && !defined(HW_OOB) ++ /* In case of SW-OOB(using edge trigger), ++ * Check interrupt status in the dongle again after enable irq on the host. ++ * and rechedule dpc if interrupt is pended in the dongle. ++ * There is a chance to miss OOB interrupt while irq is disabled on the host. ++ * No need to do this with HW-OOB(level trigger) ++ */ ++ R_SDREG(newstatus, ®s->intstatus, retries); ++ if (bcmsdh_regfail(bus->sdh)) ++ newstatus = 0; ++ if (newstatus & bus->hostintmask) { ++ bus->ipend = TRUE; ++ resched = TRUE; ++ } ++#endif /* defined(OOB_INTR_ONLY) && !defined(HW_OOB) */ ++#ifdef PROP_TXSTATUS ++ dhd_wlfc_trigger_pktcommit(bus->dhd); ++#endif ++ if (TXCTLOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) { ++ int ret, i; ++ ++ uint8* frame_seq = bus->ctrl_frame_buf + SDPCM_FRAMETAG_LEN; ++ ++ if (*frame_seq != bus->tx_seq) { ++ DHD_INFO(("%s IOCTL frame seq lag detected!" ++ " frm_seq:%d != bus->tx_seq:%d, corrected\n", ++ __FUNCTION__, *frame_seq, bus->tx_seq)); ++ *frame_seq = bus->tx_seq; ++ } ++ ++ ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, ++ (uint8 *)bus->ctrl_frame_buf, (uint32)bus->ctrl_frame_len, ++ NULL, NULL, NULL); ++ ASSERT(ret != BCME_PENDING); ++ if (ret == BCME_NODEVICE) { ++ DHD_ERROR(("%s: Device asleep already\n", __FUNCTION__)); ++ } else if (ret < 0) { ++ /* On failure, abort the command and terminate the frame */ ++ DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n", ++ __FUNCTION__, ret)); ++ bus->tx_sderrs++; ++ ++ bcmsdh_abort(sdh, SDIO_FUNC_2); ++ ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, ++ SFC_WF_TERM, NULL); ++ bus->f1regdata++; ++ ++ for (i = 0; i < 3; i++) { ++ uint8 hi, lo; ++ hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_WFRAMEBCHI, NULL); ++ lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_WFRAMEBCLO, NULL); ++ bus->f1regdata += 2; ++ if ((hi == 0) && (lo == 0)) ++ break; ++ } ++ } ++ if (ret == 0) { ++ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; ++ } ++ ++ bus->ctrl_frame_stat = FALSE; ++ dhd_wait_event_wakeup(bus->dhd); ++ } ++ /* Send queued frames (limit 1 if rx may still be pending) */ ++ else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && ++ pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && DATAOK(bus)) { ++ framecnt = rxdone ? txlimit : MIN(txlimit, dhd_txminmax); ++ framecnt = dhdsdio_sendfromq(bus, framecnt); ++ txlimit -= framecnt; ++ } ++ /* Resched the DPC if ctrl cmd is pending on bus credit */ ++ if (bus->ctrl_frame_stat) ++ resched = TRUE; ++ ++ /* Resched if events or tx frames are pending, else await next interrupt */ ++ /* On failed register access, all bets are off: no resched or interrupts */ ++ if ((bus->dhd->busstate == DHD_BUS_DOWN) || bcmsdh_regfail(sdh)) { ++ if ((bus->sih && bus->sih->buscorerev >= 12) && !(dhdsdio_sleepcsr_get(bus) & ++ SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) { ++ /* Bus failed because of KSO */ ++ DHD_ERROR(("%s: Bus failed due to KSO\n", __FUNCTION__)); ++ bus->kso = FALSE; ++ } else { ++ DHD_ERROR(("%s: failed backplane access over SDIO, halting operation\n", ++ __FUNCTION__)); ++ bus->dhd->busstate = DHD_BUS_DOWN; ++ bus->intstatus = 0; ++ } ++ } else if (bus->clkstate == CLK_PENDING) { ++ /* Awaiting I_CHIPACTIVE; don't resched */ ++ } else if (bus->intstatus || bus->ipend || ++ (!bus->fcstate && pktq_mlen(&bus->txq, ~bus->flowcontrol) && DATAOK(bus)) || ++ PKT_AVAILABLE(bus, bus->intstatus)) { /* Read multiple frames */ ++ resched = TRUE; ++ } ++ ++ bus->dpc_sched = resched; ++ ++ /* If we're done for now, turn off clock request. */ ++ if ((bus->idletime == DHD_IDLE_IMMEDIATE) && (bus->clkstate != CLK_PENDING)) { ++ bus->activity = FALSE; ++ dhdsdio_clkctl(bus, CLK_NONE, FALSE); ++ } ++ ++exit: ++ /* attemp to update tx credit before exiting dpc */ ++ if (!resched && dhd_dpcpoll) { ++ if (dhdsdio_readframes(bus, dhd_rxbound, &rxdone) != 0) ++ resched = TRUE; ++ } ++ dhd_os_sdunlock(bus->dhd); ++ return resched; ++} ++ ++bool ++dhd_bus_dpc(struct dhd_bus *bus) ++{ ++ bool resched; ++ ++ /* Call the DPC directly. */ ++ DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __FUNCTION__)); ++ resched = dhdsdio_dpc(bus); ++ ++ return resched; ++} ++ ++void ++dhdsdio_isr(void *arg) ++{ ++ dhd_bus_t *bus = (dhd_bus_t*)arg; ++ bcmsdh_info_t *sdh; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (!bus) { ++ DHD_ERROR(("%s : bus is null pointer , exit \n", __FUNCTION__)); ++ return; ++ } ++ sdh = bus->sdh; ++ ++ if (bus->dhd->busstate == DHD_BUS_DOWN) { ++ DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); ++ return; ++ } ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ /* Count the interrupt call */ ++ bus->intrcount++; ++ bus->ipend = TRUE; ++ ++ /* Shouldn't get this interrupt if we're sleeping? */ ++ if (!SLPAUTO_ENAB(bus)) { ++ if (bus->sleeping) { ++ DHD_ERROR(("INTERRUPT WHILE SLEEPING??\n")); ++ return; ++ } else if (!KSO_ENAB(bus)) { ++ DHD_ERROR(("ISR in devsleep 1\n")); ++ } ++ } ++ ++ /* Disable additional interrupts (is this needed now)? */ ++ if (bus->intr) { ++ DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); ++ } else { ++ DHD_ERROR(("dhdsdio_isr() w/o interrupt configured!\n")); ++ } ++ ++ bcmsdh_intr_disable(sdh); ++ bus->intdis = TRUE; ++ ++#if defined(SDIO_ISR_THREAD) ++ DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __FUNCTION__)); ++ DHD_OS_WAKE_LOCK(bus->dhd); ++ while (dhdsdio_dpc(bus)); ++ DHD_OS_WAKE_UNLOCK(bus->dhd); ++#else ++ bus->dpc_sched = TRUE; ++ dhd_sched_dpc(bus->dhd); ++#endif ++ ++} ++ ++#ifdef SDTEST ++static void ++dhdsdio_pktgen_init(dhd_bus_t *bus) ++{ ++ /* Default to specified length, or full range */ ++ if (dhd_pktgen_len) { ++ bus->pktgen_maxlen = MIN(dhd_pktgen_len, MAX_PKTGEN_LEN); ++ bus->pktgen_minlen = bus->pktgen_maxlen; ++ } else { ++ bus->pktgen_maxlen = MAX_PKTGEN_LEN; ++ bus->pktgen_minlen = 0; ++ } ++ bus->pktgen_len = (uint16)bus->pktgen_minlen; ++ ++ /* Default to per-watchdog burst with 10s print time */ ++ bus->pktgen_freq = 1; ++ bus->pktgen_print = dhd_watchdog_ms ? (10000/dhd_watchdog_ms):0; ++ bus->pktgen_count = (dhd_pktgen * dhd_watchdog_ms + 999) / 1000; ++ ++ /* Default to echo mode */ ++ bus->pktgen_mode = DHD_PKTGEN_ECHO; ++ bus->pktgen_stop = 1; ++} ++ ++static void ++dhdsdio_pktgen(dhd_bus_t *bus) ++{ ++ void *pkt; ++ uint8 *data; ++ uint pktcount; ++ uint fillbyte; ++ osl_t *osh = bus->dhd->osh; ++ uint16 len; ++ ulong time_lapse; ++ uint sent_pkts; ++ uint rcvd_pkts; ++ ++ /* Display current count if appropriate */ ++ if (bus->pktgen_print && (++bus->pktgen_ptick >= bus->pktgen_print)) { ++ bus->pktgen_ptick = 0; ++ printf("%s: send attempts %d, rcvd %d, errors %d\n", ++ __FUNCTION__, bus->pktgen_sent, bus->pktgen_rcvd, bus->pktgen_fail); ++ ++ /* Print throughput stats only for constant length packet runs */ ++ if (bus->pktgen_minlen == bus->pktgen_maxlen) { ++ time_lapse = jiffies - bus->pktgen_prev_time; ++ bus->pktgen_prev_time = jiffies; ++ sent_pkts = bus->pktgen_sent - bus->pktgen_prev_sent; ++ bus->pktgen_prev_sent = bus->pktgen_sent; ++ rcvd_pkts = bus->pktgen_rcvd - bus->pktgen_prev_rcvd; ++ bus->pktgen_prev_rcvd = bus->pktgen_rcvd; ++ ++ printf("%s: Tx Throughput %d kbps, Rx Throughput %d kbps\n", ++ __FUNCTION__, ++ (sent_pkts * bus->pktgen_len / jiffies_to_msecs(time_lapse)) * 8, ++ (rcvd_pkts * bus->pktgen_len / jiffies_to_msecs(time_lapse)) * 8); ++ } ++ } ++ ++ /* For recv mode, just make sure dongle has started sending */ ++ if (bus->pktgen_mode == DHD_PKTGEN_RECV) { ++ if (bus->pktgen_rcv_state == PKTGEN_RCV_IDLE) { ++ bus->pktgen_rcv_state = PKTGEN_RCV_ONGOING; ++ dhdsdio_sdtest_set(bus, bus->pktgen_total); ++ } ++ return; ++ } ++ ++ /* Otherwise, generate or request the specified number of packets */ ++ for (pktcount = 0; pktcount < bus->pktgen_count; pktcount++) { ++ /* Stop if total has been reached */ ++ if (bus->pktgen_total && (bus->pktgen_sent >= bus->pktgen_total)) { ++ bus->pktgen_count = 0; ++ break; ++ } ++ ++ /* Allocate an appropriate-sized packet */ ++ if (bus->pktgen_mode == DHD_PKTGEN_RXBURST) { ++ len = SDPCM_TEST_PKT_CNT_FLD_LEN; ++ } else { ++ len = bus->pktgen_len; ++ } ++ if (!(pkt = PKTGET(osh, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN), ++ TRUE))) {; ++ DHD_ERROR(("%s: PKTGET failed!\n", __FUNCTION__)); ++ break; ++ } ++ PKTALIGN(osh, pkt, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN), DHD_SDALIGN); ++ data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN; ++ ++ /* Write test header cmd and extra based on mode */ ++ switch (bus->pktgen_mode) { ++ case DHD_PKTGEN_ECHO: ++ *data++ = SDPCM_TEST_ECHOREQ; ++ *data++ = (uint8)bus->pktgen_sent; ++ break; ++ ++ case DHD_PKTGEN_SEND: ++ *data++ = SDPCM_TEST_DISCARD; ++ *data++ = (uint8)bus->pktgen_sent; ++ break; ++ ++ case DHD_PKTGEN_RXBURST: ++ *data++ = SDPCM_TEST_BURST; ++ *data++ = (uint8)bus->pktgen_count; /* Just for backward compatability */ ++ break; ++ ++ default: ++ DHD_ERROR(("Unrecognized pktgen mode %d\n", bus->pktgen_mode)); ++ PKTFREE(osh, pkt, TRUE); ++ bus->pktgen_count = 0; ++ return; ++ } ++ ++ /* Write test header length field */ ++ *data++ = (bus->pktgen_len >> 0); ++ *data++ = (bus->pktgen_len >> 8); ++ ++ /* Write frame count in a 4 byte field adjucent to SDPCM test header for ++ * burst mode ++ */ ++ if (bus->pktgen_mode == DHD_PKTGEN_RXBURST) { ++ *data++ = (uint8)(bus->pktgen_count >> 0); ++ *data++ = (uint8)(bus->pktgen_count >> 8); ++ *data++ = (uint8)(bus->pktgen_count >> 16); ++ *data++ = (uint8)(bus->pktgen_count >> 24); ++ } else { ++ ++ /* Then fill in the remainder -- N/A for burst */ ++ for (fillbyte = 0; fillbyte < len; fillbyte++) ++ *data++ = SDPCM_TEST_FILL(fillbyte, (uint8)bus->pktgen_sent); ++ } ++ ++#ifdef DHD_DEBUG ++ if (DHD_BYTES_ON() && DHD_DATA_ON()) { ++ data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN; ++ prhex("dhdsdio_pktgen: Tx Data", data, PKTLEN(osh, pkt) - SDPCM_HDRLEN); ++ } ++#endif ++ ++ /* Send it */ ++ if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE, FALSE)) { ++ bus->pktgen_fail++; ++ if (bus->pktgen_stop && bus->pktgen_stop == bus->pktgen_fail) ++ bus->pktgen_count = 0; ++ } ++ bus->pktgen_sent++; ++ ++ /* Bump length if not fixed, wrap at max */ ++ if (++bus->pktgen_len > bus->pktgen_maxlen) ++ bus->pktgen_len = (uint16)bus->pktgen_minlen; ++ ++ /* Special case for burst mode: just send one request! */ ++ if (bus->pktgen_mode == DHD_PKTGEN_RXBURST) ++ break; ++ } ++} ++ ++static void ++dhdsdio_sdtest_set(dhd_bus_t *bus, uint count) ++{ ++ void *pkt; ++ uint8 *data; ++ osl_t *osh = bus->dhd->osh; ++ ++ /* Allocate the packet */ ++ if (!(pkt = PKTGET(osh, SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + ++ SDPCM_TEST_PKT_CNT_FLD_LEN + DHD_SDALIGN, TRUE))) { ++ DHD_ERROR(("%s: PKTGET failed!\n", __FUNCTION__)); ++ return; ++ } ++ PKTALIGN(osh, pkt, (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + ++ SDPCM_TEST_PKT_CNT_FLD_LEN), DHD_SDALIGN); ++ data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN; ++ ++ /* Fill in the test header */ ++ *data++ = SDPCM_TEST_SEND; ++ *data++ = (count > 0)?TRUE:FALSE; ++ *data++ = (bus->pktgen_maxlen >> 0); ++ *data++ = (bus->pktgen_maxlen >> 8); ++ *data++ = (uint8)(count >> 0); ++ *data++ = (uint8)(count >> 8); ++ *data++ = (uint8)(count >> 16); ++ *data++ = (uint8)(count >> 24); ++ ++ /* Send it */ ++ if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE, FALSE)) ++ bus->pktgen_fail++; ++} ++ ++ ++static void ++dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq) ++{ ++ osl_t *osh = bus->dhd->osh; ++ uint8 *data; ++ uint pktlen; ++ ++ uint8 cmd; ++ uint8 extra; ++ uint16 len; ++ uint16 offset; ++ ++ /* Check for min length */ ++ if ((pktlen = PKTLEN(osh, pkt)) < SDPCM_TEST_HDRLEN) { ++ DHD_ERROR(("dhdsdio_restrcv: toss runt frame, pktlen %d\n", pktlen)); ++ PKTFREE(osh, pkt, FALSE); ++ return; ++ } ++ ++ /* Extract header fields */ ++ data = PKTDATA(osh, pkt); ++ cmd = *data++; ++ extra = *data++; ++ len = *data++; len += *data++ << 8; ++ DHD_TRACE(("%s:cmd:%d, xtra:%d,len:%d\n", __FUNCTION__, cmd, extra, len)); ++ /* Check length for relevant commands */ ++ if (cmd == SDPCM_TEST_DISCARD || cmd == SDPCM_TEST_ECHOREQ || cmd == SDPCM_TEST_ECHORSP) { ++ if (pktlen != len + SDPCM_TEST_HDRLEN) { ++ DHD_ERROR(("dhdsdio_testrcv: frame length mismatch, pktlen %d seq %d" ++ " cmd %d extra %d len %d\n", pktlen, seq, cmd, extra, len)); ++ PKTFREE(osh, pkt, FALSE); ++ return; ++ } ++ } ++ ++ /* Process as per command */ ++ switch (cmd) { ++ case SDPCM_TEST_ECHOREQ: ++ /* Rx->Tx turnaround ok (even on NDIS w/current implementation) */ ++ *(uint8 *)(PKTDATA(osh, pkt)) = SDPCM_TEST_ECHORSP; ++ if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE, FALSE) == 0) { ++ bus->pktgen_sent++; ++ } else { ++ bus->pktgen_fail++; ++ PKTFREE(osh, pkt, FALSE); ++ } ++ bus->pktgen_rcvd++; ++ break; ++ ++ case SDPCM_TEST_ECHORSP: ++ if (bus->ext_loop) { ++ PKTFREE(osh, pkt, FALSE); ++ bus->pktgen_rcvd++; ++ break; ++ } ++ ++ for (offset = 0; offset < len; offset++, data++) { ++ if (*data != SDPCM_TEST_FILL(offset, extra)) { ++ DHD_ERROR(("dhdsdio_testrcv: echo data mismatch: " ++ "offset %d (len %d) expect 0x%02x rcvd 0x%02x\n", ++ offset, len, SDPCM_TEST_FILL(offset, extra), *data)); ++ break; ++ } ++ } ++ PKTFREE(osh, pkt, FALSE); ++ bus->pktgen_rcvd++; ++ break; ++ ++ case SDPCM_TEST_DISCARD: ++ { ++ int i = 0; ++ uint8 *prn = data; ++ uint8 testval = extra; ++ for (i = 0; i < len; i++) { ++ if (*prn != testval) { ++ DHD_ERROR(("DIErr@Pkt#:%d,Ix:%d, expected:0x%x, got:0x%x\n", ++ i, bus->pktgen_rcvd_rcvsession, testval, *prn)); ++ prn++; testval++; ++ } ++ } ++ } ++ PKTFREE(osh, pkt, FALSE); ++ bus->pktgen_rcvd++; ++ break; ++ ++ case SDPCM_TEST_BURST: ++ case SDPCM_TEST_SEND: ++ default: ++ DHD_INFO(("dhdsdio_testrcv: unsupported or unknown command, pktlen %d seq %d" ++ " cmd %d extra %d len %d\n", pktlen, seq, cmd, extra, len)); ++ PKTFREE(osh, pkt, FALSE); ++ break; ++ } ++ ++ /* For recv mode, stop at limit (and tell dongle to stop sending) */ ++ if (bus->pktgen_mode == DHD_PKTGEN_RECV) { ++ if (bus->pktgen_rcv_state != PKTGEN_RCV_IDLE) { ++ bus->pktgen_rcvd_rcvsession++; ++ ++ if (bus->pktgen_total && ++ (bus->pktgen_rcvd_rcvsession >= bus->pktgen_total)) { ++ bus->pktgen_count = 0; ++ DHD_ERROR(("Pktgen:rcv test complete!\n")); ++ bus->pktgen_rcv_state = PKTGEN_RCV_IDLE; ++ dhdsdio_sdtest_set(bus, FALSE); ++ bus->pktgen_rcvd_rcvsession = 0; ++ } ++ } ++ } ++} ++#endif /* SDTEST */ ++ ++extern void ++dhd_disable_intr(dhd_pub_t *dhdp) ++{ ++ dhd_bus_t *bus; ++ bus = dhdp->bus; ++ bcmsdh_intr_disable(bus->sdh); ++} ++ ++extern bool ++dhd_bus_watchdog(dhd_pub_t *dhdp) ++{ ++ dhd_bus_t *bus; ++ ++ DHD_TIMER(("%s: Enter\n", __FUNCTION__)); ++ ++ bus = dhdp->bus; ++ ++ if (bus->dhd->dongle_reset) ++ return FALSE; ++ ++ /* Ignore the timer if simulating bus down */ ++ if (!SLPAUTO_ENAB(bus) && bus->sleeping) ++ return FALSE; ++ ++ if (dhdp->busstate == DHD_BUS_DOWN) ++ return FALSE; ++ ++ /* Poll period: check device if appropriate. */ ++ if (!SLPAUTO_ENAB(bus) && (bus->poll && (++bus->polltick >= bus->pollrate))) { ++ uint32 intstatus = 0; ++ ++ /* Reset poll tick */ ++ bus->polltick = 0; ++ ++ /* Check device if no interrupts */ ++ if (!bus->intr || (bus->intrcount == bus->lastintrs)) { ++ ++ if (!bus->dpc_sched) { ++ uint8 devpend; ++ devpend = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, ++ SDIOD_CCCR_INTPEND, NULL); ++ intstatus = devpend & (INTR_STATUS_FUNC1 | INTR_STATUS_FUNC2); ++ } ++ ++ /* If there is something, make like the ISR and schedule the DPC */ ++ if (intstatus) { ++ bus->pollcnt++; ++ bus->ipend = TRUE; ++ if (bus->intr) { ++ bcmsdh_intr_disable(bus->sdh); ++ } ++ bus->dpc_sched = TRUE; ++ dhd_sched_dpc(bus->dhd); ++ ++ } ++ } ++ ++ /* Update interrupt tracking */ ++ bus->lastintrs = bus->intrcount; ++ } ++ ++#ifdef DHD_DEBUG ++ /* Poll for console output periodically */ ++ if (dhdp->busstate == DHD_BUS_DATA && dhd_console_ms != 0) { ++ bus->console.count += dhd_watchdog_ms; ++ if (bus->console.count >= dhd_console_ms) { ++ bus->console.count -= dhd_console_ms; ++ /* Make sure backplane clock is on */ ++ if (SLPAUTO_ENAB(bus)) ++ dhdsdio_bussleep(bus, FALSE); ++ else ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ if (dhdsdio_readconsole(bus) < 0) ++ dhd_console_ms = 0; /* On error, stop trying */ ++ } ++ } ++#endif /* DHD_DEBUG */ ++ ++#ifdef SDTEST ++ /* Generate packets if configured */ ++ if (bus->pktgen_count && (++bus->pktgen_tick >= bus->pktgen_freq)) { ++ /* Make sure backplane clock is on */ ++ if (SLPAUTO_ENAB(bus)) ++ dhdsdio_bussleep(bus, FALSE); ++ else ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ bus->pktgen_tick = 0; ++ dhdsdio_pktgen(bus); ++ } ++#endif ++ ++ /* On idle timeout clear activity flag and/or turn off clock */ ++#ifdef DHD_USE_IDLECOUNT ++ if (bus->activity) ++ bus->activity = FALSE; ++ else { ++ bus->idlecount++; ++ ++ if (bus->idlecount >= bus->idletime) { ++ DHD_TIMER(("%s: DHD Idle state!!\n", __FUNCTION__)); ++ ++ if (SLPAUTO_ENAB(bus)) { ++ if (dhdsdio_bussleep(bus, TRUE) != BCME_BUSY) ++ dhd_os_wd_timer(bus->dhd, 0); ++ } else ++ dhdsdio_clkctl(bus, CLK_NONE, FALSE); ++ ++ bus->idlecount = 0; ++ } ++ } ++#else ++ if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { ++ if (++bus->idlecount > bus->idletime) { ++ bus->idlecount = 0; ++ if (bus->activity) { ++ bus->activity = FALSE; ++ if (SLPAUTO_ENAB(bus)) { ++ if (!bus->readframes) ++ dhdsdio_bussleep(bus, TRUE); ++ else ++ bus->reqbussleep = TRUE; ++ } ++ else ++ dhdsdio_clkctl(bus, CLK_NONE, FALSE); ++ } ++ } ++ } ++#endif /* DHD_USE_IDLECOUNT */ ++ ++ return bus->ipend; ++} ++ ++#ifdef DHD_DEBUG ++extern int ++dhd_bus_console_in(dhd_pub_t *dhdp, uchar *msg, uint msglen) ++{ ++ dhd_bus_t *bus = dhdp->bus; ++ uint32 addr, val; ++ int rv; ++ void *pkt; ++ ++ /* Address could be zero if CONSOLE := 0 in dongle Makefile */ ++ if (bus->console_addr == 0) ++ return BCME_UNSUPPORTED; ++ ++ /* Exclusive bus access */ ++ dhd_os_sdlock(bus->dhd); ++ ++ /* Don't allow input if dongle is in reset */ ++ if (bus->dhd->dongle_reset) { ++ dhd_os_sdunlock(bus->dhd); ++ return BCME_NOTREADY; ++ } ++ ++ /* Request clock to allow SDIO accesses */ ++ BUS_WAKE(bus); ++ /* No pend allowed since txpkt is called later, ht clk has to be on */ ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ ++ /* Zero cbuf_index */ ++ addr = bus->console_addr + OFFSETOF(hndrte_cons_t, cbuf_idx); ++ val = htol32(0); ++ if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)&val, sizeof(val))) < 0) ++ goto done; ++ ++ /* Write message into cbuf */ ++ addr = bus->console_addr + OFFSETOF(hndrte_cons_t, cbuf); ++ if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)msg, msglen)) < 0) ++ goto done; ++ ++ /* Write length into vcons_in */ ++ addr = bus->console_addr + OFFSETOF(hndrte_cons_t, vcons_in); ++ val = htol32(msglen); ++ if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)&val, sizeof(val))) < 0) ++ goto done; ++ ++ /* Bump dongle by sending an empty packet on the event channel. ++ * sdpcm_sendup (RX) checks for virtual console input. ++ */ ++ if ((pkt = PKTGET(bus->dhd->osh, 4 + SDPCM_RESERVE, TRUE)) != NULL) ++ dhdsdio_txpkt(bus, pkt, SDPCM_EVENT_CHANNEL, TRUE, FALSE); ++ ++done: ++ if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { ++ bus->activity = FALSE; ++ dhdsdio_clkctl(bus, CLK_NONE, TRUE); ++ } ++ ++ dhd_os_sdunlock(bus->dhd); ++ ++ return rv; ++} ++#endif /* DHD_DEBUG */ ++ ++#ifdef DHD_DEBUG ++static void ++dhd_dump_cis(uint fn, uint8 *cis) ++{ ++ uint byte, tag, tdata; ++ DHD_INFO(("Function %d CIS:\n", fn)); ++ ++ for (tdata = byte = 0; byte < SBSDIO_CIS_SIZE_LIMIT; byte++) { ++ if ((byte % 16) == 0) ++ DHD_INFO((" ")); ++ DHD_INFO(("%02x ", cis[byte])); ++ if ((byte % 16) == 15) ++ DHD_INFO(("\n")); ++ if (!tdata--) { ++ tag = cis[byte]; ++ if (tag == 0xff) ++ break; ++ else if (!tag) ++ tdata = 0; ++ else if ((byte + 1) < SBSDIO_CIS_SIZE_LIMIT) ++ tdata = cis[byte + 1] + 1; ++ else ++ DHD_INFO(("]")); ++ } ++ } ++ if ((byte % 16) != 15) ++ DHD_INFO(("\n")); ++} ++#endif /* DHD_DEBUG */ ++ ++static bool ++dhdsdio_chipmatch(uint16 chipid) ++{ ++ if (chipid == BCM4325_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM4329_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM4315_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM4319_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM4336_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM4330_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM43237_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM43362_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM4314_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM4334_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM43341_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM43239_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM4324_CHIP_ID) ++ return TRUE; ++ if (chipid == BCM4335_CHIP_ID) ++ return TRUE; ++ return FALSE; ++} ++ ++static void * ++dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, ++ uint16 func, uint bustype, void *regsva, osl_t * osh, void *sdh) ++{ ++ int ret; ++ dhd_bus_t *bus; ++#ifdef GET_CUSTOM_MAC_ENABLE ++ struct ether_addr ea_addr; ++#endif /* GET_CUSTOM_MAC_ENABLE */ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ ++ if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) { ++ DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__)); ++ } ++ else { ++ DHD_ERROR(("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__)); ++ } ++ mutex_lock(&_dhd_sdio_mutex_lock_); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ ++ ++ /* Init global variables at run-time, not as part of the declaration. ++ * This is required to support init/de-init of the driver. Initialization ++ * of globals as part of the declaration results in non-deterministic ++ * behavior since the value of the globals may be different on the ++ * first time that the driver is initialized vs subsequent initializations. ++ */ ++ dhd_txbound = DHD_TXBOUND; ++ dhd_rxbound = DHD_RXBOUND; ++ dhd_alignctl = TRUE; ++ sd1idle = TRUE; ++ dhd_readahead = TRUE; ++ retrydata = FALSE; ++ dhd_doflow = FALSE; ++ dhd_dongle_memsize = 0; ++ dhd_txminmax = DHD_TXMINMAX; ++ ++ forcealign = TRUE; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ DHD_INFO(("%s: venid 0x%04x devid 0x%04x\n", __FUNCTION__, venid, devid)); ++ ++ /* We make assumptions about address window mappings */ ++ ASSERT((uintptr)regsva == SI_ENUM_BASE); ++ ++ /* BCMSDH passes venid and devid based on CIS parsing -- but low-power start ++ * means early parse could fail, so here we should get either an ID ++ * we recognize OR (-1) indicating we must request power first. ++ */ ++ /* Check the Vendor ID */ ++ switch (venid) { ++ case 0x0000: ++ case VENDOR_BROADCOM: ++ break; ++ default: ++ DHD_ERROR(("%s: unknown vendor: 0x%04x\n", ++ __FUNCTION__, venid)); ++ goto forcereturn; ++ } ++ ++ /* Check the Device ID and make sure it's one that we support */ ++ switch (devid) { ++ case BCM4325_D11DUAL_ID: /* 4325 802.11a/g id */ ++ case BCM4325_D11G_ID: /* 4325 802.11g 2.4Ghz band id */ ++ case BCM4325_D11A_ID: /* 4325 802.11a 5Ghz band id */ ++ DHD_INFO(("%s: found 4325 Dongle\n", __FUNCTION__)); ++ break; ++ case BCM4329_D11N_ID: /* 4329 802.11n dualband device */ ++ case BCM4329_D11N2G_ID: /* 4329 802.11n 2.4G device */ ++ case BCM4329_D11N5G_ID: /* 4329 802.11n 5G device */ ++ case 0x4329: ++ DHD_INFO(("%s: found 4329 Dongle\n", __FUNCTION__)); ++ break; ++ case BCM4315_D11DUAL_ID: /* 4315 802.11a/g id */ ++ case BCM4315_D11G_ID: /* 4315 802.11g id */ ++ case BCM4315_D11A_ID: /* 4315 802.11a id */ ++ DHD_INFO(("%s: found 4315 Dongle\n", __FUNCTION__)); ++ break; ++ case BCM4319_D11N_ID: /* 4319 802.11n id */ ++ case BCM4319_D11N2G_ID: /* 4319 802.11n2g id */ ++ case BCM4319_D11N5G_ID: /* 4319 802.11n5g id */ ++ DHD_INFO(("%s: found 4319 Dongle\n", __FUNCTION__)); ++ break; ++ case 0: ++ DHD_INFO(("%s: allow device id 0, will check chip internals\n", ++ __FUNCTION__)); ++ break; ++ ++ default: ++ DHD_ERROR(("%s: skipping 0x%04x/0x%04x, not a dongle\n", ++ __FUNCTION__, venid, devid)); ++ goto forcereturn; ++ } ++ ++ if (osh == NULL) { ++ /* Ask the OS interface part for an OSL handle */ ++ if (!(osh = dhd_osl_attach(sdh, DHD_BUS))) { ++ DHD_ERROR(("%s: osl_attach failed!\n", __FUNCTION__)); ++ goto forcereturn; ++ } ++ } ++ ++ /* Allocate private bus interface state */ ++ if (!(bus = MALLOC(osh, sizeof(dhd_bus_t)))) { ++ DHD_ERROR(("%s: MALLOC of dhd_bus_t failed\n", __FUNCTION__)); ++ goto fail; ++ } ++ bzero(bus, sizeof(dhd_bus_t)); ++ bus->sdh = sdh; ++ bus->cl_devid = (uint16)devid; ++ bus->bus = DHD_BUS; ++ bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1; ++ bus->usebufpool = FALSE; /* Use bufpool if allocated, else use locally malloced rxbuf */ ++ ++ /* attach the common module */ ++ dhd_common_init(osh); ++ ++ /* attempt to attach to the dongle */ ++ if (!(dhdsdio_probe_attach(bus, osh, sdh, regsva, devid))) { ++ DHD_ERROR(("%s: dhdsdio_probe_attach failed\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ /* Attach to the dhd/OS/network interface */ ++ if (!(bus->dhd = dhd_attach(osh, bus, SDPCM_RESERVE))) { ++ DHD_ERROR(("%s: dhd_attach failed\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ /* Allocate buffers */ ++ if (!(dhdsdio_probe_malloc(bus, osh, sdh))) { ++ DHD_ERROR(("%s: dhdsdio_probe_malloc failed\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ if (!(dhdsdio_probe_init(bus, osh, sdh))) { ++ DHD_ERROR(("%s: dhdsdio_probe_init failed\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ if (bus->intr) { ++ /* Register interrupt callback, but mask it (not operational yet). */ ++ DHD_INTR(("%s: disable SDIO interrupts (not interested yet)\n", __FUNCTION__)); ++ bcmsdh_intr_disable(sdh); ++ if ((ret = bcmsdh_intr_reg(sdh, dhdsdio_isr, bus)) != 0) { ++ DHD_ERROR(("%s: FAILED: bcmsdh_intr_reg returned %d\n", ++ __FUNCTION__, ret)); ++ goto fail; ++ } ++ DHD_INTR(("%s: registered SDIO interrupt function ok\n", __FUNCTION__)); ++ } else { ++ DHD_INFO(("%s: SDIO interrupt function is NOT registered due to polling mode\n", ++ __FUNCTION__)); ++ } ++ ++ DHD_INFO(("%s: completed!!\n", __FUNCTION__)); ++ ++#ifdef GET_CUSTOM_MAC_ENABLE ++ /* Read MAC address from external customer place */ ++ memset(&ea_addr, 0, sizeof(ea_addr)); ++ ret = dhd_custom_get_mac_address(ea_addr.octet); ++ if (!ret) { ++ memcpy(bus->dhd->mac.octet, (void *)&ea_addr, ETHER_ADDR_LEN); ++ } ++#endif /* GET_CUSTOM_MAC_ENABLE */ ++ ++ /* if firmware path present try to download and bring up bus */ ++ if (dhd_download_fw_on_driverload) { ++ if ((ret = dhd_bus_start(bus->dhd)) != 0) { ++ DHD_ERROR(("%s: dhd_bus_start failed\n", __FUNCTION__)); ++ goto fail; ++ } ++ } ++ /* Ok, have the per-port tell the stack we're open for business */ ++ if (dhd_net_attach(bus->dhd, 0) != 0) { ++ DHD_ERROR(("%s: Net attach failed!!\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ mutex_unlock(&_dhd_sdio_mutex_lock_); ++ DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++ ++ return bus; ++ ++fail: ++ dhdsdio_release(bus, osh); ++ ++forcereturn: ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ mutex_unlock(&_dhd_sdio_mutex_lock_); ++ DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++ ++ return NULL; ++} ++ ++static bool ++dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, ++ uint16 devid) ++{ ++ int err = 0; ++ uint8 clkctl = 0; ++ ++ bus->alp_only = TRUE; ++ bus->sih = NULL; ++ ++ /* Return the window to backplane enumeration space for core access */ ++ if (dhdsdio_set_siaddr_window(bus, SI_ENUM_BASE)) { ++ DHD_ERROR(("%s: FAILED to return to SI_ENUM_BASE\n", __FUNCTION__)); ++ } ++ ++#ifdef DHD_DEBUG ++ DHD_ERROR(("F1 signature read @0x18000000=0x%4x\n", ++ bcmsdh_reg_read(bus->sdh, SI_ENUM_BASE, 4))); ++ ++#endif /* DHD_DEBUG */ ++ ++ ++ /* Force PLL off until si_attach() programs PLL control regs */ ++ ++ ++ ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, DHD_INIT_CLKCTL1, &err); ++ if (!err) ++ clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); ++ ++ if (err || ((clkctl & ~SBSDIO_AVBITS) != DHD_INIT_CLKCTL1)) { ++ DHD_ERROR(("dhdsdio_probe: ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", ++ err, DHD_INIT_CLKCTL1, clkctl)); ++ goto fail; ++ } ++ ++#ifdef DHD_DEBUG ++ if (DHD_INFO_ON()) { ++ uint fn, numfn; ++ uint8 *cis[SDIOD_MAX_IOFUNCS]; ++ int err = 0; ++ ++ numfn = bcmsdh_query_iofnum(sdh); ++ ASSERT(numfn <= SDIOD_MAX_IOFUNCS); ++ ++ /* Make sure ALP is available before trying to read CIS */ ++ SPINWAIT(((clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_CHIPCLKCSR, NULL)), ++ !SBSDIO_ALPAV(clkctl)), PMU_MAX_TRANSITION_DLY); ++ ++ /* Now request ALP be put on the bus */ ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, ++ DHD_INIT_CLKCTL2, &err); ++ OSL_DELAY(65); ++ ++ for (fn = 0; fn <= numfn; fn++) { ++ if (!(cis[fn] = MALLOC(osh, SBSDIO_CIS_SIZE_LIMIT))) { ++ DHD_INFO(("dhdsdio_probe: fn %d cis malloc failed\n", fn)); ++ break; ++ } ++ bzero(cis[fn], SBSDIO_CIS_SIZE_LIMIT); ++ ++ if ((err = bcmsdh_cis_read(sdh, fn, cis[fn], SBSDIO_CIS_SIZE_LIMIT))) { ++ DHD_INFO(("dhdsdio_probe: fn %d cis read err %d\n", fn, err)); ++ MFREE(osh, cis[fn], SBSDIO_CIS_SIZE_LIMIT); ++ break; ++ } ++ dhd_dump_cis(fn, cis[fn]); ++ } ++ ++ while (fn-- > 0) { ++ ASSERT(cis[fn]); ++ MFREE(osh, cis[fn], SBSDIO_CIS_SIZE_LIMIT); ++ } ++ ++ if (err) { ++ DHD_ERROR(("dhdsdio_probe: failure reading or parsing CIS\n")); ++ goto fail; ++ } ++ } ++#endif /* DHD_DEBUG */ ++ ++ /* si_attach() will provide an SI handle and scan the backplane */ ++ if (!(bus->sih = si_attach((uint)devid, osh, regsva, DHD_BUS, sdh, ++ &bus->vars, &bus->varsz))) { ++ DHD_ERROR(("%s: si_attach failed!\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ bcmsdh_chipinfo(sdh, bus->sih->chip, bus->sih->chiprev); ++ ++ if (!dhdsdio_chipmatch((uint16)bus->sih->chip)) { ++ DHD_ERROR(("%s: unsupported chip: 0x%04x\n", ++ __FUNCTION__, bus->sih->chip)); ++ goto fail; ++ } ++ ++ if (bus->sih->buscorerev >= 12) ++ dhdsdio_clk_kso_init(bus); ++ else ++ bus->kso = TRUE; ++ ++ if (CST4330_CHIPMODE_SDIOD(bus->sih->chipst)) { ++ } ++ ++ si_sdiod_drive_strength_init(bus->sih, osh, dhd_sdiod_drive_strength); ++ ++ ++ /* Get info on the ARM and SOCRAM cores... */ ++ if (!DHD_NOPMU(bus)) { ++ if ((si_setcore(bus->sih, ARM7S_CORE_ID, 0)) || ++ (si_setcore(bus->sih, ARMCM3_CORE_ID, 0)) || ++ (si_setcore(bus->sih, ARMCR4_CORE_ID, 0))) { ++ bus->armrev = si_corerev(bus->sih); ++ } else { ++ DHD_ERROR(("%s: failed to find ARM core!\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ if (!si_setcore(bus->sih, ARMCR4_CORE_ID, 0)) { ++ if (!(bus->orig_ramsize = si_socram_size(bus->sih))) { ++ DHD_ERROR(("%s: failed to find SOCRAM memory!\n", __FUNCTION__)); ++ goto fail; ++ } ++ } else { ++ /* cr4 has a different way to find the RAM size from TCM's */ ++ if (!(bus->orig_ramsize = si_tcm_size(bus->sih))) { ++ DHD_ERROR(("%s: failed to find CR4-TCM memory!\n", __FUNCTION__)); ++ goto fail; ++ } ++ /* also populate base address */ ++ bus->dongle_ram_base = CR4_RAM_BASE; ++ } ++ bus->ramsize = bus->orig_ramsize; ++ if (dhd_dongle_memsize) ++ dhd_dongle_setmemsize(bus, dhd_dongle_memsize); ++ ++ DHD_ERROR(("DHD: dongle ram size is set to %d(orig %d)\n", ++ bus->ramsize, bus->orig_ramsize)); ++ ++ bus->srmemsize = si_socram_srmem_size(bus->sih); ++ } ++ ++ /* ...but normally deal with the SDPCMDEV core */ ++ if (!(bus->regs = si_setcore(bus->sih, PCMCIA_CORE_ID, 0)) && ++ !(bus->regs = si_setcore(bus->sih, SDIOD_CORE_ID, 0))) { ++ DHD_ERROR(("%s: failed to find SDIODEV core!\n", __FUNCTION__)); ++ goto fail; ++ } ++ bus->sdpcmrev = si_corerev(bus->sih); ++ ++ /* Set core control so an SDIO reset does a backplane reset */ ++ OR_REG(osh, &bus->regs->corecontrol, CC_BPRESEN); ++ bus->rxint_mode = SDIO_DEVICE_HMB_RXINT; ++ ++ if ((bus->sih->buscoretype == SDIOD_CORE_ID) && (bus->sdpcmrev >= 4) && ++ (bus->rxint_mode == SDIO_DEVICE_RXDATAINT_MODE_1)) ++ { ++ uint32 val; ++ ++ val = R_REG(osh, &bus->regs->corecontrol); ++ val &= ~CC_XMTDATAAVAIL_MODE; ++ val |= CC_XMTDATAAVAIL_CTRL; ++ W_REG(osh, &bus->regs->corecontrol, val); ++ } ++ ++ ++ pktq_init(&bus->txq, (PRIOMASK + 1), QLEN); ++ ++ /* Locate an appropriately-aligned portion of hdrbuf */ ++ bus->rxhdr = (uint8 *)ROUNDUP((uintptr)&bus->hdrbuf[0], DHD_SDALIGN); ++ ++ /* Set the poll and/or interrupt flags */ ++ bus->intr = (bool)dhd_intr; ++ if ((bus->poll = (bool)dhd_poll)) ++ bus->pollrate = 1; ++ ++#ifdef BCMSDIOH_TXGLOM ++ /* Setting default Glom mode */ ++ bus->glom_mode = SDPCM_TXGLOM_CPY; ++ /* Setting default Glom size */ ++ bus->glomsize = SDPCM_DEFGLOM_SIZE; ++#endif ++ ++ return TRUE; ++ ++fail: ++ if (bus->sih != NULL) { ++ si_detach(bus->sih); ++ bus->sih = NULL; ++ } ++ return FALSE; ++} ++ ++static bool ++dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh) ++{ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (bus->dhd->maxctl) { ++ bus->rxblen = ROUNDUP((bus->dhd->maxctl + SDPCM_HDRLEN), ALIGNMENT) + DHD_SDALIGN; ++ if (!(bus->rxbuf = DHD_OS_PREALLOC(osh, DHD_PREALLOC_RXBUF, bus->rxblen))) { ++ DHD_ERROR(("%s: MALLOC of %d-byte rxbuf failed\n", ++ __FUNCTION__, bus->rxblen)); ++ goto fail; ++ } ++ } ++ /* Allocate buffer to receive glomed packet */ ++ if (!(bus->databuf = DHD_OS_PREALLOC(osh, DHD_PREALLOC_DATABUF, MAX_DATA_BUF))) { ++ DHD_ERROR(("%s: MALLOC of %d-byte databuf failed\n", ++ __FUNCTION__, MAX_DATA_BUF)); ++ /* release rxbuf which was already located as above */ ++ if (!bus->rxblen) ++ DHD_OS_PREFREE(osh, bus->rxbuf, bus->rxblen); ++ goto fail; ++ } ++ ++ /* Align the buffer */ ++ if ((uintptr)bus->databuf % DHD_SDALIGN) ++ bus->dataptr = bus->databuf + (DHD_SDALIGN - ((uintptr)bus->databuf % DHD_SDALIGN)); ++ else ++ bus->dataptr = bus->databuf; ++ ++ return TRUE; ++ ++fail: ++ return FALSE; ++} ++ ++static bool ++dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh) ++{ ++ int32 fnum; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++#ifdef SDTEST ++ dhdsdio_pktgen_init(bus); ++#endif /* SDTEST */ ++ ++ /* Disable F2 to clear any intermediate frame state on the dongle */ ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, SDIO_FUNC_ENABLE_1, NULL); ++ ++ bus->dhd->busstate = DHD_BUS_DOWN; ++ bus->sleeping = FALSE; ++ bus->rxflow = FALSE; ++ bus->prev_rxlim_hit = 0; ++ ++ /* Done with backplane-dependent accesses, can drop clock... */ ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); ++ ++ /* ...and initialize clock/power states */ ++ bus->clkstate = CLK_SDONLY; ++ bus->idletime = (int32)dhd_idletime; ++ bus->idleclock = DHD_IDLE_ACTIVE; ++ ++ /* Query the SD clock speed */ ++ if (bcmsdh_iovar_op(sdh, "sd_divisor", NULL, 0, ++ &bus->sd_divisor, sizeof(int32), FALSE) != BCME_OK) { ++ DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_divisor")); ++ bus->sd_divisor = -1; ++ } else { ++ DHD_INFO(("%s: Initial value for %s is %d\n", ++ __FUNCTION__, "sd_divisor", bus->sd_divisor)); ++ } ++ ++ /* Query the SD bus mode */ ++ if (bcmsdh_iovar_op(sdh, "sd_mode", NULL, 0, ++ &bus->sd_mode, sizeof(int32), FALSE) != BCME_OK) { ++ DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_mode")); ++ bus->sd_mode = -1; ++ } else { ++ DHD_INFO(("%s: Initial value for %s is %d\n", ++ __FUNCTION__, "sd_mode", bus->sd_mode)); ++ } ++ ++ /* Query the F2 block size, set roundup accordingly */ ++ fnum = 2; ++ if (bcmsdh_iovar_op(sdh, "sd_blocksize", &fnum, sizeof(int32), ++ &bus->blocksize, sizeof(int32), FALSE) != BCME_OK) { ++ bus->blocksize = 0; ++ DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_blocksize")); ++ } else { ++ DHD_INFO(("%s: Initial value for %s is %d\n", ++ __FUNCTION__, "sd_blocksize", bus->blocksize)); ++ } ++ bus->roundup = MIN(max_roundup, bus->blocksize); ++ ++ /* Query if bus module supports packet chaining, default to use if supported */ ++ if (bcmsdh_iovar_op(sdh, "sd_rxchain", NULL, 0, ++ &bus->sd_rxchain, sizeof(int32), FALSE) != BCME_OK) { ++ bus->sd_rxchain = FALSE; ++ } else { ++ DHD_INFO(("%s: bus module (through bcmsdh API) %s chaining\n", ++ __FUNCTION__, (bus->sd_rxchain ? "supports" : "does not support"))); ++ } ++ bus->use_rxchain = (bool)bus->sd_rxchain; ++ ++ return TRUE; ++} ++ ++bool ++dhd_bus_download_firmware(struct dhd_bus *bus, osl_t *osh, ++ char *pfw_path, char *pnv_path) ++{ ++ bool ret; ++ bus->fw_path = pfw_path; ++ bus->nv_path = pnv_path; ++ ++ ret = dhdsdio_download_firmware(bus, osh, bus->sdh); ++ ++ ++ return ret; ++} ++ ++static bool ++dhdsdio_download_firmware(struct dhd_bus *bus, osl_t *osh, void *sdh) ++{ ++ bool ret; ++ ++ DHD_OS_WAKE_LOCK(bus->dhd); ++ ++ /* Download the firmware */ ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ ++ ret = _dhdsdio_download_firmware(bus) == 0; ++ ++ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); ++ ++ DHD_OS_WAKE_UNLOCK(bus->dhd); ++ return ret; ++} ++ ++/* Detach and free everything */ ++static void ++dhdsdio_release(dhd_bus_t *bus, osl_t *osh) ++{ ++ bool dongle_isolation = FALSE; ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (bus) { ++ ASSERT(osh); ++ ++ if (bus->dhd) { ++ dongle_isolation = bus->dhd->dongle_isolation; ++ dhd_detach(bus->dhd); ++ } ++ ++ /* De-register interrupt handler */ ++ bcmsdh_intr_disable(bus->sdh); ++ bcmsdh_intr_dereg(bus->sdh); ++ ++ if (bus->dhd) { ++ dhdsdio_release_dongle(bus, osh, dongle_isolation, TRUE); ++ dhd_free(bus->dhd); ++ bus->dhd = NULL; ++ } ++ ++ dhdsdio_release_malloc(bus, osh); ++ ++#ifdef DHD_DEBUG ++ if (bus->console.buf != NULL) ++ MFREE(osh, bus->console.buf, bus->console.bufsize); ++#endif ++ ++ MFREE(osh, bus, sizeof(dhd_bus_t)); ++ } ++ ++ if (osh) ++ dhd_osl_detach(osh); ++ ++ DHD_TRACE(("%s: Disconnected\n", __FUNCTION__)); ++} ++ ++static void ++dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh) ++{ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (bus->dhd && bus->dhd->dongle_reset) ++ return; ++ ++ if (bus->rxbuf) { ++#ifndef CONFIG_DHD_USE_STATIC_BUF ++ MFREE(osh, bus->rxbuf, bus->rxblen); ++#endif ++ bus->rxctl = bus->rxbuf = NULL; ++ bus->rxlen = 0; ++ } ++ ++ if (bus->databuf) { ++#ifndef CONFIG_DHD_USE_STATIC_BUF ++ MFREE(osh, bus->databuf, MAX_DATA_BUF); ++#endif ++ bus->databuf = NULL; ++ } ++ ++ if (bus->vars && bus->varsz) { ++ MFREE(osh, bus->vars, bus->varsz); ++ bus->vars = NULL; ++ } ++ ++} ++ ++ ++static void ++dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation, bool reset_flag) ++{ ++ DHD_TRACE(("%s: Enter bus->dhd %p bus->dhd->dongle_reset %d \n", __FUNCTION__, ++ bus->dhd, bus->dhd->dongle_reset)); ++ ++ if ((bus->dhd && bus->dhd->dongle_reset) && reset_flag) ++ return; ++ ++ if (bus->sih) { ++#if !defined(BCMLXSDMMC) ++ if (bus->dhd) { ++ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); ++ } ++ if (KSO_ENAB(bus) && (dongle_isolation == FALSE)) ++ si_watchdog(bus->sih, 4); ++#endif /* !defined(BCMLXSDMMC) */ ++ if (bus->dhd) { ++ dhdsdio_clkctl(bus, CLK_NONE, FALSE); ++ } ++ si_detach(bus->sih); ++ bus->sih = NULL; ++ if (bus->vars && bus->varsz) ++ MFREE(osh, bus->vars, bus->varsz); ++ bus->vars = NULL; ++ } ++ ++ DHD_TRACE(("%s: Disconnected\n", __FUNCTION__)); ++} ++ ++static void ++dhdsdio_disconnect(void *ptr) ++{ ++ dhd_bus_t *bus = (dhd_bus_t *)ptr; ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ ++ if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) { ++ DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__)); ++ } ++ else { ++ DHD_ERROR(("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__)); ++ } ++ mutex_lock(&_dhd_sdio_mutex_lock_); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (bus) { ++ ASSERT(bus->dhd); ++ dhdsdio_release(bus, bus->dhd->osh); ++ } ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ mutex_unlock(&_dhd_sdio_mutex_lock_); ++ DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ ++ ++ DHD_TRACE(("%s: Disconnected\n", __FUNCTION__)); ++} ++ ++ ++/* Register/Unregister functions are called by the main DHD entry ++ * point (e.g. module insertion) to link with the bus driver, in ++ * order to look for or await the device. ++ */ ++ ++static bcmsdh_driver_t dhd_sdio = { ++ dhdsdio_probe, ++ dhdsdio_disconnect ++}; ++ ++int ++dhd_bus_register(void) ++{ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ return bcmsdh_register(&dhd_sdio); ++} ++ ++void ++dhd_bus_unregister(void) ++{ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ bcmsdh_unregister(); ++} ++ ++#if defined(BCMLXSDMMC) ++/* Register a dummy SDIO client driver in order to be notified of new SDIO device */ ++int dhd_bus_reg_sdio_notify(void* semaphore) ++{ ++ return bcmsdh_reg_sdio_notify(semaphore); ++} ++ ++void dhd_bus_unreg_sdio_notify(void) ++{ ++ bcmsdh_unreg_sdio_notify(); ++} ++#endif /* defined(BCMLXSDMMC) */ ++ ++#ifdef BCMEMBEDIMAGE ++static int ++dhdsdio_download_code_array(struct dhd_bus *bus) ++{ ++ int bcmerror = -1; ++ int offset = 0; ++ unsigned char *ularray = NULL; ++ ++ DHD_INFO(("%s: download embedded firmware...\n", __FUNCTION__)); ++ ++ /* Download image */ ++ while ((offset + MEMBLOCK) < sizeof(dlarray)) { ++ bcmerror = dhdsdio_membytes(bus, TRUE, offset, ++ (uint8 *) (dlarray + offset), MEMBLOCK); ++ if (bcmerror) { ++ DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n", ++ __FUNCTION__, bcmerror, MEMBLOCK, offset)); ++ goto err; ++ } ++ ++ offset += MEMBLOCK; ++ } ++ ++ if (offset < sizeof(dlarray)) { ++ bcmerror = dhdsdio_membytes(bus, TRUE, offset, ++ (uint8 *) (dlarray + offset), sizeof(dlarray) - offset); ++ if (bcmerror) { ++ DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n", ++ __FUNCTION__, bcmerror, sizeof(dlarray) - offset, offset)); ++ goto err; ++ } ++ } ++ ++#ifdef DHD_DEBUG ++ /* Upload and compare the downloaded code */ ++ { ++ ularray = MALLOC(bus->dhd->osh, bus->ramsize); ++ /* Upload image to verify downloaded contents. */ ++ offset = 0; ++ memset(ularray, 0xaa, bus->ramsize); ++ while ((offset + MEMBLOCK) < sizeof(dlarray)) { ++ bcmerror = dhdsdio_membytes(bus, FALSE, offset, ularray + offset, MEMBLOCK); ++ if (bcmerror) { ++ DHD_ERROR(("%s: error %d on reading %d membytes at 0x%08x\n", ++ __FUNCTION__, bcmerror, MEMBLOCK, offset)); ++ goto err; ++ } ++ ++ offset += MEMBLOCK; ++ } ++ ++ if (offset < sizeof(dlarray)) { ++ bcmerror = dhdsdio_membytes(bus, FALSE, offset, ++ ularray + offset, sizeof(dlarray) - offset); ++ if (bcmerror) { ++ DHD_ERROR(("%s: error %d on reading %d membytes at 0x%08x\n", ++ __FUNCTION__, bcmerror, sizeof(dlarray) - offset, offset)); ++ goto err; ++ } ++ } ++ ++ if (memcmp(dlarray, ularray, sizeof(dlarray))) { ++ DHD_ERROR(("%s: Downloaded image is corrupted (%s, %s, %s).\n", ++ __FUNCTION__, dlimagename, dlimagever, dlimagedate)); ++ goto err; ++ } else ++ DHD_ERROR(("%s: Download, Upload and compare succeeded (%s, %s, %s).\n", ++ __FUNCTION__, dlimagename, dlimagever, dlimagedate)); ++ ++ } ++#endif /* DHD_DEBUG */ ++ ++err: ++ if (ularray) ++ MFREE(bus->dhd->osh, ularray, bus->ramsize); ++ return bcmerror; ++} ++#endif /* BCMEMBEDIMAGE */ ++ ++static int ++dhdsdio_download_code_file(struct dhd_bus *bus, char *pfw_path) ++{ ++ int bcmerror = -1; ++ int offset = 0; ++ int len; ++ void *image = NULL; ++ uint8 *memblock = NULL, *memptr; ++ ++ DHD_INFO(("%s: download firmware %s\n", __FUNCTION__, pfw_path)); ++ ++ image = dhd_os_open_image(pfw_path); ++ if (image == NULL) ++ goto err; ++ ++ memptr = memblock = MALLOC(bus->dhd->osh, MEMBLOCK + DHD_SDALIGN); ++ if (memblock == NULL) { ++ DHD_ERROR(("%s: Failed to allocate memory %d bytes\n", __FUNCTION__, MEMBLOCK)); ++ goto err; ++ } ++ if ((uint32)(uintptr)memblock % DHD_SDALIGN) ++ memptr += (DHD_SDALIGN - ((uint32)(uintptr)memblock % DHD_SDALIGN)); ++ ++ /* Download image */ ++ while ((len = dhd_os_get_image_block((char*)memptr, MEMBLOCK, image))) { ++ if (len < 0) { ++ DHD_ERROR(("%s: dhd_os_get_image_block failed (%d)\n", __FUNCTION__, len)); ++ bcmerror = BCME_ERROR; ++ goto err; ++ } ++ bcmerror = dhdsdio_membytes(bus, TRUE, offset, memptr, len); ++ if (bcmerror) { ++ DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n", ++ __FUNCTION__, bcmerror, MEMBLOCK, offset)); ++ goto err; ++ } ++ ++ offset += MEMBLOCK; ++ } ++ ++err: ++ if (memblock) ++ MFREE(bus->dhd->osh, memblock, MEMBLOCK + DHD_SDALIGN); ++ ++ if (image) ++ dhd_os_close_image(image); ++ ++ return bcmerror; ++} ++ ++/* ++ EXAMPLE: nvram_array ++ nvram_arry format: ++ name=value ++ Use carriage return at the end of each assignment, and an empty string with ++ carriage return at the end of array. ++ ++ For example: ++ unsigned char nvram_array[] = {"name1=value1\n", "name2=value2\n", "\n"}; ++ Hex values start with 0x, and mac addr format: xx:xx:xx:xx:xx:xx. ++ ++ Search "EXAMPLE: nvram_array" to see how the array is activated. ++*/ ++ ++void ++dhd_bus_set_nvram_params(struct dhd_bus * bus, const char *nvram_params) ++{ ++ bus->nvram_params = nvram_params; ++} ++ ++static int ++dhdsdio_download_nvram(struct dhd_bus *bus) ++{ ++ int bcmerror = -1; ++ uint len; ++ void * image = NULL; ++ char * memblock = NULL; ++ char *bufp; ++ char *pnv_path; ++ bool nvram_file_exists; ++ ++ pnv_path = bus->nv_path; ++ ++ nvram_file_exists = ((pnv_path != NULL) && (pnv_path[0] != '\0')); ++ if (!nvram_file_exists && (bus->nvram_params == NULL)) ++ return (0); ++ ++ if (nvram_file_exists) { ++ image = dhd_os_open_image(pnv_path); ++ if (image == NULL) ++ goto err; ++ } ++ ++ memblock = MALLOC(bus->dhd->osh, MAX_NVRAMBUF_SIZE); ++ if (memblock == NULL) { ++ DHD_ERROR(("%s: Failed to allocate memory %d bytes\n", ++ __FUNCTION__, MAX_NVRAMBUF_SIZE)); ++ goto err; ++ } ++ ++ /* Download variables */ ++ if (nvram_file_exists) { ++ len = dhd_os_get_image_block(memblock, MAX_NVRAMBUF_SIZE, image); ++ } ++ else { ++ len = strlen(bus->nvram_params); ++ ASSERT(len <= MAX_NVRAMBUF_SIZE); ++ memcpy(memblock, bus->nvram_params, len); ++ } ++ if (len > 0 && len < MAX_NVRAMBUF_SIZE) { ++ bufp = (char *)memblock; ++ bufp[len] = 0; ++ len = process_nvram_vars(bufp, len); ++ if (len % 4) { ++ len += 4 - (len % 4); ++ } ++ bufp += len; ++ *bufp++ = 0; ++ if (len) ++ bcmerror = dhdsdio_downloadvars(bus, memblock, len + 1); ++ if (bcmerror) { ++ DHD_ERROR(("%s: error downloading vars: %d\n", ++ __FUNCTION__, bcmerror)); ++ } ++ } ++ else { ++ DHD_ERROR(("%s: error reading nvram file: %d\n", ++ __FUNCTION__, len)); ++ bcmerror = BCME_SDIO_ERROR; ++ } ++ ++err: ++ if (memblock) ++ MFREE(bus->dhd->osh, memblock, MAX_NVRAMBUF_SIZE); ++ ++ if (image) ++ dhd_os_close_image(image); ++ ++ return bcmerror; ++} ++ ++static int ++_dhdsdio_download_firmware(struct dhd_bus *bus) ++{ ++ int bcmerror = -1; ++ ++ bool embed = FALSE; /* download embedded firmware */ ++ bool dlok = FALSE; /* download firmware succeeded */ ++ ++ /* Out immediately if no image to download */ ++ if ((bus->fw_path == NULL) || (bus->fw_path[0] == '\0')) { ++#ifdef BCMEMBEDIMAGE ++ embed = TRUE; ++#else ++ return 0; ++#endif ++ } ++ ++ /* Keep arm in reset */ ++ if (dhdsdio_download_state(bus, TRUE)) { ++ DHD_ERROR(("%s: error placing ARM core in reset\n", __FUNCTION__)); ++ goto err; ++ } ++ ++ /* External image takes precedence if specified */ ++ if ((bus->fw_path != NULL) && (bus->fw_path[0] != '\0')) { ++ if (dhdsdio_download_code_file(bus, bus->fw_path)) { ++ DHD_ERROR(("%s: dongle image file download failed\n", __FUNCTION__)); ++#ifdef BCMEMBEDIMAGE ++ embed = TRUE; ++#else ++ goto err; ++#endif ++ } ++ else { ++ embed = FALSE; ++ dlok = TRUE; ++ } ++ } ++#ifdef BCMEMBEDIMAGE ++ if (embed) { ++ if (dhdsdio_download_code_array(bus)) { ++ DHD_ERROR(("%s: dongle image array download failed\n", __FUNCTION__)); ++ goto err; ++ } ++ else { ++ dlok = TRUE; ++ } ++ } ++#else ++ BCM_REFERENCE(embed); ++#endif ++ if (!dlok) { ++ DHD_ERROR(("%s: dongle image download failed\n", __FUNCTION__)); ++ goto err; ++ } ++ ++ /* EXAMPLE: nvram_array */ ++ /* If a valid nvram_arry is specified as above, it can be passed down to dongle */ ++ /* dhd_bus_set_nvram_params(bus, (char *)&nvram_array); */ ++ ++ /* External nvram takes precedence if specified */ ++ if (dhdsdio_download_nvram(bus)) { ++ DHD_ERROR(("%s: dongle nvram file download failed\n", __FUNCTION__)); ++ goto err; ++ } ++ ++ /* Take arm out of reset */ ++ if (dhdsdio_download_state(bus, FALSE)) { ++ DHD_ERROR(("%s: error getting out of ARM core reset\n", __FUNCTION__)); ++ goto err; ++ } ++ ++ bcmerror = 0; ++ ++err: ++ return bcmerror; ++} ++ ++static int ++dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes, ++ void *pkt, bcmsdh_cmplt_fn_t complete, void *handle) ++{ ++ int status; ++ ++ if (!KSO_ENAB(bus)) { ++ DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); ++ return BCME_NODEVICE; ++ } ++ ++ status = bcmsdh_recv_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle); ++ ++ return status; ++} ++ ++static int ++dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes, ++ void *pkt, bcmsdh_cmplt_fn_t complete, void *handle) ++{ ++ if (!KSO_ENAB(bus)) { ++ DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); ++ return BCME_NODEVICE; ++ } ++ ++ return (bcmsdh_send_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle)); ++} ++ ++#ifdef BCMSDIOH_TXGLOM ++static void ++dhd_bcmsdh_glom_post(dhd_bus_t *bus, uint8 *frame, uint len) ++{ ++ bcmsdh_glom_post(bus->sdh, frame, len); ++} ++ ++static void ++dhd_bcmsdh_glom_clear(dhd_bus_t *bus) ++{ ++ bcmsdh_glom_clear(bus->sdh); ++} ++#endif ++ ++uint ++dhd_bus_chip(struct dhd_bus *bus) ++{ ++ ASSERT(bus->sih != NULL); ++ return bus->sih->chip; ++} ++ ++void * ++dhd_bus_pub(struct dhd_bus *bus) ++{ ++ return bus->dhd; ++} ++ ++void * ++dhd_bus_txq(struct dhd_bus *bus) ++{ ++ return &bus->txq; ++} ++ ++uint ++dhd_bus_hdrlen(struct dhd_bus *bus) ++{ ++ return SDPCM_HDRLEN; ++} ++ ++int ++dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) ++{ ++ int bcmerror = 0; ++ dhd_bus_t *bus; ++ ++ bus = dhdp->bus; ++ ++ if (flag == TRUE) { ++ if (!bus->dhd->dongle_reset) { ++ dhd_os_sdlock(dhdp); ++ dhd_os_wd_timer(dhdp, 0); ++#if !defined(IGNORE_ETH0_DOWN) ++ /* Force flow control as protection when stop come before ifconfig_down */ ++ dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, ON); ++#endif /* !defined(IGNORE_ETH0_DOWN) */ ++ /* Expect app to have torn down any connection before calling */ ++ /* Stop the bus, disable F2 */ ++ dhd_bus_stop(bus, FALSE); ++ ++#if defined(OOB_INTR_ONLY) ++ /* Clean up any pending IRQ */ ++ bcmsdh_set_irq(FALSE); ++#endif /* defined(OOB_INTR_ONLY) */ ++ ++ /* Clean tx/rx buffer pointers, detach from the dongle */ ++ dhdsdio_release_dongle(bus, bus->dhd->osh, TRUE, TRUE); ++ ++ bus->dhd->dongle_reset = TRUE; ++ bus->dhd->up = FALSE; ++#ifdef BCMSDIOH_TXGLOM ++ dhd_txglom_enable(dhdp, FALSE); ++#endif ++ dhd_os_sdunlock(dhdp); ++ ++ DHD_TRACE(("%s: WLAN OFF DONE\n", __FUNCTION__)); ++ /* App can now remove power from device */ ++ } else ++ bcmerror = BCME_SDIO_ERROR; ++ } else { ++ /* App must have restored power to device before calling */ ++ ++ DHD_TRACE(("\n\n%s: == WLAN ON ==\n", __FUNCTION__)); ++ ++ if (bus->dhd->dongle_reset) { ++ /* Turn on WLAN */ ++#ifdef DHDTHREAD ++ dhd_os_sdlock(dhdp); ++#endif /* DHDTHREAD */ ++ /* Reset SD client */ ++ bcmsdh_reset(bus->sdh); ++ ++ /* Attempt to re-attach & download */ ++ if (dhdsdio_probe_attach(bus, bus->dhd->osh, bus->sdh, ++ (uint32 *)SI_ENUM_BASE, ++ bus->cl_devid)) { ++ /* Attempt to download binary to the dongle */ ++ if (dhdsdio_probe_init(bus, bus->dhd->osh, bus->sdh) && ++ dhdsdio_download_firmware(bus, bus->dhd->osh, bus->sdh)) { ++ ++ /* Re-init bus, enable F2 transfer */ ++ bcmerror = dhd_bus_init((dhd_pub_t *) bus->dhd, FALSE); ++ if (bcmerror == BCME_OK) { ++#if defined(OOB_INTR_ONLY) ++ bcmsdh_set_irq(TRUE); ++ dhd_enable_oob_intr(bus, TRUE); ++#endif /* defined(OOB_INTR_ONLY) */ ++ ++ bus->dhd->dongle_reset = FALSE; ++ bus->dhd->up = TRUE; ++ ++#if !defined(IGNORE_ETH0_DOWN) ++ /* Restore flow control */ ++ dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, OFF); ++#endif ++ dhd_os_wd_timer(dhdp, dhd_watchdog_ms); ++#ifdef BCMSDIOH_TXGLOM ++ if ((dhdp->busstate == DHD_BUS_DATA) && ++ bcmsdh_glom_enabled()) { ++ dhd_txglom_enable(dhdp, TRUE); ++ } ++#endif /* BCMSDIOH_TXGLOM */ ++ DHD_TRACE(("%s: WLAN ON DONE\n", __FUNCTION__)); ++ } else { ++ dhd_bus_stop(bus, FALSE); ++ dhdsdio_release_dongle(bus, bus->dhd->osh, ++ TRUE, FALSE); ++ } ++ } else ++ bcmerror = BCME_SDIO_ERROR; ++ } else ++ bcmerror = BCME_SDIO_ERROR; ++ ++#ifdef DHDTHREAD ++ dhd_os_sdunlock(dhdp); ++#endif /* DHDTHREAD */ ++ } else { ++ bcmerror = BCME_SDIO_ERROR; ++ DHD_INFO(("%s called when dongle is not in reset\n", ++ __FUNCTION__)); ++ DHD_INFO(("Will call dhd_bus_start instead\n")); ++ sdioh_start(NULL, 1); ++ if ((bcmerror = dhd_bus_start(dhdp)) != 0) ++ DHD_ERROR(("%s: dhd_bus_start fail with %d\n", ++ __FUNCTION__, bcmerror)); ++ } ++ } ++ return bcmerror; ++} ++ ++/* Get Chip ID version */ ++uint dhd_bus_chip_id(dhd_pub_t *dhdp) ++{ ++ dhd_bus_t *bus = dhdp->bus; ++ ++ return bus->sih->chip; ++} ++ ++/* Get Chip Rev ID version */ ++uint dhd_bus_chiprev_id(dhd_pub_t *dhdp) ++{ ++ dhd_bus_t *bus = dhdp->bus; ++ ++ return bus->sih->chiprev; ++} ++ ++/* Get Chip Pkg ID version */ ++uint dhd_bus_chippkg_id(dhd_pub_t *dhdp) ++{ ++ dhd_bus_t *bus = dhdp->bus; ++ ++ return bus->sih->chippkg; ++} ++ ++int ++dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *data, uint size) ++{ ++ dhd_bus_t *bus; ++ ++ bus = dhdp->bus; ++ return dhdsdio_membytes(bus, set, address, data, size); ++} +diff --git a/drivers/net/wireless/bcmdhd/dhd_wlfc.h b/drivers/net/wireless/bcmdhd/dhd_wlfc.h +new file mode 100644 +index 00000000..5a64e6fd +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dhd_wlfc.h +@@ -0,0 +1,287 @@ ++/* ++* Copyright (C) 1999-2012, Broadcom Corporation ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2 (the "GPL"), ++* available at http://www.broadcom.com/licenses/GPLv2.php, with the ++* following added to such license: ++* ++* As a special exception, the copyright holders of this software give you ++* permission to link this software with independent modules, and to copy and ++* distribute the resulting executable under terms of your choice, provided that ++* you also meet, for each linked independent module, the terms and conditions of ++* the license of that module. An independent module is a module which is not ++* derived from this software. The special exception does not apply to any ++* modifications of the software. ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a license ++* other than the GPL, without Broadcom's express prior written consent. ++* $Id: dhd_wlfc.h 361006 2012-10-05 07:45:51Z $ ++* ++*/ ++#ifndef __wlfc_host_driver_definitions_h__ ++#define __wlfc_host_driver_definitions_h__ ++ ++/* 16 bits will provide an absolute max of 65536 slots */ ++#define WLFC_HANGER_MAXITEMS 1024 ++ ++#define WLFC_HANGER_ITEM_STATE_FREE 1 ++#define WLFC_HANGER_ITEM_STATE_INUSE 2 ++#define WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED 3 ++#define WLFC_PKTID_HSLOT_MASK 0xffff /* allow 16 bits only */ ++#define WLFC_PKTID_HSLOT_SHIFT 8 ++ ++/* x -> TXSTATUS TAG to/from firmware */ ++#define WLFC_PKTID_HSLOT_GET(x) \ ++ (((x) >> WLFC_PKTID_HSLOT_SHIFT) & WLFC_PKTID_HSLOT_MASK) ++#define WLFC_PKTID_HSLOT_SET(var, slot) \ ++ ((var) = ((var) & ~(WLFC_PKTID_HSLOT_MASK << WLFC_PKTID_HSLOT_SHIFT)) | \ ++ (((slot) & WLFC_PKTID_HSLOT_MASK) << WLFC_PKTID_HSLOT_SHIFT)) ++ ++#define WLFC_PKTID_FREERUNCTR_MASK 0xff ++ ++#define WLFC_PKTID_FREERUNCTR_GET(x) ((x) & WLFC_PKTID_FREERUNCTR_MASK) ++#define WLFC_PKTID_FREERUNCTR_SET(var, ctr) \ ++ ((var) = (((var) & ~WLFC_PKTID_FREERUNCTR_MASK) | \ ++ (((ctr) & WLFC_PKTID_FREERUNCTR_MASK)))) ++ ++#define WLFC_PKTQ_PENQ(pq, prec, p) ((pktq_full((pq)) || pktq_pfull((pq), (prec)))? \ ++ NULL : pktq_penq((pq), (prec), (p))) ++#define WLFC_PKTQ_PENQ_HEAD(pq, prec, p) ((pktq_full((pq)) || pktq_pfull((pq), (prec))) ? \ ++ NULL : pktq_penq_head((pq), (prec), (p))) ++ ++typedef enum ewlfc_packet_state { ++ eWLFC_PKTTYPE_NEW, ++ eWLFC_PKTTYPE_DELAYED, ++ eWLFC_PKTTYPE_SUPPRESSED, ++ eWLFC_PKTTYPE_MAX ++} ewlfc_packet_state_t; ++ ++typedef enum ewlfc_mac_entry_action { ++ eWLFC_MAC_ENTRY_ACTION_ADD, ++ eWLFC_MAC_ENTRY_ACTION_DEL, ++ eWLFC_MAC_ENTRY_ACTION_UPDATE, ++ eWLFC_MAC_ENTRY_ACTION_MAX ++} ewlfc_mac_entry_action_t; ++ ++typedef struct wlfc_hanger_item { ++ uint8 state; ++ uint8 gen; ++ uint8 pad[2]; ++ uint32 identifier; ++ void* pkt; ++#ifdef PROP_TXSTATUS_DEBUG ++ uint32 push_time; ++#endif ++} wlfc_hanger_item_t; ++ ++typedef struct wlfc_hanger { ++ int max_items; ++ uint32 pushed; ++ uint32 popped; ++ uint32 failed_to_push; ++ uint32 failed_to_pop; ++ uint32 failed_slotfind; ++ wlfc_hanger_item_t items[1]; ++ uint32 slot_pos; ++} wlfc_hanger_t; ++ ++#define WLFC_HANGER_SIZE(n) ((sizeof(wlfc_hanger_t) - \ ++ sizeof(wlfc_hanger_item_t)) + ((n)*sizeof(wlfc_hanger_item_t))) ++ ++#define WLFC_STATE_OPEN 1 ++#define WLFC_STATE_CLOSE 2 ++ ++#define WLFC_PSQ_PREC_COUNT ((AC_COUNT + 1) * 2) /* 2 for each AC traffic and bc/mc */ ++ ++#define WLFC_PSQ_LEN 2048 ++ ++#define WLFC_SENDQ_LEN 256 ++ ++ ++#define WLFC_FLOWCONTROL_HIWATER (2048 - 256) ++#define WLFC_FLOWCONTROL_LOWATER 256 ++ ++ ++typedef struct wlfc_mac_descriptor { ++ uint8 occupied; ++ uint8 interface_id; ++ uint8 iftype; ++ uint8 state; ++ uint8 ac_bitmap; /* for APSD */ ++ uint8 requested_credit; ++ uint8 requested_packet; ++ uint8 ea[ETHER_ADDR_LEN]; ++ /* ++ maintain (MAC,AC) based seq count for ++ packets going to the device. As well as bc/mc. ++ */ ++ uint8 seq[AC_COUNT + 1]; ++ uint8 generation; ++ struct pktq psq; ++ /* The AC pending bitmap that was reported to the fw at last change */ ++ uint8 traffic_lastreported_bmp; ++ /* The new AC pending bitmap */ ++ uint8 traffic_pending_bmp; ++ /* 1= send on next opportunity */ ++ uint8 send_tim_signal; ++ uint8 mac_handle; ++ uint transit_count; ++ uint suppr_transit_count; ++ uint suppress_count; ++ uint8 suppressed; ++ ++#ifdef PROP_TXSTATUS_DEBUG ++ uint32 dstncredit_sent_packets; ++ uint32 dstncredit_acks; ++ uint32 opened_ct; ++ uint32 closed_ct; ++#endif ++} wlfc_mac_descriptor_t; ++ ++#define WLFC_DECR_SEQCOUNT(entry, prec) do { if (entry->seq[(prec)] == 0) {\ ++ entry->seq[prec] = 0xff; } else entry->seq[prec]--;} while (0) ++ ++#define WLFC_INCR_SEQCOUNT(entry, prec) entry->seq[(prec)]++ ++#define WLFC_SEQCOUNT(entry, prec) entry->seq[(prec)] ++ ++typedef struct athost_wl_stat_counters { ++ uint32 pktin; ++ uint32 pkt2bus; ++ uint32 pktdropped; ++ uint32 tlv_parse_failed; ++ uint32 rollback; ++ uint32 rollback_failed; ++ uint32 sendq_full_error; ++ uint32 delayq_full_error; ++ uint32 credit_request_failed; ++ uint32 packet_request_failed; ++ uint32 mac_update_failed; ++ uint32 psmode_update_failed; ++ uint32 interface_update_failed; ++ uint32 wlfc_header_only_pkt; ++ uint32 txstatus_in; ++ uint32 d11_suppress; ++ uint32 wl_suppress; ++ uint32 bad_suppress; ++ uint32 pkt_freed; ++ uint32 pkt_free_err; ++ uint32 psq_wlsup_retx; ++ uint32 psq_wlsup_enq; ++ uint32 psq_d11sup_retx; ++ uint32 psq_d11sup_enq; ++ uint32 psq_hostq_retx; ++ uint32 psq_hostq_enq; ++ uint32 mac_handle_notfound; ++ uint32 wlc_tossed_pkts; ++ uint32 dhd_hdrpulls; ++ uint32 generic_error; ++ /* an extra one for bc/mc traffic */ ++ uint32 sendq_pkts[AC_COUNT + 1]; ++#ifdef PROP_TXSTATUS_DEBUG ++ /* all pkt2bus -> txstatus latency accumulated */ ++ uint32 latency_sample_count; ++ uint32 total_status_latency; ++ uint32 latency_most_recent; ++ int idx_delta; ++ uint32 deltas[10]; ++ uint32 fifo_credits_sent[6]; ++ uint32 fifo_credits_back[6]; ++ uint32 dropped_qfull[6]; ++ uint32 signal_only_pkts_sent; ++ uint32 signal_only_pkts_freed; ++#endif ++} athost_wl_stat_counters_t; ++ ++#ifdef PROP_TXSTATUS_DEBUG ++#define WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac) do { \ ++ (ctx)->stats.fifo_credits_sent[(ac)]++;} while (0) ++#define WLFC_HOST_FIFO_CREDIT_INC_BACKCTRS(ctx, ac) do { \ ++ (ctx)->stats.fifo_credits_back[(ac)]++;} while (0) ++#define WLFC_HOST_FIFO_DROPPEDCTR_INC(ctx, ac) do { \ ++ (ctx)->stats.dropped_qfull[(ac)]++;} while (0) ++#else ++#define WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac) do {} while (0) ++#define WLFC_HOST_FIFO_CREDIT_INC_BACKCTRS(ctx, ac) do {} while (0) ++#define WLFC_HOST_FIFO_DROPPEDCTR_INC(ctx, ac) do {} while (0) ++#endif ++ ++#define WLFC_FCMODE_NONE 0 ++#define WLFC_FCMODE_IMPLIED_CREDIT 1 ++#define WLFC_FCMODE_EXPLICIT_CREDIT 2 ++ ++/* How long to defer borrowing in milliseconds */ ++#define WLFC_BORROW_DEFER_PERIOD_MS 100 ++ ++/* Mask to represent available ACs (note: BC/MC is ignored */ ++#define WLFC_AC_MASK 0xF ++ ++/* Mask to check for only on-going AC_BE traffic */ ++#define WLFC_AC_BE_TRAFFIC_ONLY 0xD ++ ++typedef struct athost_wl_status_info { ++ uint8 last_seqid_to_wlc; ++ ++ /* OSL handle */ ++ osl_t* osh; ++ /* dhd pub */ ++ void* dhdp; ++ ++ /* stats */ ++ athost_wl_stat_counters_t stats; ++ ++ /* the additional ones are for bc/mc and ATIM FIFO */ ++ int FIFO_credit[AC_COUNT + 2]; ++ ++ /* Credit borrow counts for each FIFO from each of the other FIFOs */ ++ int credits_borrowed[AC_COUNT + 2][AC_COUNT + 2]; ++ ++ struct pktq SENDQ; ++ ++ /* packet hanger and MAC->handle lookup table */ ++ void* hanger; ++ struct { ++ /* table for individual nodes */ ++ wlfc_mac_descriptor_t nodes[WLFC_MAC_DESC_TABLE_SIZE]; ++ /* table for interfaces */ ++ wlfc_mac_descriptor_t interfaces[WLFC_MAX_IFNUM]; ++ /* OS may send packets to unknown (unassociated) destinations */ ++ /* A place holder for bc/mc and packets to unknown destinations */ ++ wlfc_mac_descriptor_t other; ++ } destination_entries; ++ /* token position for different priority packets */ ++ uint8 token_pos[AC_COUNT+1]; ++ /* ON/OFF state for flow control to the host network interface */ ++ uint8 hostif_flow_state[WLFC_MAX_IFNUM]; ++ uint8 host_ifidx; ++ /* to flow control an OS interface */ ++ uint8 toggle_host_if; ++ ++ /* ++ Mode in which the dhd flow control shall operate. Must be set before ++ traffic starts to the device. ++ 0 - Do not do any proptxtstatus flow control ++ 1 - Use implied credit from a packet status ++ 2 - Use explicit credit ++ */ ++ uint8 proptxstatus_mode; ++ ++ /* To borrow credits */ ++ uint8 allow_credit_borrow; ++ ++ /* Timestamp to compute how long to defer borrowing for */ ++ uint32 borrow_defer_timestamp; ++ ++} athost_wl_status_info_t; ++ ++int dhd_wlfc_enable(dhd_pub_t *dhd); ++int dhd_wlfc_interface_event(struct dhd_info *, ++ ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea); ++int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data); ++int dhd_wlfc_event(struct dhd_info *dhd); ++int dhd_os_wlfc_block(dhd_pub_t *pub); ++int dhd_os_wlfc_unblock(dhd_pub_t *pub); ++ ++#endif /* __wlfc_host_driver_definitions_h__ */ +diff --git a/drivers/net/wireless/bcmdhd/dngl_stats.h b/drivers/net/wireless/bcmdhd/dngl_stats.h +new file mode 100644 +index 00000000..5e5a2e2e +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dngl_stats.h +@@ -0,0 +1,43 @@ ++/* ++ * Common stats definitions for clients of dongle ++ * ports ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dngl_stats.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++#ifndef _dngl_stats_h_ ++#define _dngl_stats_h_ ++ ++typedef struct { ++ unsigned long rx_packets; /* total packets received */ ++ unsigned long tx_packets; /* total packets transmitted */ ++ unsigned long rx_bytes; /* total bytes received */ ++ unsigned long tx_bytes; /* total bytes transmitted */ ++ unsigned long rx_errors; /* bad packets received */ ++ unsigned long tx_errors; /* packet transmit problems */ ++ unsigned long rx_dropped; /* packets dropped by dongle */ ++ unsigned long tx_dropped; /* packets dropped by dongle */ ++ unsigned long multicast; /* multicast packets received */ ++} dngl_stats_t; ++ ++#endif /* _dngl_stats_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/dngl_wlhdr.h b/drivers/net/wireless/bcmdhd/dngl_wlhdr.h +new file mode 100644 +index 00000000..0e37df6e +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/dngl_wlhdr.h +@@ -0,0 +1,40 @@ ++/* ++ * Dongle WL Header definitions ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dngl_wlhdr.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++#ifndef _dngl_wlhdr_h_ ++#define _dngl_wlhdr_h_ ++ ++typedef struct wl_header { ++ uint8 type; /* Header type */ ++ uint8 version; /* Header version */ ++ int8 rssi; /* RSSI */ ++ uint8 pad; /* Unused */ ++} wl_header_t; ++ ++#define WL_HEADER_LEN sizeof(wl_header_t) ++#define WL_HEADER_TYPE 0 ++#define WL_HEADER_VER 1 ++#endif /* _dngl_wlhdr_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/hndpmu.c b/drivers/net/wireless/bcmdhd/hndpmu.c +new file mode 100644 +index 00000000..e639015b +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/hndpmu.c +@@ -0,0 +1,208 @@ ++/* ++ * Misc utility routines for accessing PMU corerev specific features ++ * of the SiliconBackplane-based Broadcom chips. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: hndpmu.c 354194 2012-08-30 08:39:03Z $ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PMU_ERROR(args) ++ ++#define PMU_MSG(args) ++ ++/* To check in verbose debugging messages not intended ++ * to be on except on private builds. ++ */ ++#define PMU_NONE(args) ++ ++ ++/* SDIO Pad drive strength to select value mappings. ++ * The last strength value in each table must be 0 (the tri-state value). ++ */ ++typedef struct { ++ uint8 strength; /* Pad Drive Strength in mA */ ++ uint8 sel; /* Chip-specific select value */ ++} sdiod_drive_str_t; ++ ++/* SDIO Drive Strength to sel value table for PMU Rev 1 */ ++static const sdiod_drive_str_t sdiod_drive_strength_tab1[] = { ++ {4, 0x2}, ++ {2, 0x3}, ++ {1, 0x0}, ++ {0, 0x0} }; ++ ++/* SDIO Drive Strength to sel value table for PMU Rev 2, 3 */ ++static const sdiod_drive_str_t sdiod_drive_strength_tab2[] = { ++ {12, 0x7}, ++ {10, 0x6}, ++ {8, 0x5}, ++ {6, 0x4}, ++ {4, 0x2}, ++ {2, 0x1}, ++ {0, 0x0} }; ++ ++/* SDIO Drive Strength to sel value table for PMU Rev 8 (1.8V) */ ++static const sdiod_drive_str_t sdiod_drive_strength_tab3[] = { ++ {32, 0x7}, ++ {26, 0x6}, ++ {22, 0x5}, ++ {16, 0x4}, ++ {12, 0x3}, ++ {8, 0x2}, ++ {4, 0x1}, ++ {0, 0x0} }; ++ ++/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8v) */ ++static const sdiod_drive_str_t sdiod_drive_strength_tab4_1v8[] = { ++ {32, 0x6}, ++ {26, 0x7}, ++ {22, 0x4}, ++ {16, 0x5}, ++ {12, 0x2}, ++ {8, 0x3}, ++ {4, 0x0}, ++ {0, 0x1} }; ++ ++/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.2v) */ ++ ++/* SDIO Drive Strength to sel value table for PMU Rev 11 (2.5v) */ ++ ++/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ ++static const sdiod_drive_str_t sdiod_drive_strength_tab5_1v8[] = { ++ {6, 0x7}, ++ {5, 0x6}, ++ {4, 0x5}, ++ {3, 0x4}, ++ {2, 0x2}, ++ {1, 0x1}, ++ {0, 0x0} }; ++ ++/* SDIO Drive Strength to sel value table for PMU Rev 13 (3.3v) */ ++ ++/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ ++static const sdiod_drive_str_t sdiod_drive_strength_tab6_1v8[] = { ++ {3, 0x3}, ++ {2, 0x2}, ++ {1, 0x1}, ++ {0, 0x0} }; ++ ++#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) ++ ++void ++si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength) ++{ ++ chipcregs_t *cc; ++ uint origidx, intr_val = 0; ++ sdiod_drive_str_t *str_tab = NULL; ++ uint32 str_mask = 0; ++ uint32 str_shift = 0; ++ ++ if (!(sih->cccaps & CC_CAP_PMU)) { ++ return; ++ } ++ ++ /* Remember original core before switch to chipc */ ++ cc = (chipcregs_t *) si_switch_core(sih, CC_CORE_ID, &origidx, &intr_val); ++ ++ switch (SDIOD_DRVSTR_KEY(sih->chip, sih->pmurev)) { ++ case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 1): ++ str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab1; ++ str_mask = 0x30000000; ++ str_shift = 28; ++ break; ++ case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 2): ++ case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 3): ++ case SDIOD_DRVSTR_KEY(BCM4315_CHIP_ID, 4): ++ str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab2; ++ str_mask = 0x00003800; ++ str_shift = 11; ++ break; ++ case SDIOD_DRVSTR_KEY(BCM4336_CHIP_ID, 8): ++ case SDIOD_DRVSTR_KEY(BCM4336_CHIP_ID, 11): ++ if (sih->pmurev == 8) { ++ str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab3; ++ } ++ else if (sih->pmurev == 11) { ++ str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab4_1v8; ++ } ++ str_mask = 0x00003800; ++ str_shift = 11; ++ break; ++ case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): ++ str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab4_1v8; ++ str_mask = 0x00003800; ++ str_shift = 11; ++ break; ++ case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): ++ str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab5_1v8; ++ str_mask = 0x00003800; ++ str_shift = 11; ++ break; ++ case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17): ++ str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab6_1v8; ++ str_mask = 0x00001800; ++ str_shift = 11; ++ break; ++ default: ++ PMU_MSG(("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", ++ bcm_chipname(sih->chip, chn, 8), sih->chiprev, sih->pmurev)); ++ ++ break; ++ } ++ ++ if (str_tab != NULL && cc != NULL) { ++ uint32 cc_data_temp; ++ int i; ++ ++ /* Pick the lowest available drive strength equal or greater than the ++ * requested strength. Drive strength of 0 requests tri-state. ++ */ ++ for (i = 0; drivestrength < str_tab[i].strength; i++) ++ ; ++ ++ if (i > 0 && drivestrength > str_tab[i].strength) ++ i--; ++ ++ W_REG(osh, &cc->chipcontrol_addr, 1); ++ cc_data_temp = R_REG(osh, &cc->chipcontrol_data); ++ cc_data_temp &= ~str_mask; ++ cc_data_temp |= str_tab[i].sel << str_shift; ++ W_REG(osh, &cc->chipcontrol_data, cc_data_temp); ++ ++ PMU_MSG(("SDIO: %dmA drive strength requested; set to %dmA\n", ++ drivestrength, str_tab[i].strength)); ++ } ++ ++ /* Return to original core */ ++ si_restore_core(sih, origidx, intr_val); ++} +diff --git a/drivers/net/wireless/bcmdhd/include/Makefile b/drivers/net/wireless/bcmdhd/include/Makefile +new file mode 100644 +index 00000000..42b3b689 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/Makefile +@@ -0,0 +1,53 @@ ++#!/bin/bash ++# ++# This script serves following purpose: ++# ++# 1. It generates native version information by querying ++# automerger maintained database to see where src/include ++# came from ++# 2. For select components, as listed in compvers.sh ++# it generates component version files ++# ++# Copyright 2005, Broadcom, Inc. ++# ++# $Id: Makefile 241686 2011-02-19 00:22:45Z $ ++# ++ ++SRCBASE := .. ++ ++TARGETS := epivers.h ++ ++ifdef VERBOSE ++export VERBOSE ++endif ++ ++all release: epivers compvers ++ ++# Generate epivers.h for native branch version ++epivers: ++ bash epivers.sh ++ ++# Generate epivers.h for native branch version ++compvers: ++ @if [ -s "compvers.sh" ]; then \ ++ echo "Generating component versions, if any"; \ ++ bash compvers.sh; \ ++ else \ ++ echo "Skipping component version generation"; \ ++ fi ++ ++# Generate epivers.h for native branch version ++clean_compvers: ++ @if [ -s "compvers.sh" ]; then \ ++ echo "bash compvers.sh clean"; \ ++ bash compvers.sh clean; \ ++ else \ ++ echo "Skipping component version clean"; \ ++ fi ++ ++clean: ++ rm -f $(TARGETS) *.prev ++ ++clean_all: clean clean_compvers ++ ++.PHONY: all release clean epivers compvers clean_compvers +diff --git a/drivers/net/wireless/bcmdhd/include/aidmp.h b/drivers/net/wireless/bcmdhd/include/aidmp.h +new file mode 100644 +index 00000000..63513e6f +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/aidmp.h +@@ -0,0 +1,375 @@ ++/* ++ * Broadcom AMBA Interconnect definitions. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: aidmp.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++#ifndef _AIDMP_H ++#define _AIDMP_H ++ ++ ++#define MFGID_ARM 0x43b ++#define MFGID_BRCM 0x4bf ++#define MFGID_MIPS 0x4a7 ++ ++ ++#define CC_SIM 0 ++#define CC_EROM 1 ++#define CC_CORESIGHT 9 ++#define CC_VERIF 0xb ++#define CC_OPTIMO 0xd ++#define CC_GEN 0xe ++#define CC_PRIMECELL 0xf ++ ++ ++#define ER_EROMENTRY 0x000 ++#define ER_REMAPCONTROL 0xe00 ++#define ER_REMAPSELECT 0xe04 ++#define ER_MASTERSELECT 0xe10 ++#define ER_ITCR 0xf00 ++#define ER_ITIP 0xf04 ++ ++ ++#define ER_TAG 0xe ++#define ER_TAG1 0x6 ++#define ER_VALID 1 ++#define ER_CI 0 ++#define ER_MP 2 ++#define ER_ADD 4 ++#define ER_END 0xe ++#define ER_BAD 0xffffffff ++ ++ ++#define CIA_MFG_MASK 0xfff00000 ++#define CIA_MFG_SHIFT 20 ++#define CIA_CID_MASK 0x000fff00 ++#define CIA_CID_SHIFT 8 ++#define CIA_CCL_MASK 0x000000f0 ++#define CIA_CCL_SHIFT 4 ++ ++ ++#define CIB_REV_MASK 0xff000000 ++#define CIB_REV_SHIFT 24 ++#define CIB_NSW_MASK 0x00f80000 ++#define CIB_NSW_SHIFT 19 ++#define CIB_NMW_MASK 0x0007c000 ++#define CIB_NMW_SHIFT 14 ++#define CIB_NSP_MASK 0x00003e00 ++#define CIB_NSP_SHIFT 9 ++#define CIB_NMP_MASK 0x000001f0 ++#define CIB_NMP_SHIFT 4 ++ ++ ++#define MPD_MUI_MASK 0x0000ff00 ++#define MPD_MUI_SHIFT 8 ++#define MPD_MP_MASK 0x000000f0 ++#define MPD_MP_SHIFT 4 ++ ++ ++#define AD_ADDR_MASK 0xfffff000 ++#define AD_SP_MASK 0x00000f00 ++#define AD_SP_SHIFT 8 ++#define AD_ST_MASK 0x000000c0 ++#define AD_ST_SHIFT 6 ++#define AD_ST_SLAVE 0x00000000 ++#define AD_ST_BRIDGE 0x00000040 ++#define AD_ST_SWRAP 0x00000080 ++#define AD_ST_MWRAP 0x000000c0 ++#define AD_SZ_MASK 0x00000030 ++#define AD_SZ_SHIFT 4 ++#define AD_SZ_4K 0x00000000 ++#define AD_SZ_8K 0x00000010 ++#define AD_SZ_16K 0x00000020 ++#define AD_SZ_SZD 0x00000030 ++#define AD_AG32 0x00000008 ++#define AD_ADDR_ALIGN 0x00000fff ++#define AD_SZ_BASE 0x00001000 ++ ++ ++#define SD_SZ_MASK 0xfffff000 ++#define SD_SG32 0x00000008 ++#define SD_SZ_ALIGN 0x00000fff ++ ++ ++#ifndef _LANGUAGE_ASSEMBLY ++ ++typedef volatile struct _aidmp { ++ uint32 oobselina30; ++ uint32 oobselina74; ++ uint32 PAD[6]; ++ uint32 oobselinb30; ++ uint32 oobselinb74; ++ uint32 PAD[6]; ++ uint32 oobselinc30; ++ uint32 oobselinc74; ++ uint32 PAD[6]; ++ uint32 oobselind30; ++ uint32 oobselind74; ++ uint32 PAD[38]; ++ uint32 oobselouta30; ++ uint32 oobselouta74; ++ uint32 PAD[6]; ++ uint32 oobseloutb30; ++ uint32 oobseloutb74; ++ uint32 PAD[6]; ++ uint32 oobseloutc30; ++ uint32 oobseloutc74; ++ uint32 PAD[6]; ++ uint32 oobseloutd30; ++ uint32 oobseloutd74; ++ uint32 PAD[38]; ++ uint32 oobsynca; ++ uint32 oobseloutaen; ++ uint32 PAD[6]; ++ uint32 oobsyncb; ++ uint32 oobseloutben; ++ uint32 PAD[6]; ++ uint32 oobsyncc; ++ uint32 oobseloutcen; ++ uint32 PAD[6]; ++ uint32 oobsyncd; ++ uint32 oobseloutden; ++ uint32 PAD[38]; ++ uint32 oobaextwidth; ++ uint32 oobainwidth; ++ uint32 oobaoutwidth; ++ uint32 PAD[5]; ++ uint32 oobbextwidth; ++ uint32 oobbinwidth; ++ uint32 oobboutwidth; ++ uint32 PAD[5]; ++ uint32 oobcextwidth; ++ uint32 oobcinwidth; ++ uint32 oobcoutwidth; ++ uint32 PAD[5]; ++ uint32 oobdextwidth; ++ uint32 oobdinwidth; ++ uint32 oobdoutwidth; ++ uint32 PAD[37]; ++ uint32 ioctrlset; ++ uint32 ioctrlclear; ++ uint32 ioctrl; ++ uint32 PAD[61]; ++ uint32 iostatus; ++ uint32 PAD[127]; ++ uint32 ioctrlwidth; ++ uint32 iostatuswidth; ++ uint32 PAD[62]; ++ uint32 resetctrl; ++ uint32 resetstatus; ++ uint32 resetreadid; ++ uint32 resetwriteid; ++ uint32 PAD[60]; ++ uint32 errlogctrl; ++ uint32 errlogdone; ++ uint32 errlogstatus; ++ uint32 errlogaddrlo; ++ uint32 errlogaddrhi; ++ uint32 errlogid; ++ uint32 errloguser; ++ uint32 errlogflags; ++ uint32 PAD[56]; ++ uint32 intstatus; ++ uint32 PAD[255]; ++ uint32 config; ++ uint32 PAD[63]; ++ uint32 itcr; ++ uint32 PAD[3]; ++ uint32 itipooba; ++ uint32 itipoobb; ++ uint32 itipoobc; ++ uint32 itipoobd; ++ uint32 PAD[4]; ++ uint32 itipoobaout; ++ uint32 itipoobbout; ++ uint32 itipoobcout; ++ uint32 itipoobdout; ++ uint32 PAD[4]; ++ uint32 itopooba; ++ uint32 itopoobb; ++ uint32 itopoobc; ++ uint32 itopoobd; ++ uint32 PAD[4]; ++ uint32 itopoobain; ++ uint32 itopoobbin; ++ uint32 itopoobcin; ++ uint32 itopoobdin; ++ uint32 PAD[4]; ++ uint32 itopreset; ++ uint32 PAD[15]; ++ uint32 peripherialid4; ++ uint32 peripherialid5; ++ uint32 peripherialid6; ++ uint32 peripherialid7; ++ uint32 peripherialid0; ++ uint32 peripherialid1; ++ uint32 peripherialid2; ++ uint32 peripherialid3; ++ uint32 componentid0; ++ uint32 componentid1; ++ uint32 componentid2; ++ uint32 componentid3; ++} aidmp_t; ++ ++#endif ++ ++ ++#define OOB_BUSCONFIG 0x020 ++#define OOB_STATUSA 0x100 ++#define OOB_STATUSB 0x104 ++#define OOB_STATUSC 0x108 ++#define OOB_STATUSD 0x10c ++#define OOB_ENABLEA0 0x200 ++#define OOB_ENABLEA1 0x204 ++#define OOB_ENABLEA2 0x208 ++#define OOB_ENABLEA3 0x20c ++#define OOB_ENABLEB0 0x280 ++#define OOB_ENABLEB1 0x284 ++#define OOB_ENABLEB2 0x288 ++#define OOB_ENABLEB3 0x28c ++#define OOB_ENABLEC0 0x300 ++#define OOB_ENABLEC1 0x304 ++#define OOB_ENABLEC2 0x308 ++#define OOB_ENABLEC3 0x30c ++#define OOB_ENABLED0 0x380 ++#define OOB_ENABLED1 0x384 ++#define OOB_ENABLED2 0x388 ++#define OOB_ENABLED3 0x38c ++#define OOB_ITCR 0xf00 ++#define OOB_ITIPOOBA 0xf10 ++#define OOB_ITIPOOBB 0xf14 ++#define OOB_ITIPOOBC 0xf18 ++#define OOB_ITIPOOBD 0xf1c ++#define OOB_ITOPOOBA 0xf30 ++#define OOB_ITOPOOBB 0xf34 ++#define OOB_ITOPOOBC 0xf38 ++#define OOB_ITOPOOBD 0xf3c ++ ++ ++#define AI_OOBSELINA30 0x000 ++#define AI_OOBSELINA74 0x004 ++#define AI_OOBSELINB30 0x020 ++#define AI_OOBSELINB74 0x024 ++#define AI_OOBSELINC30 0x040 ++#define AI_OOBSELINC74 0x044 ++#define AI_OOBSELIND30 0x060 ++#define AI_OOBSELIND74 0x064 ++#define AI_OOBSELOUTA30 0x100 ++#define AI_OOBSELOUTA74 0x104 ++#define AI_OOBSELOUTB30 0x120 ++#define AI_OOBSELOUTB74 0x124 ++#define AI_OOBSELOUTC30 0x140 ++#define AI_OOBSELOUTC74 0x144 ++#define AI_OOBSELOUTD30 0x160 ++#define AI_OOBSELOUTD74 0x164 ++#define AI_OOBSYNCA 0x200 ++#define AI_OOBSELOUTAEN 0x204 ++#define AI_OOBSYNCB 0x220 ++#define AI_OOBSELOUTBEN 0x224 ++#define AI_OOBSYNCC 0x240 ++#define AI_OOBSELOUTCEN 0x244 ++#define AI_OOBSYNCD 0x260 ++#define AI_OOBSELOUTDEN 0x264 ++#define AI_OOBAEXTWIDTH 0x300 ++#define AI_OOBAINWIDTH 0x304 ++#define AI_OOBAOUTWIDTH 0x308 ++#define AI_OOBBEXTWIDTH 0x320 ++#define AI_OOBBINWIDTH 0x324 ++#define AI_OOBBOUTWIDTH 0x328 ++#define AI_OOBCEXTWIDTH 0x340 ++#define AI_OOBCINWIDTH 0x344 ++#define AI_OOBCOUTWIDTH 0x348 ++#define AI_OOBDEXTWIDTH 0x360 ++#define AI_OOBDINWIDTH 0x364 ++#define AI_OOBDOUTWIDTH 0x368 ++ ++ ++#define AI_IOCTRLSET 0x400 ++#define AI_IOCTRLCLEAR 0x404 ++#define AI_IOCTRL 0x408 ++#define AI_IOSTATUS 0x500 ++#define AI_RESETCTRL 0x800 ++#define AI_RESETSTATUS 0x804 ++ ++#define AI_IOCTRLWIDTH 0x700 ++#define AI_IOSTATUSWIDTH 0x704 ++ ++#define AI_RESETREADID 0x808 ++#define AI_RESETWRITEID 0x80c ++#define AI_ERRLOGCTRL 0xa00 ++#define AI_ERRLOGDONE 0xa04 ++#define AI_ERRLOGSTATUS 0xa08 ++#define AI_ERRLOGADDRLO 0xa0c ++#define AI_ERRLOGADDRHI 0xa10 ++#define AI_ERRLOGID 0xa14 ++#define AI_ERRLOGUSER 0xa18 ++#define AI_ERRLOGFLAGS 0xa1c ++#define AI_INTSTATUS 0xa00 ++#define AI_CONFIG 0xe00 ++#define AI_ITCR 0xf00 ++#define AI_ITIPOOBA 0xf10 ++#define AI_ITIPOOBB 0xf14 ++#define AI_ITIPOOBC 0xf18 ++#define AI_ITIPOOBD 0xf1c ++#define AI_ITIPOOBAOUT 0xf30 ++#define AI_ITIPOOBBOUT 0xf34 ++#define AI_ITIPOOBCOUT 0xf38 ++#define AI_ITIPOOBDOUT 0xf3c ++#define AI_ITOPOOBA 0xf50 ++#define AI_ITOPOOBB 0xf54 ++#define AI_ITOPOOBC 0xf58 ++#define AI_ITOPOOBD 0xf5c ++#define AI_ITOPOOBAIN 0xf70 ++#define AI_ITOPOOBBIN 0xf74 ++#define AI_ITOPOOBCIN 0xf78 ++#define AI_ITOPOOBDIN 0xf7c ++#define AI_ITOPRESET 0xf90 ++#define AI_PERIPHERIALID4 0xfd0 ++#define AI_PERIPHERIALID5 0xfd4 ++#define AI_PERIPHERIALID6 0xfd8 ++#define AI_PERIPHERIALID7 0xfdc ++#define AI_PERIPHERIALID0 0xfe0 ++#define AI_PERIPHERIALID1 0xfe4 ++#define AI_PERIPHERIALID2 0xfe8 ++#define AI_PERIPHERIALID3 0xfec ++#define AI_COMPONENTID0 0xff0 ++#define AI_COMPONENTID1 0xff4 ++#define AI_COMPONENTID2 0xff8 ++#define AI_COMPONENTID3 0xffc ++ ++ ++#define AIRC_RESET 1 ++ ++ ++#define AICFG_OOB 0x00000020 ++#define AICFG_IOS 0x00000010 ++#define AICFG_IOC 0x00000008 ++#define AICFG_TO 0x00000004 ++#define AICFG_ERRL 0x00000002 ++#define AICFG_RST 0x00000001 ++ ++ ++#define OOB_SEL_OUTEN_B_5 15 ++#define OOB_SEL_OUTEN_B_6 23 ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/bcm_cfg.h b/drivers/net/wireless/bcmdhd/include/bcm_cfg.h +new file mode 100644 +index 00000000..26da7521 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcm_cfg.h +@@ -0,0 +1,29 @@ ++/* ++ * BCM common config options ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcm_cfg.h 294399 2011-11-07 03:31:22Z $ ++ */ ++ ++#ifndef _bcm_cfg_h_ ++#define _bcm_cfg_h_ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/bcm_mpool_pub.h b/drivers/net/wireless/bcmdhd/include/bcm_mpool_pub.h +new file mode 100644 +index 00000000..8fe3de7a +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcm_mpool_pub.h +@@ -0,0 +1,361 @@ ++/* ++ * Memory pools library, Public interface ++ * ++ * API Overview ++ * ++ * This package provides a memory allocation subsystem based on pools of ++ * homogenous objects. ++ * ++ * Instrumentation is available for reporting memory utilization both ++ * on a per-data-structure basis and system wide. ++ * ++ * There are two main types defined in this API. ++ * ++ * pool manager: A singleton object that acts as a factory for ++ * pool allocators. It also is used for global ++ * instrumentation, such as reporting all blocks ++ * in use across all data structures. The pool manager ++ * creates and provides individual memory pools ++ * upon request to application code. ++ * ++ * memory pool: An object for allocating homogenous memory blocks. ++ * ++ * Global identifiers in this module use the following prefixes: ++ * bcm_mpm_* Memory pool manager ++ * bcm_mp_* Memory pool ++ * ++ * There are two main types of memory pools: ++ * ++ * prealloc: The contiguous memory block of objects can either be supplied ++ * by the client or malloc'ed by the memory manager. The objects are ++ * allocated out of a block of memory and freed back to the block. ++ * ++ * heap: The memory pool allocator uses the heap (malloc/free) for memory. ++ * In this case, the pool allocator is just providing statistics ++ * and instrumentation on top of the heap, without modifying the heap ++ * allocation implementation. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id$ ++ */ ++ ++#ifndef _BCM_MPOOL_PUB_H ++#define _BCM_MPOOL_PUB_H 1 ++ ++#include /* needed for uint16 */ ++ ++ ++/* ++************************************************************************** ++* ++* Type definitions, handles ++* ++************************************************************************** ++*/ ++ ++/* Forward declaration of OSL handle. */ ++struct osl_info; ++ ++/* Forward declaration of string buffer. */ ++struct bcmstrbuf; ++ ++/* ++ * Opaque type definition for the pool manager handle. This object is used for global ++ * memory pool operations such as obtaining a new pool, deleting a pool, iterating and ++ * instrumentation/debugging. ++ */ ++struct bcm_mpm_mgr; ++typedef struct bcm_mpm_mgr *bcm_mpm_mgr_h; ++ ++/* ++ * Opaque type definition for an instance of a pool. This handle is used for allocating ++ * and freeing memory through the pool, as well as management/instrumentation on this ++ * specific pool. ++ */ ++struct bcm_mp_pool; ++typedef struct bcm_mp_pool *bcm_mp_pool_h; ++ ++ ++/* ++ * To make instrumentation more readable, every memory ++ * pool must have a readable name. Pool names are up to ++ * 8 bytes including '\0' termination. (7 printable characters.) ++ */ ++#define BCM_MP_NAMELEN 8 ++ ++ ++/* ++ * Type definition for pool statistics. ++ */ ++typedef struct bcm_mp_stats { ++ char name[BCM_MP_NAMELEN]; /* Name of this pool. */ ++ unsigned int objsz; /* Object size allocated in this pool */ ++ uint16 nobj; /* Total number of objects in this pool */ ++ uint16 num_alloc; /* Number of objects currently allocated */ ++ uint16 high_water; /* Max number of allocated objects. */ ++ uint16 failed_alloc; /* Failed allocations. */ ++} bcm_mp_stats_t; ++ ++ ++/* ++************************************************************************** ++* ++* API Routines on the pool manager. ++* ++************************************************************************** ++*/ ++ ++/* ++ * bcm_mpm_init() - initialize the whole memory pool system. ++ * ++ * Parameters: ++ * osh: INPUT Operating system handle. Needed for heap memory allocation. ++ * max_pools: INPUT Maximum number of mempools supported. ++ * mgr: OUTPUT The handle is written with the new pools manager object/handle. ++ * ++ * Returns: ++ * BCME_OK Object initialized successfully. May be used. ++ * BCME_NOMEM Initialization failed due to no memory. Object must not be used. ++ */ ++int bcm_mpm_init(struct osl_info *osh, int max_pools, bcm_mpm_mgr_h *mgrp); ++ ++ ++/* ++ * bcm_mpm_deinit() - de-initialize the whole memory pool system. ++ * ++ * Parameters: ++ * mgr: INPUT Pointer to pool manager handle. ++ * ++ * Returns: ++ * BCME_OK Memory pool manager successfully de-initialized. ++ * other Indicated error occured during de-initialization. ++ */ ++int bcm_mpm_deinit(bcm_mpm_mgr_h *mgrp); ++ ++/* ++ * bcm_mpm_create_prealloc_pool() - Create a new pool for fixed size objects. The ++ * pool uses a contiguous block of pre-alloced ++ * memory. The memory block may either be provided ++ * by the client or dynamically allocated by the ++ * pool manager. ++ * ++ * Parameters: ++ * mgr: INPUT The handle to the pool manager ++ * obj_sz: INPUT Size of objects that will be allocated by the new pool ++ * Must be >= sizeof(void *). ++ * nobj: INPUT Maximum number of concurrently existing objects to support ++ * memstart INPUT Pointer to the memory to use, or NULL to malloc() ++ * memsize INPUT Number of bytes referenced from memstart (for error checking). ++ * Must be 0 if 'memstart' is NULL. ++ * poolname INPUT For instrumentation, the name of the pool ++ * newp: OUTPUT The handle for the new pool, if creation is successful ++ * ++ * Returns: ++ * BCME_OK Pool created ok. ++ * other Pool not created due to indicated error. newpoolp set to NULL. ++ * ++ * ++ */ ++int bcm_mpm_create_prealloc_pool(bcm_mpm_mgr_h mgr, ++ unsigned int obj_sz, ++ int nobj, ++ void *memstart, ++ unsigned int memsize, ++ char poolname[BCM_MP_NAMELEN], ++ bcm_mp_pool_h *newp); ++ ++ ++/* ++ * bcm_mpm_delete_prealloc_pool() - Delete a memory pool. This should only be called after ++ * all memory objects have been freed back to the pool. ++ * ++ * Parameters: ++ * mgr: INPUT The handle to the pools manager ++ * pool: INPUT The handle of the pool to delete ++ * ++ * Returns: ++ * BCME_OK Pool deleted ok. ++ * other Pool not deleted due to indicated error. ++ * ++ */ ++int bcm_mpm_delete_prealloc_pool(bcm_mpm_mgr_h mgr, bcm_mp_pool_h *poolp); ++ ++/* ++ * bcm_mpm_create_heap_pool() - Create a new pool for fixed size objects. The memory ++ * pool allocator uses the heap (malloc/free) for memory. ++ * In this case, the pool allocator is just providing ++ * statistics and instrumentation on top of the heap, ++ * without modifying the heap allocation implementation. ++ * ++ * Parameters: ++ * mgr: INPUT The handle to the pool manager ++ * obj_sz: INPUT Size of objects that will be allocated by the new pool ++ * poolname INPUT For instrumentation, the name of the pool ++ * newp: OUTPUT The handle for the new pool, if creation is successful ++ * ++ * Returns: ++ * BCME_OK Pool created ok. ++ * other Pool not created due to indicated error. newpoolp set to NULL. ++ * ++ * ++ */ ++int bcm_mpm_create_heap_pool(bcm_mpm_mgr_h mgr, unsigned int obj_sz, ++ char poolname[BCM_MP_NAMELEN], ++ bcm_mp_pool_h *newp); ++ ++ ++/* ++ * bcm_mpm_delete_heap_pool() - Delete a memory pool. This should only be called after ++ * all memory objects have been freed back to the pool. ++ * ++ * Parameters: ++ * mgr: INPUT The handle to the pools manager ++ * pool: INPUT The handle of the pool to delete ++ * ++ * Returns: ++ * BCME_OK Pool deleted ok. ++ * other Pool not deleted due to indicated error. ++ * ++ */ ++int bcm_mpm_delete_heap_pool(bcm_mpm_mgr_h mgr, bcm_mp_pool_h *poolp); ++ ++ ++/* ++ * bcm_mpm_stats() - Return stats for all pools ++ * ++ * Parameters: ++ * mgr: INPUT The handle to the pools manager ++ * stats: OUTPUT Array of pool statistics. ++ * nentries: MOD Max elements in 'stats' array on INPUT. Actual number ++ * of array elements copied to 'stats' on OUTPUT. ++ * ++ * Returns: ++ * BCME_OK Ok ++ * other Error getting stats. ++ * ++ */ ++int bcm_mpm_stats(bcm_mpm_mgr_h mgr, bcm_mp_stats_t *stats, int *nentries); ++ ++ ++/* ++ * bcm_mpm_dump() - Display statistics on all pools ++ * ++ * Parameters: ++ * mgr: INPUT The handle to the pools manager ++ * b: OUTPUT Output buffer. ++ * ++ * Returns: ++ * BCME_OK Ok ++ * other Error during dump. ++ * ++ */ ++int bcm_mpm_dump(bcm_mpm_mgr_h mgr, struct bcmstrbuf *b); ++ ++ ++/* ++ * bcm_mpm_get_obj_size() - The size of memory objects may need to be padded to ++ * compensate for alignment requirements of the objects. ++ * This function provides the padded object size. If clients ++ * pre-allocate a memory slab for a memory pool, the ++ * padded object size should be used by the client to allocate ++ * the memory slab (in order to provide sufficent space for ++ * the maximum number of objects). ++ * ++ * Parameters: ++ * mgr: INPUT The handle to the pools manager. ++ * obj_sz: INPUT Input object size. ++ * padded_obj_sz: OUTPUT Padded object size. ++ * ++ * Returns: ++ * BCME_OK Ok ++ * BCME_BADARG Bad arguments. ++ * ++ */ ++int bcm_mpm_get_obj_size(bcm_mpm_mgr_h mgr, unsigned int obj_sz, unsigned int *padded_obj_sz); ++ ++ ++/* ++*************************************************************************** ++* ++* API Routines on a specific pool. ++* ++*************************************************************************** ++*/ ++ ++ ++/* ++ * bcm_mp_alloc() - Allocate a memory pool object. ++ * ++ * Parameters: ++ * pool: INPUT The handle to the pool. ++ * ++ * Returns: ++ * A pointer to the new object. NULL on error. ++ * ++ */ ++void* bcm_mp_alloc(bcm_mp_pool_h pool); ++ ++/* ++ * bcm_mp_free() - Free a memory pool object. ++ * ++ * Parameters: ++ * pool: INPUT The handle to the pool. ++ * objp: INPUT A pointer to the object to free. ++ * ++ * Returns: ++ * BCME_OK Ok ++ * other Error during free. ++ * ++ */ ++int bcm_mp_free(bcm_mp_pool_h pool, void *objp); ++ ++/* ++ * bcm_mp_stats() - Return stats for this pool ++ * ++ * Parameters: ++ * pool: INPUT The handle to the pool ++ * stats: OUTPUT Pool statistics ++ * ++ * Returns: ++ * BCME_OK Ok ++ * other Error getting statistics. ++ * ++ */ ++int bcm_mp_stats(bcm_mp_pool_h pool, bcm_mp_stats_t *stats); ++ ++ ++/* ++ * bcm_mp_dump() - Dump a pool ++ * ++ * Parameters: ++ * pool: INPUT The handle to the pool ++ * b OUTPUT Output buffer ++ * ++ * Returns: ++ * BCME_OK Ok ++ * other Error during dump. ++ * ++ */ ++int bcm_mp_dump(bcm_mp_pool_h pool, struct bcmstrbuf *b); ++ ++ ++#endif /* _BCM_MPOOL_PUB_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/bcmcdc.h b/drivers/net/wireless/bcmdhd/include/bcmcdc.h +new file mode 100644 +index 00000000..9bae1c20 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmcdc.h +@@ -0,0 +1,126 @@ ++/* ++ * CDC network driver ioctl/indication encoding ++ * Broadcom 802.11abg Networking Device Driver ++ * ++ * Definitions subject to change without notice. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmcdc.h 318308 2012-03-02 02:23:42Z $ ++ */ ++#ifndef _bcmcdc_h_ ++#define _bcmcdc_h_ ++#include ++ ++typedef struct cdc_ioctl { ++ uint32 cmd; ++ uint32 len; ++ uint32 flags; ++ uint32 status; ++} cdc_ioctl_t; ++ ++ ++#define CDC_MAX_MSG_SIZE ETHER_MAX_LEN ++ ++ ++#define CDCL_IOC_OUTLEN_MASK 0x0000FFFF ++ ++#define CDCL_IOC_OUTLEN_SHIFT 0 ++#define CDCL_IOC_INLEN_MASK 0xFFFF0000 ++#define CDCL_IOC_INLEN_SHIFT 16 ++ ++ ++#define CDCF_IOC_ERROR 0x01 ++#define CDCF_IOC_SET 0x02 ++#define CDCF_IOC_OVL_IDX_MASK 0x3c ++#define CDCF_IOC_OVL_RSV 0x40 ++#define CDCF_IOC_OVL 0x80 ++#define CDCF_IOC_ACTION_MASK 0xfe ++#define CDCF_IOC_ACTION_SHIFT 1 ++#define CDCF_IOC_IF_MASK 0xF000 ++#define CDCF_IOC_IF_SHIFT 12 ++#define CDCF_IOC_ID_MASK 0xFFFF0000 ++#define CDCF_IOC_ID_SHIFT 16 ++ ++#define CDC_IOC_IF_IDX(flags) (((flags) & CDCF_IOC_IF_MASK) >> CDCF_IOC_IF_SHIFT) ++#define CDC_IOC_ID(flags) (((flags) & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT) ++ ++#define CDC_GET_IF_IDX(hdr) \ ++ ((int)((((hdr)->flags) & CDCF_IOC_IF_MASK) >> CDCF_IOC_IF_SHIFT)) ++#define CDC_SET_IF_IDX(hdr, idx) \ ++ ((hdr)->flags = (((hdr)->flags & ~CDCF_IOC_IF_MASK) | ((idx) << CDCF_IOC_IF_SHIFT))) ++ ++ ++ ++struct bdc_header { ++ uint8 flags; ++ uint8 priority; ++ uint8 flags2; ++ uint8 dataOffset; ++}; ++ ++#define BDC_HEADER_LEN 4 ++ ++ ++#define BDC_FLAG_80211_PKT 0x01 ++#define BDC_FLAG_SUM_GOOD 0x04 ++#define BDC_FLAG_SUM_NEEDED 0x08 ++#define BDC_FLAG_EVENT_MSG 0x08 ++#define BDC_FLAG_VER_MASK 0xf0 ++#define BDC_FLAG_VER_SHIFT 4 ++ ++ ++#define BDC_PRIORITY_MASK 0x07 ++#define BDC_PRIORITY_FC_MASK 0xf0 ++#define BDC_PRIORITY_FC_SHIFT 4 ++ ++ ++#define BDC_FLAG2_IF_MASK 0x0f ++#define BDC_FLAG2_IF_SHIFT 0 ++#define BDC_FLAG2_FC_FLAG 0x10 ++ ++ ++ ++#define BDC_PROTO_VER_1 1 ++#define BDC_PROTO_VER 2 ++ ++ ++#define BDC_GET_IF_IDX(hdr) \ ++ ((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT)) ++#define BDC_SET_IF_IDX(hdr, idx) \ ++ ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | ((idx) << BDC_FLAG2_IF_SHIFT))) ++ ++#define BDC_FLAG2_PAD_MASK 0xf0 ++#define BDC_FLAG_PAD_MASK 0x03 ++#define BDC_FLAG2_PAD_SHIFT 2 ++#define BDC_FLAG_PAD_SHIFT 0 ++#define BDC_FLAG2_PAD_IDX 0x3c ++#define BDC_FLAG_PAD_IDX 0x03 ++#define BDC_GET_PAD_LEN(hdr) \ ++ ((int)(((((hdr)->flags2) & BDC_FLAG2_PAD_MASK) >> BDC_FLAG2_PAD_SHIFT) | \ ++ ((((hdr)->flags) & BDC_FLAG_PAD_MASK) >> BDC_FLAG_PAD_SHIFT))) ++#define BDC_SET_PAD_LEN(hdr, idx) \ ++ ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_PAD_MASK) | \ ++ (((idx) & BDC_FLAG2_PAD_IDX) << BDC_FLAG2_PAD_SHIFT))); \ ++ ((hdr)->flags = (((hdr)->flags & ~BDC_FLAG_PAD_MASK) | \ ++ (((idx) & BDC_FLAG_PAD_IDX) << BDC_FLAG_PAD_SHIFT))) ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/bcmdefs.h b/drivers/net/wireless/bcmdhd/include/bcmdefs.h +new file mode 100644 +index 00000000..a35ed72a +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmdefs.h +@@ -0,0 +1,239 @@ ++/* ++ * Misc system wide definitions ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmdefs.h 316830 2012-02-23 20:29:22Z $ ++ */ ++ ++#ifndef _bcmdefs_h_ ++#define _bcmdefs_h_ ++ ++ ++ ++ ++#define BCM_REFERENCE(data) ((void)(data)) ++ ++ ++#define STATIC_ASSERT(expr) { \ ++ \ ++ typedef enum { _STATIC_ASSERT_NOT_CONSTANT = (expr) } _static_assert_e; \ ++ \ ++ typedef char STATIC_ASSERT_FAIL[(expr) ? 1 : -1]; \ ++} ++ ++ ++ ++#define bcmreclaimed 0 ++#define _data _data ++#define _fn _fn ++#define BCMPREATTACHDATA(_data) _data ++#define BCMPREATTACHFN(_fn) _fn ++#define _data _data ++#define _fn _fn ++#define _fn _fn ++#define BCMNMIATTACHFN(_fn) _fn ++#define BCMNMIATTACHDATA(_data) _data ++#define CONST const ++#ifndef BCMFASTPATH ++#define BCMFASTPATH ++#define BCMFASTPATH_HOST ++#endif ++ ++ ++ ++#define _data _data ++#define BCMROMDAT_NAME(_data) _data ++#define _fn _fn ++#define _fn _fn ++#define STATIC static ++#define BCMROMDAT_ARYSIZ(data) ARRAYSIZE(data) ++#define BCMROMDAT_SIZEOF(data) sizeof(data) ++#define BCMROMDAT_APATCH(data) ++#define BCMROMDAT_SPATCH(data) ++ ++ ++#define SI_BUS 0 ++#define PCI_BUS 1 ++#define PCMCIA_BUS 2 ++#define SDIO_BUS 3 ++#define JTAG_BUS 4 ++#define USB_BUS 5 ++#define SPI_BUS 6 ++#define RPC_BUS 7 ++ ++ ++#ifdef BCMBUSTYPE ++#define BUSTYPE(bus) (BCMBUSTYPE) ++#else ++#define BUSTYPE(bus) (bus) ++#endif ++ ++ ++#ifdef BCMCHIPTYPE ++#define CHIPTYPE(bus) (BCMCHIPTYPE) ++#else ++#define CHIPTYPE(bus) (bus) ++#endif ++ ++ ++ ++#if defined(BCMSPROMBUS) ++#define SPROMBUS (BCMSPROMBUS) ++#elif defined(SI_PCMCIA_SROM) ++#define SPROMBUS (PCMCIA_BUS) ++#else ++#define SPROMBUS (PCI_BUS) ++#endif ++ ++ ++#ifdef BCMCHIPID ++#define CHIPID(chip) (BCMCHIPID) ++#else ++#define CHIPID(chip) (chip) ++#endif ++ ++#ifdef BCMCHIPREV ++#define CHIPREV(rev) (BCMCHIPREV) ++#else ++#define CHIPREV(rev) (rev) ++#endif ++ ++ ++#define DMADDR_MASK_32 0x0 ++#define DMADDR_MASK_30 0xc0000000 ++#define DMADDR_MASK_0 0xffffffff ++ ++#define DMADDRWIDTH_30 30 ++#define DMADDRWIDTH_32 32 ++#define DMADDRWIDTH_63 63 ++#define DMADDRWIDTH_64 64 ++ ++#ifdef BCMDMA64OSL ++typedef struct { ++ uint32 loaddr; ++ uint32 hiaddr; ++} dma64addr_t; ++ ++typedef dma64addr_t dmaaddr_t; ++#define PHYSADDRHI(_pa) ((_pa).hiaddr) ++#define PHYSADDRHISET(_pa, _val) \ ++ do { \ ++ (_pa).hiaddr = (_val); \ ++ } while (0) ++#define PHYSADDRLO(_pa) ((_pa).loaddr) ++#define PHYSADDRLOSET(_pa, _val) \ ++ do { \ ++ (_pa).loaddr = (_val); \ ++ } while (0) ++ ++#else ++typedef unsigned long dmaaddr_t; ++#define PHYSADDRHI(_pa) (0) ++#define PHYSADDRHISET(_pa, _val) ++#define PHYSADDRLO(_pa) ((_pa)) ++#define PHYSADDRLOSET(_pa, _val) \ ++ do { \ ++ (_pa) = (_val); \ ++ } while (0) ++#endif ++ ++ ++typedef struct { ++ dmaaddr_t addr; ++ uint32 length; ++} hnddma_seg_t; ++ ++#define MAX_DMA_SEGS 4 ++ ++ ++typedef struct { ++ void *oshdmah; ++ uint origsize; ++ uint nsegs; ++ hnddma_seg_t segs[MAX_DMA_SEGS]; ++} hnddma_seg_map_t; ++ ++ ++ ++ ++#if defined(BCM_RPC_NOCOPY) || defined(BCM_RCP_TXNOCOPY) ++ ++#define BCMEXTRAHDROOM 220 ++#else ++#define BCMEXTRAHDROOM 172 ++#endif ++ ++ ++#ifndef SDALIGN ++#define SDALIGN 32 ++#endif ++ ++ ++#define BCMDONGLEHDRSZ 12 ++#define BCMDONGLEPADSZ 16 ++ ++#define BCMDONGLEOVERHEAD (BCMDONGLEHDRSZ + BCMDONGLEPADSZ) ++ ++ ++#if defined(NO_BCMDBG_ASSERT) ++# undef BCMDBG_ASSERT ++# undef BCMASSERT_LOG ++#endif ++ ++#if defined(BCMASSERT_LOG) ++#define BCMASSERT_SUPPORT ++#endif ++ ++ ++#define BITFIELD_MASK(width) \ ++ (((unsigned)1 << (width)) - 1) ++#define GFIELD(val, field) \ ++ (((val) >> field ## _S) & field ## _M) ++#define SFIELD(val, field, bits) \ ++ (((val) & (~(field ## _M << field ## _S))) | \ ++ ((unsigned)(bits) << field ## _S)) ++ ++ ++#ifdef BCMSMALL ++#undef BCMSPACE ++#define bcmspace FALSE ++#else ++#define BCMSPACE ++#define bcmspace TRUE ++#endif ++ ++ ++#define MAXSZ_NVRAM_VARS 4096 ++ ++ ++ ++#ifdef DL_NVRAM ++#define NVRAM_ARRAY_MAXSIZE DL_NVRAM ++#else ++#define NVRAM_ARRAY_MAXSIZE MAXSZ_NVRAM_VARS ++#endif ++ ++#ifdef BCMUSBDEV_ENABLED ++extern uint32 gFWID; ++#endif ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/bcmdevs.h b/drivers/net/wireless/bcmdhd/include/bcmdevs.h +new file mode 100644 +index 00000000..c7e06ff4 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmdevs.h +@@ -0,0 +1,495 @@ ++/* ++ * Broadcom device-specific manifest constants. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmdevs.h 329854 2012-04-27 01:42:28Z $ ++ */ ++ ++#ifndef _BCMDEVS_H ++#define _BCMDEVS_H ++ ++ ++#define VENDOR_EPIGRAM 0xfeda ++#define VENDOR_BROADCOM 0x14e4 ++#define VENDOR_3COM 0x10b7 ++#define VENDOR_NETGEAR 0x1385 ++#define VENDOR_DIAMOND 0x1092 ++#define VENDOR_INTEL 0x8086 ++#define VENDOR_DELL 0x1028 ++#define VENDOR_HP 0x103c ++#define VENDOR_HP_COMPAQ 0x0e11 ++#define VENDOR_APPLE 0x106b ++#define VENDOR_SI_IMAGE 0x1095 ++#define VENDOR_BUFFALO 0x1154 ++#define VENDOR_TI 0x104c ++#define VENDOR_RICOH 0x1180 ++#define VENDOR_JMICRON 0x197b ++ ++ ++ ++#define VENDOR_BROADCOM_PCMCIA 0x02d0 ++ ++ ++#define VENDOR_BROADCOM_SDIO 0x00BF ++ ++ ++#define BCM_DNGL_VID 0x0a5c ++#define BCM_DNGL_BL_PID_4328 0xbd12 ++#define BCM_DNGL_BL_PID_4322 0xbd13 ++#define BCM_DNGL_BL_PID_4319 0xbd16 ++#define BCM_DNGL_BL_PID_43236 0xbd17 ++#define BCM_DNGL_BL_PID_4332 0xbd18 ++#define BCM_DNGL_BL_PID_4330 0xbd19 ++#define BCM_DNGL_BL_PID_4334 0xbd1a ++#define BCM_DNGL_BL_PID_43239 0xbd1b ++#define BCM_DNGL_BL_PID_4324 0xbd1c ++#define BCM_DNGL_BL_PID_4360 0xbd1d ++ ++#define BCM_DNGL_BDC_PID 0x0bdc ++#define BCM_DNGL_JTAG_PID 0x4a44 ++ ++ ++#define BCM_HWUSB_PID_43239 43239 ++ ++ ++#define BCM4210_DEVICE_ID 0x1072 ++#define BCM4230_DEVICE_ID 0x1086 ++#define BCM4401_ENET_ID 0x170c ++#define BCM3352_DEVICE_ID 0x3352 ++#define BCM3360_DEVICE_ID 0x3360 ++#define BCM4211_DEVICE_ID 0x4211 ++#define BCM4231_DEVICE_ID 0x4231 ++#define BCM4303_D11B_ID 0x4303 ++#define BCM4311_D11G_ID 0x4311 ++#define BCM4311_D11DUAL_ID 0x4312 ++#define BCM4311_D11A_ID 0x4313 ++#define BCM4328_D11DUAL_ID 0x4314 ++#define BCM4328_D11G_ID 0x4315 ++#define BCM4328_D11A_ID 0x4316 ++#define BCM4318_D11G_ID 0x4318 ++#define BCM4318_D11DUAL_ID 0x4319 ++#define BCM4318_D11A_ID 0x431a ++#define BCM4325_D11DUAL_ID 0x431b ++#define BCM4325_D11G_ID 0x431c ++#define BCM4325_D11A_ID 0x431d ++#define BCM4306_D11G_ID 0x4320 ++#define BCM4306_D11A_ID 0x4321 ++#define BCM4306_UART_ID 0x4322 ++#define BCM4306_V90_ID 0x4323 ++#define BCM4306_D11DUAL_ID 0x4324 ++#define BCM4306_D11G_ID2 0x4325 ++#define BCM4321_D11N_ID 0x4328 ++#define BCM4321_D11N2G_ID 0x4329 ++#define BCM4321_D11N5G_ID 0x432a ++#define BCM4322_D11N_ID 0x432b ++#define BCM4322_D11N2G_ID 0x432c ++#define BCM4322_D11N5G_ID 0x432d ++#define BCM4329_D11N_ID 0x432e ++#define BCM4329_D11N2G_ID 0x432f ++#define BCM4329_D11N5G_ID 0x4330 ++#define BCM4315_D11DUAL_ID 0x4334 ++#define BCM4315_D11G_ID 0x4335 ++#define BCM4315_D11A_ID 0x4336 ++#define BCM4319_D11N_ID 0x4337 ++#define BCM4319_D11N2G_ID 0x4338 ++#define BCM4319_D11N5G_ID 0x4339 ++#define BCM43231_D11N2G_ID 0x4340 ++#define BCM43221_D11N2G_ID 0x4341 ++#define BCM43222_D11N_ID 0x4350 ++#define BCM43222_D11N2G_ID 0x4351 ++#define BCM43222_D11N5G_ID 0x4352 ++#define BCM43224_D11N_ID 0x4353 ++#define BCM43224_D11N_ID_VEN1 0x0576 ++#define BCM43226_D11N_ID 0x4354 ++#define BCM43236_D11N_ID 0x4346 ++#define BCM43236_D11N2G_ID 0x4347 ++#define BCM43236_D11N5G_ID 0x4348 ++#define BCM43225_D11N2G_ID 0x4357 ++#define BCM43421_D11N_ID 0xA99D ++#define BCM4313_D11N2G_ID 0x4727 ++#define BCM4330_D11N_ID 0x4360 ++#define BCM4330_D11N2G_ID 0x4361 ++#define BCM4330_D11N5G_ID 0x4362 ++#define BCM4336_D11N_ID 0x4343 ++#define BCM6362_D11N_ID 0x435f ++#define BCM4331_D11N_ID 0x4331 ++#define BCM4331_D11N2G_ID 0x4332 ++#define BCM4331_D11N5G_ID 0x4333 ++#define BCM43237_D11N_ID 0x4355 ++#define BCM43237_D11N5G_ID 0x4356 ++#define BCM43227_D11N2G_ID 0x4358 ++#define BCM43228_D11N_ID 0x4359 ++#define BCM43228_D11N5G_ID 0x435a ++#define BCM43362_D11N_ID 0x4363 ++#define BCM43239_D11N_ID 0x4370 ++#define BCM4324_D11N_ID 0x4374 ++#define BCM43217_D11N2G_ID 0x43a9 ++#define BCM43131_D11N2G_ID 0x43aa ++#define BCM4314_D11N2G_ID 0x4364 ++#define BCM43142_D11N2G_ID 0x4365 ++#define BCM4334_D11N_ID 0x4380 ++#define BCM4334_D11N2G_ID 0x4381 ++#define BCM4334_D11N5G_ID 0x4382 ++#define BCM43341_D11N_ID 0x4386 ++#define BCM43341_D11N2G_ID 0x4387 ++#define BCM43341_D11N5G_ID 0x4388 ++#define BCM4360_D11AC_ID 0x43a0 ++#define BCM4360_D11AC2G_ID 0x43a1 ++#define BCM4360_D11AC5G_ID 0x43a2 ++ ++ ++#define BCM943228HMB_SSID_VEN1 0x0607 ++#define BCM94313HMGBL_SSID_VEN1 0x0608 ++#define BCM94313HMG_SSID_VEN1 0x0609 ++ ++ ++#define BCM4335_D11AC_ID 0x43ae ++#define BCM4335_D11AC2G_ID 0x43af ++#define BCM4335_D11AC5G_ID 0x43b0 ++#define BCM4352_D11AC_ID 0x43b1 ++#define BCM4352_D11AC2G_ID 0x43b2 ++#define BCM4352_D11AC5G_ID 0x43b3 ++ ++#define BCMGPRS_UART_ID 0x4333 ++#define BCMGPRS2_UART_ID 0x4344 ++#define FPGA_JTAGM_ID 0x43f0 ++#define BCM_JTAGM_ID 0x43f1 ++#define SDIOH_FPGA_ID 0x43f2 ++#define BCM_SDIOH_ID 0x43f3 ++#define SDIOD_FPGA_ID 0x43f4 ++#define SPIH_FPGA_ID 0x43f5 ++#define BCM_SPIH_ID 0x43f6 ++#define MIMO_FPGA_ID 0x43f8 ++#define BCM_JTAGM2_ID 0x43f9 ++#define SDHCI_FPGA_ID 0x43fa ++#define BCM4402_ENET_ID 0x4402 ++#define BCM4402_V90_ID 0x4403 ++#define BCM4410_DEVICE_ID 0x4410 ++#define BCM4412_DEVICE_ID 0x4412 ++#define BCM4430_DEVICE_ID 0x4430 ++#define BCM4432_DEVICE_ID 0x4432 ++#define BCM4704_ENET_ID 0x4706 ++#define BCM4710_DEVICE_ID 0x4710 ++#define BCM47XX_AUDIO_ID 0x4711 ++#define BCM47XX_V90_ID 0x4712 ++#define BCM47XX_ENET_ID 0x4713 ++#define BCM47XX_EXT_ID 0x4714 ++#define BCM47XX_GMAC_ID 0x4715 ++#define BCM47XX_USBH_ID 0x4716 ++#define BCM47XX_USBD_ID 0x4717 ++#define BCM47XX_IPSEC_ID 0x4718 ++#define BCM47XX_ROBO_ID 0x4719 ++#define BCM47XX_USB20H_ID 0x471a ++#define BCM47XX_USB20D_ID 0x471b ++#define BCM47XX_ATA100_ID 0x471d ++#define BCM47XX_SATAXOR_ID 0x471e ++#define BCM47XX_GIGETH_ID 0x471f ++#define BCM4712_MIPS_ID 0x4720 ++#define BCM4716_DEVICE_ID 0x4722 ++#define BCM47XX_SMBUS_EMU_ID 0x47fe ++#define BCM47XX_XOR_EMU_ID 0x47ff ++#define EPI41210_DEVICE_ID 0xa0fa ++#define EPI41230_DEVICE_ID 0xa10e ++#define JINVANI_SDIOH_ID 0x4743 ++#define BCM27XX_SDIOH_ID 0x2702 ++#define PCIXX21_FLASHMEDIA_ID 0x803b ++#define PCIXX21_SDIOH_ID 0x803c ++#define R5C822_SDIOH_ID 0x0822 ++#define JMICRON_SDIOH_ID 0x2381 ++ ++ ++#define BCM4306_CHIP_ID 0x4306 ++#define BCM4311_CHIP_ID 0x4311 ++#define BCM43111_CHIP_ID 43111 ++#define BCM43112_CHIP_ID 43112 ++#define BCM4312_CHIP_ID 0x4312 ++#define BCM4313_CHIP_ID 0x4313 ++#define BCM43131_CHIP_ID 43131 ++#define BCM4315_CHIP_ID 0x4315 ++#define BCM4318_CHIP_ID 0x4318 ++#define BCM4319_CHIP_ID 0x4319 ++#define BCM4320_CHIP_ID 0x4320 ++#define BCM4321_CHIP_ID 0x4321 ++#define BCM43217_CHIP_ID 43217 ++#define BCM4322_CHIP_ID 0x4322 ++#define BCM43221_CHIP_ID 43221 ++#define BCM43222_CHIP_ID 43222 ++#define BCM43224_CHIP_ID 43224 ++#define BCM43225_CHIP_ID 43225 ++#define BCM43227_CHIP_ID 43227 ++#define BCM43228_CHIP_ID 43228 ++#define BCM43226_CHIP_ID 43226 ++#define BCM43231_CHIP_ID 43231 ++#define BCM43234_CHIP_ID 43234 ++#define BCM43235_CHIP_ID 43235 ++#define BCM43236_CHIP_ID 43236 ++#define BCM43237_CHIP_ID 43237 ++#define BCM43238_CHIP_ID 43238 ++#define BCM43239_CHIP_ID 43239 ++#define BCM43420_CHIP_ID 43420 ++#define BCM43421_CHIP_ID 43421 ++#define BCM43428_CHIP_ID 43428 ++#define BCM43431_CHIP_ID 43431 ++#define BCM43460_CHIP_ID 43460 ++#define BCM4325_CHIP_ID 0x4325 ++#define BCM4328_CHIP_ID 0x4328 ++#define BCM4329_CHIP_ID 0x4329 ++#define BCM4331_CHIP_ID 0x4331 ++#define BCM4336_CHIP_ID 0x4336 ++#define BCM43362_CHIP_ID 43362 ++#define BCM4330_CHIP_ID 0x4330 ++#define BCM6362_CHIP_ID 0x6362 ++#define BCM4314_CHIP_ID 0x4314 ++#define BCM43142_CHIP_ID 43142 ++#define BCM4324_CHIP_ID 0x4324 ++#define BCM43242_CHIP_ID 43242 ++#define BCM4334_CHIP_ID 0x4334 ++#define BCM4360_CHIP_ID 0x4360 ++#define BCM4352_CHIP_ID 0x4352 ++#define BCM43526_CHIP_ID 0xAA06 ++#define BCM43341_CHIP_ID 43341 ++#define BCM43342_CHIP_ID 43342 ++ ++#define BCM4335_CHIP_ID 0x4335 ++ ++#define BCM4342_CHIP_ID 4342 ++#define BCM4402_CHIP_ID 0x4402 ++#define BCM4704_CHIP_ID 0x4704 ++#define BCM4706_CHIP_ID 0x5300 ++#define BCM4710_CHIP_ID 0x4710 ++#define BCM4712_CHIP_ID 0x4712 ++#define BCM4716_CHIP_ID 0x4716 ++#define BCM47162_CHIP_ID 47162 ++#define BCM4748_CHIP_ID 0x4748 ++#define BCM4749_CHIP_ID 0x4749 ++#define BCM4785_CHIP_ID 0x4785 ++#define BCM5350_CHIP_ID 0x5350 ++#define BCM5352_CHIP_ID 0x5352 ++#define BCM5354_CHIP_ID 0x5354 ++#define BCM5365_CHIP_ID 0x5365 ++#define BCM5356_CHIP_ID 0x5356 ++#define BCM5357_CHIP_ID 0x5357 ++#define BCM53572_CHIP_ID 53572 ++ ++ ++#define BCM4303_PKG_ID 2 ++#define BCM4309_PKG_ID 1 ++#define BCM4712LARGE_PKG_ID 0 ++#define BCM4712SMALL_PKG_ID 1 ++#define BCM4712MID_PKG_ID 2 ++#define BCM4328USBD11G_PKG_ID 2 ++#define BCM4328USBDUAL_PKG_ID 3 ++#define BCM4328SDIOD11G_PKG_ID 4 ++#define BCM4328SDIODUAL_PKG_ID 5 ++#define BCM4329_289PIN_PKG_ID 0 ++#define BCM4329_182PIN_PKG_ID 1 ++#define BCM5354E_PKG_ID 1 ++#define BCM4716_PKG_ID 8 ++#define BCM4717_PKG_ID 9 ++#define BCM4718_PKG_ID 10 ++#define BCM5356_PKG_NONMODE 1 ++#define BCM5358U_PKG_ID 8 ++#define BCM5358_PKG_ID 9 ++#define BCM47186_PKG_ID 10 ++#define BCM5357_PKG_ID 11 ++#define BCM5356U_PKG_ID 12 ++#define BCM53572_PKG_ID 8 ++#define BCM5357C0_PKG_ID 8 ++#define BCM47188_PKG_ID 9 ++#define BCM5358C0_PKG_ID 0xa ++#define BCM5356C0_PKG_ID 0xb ++#define BCM4331TT_PKG_ID 8 ++#define BCM4331TN_PKG_ID 9 ++#define BCM4331TNA0_PKG_ID 0xb ++#define BCM4706L_PKG_ID 1 ++ ++#define HDLSIM5350_PKG_ID 1 ++#define HDLSIM_PKG_ID 14 ++#define HWSIM_PKG_ID 15 ++#define BCM43224_FAB_CSM 0x8 ++#define BCM43224_FAB_SMIC 0xa ++#define BCM4336_WLBGA_PKG_ID 0x8 ++#define BCM4330_WLBGA_PKG_ID 0x0 ++#define BCM4314PCIE_ARM_PKG_ID (8 | 0) ++#define BCM4314SDIO_PKG_ID (8 | 1) ++#define BCM4314PCIE_PKG_ID (8 | 2) ++#define BCM4314SDIO_ARM_PKG_ID (8 | 3) ++#define BCM4314SDIO_FPBGA_PKG_ID (8 | 4) ++#define BCM4314DEV_PKG_ID (8 | 6) ++ ++#define PCIXX21_FLASHMEDIA0_ID 0x8033 ++#define PCIXX21_SDIOH0_ID 0x8034 ++ ++ ++#define BFL_BTC2WIRE 0x00000001 ++#define BFL_BTCOEX 0x00000001 ++#define BFL_PACTRL 0x00000002 ++#define BFL_AIRLINEMODE 0x00000004 ++#define BFL_ADCDIV 0x00000008 ++#define BFL_RFPLL 0x00000008 ++#define BFL_ENETROBO 0x00000010 ++#define BFL_NOPLLDOWN 0x00000020 ++#define BFL_CCKHIPWR 0x00000040 ++#define BFL_ENETADM 0x00000080 ++#define BFL_ENETVLAN 0x00000100 ++#define BFL_UNUSED 0x00000200 ++#define BFL_NOPCI 0x00000400 ++#define BFL_FEM 0x00000800 ++#define BFL_EXTLNA 0x00001000 ++#define BFL_HGPA 0x00002000 ++#define BFL_BTC2WIRE_ALTGPIO 0x00004000 ++#define BFL_ALTIQ 0x00008000 ++#define BFL_NOPA 0x00010000 ++#define BFL_RSSIINV 0x00020000 ++#define BFL_PAREF 0x00040000 ++#define BFL_3TSWITCH 0x00080000 ++#define BFL_PHASESHIFT 0x00100000 ++#define BFL_BUCKBOOST 0x00200000 ++#define BFL_FEM_BT 0x00400000 ++#define BFL_NOCBUCK 0x00800000 ++#define BFL_CCKFAVOREVM 0x01000000 ++#define BFL_PALDO 0x02000000 ++#define BFL_LNLDO2_2P5 0x04000000 ++#define BFL_FASTPWR 0x08000000 ++#define BFL_UCPWRCTL_MININDX 0x08000000 ++#define BFL_EXTLNA_5GHz 0x10000000 ++#define BFL_TRSW_1by2 0x20000000 ++#define BFL_LO_TRSW_R_5GHz 0x40000000 ++#define BFL_ELNA_GAINDEF 0x80000000 ++#define BFL_EXTLNA_TX 0x20000000 ++ ++ ++#define BFL2_RXBB_INT_REG_DIS 0x00000001 ++#define BFL2_APLL_WAR 0x00000002 ++#define BFL2_TXPWRCTRL_EN 0x00000004 ++#define BFL2_2X4_DIV 0x00000008 ++#define BFL2_5G_PWRGAIN 0x00000010 ++#define BFL2_PCIEWAR_OVR 0x00000020 ++#define BFL2_CAESERS_BRD 0x00000040 ++#define BFL2_BTC3WIRE 0x00000080 ++#define BFL2_BTCLEGACY 0x00000080 ++#define BFL2_SKWRKFEM_BRD 0x00000100 ++#define BFL2_SPUR_WAR 0x00000200 ++#define BFL2_GPLL_WAR 0x00000400 ++#define BFL2_TRISTATE_LED 0x00000800 ++#define BFL2_SINGLEANT_CCK 0x00001000 ++#define BFL2_2G_SPUR_WAR 0x00002000 ++#define BFL2_BPHY_ALL_TXCORES 0x00004000 ++#define BFL2_FCC_BANDEDGE_WAR 0x00008000 ++#define BFL2_GPLL_WAR2 0x00010000 ++#define BFL2_IPALVLSHIFT_3P3 0x00020000 ++#define BFL2_INTERNDET_TXIQCAL 0x00040000 ++#define BFL2_XTALBUFOUTEN 0x00080000 ++ ++ ++ ++#define BFL2_ANAPACTRL_2G 0x00100000 ++#define BFL2_ANAPACTRL_5G 0x00200000 ++#define BFL2_ELNACTRL_TRSW_2G 0x00400000 ++#define BFL2_BT_SHARE_ANT0 0x00800000 ++#define BFL2_TEMPSENSE_HIGHER 0x01000000 ++#define BFL2_BTC3WIREONLY 0x02000000 ++#define BFL2_PWR_NOMINAL 0x04000000 ++#define BFL2_EXTLNA_PWRSAVE 0x08000000 ++ ++#define BFL2_4313_RADIOREG 0x10000000 ++ ++#define BFL2_SDR_EN 0x20000000 ++ ++ ++#define BOARD_GPIO_BTC3W_IN 0x850 ++#define BOARD_GPIO_BTC3W_OUT 0x020 ++#define BOARD_GPIO_BTCMOD_IN 0x010 ++#define BOARD_GPIO_BTCMOD_OUT 0x020 ++#define BOARD_GPIO_BTC_IN 0x080 ++#define BOARD_GPIO_BTC_OUT 0x100 ++#define BOARD_GPIO_PACTRL 0x200 ++#define BOARD_GPIO_12 0x1000 ++#define BOARD_GPIO_13 0x2000 ++#define BOARD_GPIO_BTC4_IN 0x0800 ++#define BOARD_GPIO_BTC4_BT 0x2000 ++#define BOARD_GPIO_BTC4_STAT 0x4000 ++#define BOARD_GPIO_BTC4_WLAN 0x8000 ++#define BOARD_GPIO_1_WLAN_PWR 0x02 ++#define BOARD_GPIO_3_WLAN_PWR 0x08 ++#define BOARD_GPIO_4_WLAN_PWR 0x10 ++ ++#define GPIO_BTC4W_OUT_4312 0x010 ++#define GPIO_BTC4W_OUT_43224 0x020 ++#define GPIO_BTC4W_OUT_43224_SHARED 0x0e0 ++#define GPIO_BTC4W_OUT_43225 0x0e0 ++#define GPIO_BTC4W_OUT_43421 0x020 ++#define GPIO_BTC4W_OUT_4313 0x060 ++#define GPIO_BTC4W_OUT_4331_SHARED 0x010 ++ ++#define PCI_CFG_GPIO_SCS 0x10 ++#define PCI_CFG_GPIO_HWRAD 0x20 ++#define PCI_CFG_GPIO_XTAL 0x40 ++#define PCI_CFG_GPIO_PLL 0x80 ++ ++ ++#define PLL_DELAY 150 ++#define FREF_DELAY 200 ++#define MIN_SLOW_CLK 32 ++#define XTAL_ON_DELAY 1000 ++ ++ ++ ++#define BCM943341WLABGS_SSID 0x062d ++ ++ ++#define GPIO_NUMPINS 32 ++ ++ ++#define RDL_RAM_BASE_4319 0x60000000 ++#define RDL_RAM_BASE_4329 0x60000000 ++#define RDL_RAM_SIZE_4319 0x48000 ++#define RDL_RAM_SIZE_4329 0x48000 ++#define RDL_RAM_SIZE_43236 0x70000 ++#define RDL_RAM_BASE_43236 0x60000000 ++#define RDL_RAM_SIZE_4328 0x60000 ++#define RDL_RAM_BASE_4328 0x80000000 ++#define RDL_RAM_SIZE_4322 0x60000 ++#define RDL_RAM_BASE_4322 0x60000000 ++ ++ ++#define MUXENAB_UART 0x00000001 ++#define MUXENAB_GPIO 0x00000002 ++#define MUXENAB_ERCX 0x00000004 ++#define MUXENAB_JTAG 0x00000008 ++#define MUXENAB_HOST_WAKE 0x00000010 ++#define MUXENAB_I2S_EN 0x00000020 ++#define MUXENAB_I2S_MASTER 0x00000040 ++#define MUXENAB_I2S_FULL 0x00000080 ++#define MUXENAB_SFLASH 0x00000100 ++#define MUXENAB_RFSWCTRL0 0x00000200 ++#define MUXENAB_RFSWCTRL1 0x00000400 ++#define MUXENAB_RFSWCTRL2 0x00000800 ++#define MUXENAB_SECI 0x00001000 ++#define MUXENAB_BT_LEGACY 0x00002000 ++#define MUXENAB_HOST_WAKE1 0x00004000 ++ ++ ++#define FLASH_KERNEL_NFLASH 0x00000001 ++#define FLASH_BOOT_NFLASH 0x00000002 ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/bcmendian.h b/drivers/net/wireless/bcmdhd/include/bcmendian.h +new file mode 100644 +index 00000000..22eb7dbc +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmendian.h +@@ -0,0 +1,278 @@ ++/* ++ * Byte order utilities ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmendian.h 241182 2011-02-17 21:50:03Z $ ++ * ++ * This file by default provides proper behavior on little-endian architectures. ++ * On big-endian architectures, IL_BIGENDIAN should be defined. ++ */ ++ ++#ifndef _BCMENDIAN_H_ ++#define _BCMENDIAN_H_ ++ ++#include ++ ++ ++#define BCMSWAP16(val) \ ++ ((uint16)((((uint16)(val) & (uint16)0x00ffU) << 8) | \ ++ (((uint16)(val) & (uint16)0xff00U) >> 8))) ++ ++ ++#define BCMSWAP32(val) \ ++ ((uint32)((((uint32)(val) & (uint32)0x000000ffU) << 24) | \ ++ (((uint32)(val) & (uint32)0x0000ff00U) << 8) | \ ++ (((uint32)(val) & (uint32)0x00ff0000U) >> 8) | \ ++ (((uint32)(val) & (uint32)0xff000000U) >> 24))) ++ ++ ++#define BCMSWAP32BY16(val) \ ++ ((uint32)((((uint32)(val) & (uint32)0x0000ffffU) << 16) | \ ++ (((uint32)(val) & (uint32)0xffff0000U) >> 16))) ++ ++ ++#ifndef hton16 ++#define HTON16(i) BCMSWAP16(i) ++#define hton16(i) bcmswap16(i) ++#define HTON32(i) BCMSWAP32(i) ++#define hton32(i) bcmswap32(i) ++#define NTOH16(i) BCMSWAP16(i) ++#define ntoh16(i) bcmswap16(i) ++#define NTOH32(i) BCMSWAP32(i) ++#define ntoh32(i) bcmswap32(i) ++#define LTOH16(i) (i) ++#define ltoh16(i) (i) ++#define LTOH32(i) (i) ++#define ltoh32(i) (i) ++#define HTOL16(i) (i) ++#define htol16(i) (i) ++#define HTOL32(i) (i) ++#define htol32(i) (i) ++#endif ++ ++#define ltoh16_buf(buf, i) ++#define htol16_buf(buf, i) ++ ++ ++#define load32_ua(a) ltoh32_ua(a) ++#define store32_ua(a, v) htol32_ua_store(v, a) ++#define load16_ua(a) ltoh16_ua(a) ++#define store16_ua(a, v) htol16_ua_store(v, a) ++ ++#define _LTOH16_UA(cp) ((cp)[0] | ((cp)[1] << 8)) ++#define _LTOH32_UA(cp) ((cp)[0] | ((cp)[1] << 8) | ((cp)[2] << 16) | ((cp)[3] << 24)) ++#define _NTOH16_UA(cp) (((cp)[0] << 8) | (cp)[1]) ++#define _NTOH32_UA(cp) (((cp)[0] << 24) | ((cp)[1] << 16) | ((cp)[2] << 8) | (cp)[3]) ++ ++#define ltoh_ua(ptr) \ ++ (sizeof(*(ptr)) == sizeof(uint8) ? *(const uint8 *)(ptr) : \ ++ sizeof(*(ptr)) == sizeof(uint16) ? _LTOH16_UA((const uint8 *)(ptr)) : \ ++ sizeof(*(ptr)) == sizeof(uint32) ? _LTOH32_UA((const uint8 *)(ptr)) : \ ++ *(uint8 *)0) ++ ++#define ntoh_ua(ptr) \ ++ (sizeof(*(ptr)) == sizeof(uint8) ? *(const uint8 *)(ptr) : \ ++ sizeof(*(ptr)) == sizeof(uint16) ? _NTOH16_UA((const uint8 *)(ptr)) : \ ++ sizeof(*(ptr)) == sizeof(uint32) ? _NTOH32_UA((const uint8 *)(ptr)) : \ ++ *(uint8 *)0) ++ ++#ifdef __GNUC__ ++ ++ ++ ++#define bcmswap16(val) ({ \ ++ uint16 _val = (val); \ ++ BCMSWAP16(_val); \ ++}) ++ ++#define bcmswap32(val) ({ \ ++ uint32 _val = (val); \ ++ BCMSWAP32(_val); \ ++}) ++ ++#define bcmswap32by16(val) ({ \ ++ uint32 _val = (val); \ ++ BCMSWAP32BY16(_val); \ ++}) ++ ++#define bcmswap16_buf(buf, len) ({ \ ++ uint16 *_buf = (uint16 *)(buf); \ ++ uint _wds = (len) / 2; \ ++ while (_wds--) { \ ++ *_buf = bcmswap16(*_buf); \ ++ _buf++; \ ++ } \ ++}) ++ ++#define htol16_ua_store(val, bytes) ({ \ ++ uint16 _val = (val); \ ++ uint8 *_bytes = (uint8 *)(bytes); \ ++ _bytes[0] = _val & 0xff; \ ++ _bytes[1] = _val >> 8; \ ++}) ++ ++#define htol32_ua_store(val, bytes) ({ \ ++ uint32 _val = (val); \ ++ uint8 *_bytes = (uint8 *)(bytes); \ ++ _bytes[0] = _val & 0xff; \ ++ _bytes[1] = (_val >> 8) & 0xff; \ ++ _bytes[2] = (_val >> 16) & 0xff; \ ++ _bytes[3] = _val >> 24; \ ++}) ++ ++#define hton16_ua_store(val, bytes) ({ \ ++ uint16 _val = (val); \ ++ uint8 *_bytes = (uint8 *)(bytes); \ ++ _bytes[0] = _val >> 8; \ ++ _bytes[1] = _val & 0xff; \ ++}) ++ ++#define hton32_ua_store(val, bytes) ({ \ ++ uint32 _val = (val); \ ++ uint8 *_bytes = (uint8 *)(bytes); \ ++ _bytes[0] = _val >> 24; \ ++ _bytes[1] = (_val >> 16) & 0xff; \ ++ _bytes[2] = (_val >> 8) & 0xff; \ ++ _bytes[3] = _val & 0xff; \ ++}) ++ ++#define ltoh16_ua(bytes) ({ \ ++ const uint8 *_bytes = (const uint8 *)(bytes); \ ++ _LTOH16_UA(_bytes); \ ++}) ++ ++#define ltoh32_ua(bytes) ({ \ ++ const uint8 *_bytes = (const uint8 *)(bytes); \ ++ _LTOH32_UA(_bytes); \ ++}) ++ ++#define ntoh16_ua(bytes) ({ \ ++ const uint8 *_bytes = (const uint8 *)(bytes); \ ++ _NTOH16_UA(_bytes); \ ++}) ++ ++#define ntoh32_ua(bytes) ({ \ ++ const uint8 *_bytes = (const uint8 *)(bytes); \ ++ _NTOH32_UA(_bytes); \ ++}) ++ ++#else ++ ++ ++static INLINE uint16 ++bcmswap16(uint16 val) ++{ ++ return BCMSWAP16(val); ++} ++ ++static INLINE uint32 ++bcmswap32(uint32 val) ++{ ++ return BCMSWAP32(val); ++} ++ ++static INLINE uint32 ++bcmswap32by16(uint32 val) ++{ ++ return BCMSWAP32BY16(val); ++} ++ ++ ++ ++ ++static INLINE void ++bcmswap16_buf(uint16 *buf, uint len) ++{ ++ len = len / 2; ++ ++ while (len--) { ++ *buf = bcmswap16(*buf); ++ buf++; ++ } ++} ++ ++ ++static INLINE void ++htol16_ua_store(uint16 val, uint8 *bytes) ++{ ++ bytes[0] = val & 0xff; ++ bytes[1] = val >> 8; ++} ++ ++ ++static INLINE void ++htol32_ua_store(uint32 val, uint8 *bytes) ++{ ++ bytes[0] = val & 0xff; ++ bytes[1] = (val >> 8) & 0xff; ++ bytes[2] = (val >> 16) & 0xff; ++ bytes[3] = val >> 24; ++} ++ ++ ++static INLINE void ++hton16_ua_store(uint16 val, uint8 *bytes) ++{ ++ bytes[0] = val >> 8; ++ bytes[1] = val & 0xff; ++} ++ ++ ++static INLINE void ++hton32_ua_store(uint32 val, uint8 *bytes) ++{ ++ bytes[0] = val >> 24; ++ bytes[1] = (val >> 16) & 0xff; ++ bytes[2] = (val >> 8) & 0xff; ++ bytes[3] = val & 0xff; ++} ++ ++ ++static INLINE uint16 ++ltoh16_ua(const void *bytes) ++{ ++ return _LTOH16_UA((const uint8 *)bytes); ++} ++ ++ ++static INLINE uint32 ++ltoh32_ua(const void *bytes) ++{ ++ return _LTOH32_UA((const uint8 *)bytes); ++} ++ ++ ++static INLINE uint16 ++ntoh16_ua(const void *bytes) ++{ ++ return _NTOH16_UA((const uint8 *)bytes); ++} ++ ++ ++static INLINE uint32 ++ntoh32_ua(const void *bytes) ++{ ++ return _NTOH32_UA((const uint8 *)bytes); ++} ++ ++#endif ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/bcmpcispi.h b/drivers/net/wireless/bcmdhd/include/bcmpcispi.h +new file mode 100644 +index 00000000..44b263ce +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmpcispi.h +@@ -0,0 +1,181 @@ ++/* ++ * Broadcom PCI-SPI Host Controller Register Definitions ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmpcispi.h 241182 2011-02-17 21:50:03Z $ ++ */ ++#ifndef _BCM_PCI_SPI_H ++#define _BCM_PCI_SPI_H ++ ++/* cpp contortions to concatenate w/arg prescan */ ++#ifndef PAD ++#define _PADLINE(line) pad ## line ++#define _XSTR(line) _PADLINE(line) ++#define PAD _XSTR(__LINE__) ++#endif /* PAD */ ++ ++ ++typedef volatile struct { ++ uint32 spih_ctrl; /* 0x00 SPI Control Register */ ++ uint32 spih_stat; /* 0x04 SPI Status Register */ ++ uint32 spih_data; /* 0x08 SPI Data Register, 32-bits wide */ ++ uint32 spih_ext; /* 0x0C SPI Extension Register */ ++ uint32 PAD[4]; /* 0x10-0x1F PADDING */ ++ ++ uint32 spih_gpio_ctrl; /* 0x20 SPI GPIO Control Register */ ++ uint32 spih_gpio_data; /* 0x24 SPI GPIO Data Register */ ++ uint32 PAD[6]; /* 0x28-0x3F PADDING */ ++ ++ uint32 spih_int_edge; /* 0x40 SPI Interrupt Edge Register (0=Level, 1=Edge) */ ++ uint32 spih_int_pol; /* 0x44 SPI Interrupt Polarity Register (0=Active Low, */ ++ /* 1=Active High) */ ++ uint32 spih_int_mask; /* 0x48 SPI Interrupt Mask */ ++ uint32 spih_int_status; /* 0x4C SPI Interrupt Status */ ++ uint32 PAD[4]; /* 0x50-0x5F PADDING */ ++ ++ uint32 spih_hex_disp; /* 0x60 SPI 4-digit hex display value */ ++ uint32 spih_current_ma; /* 0x64 SPI SD card current consumption in mA */ ++ uint32 PAD[1]; /* 0x68 PADDING */ ++ uint32 spih_disp_sel; /* 0x6c SPI 4-digit hex display mode select (1=current) */ ++ uint32 PAD[4]; /* 0x70-0x7F PADDING */ ++ uint32 PAD[8]; /* 0x80-0x9F PADDING */ ++ uint32 PAD[8]; /* 0xA0-0xBF PADDING */ ++ uint32 spih_pll_ctrl; /* 0xC0 PLL Control Register */ ++ uint32 spih_pll_status; /* 0xC4 PLL Status Register */ ++ uint32 spih_xtal_freq; /* 0xC8 External Clock Frequency in units of 10000Hz */ ++ uint32 spih_clk_count; /* 0xCC External Clock Count Register */ ++ ++} spih_regs_t; ++ ++typedef volatile struct { ++ uint32 cfg_space[0x40]; /* 0x000-0x0FF PCI Configuration Space (Read Only) */ ++ uint32 P_IMG_CTRL0; /* 0x100 PCI Image0 Control Register */ ++ ++ uint32 P_BA0; /* 0x104 32 R/W PCI Image0 Base Address register */ ++ uint32 P_AM0; /* 0x108 32 R/W PCI Image0 Address Mask register */ ++ uint32 P_TA0; /* 0x10C 32 R/W PCI Image0 Translation Address register */ ++ uint32 P_IMG_CTRL1; /* 0x110 32 R/W PCI Image1 Control register */ ++ uint32 P_BA1; /* 0x114 32 R/W PCI Image1 Base Address register */ ++ uint32 P_AM1; /* 0x118 32 R/W PCI Image1 Address Mask register */ ++ uint32 P_TA1; /* 0x11C 32 R/W PCI Image1 Translation Address register */ ++ uint32 P_IMG_CTRL2; /* 0x120 32 R/W PCI Image2 Control register */ ++ uint32 P_BA2; /* 0x124 32 R/W PCI Image2 Base Address register */ ++ uint32 P_AM2; /* 0x128 32 R/W PCI Image2 Address Mask register */ ++ uint32 P_TA2; /* 0x12C 32 R/W PCI Image2 Translation Address register */ ++ uint32 P_IMG_CTRL3; /* 0x130 32 R/W PCI Image3 Control register */ ++ uint32 P_BA3; /* 0x134 32 R/W PCI Image3 Base Address register */ ++ uint32 P_AM3; /* 0x138 32 R/W PCI Image3 Address Mask register */ ++ uint32 P_TA3; /* 0x13C 32 R/W PCI Image3 Translation Address register */ ++ uint32 P_IMG_CTRL4; /* 0x140 32 R/W PCI Image4 Control register */ ++ uint32 P_BA4; /* 0x144 32 R/W PCI Image4 Base Address register */ ++ uint32 P_AM4; /* 0x148 32 R/W PCI Image4 Address Mask register */ ++ uint32 P_TA4; /* 0x14C 32 R/W PCI Image4 Translation Address register */ ++ uint32 P_IMG_CTRL5; /* 0x150 32 R/W PCI Image5 Control register */ ++ uint32 P_BA5; /* 0x154 32 R/W PCI Image5 Base Address register */ ++ uint32 P_AM5; /* 0x158 32 R/W PCI Image5 Address Mask register */ ++ uint32 P_TA5; /* 0x15C 32 R/W PCI Image5 Translation Address register */ ++ uint32 P_ERR_CS; /* 0x160 32 R/W PCI Error Control and Status register */ ++ uint32 P_ERR_ADDR; /* 0x164 32 R PCI Erroneous Address register */ ++ uint32 P_ERR_DATA; /* 0x168 32 R PCI Erroneous Data register */ ++ ++ uint32 PAD[5]; /* 0x16C-0x17F PADDING */ ++ ++ uint32 WB_CONF_SPC_BAR; /* 0x180 32 R WISHBONE Configuration Space Base Address */ ++ uint32 W_IMG_CTRL1; /* 0x184 32 R/W WISHBONE Image1 Control register */ ++ uint32 W_BA1; /* 0x188 32 R/W WISHBONE Image1 Base Address register */ ++ uint32 W_AM1; /* 0x18C 32 R/W WISHBONE Image1 Address Mask register */ ++ uint32 W_TA1; /* 0x190 32 R/W WISHBONE Image1 Translation Address reg */ ++ uint32 W_IMG_CTRL2; /* 0x194 32 R/W WISHBONE Image2 Control register */ ++ uint32 W_BA2; /* 0x198 32 R/W WISHBONE Image2 Base Address register */ ++ uint32 W_AM2; /* 0x19C 32 R/W WISHBONE Image2 Address Mask register */ ++ uint32 W_TA2; /* 0x1A0 32 R/W WISHBONE Image2 Translation Address reg */ ++ uint32 W_IMG_CTRL3; /* 0x1A4 32 R/W WISHBONE Image3 Control register */ ++ uint32 W_BA3; /* 0x1A8 32 R/W WISHBONE Image3 Base Address register */ ++ uint32 W_AM3; /* 0x1AC 32 R/W WISHBONE Image3 Address Mask register */ ++ uint32 W_TA3; /* 0x1B0 32 R/W WISHBONE Image3 Translation Address reg */ ++ uint32 W_IMG_CTRL4; /* 0x1B4 32 R/W WISHBONE Image4 Control register */ ++ uint32 W_BA4; /* 0x1B8 32 R/W WISHBONE Image4 Base Address register */ ++ uint32 W_AM4; /* 0x1BC 32 R/W WISHBONE Image4 Address Mask register */ ++ uint32 W_TA4; /* 0x1C0 32 R/W WISHBONE Image4 Translation Address reg */ ++ uint32 W_IMG_CTRL5; /* 0x1C4 32 R/W WISHBONE Image5 Control register */ ++ uint32 W_BA5; /* 0x1C8 32 R/W WISHBONE Image5 Base Address register */ ++ uint32 W_AM5; /* 0x1CC 32 R/W WISHBONE Image5 Address Mask register */ ++ uint32 W_TA5; /* 0x1D0 32 R/W WISHBONE Image5 Translation Address reg */ ++ uint32 W_ERR_CS; /* 0x1D4 32 R/W WISHBONE Error Control and Status reg */ ++ uint32 W_ERR_ADDR; /* 0x1D8 32 R WISHBONE Erroneous Address register */ ++ uint32 W_ERR_DATA; /* 0x1DC 32 R WISHBONE Erroneous Data register */ ++ uint32 CNF_ADDR; /* 0x1E0 32 R/W Configuration Cycle register */ ++ uint32 CNF_DATA; /* 0x1E4 32 R/W Configuration Cycle Generation Data reg */ ++ ++ uint32 INT_ACK; /* 0x1E8 32 R Interrupt Acknowledge register */ ++ uint32 ICR; /* 0x1EC 32 R/W Interrupt Control register */ ++ uint32 ISR; /* 0x1F0 32 R/W Interrupt Status register */ ++} spih_pciregs_t; ++ ++/* ++ * PCI Core interrupt enable and status bit definitions. ++ */ ++ ++/* PCI Core ICR Register bit definitions */ ++#define PCI_INT_PROP_EN (1 << 0) /* Interrupt Propagation Enable */ ++#define PCI_WB_ERR_INT_EN (1 << 1) /* Wishbone Error Interrupt Enable */ ++#define PCI_PCI_ERR_INT_EN (1 << 2) /* PCI Error Interrupt Enable */ ++#define PCI_PAR_ERR_INT_EN (1 << 3) /* Parity Error Interrupt Enable */ ++#define PCI_SYS_ERR_INT_EN (1 << 4) /* System Error Interrupt Enable */ ++#define PCI_SOFTWARE_RESET (1U << 31) /* Software reset of the PCI Core. */ ++ ++ ++/* PCI Core ISR Register bit definitions */ ++#define PCI_INT_PROP_ST (1 << 0) /* Interrupt Propagation Status */ ++#define PCI_WB_ERR_INT_ST (1 << 1) /* Wishbone Error Interrupt Status */ ++#define PCI_PCI_ERR_INT_ST (1 << 2) /* PCI Error Interrupt Status */ ++#define PCI_PAR_ERR_INT_ST (1 << 3) /* Parity Error Interrupt Status */ ++#define PCI_SYS_ERR_INT_ST (1 << 4) /* System Error Interrupt Status */ ++ ++ ++/* Registers on the Wishbone bus */ ++#define SPIH_CTLR_INTR (1 << 0) /* SPI Host Controller Core Interrupt */ ++#define SPIH_DEV_INTR (1 << 1) /* SPI Device Interrupt */ ++#define SPIH_WFIFO_INTR (1 << 2) /* SPI Tx FIFO Empty Intr (FPGA Rev >= 8) */ ++ ++/* GPIO Bit definitions */ ++#define SPIH_CS (1 << 0) /* SPI Chip Select (active low) */ ++#define SPIH_SLOT_POWER (1 << 1) /* SD Card Slot Power Enable */ ++#define SPIH_CARD_DETECT (1 << 2) /* SD Card Detect */ ++ ++/* SPI Status Register Bit definitions */ ++#define SPIH_STATE_MASK 0x30 /* SPI Transfer State Machine state mask */ ++#define SPIH_STATE_SHIFT 4 /* SPI Transfer State Machine state shift */ ++#define SPIH_WFFULL (1 << 3) /* SPI Write FIFO Full */ ++#define SPIH_WFEMPTY (1 << 2) /* SPI Write FIFO Empty */ ++#define SPIH_RFFULL (1 << 1) /* SPI Read FIFO Full */ ++#define SPIH_RFEMPTY (1 << 0) /* SPI Read FIFO Empty */ ++ ++#define SPIH_EXT_CLK (1U << 31) /* Use External Clock as PLL Clock source. */ ++ ++#define SPIH_PLL_NO_CLK (1 << 1) /* Set to 1 if the PLL's input clock is lost. */ ++#define SPIH_PLL_LOCKED (1 << 3) /* Set to 1 when the PLL is locked. */ ++ ++/* Spin bit loop bound check */ ++#define SPI_SPIN_BOUND 0xf4240 /* 1 million */ ++ ++#endif /* _BCM_PCI_SPI_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/bcmperf.h b/drivers/net/wireless/bcmdhd/include/bcmperf.h +new file mode 100644 +index 00000000..74383076 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmperf.h +@@ -0,0 +1,36 @@ ++/* ++ * Performance counters software interface. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmperf.h 241182 2011-02-17 21:50:03Z $ ++ */ ++/* essai */ ++#ifndef _BCMPERF_H_ ++#define _BCMPERF_H_ ++/* get cache hits and misses */ ++#define BCMPERF_ENABLE_INSTRCOUNT() ++#define BCMPERF_ENABLE_ICACHE_MISS() ++#define BCMPERF_ENABLE_ICACHE_HIT() ++#define BCMPERF_GETICACHE_MISS(x) ((x) = 0) ++#define BCMPERF_GETICACHE_HIT(x) ((x) = 0) ++#define BCMPERF_GETINSTRCOUNT(x) ((x) = 0) ++#endif /* _BCMPERF_H_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdbus.h b/drivers/net/wireless/bcmdhd/include/bcmsdbus.h +new file mode 100644 +index 00000000..2fa706db +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmsdbus.h +@@ -0,0 +1,152 @@ ++/* ++ * Definitions for API from sdio common code (bcmsdh) to individual ++ * host controller drivers. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmsdbus.h 347614 2012-07-27 10:24:51Z $ ++ */ ++ ++#ifndef _sdio_api_h_ ++#define _sdio_api_h_ ++ ++ ++#define SDIOH_API_RC_SUCCESS (0x00) ++#define SDIOH_API_RC_FAIL (0x01) ++#define SDIOH_API_SUCCESS(status) (status == 0) ++ ++#define SDIOH_READ 0 /* Read request */ ++#define SDIOH_WRITE 1 /* Write request */ ++ ++#define SDIOH_DATA_FIX 0 /* Fixed addressing */ ++#define SDIOH_DATA_INC 1 /* Incremental addressing */ ++ ++#define SDIOH_CMD_TYPE_NORMAL 0 /* Normal command */ ++#define SDIOH_CMD_TYPE_APPEND 1 /* Append command */ ++#define SDIOH_CMD_TYPE_CUTTHRU 2 /* Cut-through command */ ++ ++#define SDIOH_DATA_PIO 0 /* PIO mode */ ++#define SDIOH_DATA_DMA 1 /* DMA mode */ ++ ++#ifdef BCMSDIOH_TXGLOM ++/* Max number of glommed pkts */ ++#define SDPCM_MAXGLOM_SIZE 10 ++#define SDPCM_DEFGLOM_SIZE 3 ++ ++#define SDPCM_TXGLOM_CPY 0 /* SDIO 2.0 should use copy mode */ ++#define SDPCM_TXGLOM_MDESC 1 /* SDIO 3.0 should use multi-desc mode */ ++#endif ++ ++ ++typedef int SDIOH_API_RC; ++ ++/* SDio Host structure */ ++typedef struct sdioh_info sdioh_info_t; ++ ++/* callback function, taking one arg */ ++typedef void (*sdioh_cb_fn_t)(void *); ++ ++/* attach, return handler on success, NULL if failed. ++ * The handler shall be provided by all subsequent calls. No local cache ++ * cfghdl points to the starting address of pci device mapped memory ++ */ ++extern sdioh_info_t * sdioh_attach(osl_t *osh, void *cfghdl, uint irq); ++extern SDIOH_API_RC sdioh_detach(osl_t *osh, sdioh_info_t *si); ++extern SDIOH_API_RC sdioh_interrupt_register(sdioh_info_t *si, sdioh_cb_fn_t fn, void *argh); ++extern SDIOH_API_RC sdioh_interrupt_deregister(sdioh_info_t *si); ++ ++/* query whether SD interrupt is enabled or not */ ++extern SDIOH_API_RC sdioh_interrupt_query(sdioh_info_t *si, bool *onoff); ++ ++/* enable or disable SD interrupt */ ++extern SDIOH_API_RC sdioh_interrupt_set(sdioh_info_t *si, bool enable_disable); ++ ++#if defined(DHD_DEBUG) ++extern bool sdioh_interrupt_pending(sdioh_info_t *si); ++#endif ++ ++/* read or write one byte using cmd52 */ ++extern SDIOH_API_RC sdioh_request_byte(sdioh_info_t *si, uint rw, uint fnc, uint addr, uint8 *byte); ++ ++/* read or write 2/4 bytes using cmd53 */ ++extern SDIOH_API_RC sdioh_request_word(sdioh_info_t *si, uint cmd_type, uint rw, uint fnc, ++ uint addr, uint32 *word, uint nbyte); ++ ++/* read or write any buffer using cmd53 */ ++extern SDIOH_API_RC sdioh_request_buffer(sdioh_info_t *si, uint pio_dma, uint fix_inc, ++ uint rw, uint fnc_num, uint32 addr, uint regwidth, uint32 buflen, uint8 *buffer, ++ void *pkt); ++ ++#ifdef BCMSDIOH_TXGLOM ++extern void sdioh_glom_post(sdioh_info_t *sd, uint8 *frame, uint len); ++extern void sdioh_glom_clear(sdioh_info_t *sd); ++extern uint sdioh_set_mode(sdioh_info_t *sd, uint mode); ++extern bool sdioh_glom_enabled(void); ++#else ++#define sdioh_glom_post(a, b, c) ++#define sdioh_glom_clear(a) ++#define sdioh_set_mode(a) (0) ++#define sdioh_glom_enabled() (FALSE) ++#endif ++ ++/* get cis data */ ++extern SDIOH_API_RC sdioh_cis_read(sdioh_info_t *si, uint fuc, uint8 *cis, uint32 length); ++ ++extern SDIOH_API_RC sdioh_cfg_read(sdioh_info_t *si, uint fuc, uint32 addr, uint8 *data); ++extern SDIOH_API_RC sdioh_cfg_write(sdioh_info_t *si, uint fuc, uint32 addr, uint8 *data); ++ ++/* query number of io functions */ ++extern uint sdioh_query_iofnum(sdioh_info_t *si); ++ ++/* handle iovars */ ++extern int sdioh_iovar_op(sdioh_info_t *si, const char *name, ++ void *params, int plen, void *arg, int len, bool set); ++ ++/* Issue abort to the specified function and clear controller as needed */ ++extern int sdioh_abort(sdioh_info_t *si, uint fnc); ++ ++/* Start and Stop SDIO without re-enumerating the SD card. */ ++extern int sdioh_start(sdioh_info_t *si, int stage); ++extern int sdioh_stop(sdioh_info_t *si); ++ ++/* Wait system lock free */ ++extern int sdioh_waitlockfree(sdioh_info_t *si); ++ ++/* Reset and re-initialize the device */ ++extern int sdioh_sdio_reset(sdioh_info_t *si); ++ ++/* Helper function */ ++void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh); ++ ++ ++ ++#if defined(BCMSDIOH_STD) ++ #define SDIOH_SLEEP_ENABLED ++#endif ++extern SDIOH_API_RC sdioh_sleep(sdioh_info_t *si, bool enab); ++ ++/* GPIO support */ ++extern SDIOH_API_RC sdioh_gpio_init(sdioh_info_t *sd); ++extern bool sdioh_gpioin(sdioh_info_t *sd, uint32 gpio); ++extern SDIOH_API_RC sdioh_gpioouten(sdioh_info_t *sd, uint32 gpio); ++extern SDIOH_API_RC sdioh_gpioout(sdioh_info_t *sd, uint32 gpio, bool enab); ++ ++#endif /* _sdio_api_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdh.h b/drivers/net/wireless/bcmdhd/include/bcmsdh.h +new file mode 100644 +index 00000000..1c8a6b3e +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmsdh.h +@@ -0,0 +1,242 @@ ++/* ++ * SDIO host client driver interface of Broadcom HNBU ++ * export functions to client drivers ++ * abstract OS and BUS specific details of SDIO ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmsdh.h 347614 2012-07-27 10:24:51Z $ ++ */ ++ ++/** ++ * @file bcmsdh.h ++ */ ++ ++#ifndef _bcmsdh_h_ ++#define _bcmsdh_h_ ++ ++#define BCMSDH_ERROR_VAL 0x0001 /* Error */ ++#define BCMSDH_INFO_VAL 0x0002 /* Info */ ++extern const uint bcmsdh_msglevel; ++ ++#define BCMSDH_ERROR(x) ++#define BCMSDH_INFO(x) ++ ++#if (defined(BCMSDIOH_STD) || defined(BCMSDIOH_BCM) || defined(BCMSDIOH_SPI)) ++#define BCMSDH_ADAPTER ++#endif /* BCMSDIO && (BCMSDIOH_STD || BCMSDIOH_BCM || BCMSDIOH_SPI) */ ++ ++/* forward declarations */ ++typedef struct bcmsdh_info bcmsdh_info_t; ++typedef void (*bcmsdh_cb_fn_t)(void *); ++ ++/* Attach and build an interface to the underlying SD host driver. ++ * - Allocates resources (structs, arrays, mem, OS handles, etc) needed by bcmsdh. ++ * - Returns the bcmsdh handle and virtual address base for register access. ++ * The returned handle should be used in all subsequent calls, but the bcmsh ++ * implementation may maintain a single "default" handle (e.g. the first or ++ * most recent one) to enable single-instance implementations to pass NULL. ++ */ ++ ++#if 0 && (NDISVER >= 0x0630) && 1 ++extern bcmsdh_info_t *bcmsdh_attach(osl_t *osh, void *cfghdl, ++ void **regsva, uint irq, shared_info_t *sh); ++#else ++extern bcmsdh_info_t *bcmsdh_attach(osl_t *osh, void *cfghdl, void **regsva, uint irq); ++#endif ++ ++/* Detach - freeup resources allocated in attach */ ++extern int bcmsdh_detach(osl_t *osh, void *sdh); ++ ++/* Query if SD device interrupts are enabled */ ++extern bool bcmsdh_intr_query(void *sdh); ++ ++/* Enable/disable SD interrupt */ ++extern int bcmsdh_intr_enable(void *sdh); ++extern int bcmsdh_intr_disable(void *sdh); ++ ++/* Register/deregister device interrupt handler. */ ++extern int bcmsdh_intr_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh); ++extern int bcmsdh_intr_dereg(void *sdh); ++/* Enable/disable SD card interrupt forward */ ++extern void bcmsdh_intr_forward(void *sdh, bool pass); ++ ++#if defined(DHD_DEBUG) ++/* Query pending interrupt status from the host controller */ ++extern bool bcmsdh_intr_pending(void *sdh); ++#endif ++ ++/* Register a callback to be called if and when bcmsdh detects ++ * device removal. No-op in the case of non-removable/hardwired devices. ++ */ ++extern int bcmsdh_devremove_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh); ++ ++/* Access SDIO address space (e.g. CCCR) using CMD52 (single-byte interface). ++ * fn: function number ++ * addr: unmodified SDIO-space address ++ * data: data byte to write ++ * err: pointer to error code (or NULL) ++ */ ++extern uint8 bcmsdh_cfg_read(void *sdh, uint func, uint32 addr, int *err); ++extern void bcmsdh_cfg_write(void *sdh, uint func, uint32 addr, uint8 data, int *err); ++ ++/* Read/Write 4bytes from/to cfg space */ ++extern uint32 bcmsdh_cfg_read_word(void *sdh, uint fnc_num, uint32 addr, int *err); ++extern void bcmsdh_cfg_write_word(void *sdh, uint fnc_num, uint32 addr, uint32 data, int *err); ++ ++/* Read CIS content for specified function. ++ * fn: function whose CIS is being requested (0 is common CIS) ++ * cis: pointer to memory location to place results ++ * length: number of bytes to read ++ * Internally, this routine uses the values from the cis base regs (0x9-0xB) ++ * to form an SDIO-space address to read the data from. ++ */ ++extern int bcmsdh_cis_read(void *sdh, uint func, uint8 *cis, uint length); ++ ++/* Synchronous access to device (client) core registers via CMD53 to F1. ++ * addr: backplane address (i.e. >= regsva from attach) ++ * size: register width in bytes (2 or 4) ++ * data: data for register write ++ */ ++extern uint32 bcmsdh_reg_read(void *sdh, uint32 addr, uint size); ++extern uint32 bcmsdh_reg_write(void *sdh, uint32 addr, uint size, uint32 data); ++ ++/* set sb address window */ ++extern int bcmsdhsdio_set_sbaddr_window(void *sdh, uint32 address, bool force_set); ++ ++/* Indicate if last reg read/write failed */ ++extern bool bcmsdh_regfail(void *sdh); ++ ++/* Buffer transfer to/from device (client) core via cmd53. ++ * fn: function number ++ * addr: backplane address (i.e. >= regsva from attach) ++ * flags: backplane width, address increment, sync/async ++ * buf: pointer to memory data buffer ++ * nbytes: number of bytes to transfer to/from buf ++ * pkt: pointer to packet associated with buf (if any) ++ * complete: callback function for command completion (async only) ++ * handle: handle for completion callback (first arg in callback) ++ * Returns 0 or error code. ++ * NOTE: Async operation is not currently supported. ++ */ ++typedef void (*bcmsdh_cmplt_fn_t)(void *handle, int status, bool sync_waiting); ++extern int bcmsdh_send_buf(void *sdh, uint32 addr, uint fn, uint flags, ++ uint8 *buf, uint nbytes, void *pkt, ++ bcmsdh_cmplt_fn_t complete_fn, void *handle); ++extern int bcmsdh_recv_buf(void *sdh, uint32 addr, uint fn, uint flags, ++ uint8 *buf, uint nbytes, void *pkt, ++ bcmsdh_cmplt_fn_t complete_fn, void *handle); ++ ++extern void bcmsdh_glom_post(void *sdh, uint8 *frame, uint len); ++extern void bcmsdh_glom_clear(void *sdh); ++extern uint bcmsdh_set_mode(void *sdh, uint mode); ++extern bool bcmsdh_glom_enabled(void); ++/* Flags bits */ ++#define SDIO_REQ_4BYTE 0x1 /* Four-byte target (backplane) width (vs. two-byte) */ ++#define SDIO_REQ_FIXED 0x2 /* Fixed address (FIFO) (vs. incrementing address) */ ++#define SDIO_REQ_ASYNC 0x4 /* Async request (vs. sync request) */ ++#define SDIO_BYTE_MODE 0x8 /* Byte mode request(non-block mode) */ ++ ++/* Pending (non-error) return code */ ++#define BCME_PENDING 1 ++ ++/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only). ++ * rw: read or write (0/1) ++ * addr: direct SDIO address ++ * buf: pointer to memory data buffer ++ * nbytes: number of bytes to transfer to/from buf ++ * Returns 0 or error code. ++ */ ++extern int bcmsdh_rwdata(void *sdh, uint rw, uint32 addr, uint8 *buf, uint nbytes); ++ ++/* Issue an abort to the specified function */ ++extern int bcmsdh_abort(void *sdh, uint fn); ++ ++/* Start SDIO Host Controller communication */ ++extern int bcmsdh_start(void *sdh, int stage); ++ ++/* Stop SDIO Host Controller communication */ ++extern int bcmsdh_stop(void *sdh); ++ ++/* Wait system lock free */ ++extern int bcmsdh_waitlockfree(void *sdh); ++ ++/* Returns the "Device ID" of target device on the SDIO bus. */ ++extern int bcmsdh_query_device(void *sdh); ++ ++/* Returns the number of IO functions reported by the device */ ++extern uint bcmsdh_query_iofnum(void *sdh); ++ ++/* Miscellaneous knob tweaker. */ ++extern int bcmsdh_iovar_op(void *sdh, const char *name, ++ void *params, int plen, void *arg, int len, bool set); ++ ++/* Reset and reinitialize the device */ ++extern int bcmsdh_reset(bcmsdh_info_t *sdh); ++ ++/* helper functions */ ++ ++extern void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh); ++ ++/* callback functions */ ++typedef struct { ++ /* attach to device */ ++ void *(*attach)(uint16 vend_id, uint16 dev_id, uint16 bus, uint16 slot, ++ uint16 func, uint bustype, void * regsva, osl_t * osh, ++ void * param); ++ /* detach from device */ ++ void (*detach)(void *ch); ++} bcmsdh_driver_t; ++ ++/* platform specific/high level functions */ ++extern int bcmsdh_register(bcmsdh_driver_t *driver); ++extern void bcmsdh_unregister(void); ++extern bool bcmsdh_chipmatch(uint16 vendor, uint16 device); ++extern void bcmsdh_device_remove(void * sdh); ++ ++extern int bcmsdh_reg_sdio_notify(void* semaphore); ++extern void bcmsdh_unreg_sdio_notify(void); ++ ++#if defined(OOB_INTR_ONLY) ++extern int bcmsdh_register_oob_intr(void * dhdp); ++extern void bcmsdh_unregister_oob_intr(void); ++extern void bcmsdh_oob_intr_set(bool enable); ++#endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */ ++ ++/* Function to pass device-status bits to DHD. */ ++extern uint32 bcmsdh_get_dstatus(void *sdh); ++ ++/* Function to return current window addr */ ++extern uint32 bcmsdh_cur_sbwad(void *sdh); ++ ++/* Function to pass chipid and rev to lower layers for controlling pr's */ ++extern void bcmsdh_chipinfo(void *sdh, uint32 chip, uint32 chiprev); ++ ++ ++extern int bcmsdh_sleep(void *sdh, bool enab); ++ ++/* GPIO support */ ++extern int bcmsdh_gpio_init(void *sd); ++extern bool bcmsdh_gpioin(void *sd, uint32 gpio); ++extern int bcmsdh_gpioouten(void *sd, uint32 gpio); ++extern int bcmsdh_gpioout(void *sd, uint32 gpio, bool enab); ++ ++#endif /* _bcmsdh_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h b/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h +new file mode 100644 +index 00000000..0e11b11e +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h +@@ -0,0 +1,125 @@ ++/* ++ * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmsdh_sdmmc.h 355594 2012-09-07 10:22:02Z $ ++ */ ++ ++#ifndef __BCMSDH_SDMMC_H__ ++#define __BCMSDH_SDMMC_H__ ++ ++#define sd_err(x) ++#define sd_trace(x) ++#define sd_info(x) ++#define sd_debug(x) ++#define sd_data(x) ++#define sd_ctrl(x) ++ ++#define sd_trace_hw4 sd_trace ++ ++#define sd_sync_dma(sd, read, nbytes) ++#define sd_init_dma(sd) ++#define sd_ack_intr(sd) ++#define sd_wakeup(sd); ++ ++/* Allocate/init/free per-OS private data */ ++extern int sdioh_sdmmc_osinit(sdioh_info_t *sd); ++extern void sdioh_sdmmc_osfree(sdioh_info_t *sd); ++ ++#define sd_log(x) ++ ++#define SDIOH_ASSERT(exp) \ ++ do { if (!(exp)) \ ++ printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \ ++ } while (0) ++ ++#define BLOCK_SIZE_4318 64 ++#define BLOCK_SIZE_4328 512 ++ ++/* internal return code */ ++#define SUCCESS 0 ++#define ERROR 1 ++ ++/* private bus modes */ ++#define SDIOH_MODE_SD4 2 ++#define CLIENT_INTR 0x100 /* Get rid of this! */ ++ ++struct sdioh_info { ++ osl_t *osh; /* osh handler */ ++ bool client_intr_enabled; /* interrupt connnected flag */ ++ bool intr_handler_valid; /* client driver interrupt handler valid */ ++ sdioh_cb_fn_t intr_handler; /* registered interrupt handler */ ++ void *intr_handler_arg; /* argument to call interrupt handler */ ++ uint16 intmask; /* Current active interrupts */ ++ void *sdos_info; /* Pointer to per-OS private data */ ++ ++ uint irq; /* Client irq */ ++ int intrcount; /* Client interrupts */ ++ ++ bool sd_use_dma; /* DMA on CMD53 */ ++ bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */ ++ /* Must be on for sd_multiblock to be effective */ ++ bool use_client_ints; /* If this is false, make sure to restore */ ++ int sd_mode; /* SD1/SD4/SPI */ ++ int client_block_size[SDIOD_MAX_IOFUNCS]; /* Blocksize */ ++ uint8 num_funcs; /* Supported funcs on client */ ++ uint32 com_cis_ptr; ++ uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; ++ ++#define SDIOH_SDMMC_MAX_SG_ENTRIES 32 ++ struct scatterlist sg_list[SDIOH_SDMMC_MAX_SG_ENTRIES]; ++ bool use_rxchain; ++}; ++ ++/************************************************************ ++ * Internal interfaces: per-port references into bcmsdh_sdmmc.c ++ */ ++ ++/* Global message bits */ ++extern uint sd_msglevel; ++ ++/* OS-independent interrupt handler */ ++extern bool check_client_intr(sdioh_info_t *sd); ++ ++/* Core interrupt enable/disable of device interrupts */ ++extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd); ++extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd); ++ ++ ++/************************************************************** ++ * Internal interfaces: bcmsdh_sdmmc.c references to per-port code ++ */ ++ ++/* Register mapping routines */ ++extern uint32 *sdioh_sdmmc_reg_map(osl_t *osh, int32 addr, int size); ++extern void sdioh_sdmmc_reg_unmap(osl_t *osh, int32 addr, int size); ++ ++/* Interrupt (de)registration routines */ ++extern int sdioh_sdmmc_register_irq(sdioh_info_t *sd, uint irq); ++extern void sdioh_sdmmc_free_irq(uint irq, sdioh_info_t *sd); ++ ++typedef struct _BCMSDH_SDMMC_INSTANCE { ++ sdioh_info_t *sd; ++ struct sdio_func *func[SDIOD_MAX_IOFUNCS]; ++} BCMSDH_SDMMC_INSTANCE, *PBCMSDH_SDMMC_INSTANCE; ++ ++#endif /* __BCMSDH_SDMMC_H__ */ +diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h b/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h +new file mode 100644 +index 00000000..fb2ec3af +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h +@@ -0,0 +1,281 @@ ++/* ++ * Broadcom SDIO/PCMCIA ++ * Software-specific definitions shared between device and host side ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmsdpcm.h 362722 2012-10-12 23:55:55Z $ ++ */ ++ ++#ifndef _bcmsdpcm_h_ ++#define _bcmsdpcm_h_ ++ ++/* ++ * Software allocation of To SB Mailbox resources ++ */ ++ ++/* intstatus bits */ ++#define I_SMB_NAK I_SMB_SW0 /* To SB Mailbox Frame NAK */ ++#define I_SMB_INT_ACK I_SMB_SW1 /* To SB Mailbox Host Interrupt ACK */ ++#define I_SMB_USE_OOB I_SMB_SW2 /* To SB Mailbox Use OOB Wakeup */ ++#define I_SMB_DEV_INT I_SMB_SW3 /* To SB Mailbox Miscellaneous Interrupt */ ++ ++#define I_TOSBMAIL (I_SMB_NAK | I_SMB_INT_ACK | I_SMB_USE_OOB | I_SMB_DEV_INT) ++ ++/* tosbmailbox bits corresponding to intstatus bits */ ++#define SMB_NAK (1 << 0) /* To SB Mailbox Frame NAK */ ++#define SMB_INT_ACK (1 << 1) /* To SB Mailbox Host Interrupt ACK */ ++#define SMB_USE_OOB (1 << 2) /* To SB Mailbox Use OOB Wakeup */ ++#define SMB_DEV_INT (1 << 3) /* To SB Mailbox Miscellaneous Interrupt */ ++#define SMB_MASK 0x0000000f /* To SB Mailbox Mask */ ++ ++/* tosbmailboxdata */ ++#define SMB_DATA_VERSION_MASK 0x00ff0000 /* host protocol version (sent with F2 enable) */ ++#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version (sent with F2 enable) */ ++ ++/* ++ * Software allocation of To Host Mailbox resources ++ */ ++ ++/* intstatus bits */ ++#define I_HMB_FC_STATE I_HMB_SW0 /* To Host Mailbox Flow Control State */ ++#define I_HMB_FC_CHANGE I_HMB_SW1 /* To Host Mailbox Flow Control State Changed */ ++#define I_HMB_FRAME_IND I_HMB_SW2 /* To Host Mailbox Frame Indication */ ++#define I_HMB_HOST_INT I_HMB_SW3 /* To Host Mailbox Miscellaneous Interrupt */ ++ ++#define I_TOHOSTMAIL (I_HMB_FC_CHANGE | I_HMB_FRAME_IND | I_HMB_HOST_INT) ++ ++/* tohostmailbox bits corresponding to intstatus bits */ ++#define HMB_FC_ON (1 << 0) /* To Host Mailbox Flow Control State */ ++#define HMB_FC_CHANGE (1 << 1) /* To Host Mailbox Flow Control State Changed */ ++#define HMB_FRAME_IND (1 << 2) /* To Host Mailbox Frame Indication */ ++#define HMB_HOST_INT (1 << 3) /* To Host Mailbox Miscellaneous Interrupt */ ++#define HMB_MASK 0x0000000f /* To Host Mailbox Mask */ ++ ++/* tohostmailboxdata */ ++#define HMB_DATA_NAKHANDLED 0x01 /* we're ready to retransmit NAK'd frame to host */ ++#define HMB_DATA_DEVREADY 0x02 /* we're ready to to talk to host after enable */ ++#define HMB_DATA_FC 0x04 /* per prio flowcontrol update flag to host */ ++#define HMB_DATA_FWREADY 0x08 /* firmware is ready for protocol activity */ ++#define HMB_DATA_FWHALT 0x10 /* firmware has halted operation */ ++ ++#define HMB_DATA_FCDATA_MASK 0xff000000 /* per prio flowcontrol data */ ++#define HMB_DATA_FCDATA_SHIFT 24 /* per prio flowcontrol data */ ++ ++#define HMB_DATA_VERSION_MASK 0x00ff0000 /* device protocol version (with devready) */ ++#define HMB_DATA_VERSION_SHIFT 16 /* device protocol version (with devready) */ ++ ++/* ++ * Software-defined protocol header ++ */ ++ ++/* Current protocol version */ ++#define SDPCM_PROT_VERSION 4 ++ ++/* SW frame header */ ++#define SDPCM_SEQUENCE_MASK 0x000000ff /* Sequence Number Mask */ ++#define SDPCM_PACKET_SEQUENCE(p) (((uint8 *)p)[0] & 0xff) /* p starts w/SW Header */ ++ ++#define SDPCM_CHANNEL_MASK 0x00000f00 /* Channel Number Mask */ ++#define SDPCM_CHANNEL_SHIFT 8 /* Channel Number Shift */ ++#define SDPCM_PACKET_CHANNEL(p) (((uint8 *)p)[1] & 0x0f) /* p starts w/SW Header */ ++ ++#define SDPCM_FLAGS_MASK 0x0000f000 /* Mask of flag bits */ ++#define SDPCM_FLAGS_SHIFT 12 /* Flag bits shift */ ++#define SDPCM_PACKET_FLAGS(p) ((((uint8 *)p)[1] & 0xf0) >> 4) /* p starts w/SW Header */ ++ ++/* Next Read Len: lookahead length of next frame, in 16-byte units (rounded up) */ ++#define SDPCM_NEXTLEN_MASK 0x00ff0000 /* Next Read Len Mask */ ++#define SDPCM_NEXTLEN_SHIFT 16 /* Next Read Len Shift */ ++#define SDPCM_NEXTLEN_VALUE(p) ((((uint8 *)p)[2] & 0xff) << 4) /* p starts w/SW Header */ ++#define SDPCM_NEXTLEN_OFFSET 2 ++ ++/* Data Offset from SOF (HW Tag, SW Tag, Pad) */ ++#define SDPCM_DOFFSET_OFFSET 3 /* Data Offset */ ++#define SDPCM_DOFFSET_VALUE(p) (((uint8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff) ++#define SDPCM_DOFFSET_MASK 0xff000000 ++#define SDPCM_DOFFSET_SHIFT 24 ++ ++#define SDPCM_FCMASK_OFFSET 4 /* Flow control */ ++#define SDPCM_FCMASK_VALUE(p) (((uint8 *)p)[SDPCM_FCMASK_OFFSET ] & 0xff) ++#define SDPCM_WINDOW_OFFSET 5 /* Credit based fc */ ++#define SDPCM_WINDOW_VALUE(p) (((uint8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff) ++#define SDPCM_VERSION_OFFSET 6 /* Version # */ ++#define SDPCM_VERSION_VALUE(p) (((uint8 *)p)[SDPCM_VERSION_OFFSET] & 0xff) ++#define SDPCM_UNUSED_OFFSET 7 /* Spare */ ++#define SDPCM_UNUSED_VALUE(p) (((uint8 *)p)[SDPCM_UNUSED_OFFSET] & 0xff) ++ ++#define SDPCM_SWHEADER_LEN 8 /* SW header is 64 bits */ ++ ++/* logical channel numbers */ ++#define SDPCM_CONTROL_CHANNEL 0 /* Control Request/Response Channel Id */ ++#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication Channel Id */ ++#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv Channel Id */ ++#define SDPCM_GLOM_CHANNEL 3 /* For coalesced packets (superframes) */ ++#define SDPCM_TEST_CHANNEL 15 /* Reserved for test/debug packets */ ++#define SDPCM_MAX_CHANNEL 15 ++ ++#define SDPCM_SEQUENCE_WRAP 256 /* wrap-around val for eight-bit frame seq number */ ++ ++#define SDPCM_FLAG_RESVD0 0x01 ++#define SDPCM_FLAG_RESVD1 0x02 ++#define SDPCM_FLAG_GSPI_TXENAB 0x04 ++#define SDPCM_FLAG_GLOMDESC 0x08 /* Superframe descriptor mask */ ++ ++/* For GLOM_CHANNEL frames, use a flag to indicate descriptor frame */ ++#define SDPCM_GLOMDESC_FLAG (SDPCM_FLAG_GLOMDESC << SDPCM_FLAGS_SHIFT) ++ ++#define SDPCM_GLOMDESC(p) (((uint8 *)p)[1] & 0x80) ++ ++/* For TEST_CHANNEL packets, define another 4-byte header */ ++#define SDPCM_TEST_HDRLEN 4 /* Generally: Cmd(1), Ext(1), Len(2); ++ * Semantics of Ext byte depend on command. ++ * Len is current or requested frame length, not ++ * including test header; sent little-endian. ++ */ ++#define SDPCM_TEST_PKT_CNT_FLD_LEN 4 /* Packet count filed legth */ ++#define SDPCM_TEST_DISCARD 0x01 /* Receiver discards. Ext is a pattern id. */ ++#define SDPCM_TEST_ECHOREQ 0x02 /* Echo request. Ext is a pattern id. */ ++#define SDPCM_TEST_ECHORSP 0x03 /* Echo response. Ext is a pattern id. */ ++#define SDPCM_TEST_BURST 0x04 /* Receiver to send a burst. Ext is a frame count ++ * (Backward compatabilty) Set frame count in a ++ * 4 byte filed adjacent to the HDR ++ */ ++#define SDPCM_TEST_SEND 0x05 /* Receiver sets send mode. Ext is boolean on/off ++ * Set frame count in a 4 byte filed adjacent to ++ * the HDR ++ */ ++ ++/* Handy macro for filling in datagen packets with a pattern */ ++#define SDPCM_TEST_FILL(byteno, id) ((uint8)(id + byteno)) ++ ++/* ++ * Software counters (first part matches hardware counters) ++ */ ++ ++typedef volatile struct { ++ uint32 cmd52rd; /* Cmd52RdCount, SDIO: cmd52 reads */ ++ uint32 cmd52wr; /* Cmd52WrCount, SDIO: cmd52 writes */ ++ uint32 cmd53rd; /* Cmd53RdCount, SDIO: cmd53 reads */ ++ uint32 cmd53wr; /* Cmd53WrCount, SDIO: cmd53 writes */ ++ uint32 abort; /* AbortCount, SDIO: aborts */ ++ uint32 datacrcerror; /* DataCrcErrorCount, SDIO: frames w/CRC error */ ++ uint32 rdoutofsync; /* RdOutOfSyncCount, SDIO/PCMCIA: Rd Frm out of sync */ ++ uint32 wroutofsync; /* RdOutOfSyncCount, SDIO/PCMCIA: Wr Frm out of sync */ ++ uint32 writebusy; /* WriteBusyCount, SDIO: device asserted "busy" */ ++ uint32 readwait; /* ReadWaitCount, SDIO: no data ready for a read cmd */ ++ uint32 readterm; /* ReadTermCount, SDIO: read frame termination cmds */ ++ uint32 writeterm; /* WriteTermCount, SDIO: write frames termination cmds */ ++ uint32 rxdescuflo; /* receive descriptor underflows */ ++ uint32 rxfifooflo; /* receive fifo overflows */ ++ uint32 txfifouflo; /* transmit fifo underflows */ ++ uint32 runt; /* runt (too short) frames recv'd from bus */ ++ uint32 badlen; /* frame's rxh len does not match its hw tag len */ ++ uint32 badcksum; /* frame's hw tag chksum doesn't agree with len value */ ++ uint32 seqbreak; /* break in sequence # space from one rx frame to the next */ ++ uint32 rxfcrc; /* frame rx header indicates crc error */ ++ uint32 rxfwoos; /* frame rx header indicates write out of sync */ ++ uint32 rxfwft; /* frame rx header indicates write frame termination */ ++ uint32 rxfabort; /* frame rx header indicates frame aborted */ ++ uint32 woosint; /* write out of sync interrupt */ ++ uint32 roosint; /* read out of sync interrupt */ ++ uint32 rftermint; /* read frame terminate interrupt */ ++ uint32 wftermint; /* write frame terminate interrupt */ ++} sdpcmd_cnt_t; ++ ++/* ++ * Register Access Macros ++ */ ++ ++#define SDIODREV_IS(var, val) ((var) == (val)) ++#define SDIODREV_GE(var, val) ((var) >= (val)) ++#define SDIODREV_GT(var, val) ((var) > (val)) ++#define SDIODREV_LT(var, val) ((var) < (val)) ++#define SDIODREV_LE(var, val) ((var) <= (val)) ++ ++#define SDIODDMAREG32(h, dir, chnl) \ ++ ((dir) == DMA_TX ? \ ++ (void *)(uintptr)&((h)->regs->dma.sdiod32.dma32regs[chnl].xmt) : \ ++ (void *)(uintptr)&((h)->regs->dma.sdiod32.dma32regs[chnl].rcv)) ++ ++#define SDIODDMAREG64(h, dir, chnl) \ ++ ((dir) == DMA_TX ? \ ++ (void *)(uintptr)&((h)->regs->dma.sdiod64.dma64regs[chnl].xmt) : \ ++ (void *)(uintptr)&((h)->regs->dma.sdiod64.dma64regs[chnl].rcv)) ++ ++#define SDIODDMAREG(h, dir, chnl) \ ++ (SDIODREV_LT((h)->corerev, 1) ? \ ++ SDIODDMAREG32((h), (dir), (chnl)) : \ ++ SDIODDMAREG64((h), (dir), (chnl))) ++ ++#define PCMDDMAREG(h, dir, chnl) \ ++ ((dir) == DMA_TX ? \ ++ (void *)(uintptr)&((h)->regs->dma.pcm32.dmaregs.xmt) : \ ++ (void *)(uintptr)&((h)->regs->dma.pcm32.dmaregs.rcv)) ++ ++#define SDPCMDMAREG(h, dir, chnl, coreid) \ ++ ((coreid) == SDIOD_CORE_ID ? \ ++ SDIODDMAREG(h, dir, chnl) : \ ++ PCMDDMAREG(h, dir, chnl)) ++ ++#define SDIODFIFOREG(h, corerev) \ ++ (SDIODREV_LT((corerev), 1) ? \ ++ ((dma32diag_t *)(uintptr)&((h)->regs->dma.sdiod32.dmafifo)) : \ ++ ((dma32diag_t *)(uintptr)&((h)->regs->dma.sdiod64.dmafifo))) ++ ++#define PCMDFIFOREG(h) \ ++ ((dma32diag_t *)(uintptr)&((h)->regs->dma.pcm32.dmafifo)) ++ ++#define SDPCMFIFOREG(h, coreid, corerev) \ ++ ((coreid) == SDIOD_CORE_ID ? \ ++ SDIODFIFOREG(h, corerev) : \ ++ PCMDFIFOREG(h)) ++ ++/* ++ * Shared structure between dongle and the host. ++ * The structure contains pointers to trap or assert information. ++ */ ++#define SDPCM_SHARED_VERSION 0x0001 ++#define SDPCM_SHARED_VERSION_MASK 0x00FF ++#define SDPCM_SHARED_ASSERT_BUILT 0x0100 ++#define SDPCM_SHARED_ASSERT 0x0200 ++#define SDPCM_SHARED_TRAP 0x0400 ++#define SDPCM_SHARED_IN_BRPT 0x0800 ++#define SDPCM_SHARED_SET_BRPT 0x1000 ++#define SDPCM_SHARED_PENDING_BRPT 0x2000 ++ ++typedef struct { ++ uint32 flags; ++ uint32 trap_addr; ++ uint32 assert_exp_addr; ++ uint32 assert_file_addr; ++ uint32 assert_line; ++ uint32 console_addr; /* Address of hndrte_cons_t */ ++ uint32 msgtrace_addr; ++ uint32 brpt_addr; ++} sdpcm_shared_t; ++ ++extern sdpcm_shared_t sdpcm_shared; ++ ++/* Function can be used to notify host of FW halt */ ++extern void sdpcmd_fwhalt(void); ++ ++#endif /* _bcmsdpcm_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdspi.h b/drivers/net/wireless/bcmdhd/include/bcmsdspi.h +new file mode 100644 +index 00000000..3d444f3b +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmsdspi.h +@@ -0,0 +1,135 @@ ++/* ++ * SD-SPI Protocol Conversion - BCMSDH->SPI Translation Layer ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmsdspi.h 294363 2011-11-06 23:02:20Z $ ++ */ ++#ifndef _BCM_SD_SPI_H ++#define _BCM_SD_SPI_H ++ ++/* global msglevel for debug messages - bitvals come from sdiovar.h */ ++ ++#define sd_err(x) ++#define sd_trace(x) ++#define sd_info(x) ++#define sd_debug(x) ++#define sd_data(x) ++#define sd_ctrl(x) ++ ++#define sd_log(x) ++ ++#define SDIOH_ASSERT(exp) \ ++ do { if (!(exp)) \ ++ printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \ ++ } while (0) ++ ++#define BLOCK_SIZE_4318 64 ++#define BLOCK_SIZE_4328 512 ++ ++/* internal return code */ ++#define SUCCESS 0 ++#undef ERROR ++#define ERROR 1 ++ ++/* private bus modes */ ++#define SDIOH_MODE_SPI 0 ++ ++#define USE_BLOCKMODE 0x2 /* Block mode can be single block or multi */ ++#define USE_MULTIBLOCK 0x4 ++ ++struct sdioh_info { ++ uint cfg_bar; /* pci cfg address for bar */ ++ uint32 caps; /* cached value of capabilities reg */ ++ uint bar0; /* BAR0 for PCI Device */ ++ osl_t *osh; /* osh handler */ ++ void *controller; /* Pointer to SPI Controller's private data struct */ ++ ++ uint lockcount; /* nest count of sdspi_lock() calls */ ++ bool client_intr_enabled; /* interrupt connnected flag */ ++ bool intr_handler_valid; /* client driver interrupt handler valid */ ++ sdioh_cb_fn_t intr_handler; /* registered interrupt handler */ ++ void *intr_handler_arg; /* argument to call interrupt handler */ ++ bool initialized; /* card initialized */ ++ uint32 target_dev; /* Target device ID */ ++ uint32 intmask; /* Current active interrupts */ ++ void *sdos_info; /* Pointer to per-OS private data */ ++ ++ uint32 controller_type; /* Host controller type */ ++ uint8 version; /* Host Controller Spec Compliance Version */ ++ uint irq; /* Client irq */ ++ uint32 intrcount; /* Client interrupts */ ++ uint32 local_intrcount; /* Controller interrupts */ ++ bool host_init_done; /* Controller initted */ ++ bool card_init_done; /* Client SDIO interface initted */ ++ bool polled_mode; /* polling for command completion */ ++ ++ bool sd_use_dma; /* DMA on CMD53 */ ++ bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */ ++ /* Must be on for sd_multiblock to be effective */ ++ bool use_client_ints; /* If this is false, make sure to restore */ ++ bool got_hcint; /* Host Controller interrupt. */ ++ /* polling hack in wl_linux.c:wl_timer() */ ++ int adapter_slot; /* Maybe dealing with multiple slots/controllers */ ++ int sd_mode; /* SD1/SD4/SPI */ ++ int client_block_size[SDIOD_MAX_IOFUNCS]; /* Blocksize */ ++ uint32 data_xfer_count; /* Current register transfer size */ ++ uint32 cmd53_wr_data; /* Used to pass CMD53 write data */ ++ uint32 card_response; /* Used to pass back response status byte */ ++ uint32 card_rsp_data; /* Used to pass back response data word */ ++ uint16 card_rca; /* Current Address */ ++ uint8 num_funcs; /* Supported funcs on client */ ++ uint32 com_cis_ptr; ++ uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; ++ void *dma_buf; ++ ulong dma_phys; ++ int r_cnt; /* rx count */ ++ int t_cnt; /* tx_count */ ++}; ++ ++/************************************************************ ++ * Internal interfaces: per-port references into bcmsdspi.c ++ */ ++ ++/* Global message bits */ ++extern uint sd_msglevel; ++ ++/************************************************************** ++ * Internal interfaces: bcmsdspi.c references to per-port code ++ */ ++ ++/* Register mapping routines */ ++extern uint32 *spi_reg_map(osl_t *osh, uintptr addr, int size); ++extern void spi_reg_unmap(osl_t *osh, uintptr addr, int size); ++ ++/* Interrupt (de)registration routines */ ++extern int spi_register_irq(sdioh_info_t *sd, uint irq); ++extern void spi_free_irq(uint irq, sdioh_info_t *sd); ++ ++/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */ ++extern void spi_lock(sdioh_info_t *sd); ++extern void spi_unlock(sdioh_info_t *sd); ++ ++/* Allocate/init/free per-OS private data */ ++extern int spi_osinit(sdioh_info_t *sd); ++extern void spi_osfree(sdioh_info_t *sd); ++ ++#endif /* _BCM_SD_SPI_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdstd.h b/drivers/net/wireless/bcmdhd/include/bcmsdstd.h +new file mode 100644 +index 00000000..896686cf +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmsdstd.h +@@ -0,0 +1,264 @@ ++/* ++ * 'Standard' SDIO HOST CONTROLLER driver ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmsdstd.h 347614 2012-07-27 10:24:51Z $ ++ */ ++#ifndef _BCM_SD_STD_H ++#define _BCM_SD_STD_H ++ ++/* global msglevel for debug messages - bitvals come from sdiovar.h */ ++#define sd_err(x) do { if (sd_msglevel & SDH_ERROR_VAL) printf x; } while (0) ++#define sd_trace(x) ++#define sd_info(x) ++#define sd_debug(x) ++#define sd_data(x) ++#define sd_ctrl(x) ++#define sd_dma(x) ++ ++#define sd_sync_dma(sd, read, nbytes) ++#define sd_init_dma(sd) ++#define sd_ack_intr(sd) ++#define sd_wakeup(sd); ++/* Allocate/init/free per-OS private data */ ++extern int sdstd_osinit(sdioh_info_t *sd); ++extern void sdstd_osfree(sdioh_info_t *sd); ++ ++#define sd_log(x) ++ ++#define SDIOH_ASSERT(exp) \ ++ do { if (!(exp)) \ ++ printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \ ++ } while (0) ++ ++#define BLOCK_SIZE_4318 64 ++#define BLOCK_SIZE_4328 512 ++ ++/* internal return code */ ++#define SUCCESS 0 ++#define ERROR 1 ++ ++/* private bus modes */ ++#define SDIOH_MODE_SPI 0 ++#define SDIOH_MODE_SD1 1 ++#define SDIOH_MODE_SD4 2 ++ ++#define MAX_SLOTS 6 /* For PCI: Only 6 BAR entries => 6 slots */ ++#define SDIOH_REG_WINSZ 0x100 /* Number of registers in Standard Host Controller */ ++ ++#define SDIOH_TYPE_ARASAN_HDK 1 ++#define SDIOH_TYPE_BCM27XX 2 ++#define SDIOH_TYPE_TI_PCIXX21 4 /* TI PCIxx21 Standard Host Controller */ ++#define SDIOH_TYPE_RICOH_R5C822 5 /* Ricoh Co Ltd R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter */ ++#define SDIOH_TYPE_JMICRON 6 /* JMicron Standard SDIO Host Controller */ ++ ++/* For linux, allow yielding for dongle */ ++#define BCMSDYIELD ++ ++/* Expected card status value for CMD7 */ ++#define SDIOH_CMD7_EXP_STATUS 0x00001E00 ++ ++#define RETRIES_LARGE 100000 ++#define sdstd_os_yield(sd) do {} while (0) ++#define RETRIES_SMALL 100 ++ ++ ++#define USE_BLOCKMODE 0x2 /* Block mode can be single block or multi */ ++#define USE_MULTIBLOCK 0x4 ++ ++#define USE_FIFO 0x8 /* Fifo vs non-fifo */ ++ ++#define CLIENT_INTR 0x100 /* Get rid of this! */ ++ ++#define HC_INTR_RETUNING 0x1000 ++ ++ ++#ifdef BCMSDIOH_TXGLOM ++/* Setting the MAX limit to 10 */ ++#define SDIOH_MAXGLOM_SIZE 10 ++ ++typedef struct glom_buf { ++ uint32 count; /* Total number of pkts queued */ ++ void *dma_buf_arr[SDIOH_MAXGLOM_SIZE]; /* Frame address */ ++ ulong dma_phys_arr[SDIOH_MAXGLOM_SIZE]; /* DMA_MAPed address of frames */ ++ uint16 nbytes[SDIOH_MAXGLOM_SIZE]; /* Size of each frame */ ++} glom_buf_t; ++#endif ++ ++struct sdioh_info { ++ uint cfg_bar; /* pci cfg address for bar */ ++ uint32 caps; /* cached value of capabilities reg */ ++ uint32 curr_caps; /* max current capabilities reg */ ++ ++ osl_t *osh; /* osh handler */ ++ volatile char *mem_space; /* pci device memory va */ ++ uint lockcount; /* nest count of sdstd_lock() calls */ ++ bool client_intr_enabled; /* interrupt connnected flag */ ++ bool intr_handler_valid; /* client driver interrupt handler valid */ ++ sdioh_cb_fn_t intr_handler; /* registered interrupt handler */ ++ void *intr_handler_arg; /* argument to call interrupt handler */ ++ bool initialized; /* card initialized */ ++ uint target_dev; /* Target device ID */ ++ uint16 intmask; /* Current active interrupts */ ++ void *sdos_info; /* Pointer to per-OS private data */ ++ ++ uint32 controller_type; /* Host controller type */ ++ uint8 version; /* Host Controller Spec Compliance Version */ ++ uint irq; /* Client irq */ ++ int intrcount; /* Client interrupts */ ++ int local_intrcount; /* Controller interrupts */ ++ bool host_init_done; /* Controller initted */ ++ bool card_init_done; /* Client SDIO interface initted */ ++ bool polled_mode; /* polling for command completion */ ++ ++ bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */ ++ /* Must be on for sd_multiblock to be effective */ ++ bool use_client_ints; /* If this is false, make sure to restore */ ++ /* polling hack in wl_linux.c:wl_timer() */ ++ int adapter_slot; /* Maybe dealing with multiple slots/controllers */ ++ int sd_mode; /* SD1/SD4/SPI */ ++ int client_block_size[SDIOD_MAX_IOFUNCS]; /* Blocksize */ ++ uint32 data_xfer_count; /* Current transfer */ ++ uint16 card_rca; /* Current Address */ ++ int8 sd_dma_mode; /* DMA Mode (PIO, SDMA, ... ADMA2) on CMD53 */ ++ uint8 num_funcs; /* Supported funcs on client */ ++ uint32 com_cis_ptr; ++ uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; ++ void *dma_buf; /* DMA Buffer virtual address */ ++ ulong dma_phys; /* DMA Buffer physical address */ ++ void *adma2_dscr_buf; /* ADMA2 Descriptor Buffer virtual address */ ++ ulong adma2_dscr_phys; /* ADMA2 Descriptor Buffer physical address */ ++ ++ /* adjustments needed to make the dma align properly */ ++ void *dma_start_buf; ++ ulong dma_start_phys; ++ uint alloced_dma_size; ++ void *adma2_dscr_start_buf; ++ ulong adma2_dscr_start_phys; ++ uint alloced_adma2_dscr_size; ++ ++ int r_cnt; /* rx count */ ++ int t_cnt; /* tx_count */ ++ bool got_hcint; /* local interrupt flag */ ++ uint16 last_intrstatus; /* to cache intrstatus */ ++ int host_UHSISupported; /* whether UHSI is supported for HC. */ ++ int card_UHSI_voltage_Supported; /* whether UHSI is supported for ++ * Card in terms of Voltage [1.8 or 3.3]. ++ */ ++ int global_UHSI_Supp; /* type of UHSI support in both host and card. ++ * HOST_SDR_UNSUPP: capabilities not supported/matched ++ * HOST_SDR_12_25: SDR12 and SDR25 supported ++ * HOST_SDR_50_104_DDR: one of SDR50/SDR104 or DDR50 supptd ++ */ ++ volatile int sd3_dat_state; /* data transfer state used for retuning check */ ++ volatile int sd3_tun_state; /* tuning state used for retuning check */ ++ bool sd3_tuning_reqd; /* tuning requirement parameter */ ++ uint32 caps3; /* cached value of 32 MSbits capabilities reg (SDIO 3.0) */ ++#ifdef BCMSDIOH_TXGLOM ++ glom_buf_t glom_info; /* pkt information used for glomming */ ++ uint txglom_mode; /* Txglom mode: 0 - copy, 1 - multi-descriptor */ ++#endif ++}; ++ ++#define DMA_MODE_NONE 0 ++#define DMA_MODE_SDMA 1 ++#define DMA_MODE_ADMA1 2 ++#define DMA_MODE_ADMA2 3 ++#define DMA_MODE_ADMA2_64 4 ++#define DMA_MODE_AUTO -1 ++ ++#define USE_DMA(sd) ((bool)((sd->sd_dma_mode > 0) ? TRUE : FALSE)) ++ ++/* States for Tuning and corr data */ ++#define TUNING_IDLE 0 ++#define TUNING_START 1 ++#define TUNING_START_AFTER_DAT 2 ++#define TUNING_ONGOING 3 ++ ++#define DATA_TRANSFER_IDLE 0 ++#define DATA_TRANSFER_ONGOING 1 ++ ++#define CHECK_TUNING_PRE_DATA 1 ++#define CHECK_TUNING_POST_DATA 2 ++ ++/************************************************************ ++ * Internal interfaces: per-port references into bcmsdstd.c ++ */ ++ ++/* Global message bits */ ++extern uint sd_msglevel; ++ ++/* OS-independent interrupt handler */ ++extern bool check_client_intr(sdioh_info_t *sd); ++ ++/* Core interrupt enable/disable of device interrupts */ ++extern void sdstd_devintr_on(sdioh_info_t *sd); ++extern void sdstd_devintr_off(sdioh_info_t *sd); ++ ++/* Enable/disable interrupts for local controller events */ ++extern void sdstd_intrs_on(sdioh_info_t *sd, uint16 norm, uint16 err); ++extern void sdstd_intrs_off(sdioh_info_t *sd, uint16 norm, uint16 err); ++ ++/* Wait for specified interrupt and error bits to be set */ ++extern void sdstd_spinbits(sdioh_info_t *sd, uint16 norm, uint16 err); ++ ++ ++/************************************************************** ++ * Internal interfaces: bcmsdstd.c references to per-port code ++ */ ++ ++/* Register mapping routines */ ++extern uint32 *sdstd_reg_map(osl_t *osh, int32 addr, int size); ++extern void sdstd_reg_unmap(osl_t *osh, int32 addr, int size); ++ ++/* Interrupt (de)registration routines */ ++extern int sdstd_register_irq(sdioh_info_t *sd, uint irq); ++extern void sdstd_free_irq(uint irq, sdioh_info_t *sd); ++ ++/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */ ++extern void sdstd_lock(sdioh_info_t *sd); ++extern void sdstd_unlock(sdioh_info_t *sd); ++extern void sdstd_waitlockfree(sdioh_info_t *sd); ++ ++/* OS-specific wait-for-interrupt-or-status */ ++extern int sdstd_waitbits(sdioh_info_t *sd, uint16 norm, uint16 err, bool yield, uint16 *bits); ++ ++/* used by bcmsdstd_linux [implemented in sdstd] */ ++extern void sdstd_3_enable_retuning_int(sdioh_info_t *sd); ++extern void sdstd_3_disable_retuning_int(sdioh_info_t *sd); ++extern bool sdstd_3_is_retuning_int_set(sdioh_info_t *sd); ++extern void sdstd_3_check_and_do_tuning(sdioh_info_t *sd, int tuning_param); ++extern bool sdstd_3_check_and_set_retuning(sdioh_info_t *sd); ++extern int sdstd_3_get_tune_state(sdioh_info_t *sd); ++extern int sdstd_3_get_data_state(sdioh_info_t *sd); ++extern void sdstd_3_set_tune_state(sdioh_info_t *sd, int state); ++extern void sdstd_3_set_data_state(sdioh_info_t *sd, int state); ++extern uint8 sdstd_3_get_tuning_exp(sdioh_info_t *sd); ++extern uint32 sdstd_3_get_uhsi_clkmode(sdioh_info_t *sd); ++extern int sdstd_3_clk_tuning(sdioh_info_t *sd, uint32 sd3ClkMode); ++ ++/* used by sdstd [implemented in bcmsdstd_linux/ndis] */ ++extern void sdstd_3_start_tuning(sdioh_info_t *sd); ++extern void sdstd_3_osinit_tuning(sdioh_info_t *sd); ++extern void sdstd_3_osclean_tuning(sdioh_info_t *sd); ++ ++#endif /* _BCM_SD_STD_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/bcmspi.h b/drivers/net/wireless/bcmdhd/include/bcmspi.h +new file mode 100644 +index 00000000..e226cb10 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmspi.h +@@ -0,0 +1,40 @@ ++/* ++ * Broadcom SPI Low-Level Hardware Driver API ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmspi.h 241182 2011-02-17 21:50:03Z $ ++ */ ++#ifndef _BCM_SPI_H ++#define _BCM_SPI_H ++ ++extern void spi_devintr_off(sdioh_info_t *sd); ++extern void spi_devintr_on(sdioh_info_t *sd); ++extern bool spi_start_clock(sdioh_info_t *sd, uint16 new_sd_divisor); ++extern bool spi_controller_highspeed_mode(sdioh_info_t *sd, bool hsmode); ++extern bool spi_check_client_intr(sdioh_info_t *sd, int *is_dev_intr); ++extern bool spi_hw_attach(sdioh_info_t *sd); ++extern bool spi_hw_detach(sdioh_info_t *sd); ++extern void spi_sendrecv(sdioh_info_t *sd, uint8 *msg_out, uint8 *msg_in, int msglen); ++extern void spi_spinbits(sdioh_info_t *sd); ++extern void spi_waitbits(sdioh_info_t *sd, bool yield); ++ ++#endif /* _BCM_SPI_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/bcmutils.h b/drivers/net/wireless/bcmdhd/include/bcmutils.h +new file mode 100644 +index 00000000..6db5e932 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmutils.h +@@ -0,0 +1,773 @@ ++/* ++ * Misc useful os-independent macros and functions. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmutils.h 347624 2012-07-27 10:49:56Z $ ++ */ ++ ++#ifndef _bcmutils_h_ ++#define _bcmutils_h_ ++ ++#define bcm_strcpy_s(dst, noOfElements, src) strcpy((dst), (src)) ++#define bcm_strncpy_s(dst, noOfElements, src, count) strncpy((dst), (src), (count)) ++#define bcm_strcat_s(dst, noOfElements, src) strcat((dst), (src)) ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#ifdef PKTQ_LOG ++#include ++#endif ++ ++ ++#define _BCM_U 0x01 ++#define _BCM_L 0x02 ++#define _BCM_D 0x04 ++#define _BCM_C 0x08 ++#define _BCM_P 0x10 ++#define _BCM_S 0x20 ++#define _BCM_X 0x40 ++#define _BCM_SP 0x80 ++ ++extern const unsigned char bcm_ctype[]; ++#define bcm_ismask(x) (bcm_ctype[(int)(unsigned char)(x)]) ++ ++#define bcm_isalnum(c) ((bcm_ismask(c)&(_BCM_U|_BCM_L|_BCM_D)) != 0) ++#define bcm_isalpha(c) ((bcm_ismask(c)&(_BCM_U|_BCM_L)) != 0) ++#define bcm_iscntrl(c) ((bcm_ismask(c)&(_BCM_C)) != 0) ++#define bcm_isdigit(c) ((bcm_ismask(c)&(_BCM_D)) != 0) ++#define bcm_isgraph(c) ((bcm_ismask(c)&(_BCM_P|_BCM_U|_BCM_L|_BCM_D)) != 0) ++#define bcm_islower(c) ((bcm_ismask(c)&(_BCM_L)) != 0) ++#define bcm_isprint(c) ((bcm_ismask(c)&(_BCM_P|_BCM_U|_BCM_L|_BCM_D|_BCM_SP)) != 0) ++#define bcm_ispunct(c) ((bcm_ismask(c)&(_BCM_P)) != 0) ++#define bcm_isspace(c) ((bcm_ismask(c)&(_BCM_S)) != 0) ++#define bcm_isupper(c) ((bcm_ismask(c)&(_BCM_U)) != 0) ++#define bcm_isxdigit(c) ((bcm_ismask(c)&(_BCM_D|_BCM_X)) != 0) ++#define bcm_tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c)) ++#define bcm_toupper(c) (bcm_islower((c)) ? ((c) + 'A' - 'a') : (c)) ++ ++ ++ ++struct bcmstrbuf { ++ char *buf; ++ unsigned int size; ++ char *origbuf; ++ unsigned int origsize; ++}; ++ ++ ++#ifdef BCMDRIVER ++#include ++ ++#define GPIO_PIN_NOTDEFINED 0x20 ++ ++ ++#define SPINWAIT(exp, us) { \ ++ uint countdown = (us) + 9; \ ++ while ((exp) && (countdown >= 10)) {\ ++ OSL_DELAY(10); \ ++ countdown -= 10; \ ++ } \ ++} ++ ++ ++#ifndef PKTQ_LEN_DEFAULT ++#define PKTQ_LEN_DEFAULT 128 ++#endif ++#ifndef PKTQ_MAX_PREC ++#define PKTQ_MAX_PREC 16 ++#endif ++ ++typedef struct pktq_prec { ++ void *head; ++ void *tail; ++ uint16 len; ++ uint16 max; ++} pktq_prec_t; ++ ++#ifdef PKTQ_LOG ++typedef struct { ++ uint32 requested; ++ uint32 stored; ++ uint32 saved; ++ uint32 selfsaved; ++ uint32 full_dropped; ++ uint32 dropped; ++ uint32 sacrificed; ++ uint32 busy; ++ uint32 retry; ++ uint32 ps_retry; ++ uint32 retry_drop; ++ uint32 max_avail; ++ uint32 max_used; ++ uint32 queue_capacity; ++} pktq_counters_t; ++#endif ++ ++ ++#define PKTQ_COMMON \ ++ uint16 num_prec; \ ++ uint16 hi_prec; \ ++ uint16 max; \ ++ uint16 len; ++ ++ ++struct pktq { ++ PKTQ_COMMON ++ ++ struct pktq_prec q[PKTQ_MAX_PREC]; ++#ifdef PKTQ_LOG ++ pktq_counters_t _prec_cnt[PKTQ_MAX_PREC]; ++#endif ++}; ++ ++ ++struct spktq { ++ PKTQ_COMMON ++ ++ struct pktq_prec q[1]; ++}; ++ ++#define PKTQ_PREC_ITER(pq, prec) for (prec = (pq)->num_prec - 1; prec >= 0; prec--) ++ ++ ++typedef bool (*ifpkt_cb_t)(void*, int); ++ ++#ifdef BCMPKTPOOL ++#define POOL_ENAB(pool) ((pool) && (pool)->inited) ++#define SHARED_POOL (pktpool_shared) ++#else ++#define POOL_ENAB(bus) 0 ++#define SHARED_POOL ((struct pktpool *)NULL) ++#endif ++ ++#ifndef PKTPOOL_LEN_MAX ++#define PKTPOOL_LEN_MAX 40 ++#endif ++#define PKTPOOL_CB_MAX 3 ++ ++struct pktpool; ++typedef void (*pktpool_cb_t)(struct pktpool *pool, void *arg); ++typedef struct { ++ pktpool_cb_t cb; ++ void *arg; ++} pktpool_cbinfo_t; ++ ++#ifdef BCMDBG_POOL ++ ++#define POOL_IDLE 0 ++#define POOL_RXFILL 1 ++#define POOL_RXDH 2 ++#define POOL_RXD11 3 ++#define POOL_TXDH 4 ++#define POOL_TXD11 5 ++#define POOL_AMPDU 6 ++#define POOL_TXENQ 7 ++ ++typedef struct { ++ void *p; ++ uint32 cycles; ++ uint32 dur; ++} pktpool_dbg_t; ++ ++typedef struct { ++ uint8 txdh; ++ uint8 txd11; ++ uint8 enq; ++ uint8 rxdh; ++ uint8 rxd11; ++ uint8 rxfill; ++ uint8 idle; ++} pktpool_stats_t; ++#endif ++ ++typedef struct pktpool { ++ bool inited; ++ uint16 r; ++ uint16 w; ++ uint16 len; ++ uint16 maxlen; ++ uint16 plen; ++ bool istx; ++ bool empty; ++ uint8 cbtoggle; ++ uint8 cbcnt; ++ uint8 ecbcnt; ++ bool emptycb_disable; ++ pktpool_cbinfo_t *availcb_excl; ++ pktpool_cbinfo_t cbs[PKTPOOL_CB_MAX]; ++ pktpool_cbinfo_t ecbs[PKTPOOL_CB_MAX]; ++ void *q[PKTPOOL_LEN_MAX + 1]; ++ ++#ifdef BCMDBG_POOL ++ uint8 dbg_cbcnt; ++ pktpool_cbinfo_t dbg_cbs[PKTPOOL_CB_MAX]; ++ uint16 dbg_qlen; ++ pktpool_dbg_t dbg_q[PKTPOOL_LEN_MAX + 1]; ++#endif ++} pktpool_t; ++ ++extern pktpool_t *pktpool_shared; ++ ++extern int pktpool_init(osl_t *osh, pktpool_t *pktp, int *pktplen, int plen, bool istx); ++extern int pktpool_deinit(osl_t *osh, pktpool_t *pktp); ++extern int pktpool_fill(osl_t *osh, pktpool_t *pktp, bool minimal); ++extern void* pktpool_get(pktpool_t *pktp); ++extern void pktpool_free(pktpool_t *pktp, void *p); ++extern int pktpool_add(pktpool_t *pktp, void *p); ++extern uint16 pktpool_avail(pktpool_t *pktp); ++extern int pktpool_avail_notify_normal(osl_t *osh, pktpool_t *pktp); ++extern int pktpool_avail_notify_exclusive(osl_t *osh, pktpool_t *pktp, pktpool_cb_t cb); ++extern int pktpool_avail_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); ++extern int pktpool_empty_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); ++extern int pktpool_setmaxlen(pktpool_t *pktp, uint16 maxlen); ++extern int pktpool_setmaxlen_strict(osl_t *osh, pktpool_t *pktp, uint16 maxlen); ++extern void pktpool_emptycb_disable(pktpool_t *pktp, bool disable); ++extern bool pktpool_emptycb_disabled(pktpool_t *pktp); ++ ++#define POOLPTR(pp) ((pktpool_t *)(pp)) ++#define pktpool_len(pp) (POOLPTR(pp)->len - 1) ++#define pktpool_plen(pp) (POOLPTR(pp)->plen) ++#define pktpool_maxlen(pp) (POOLPTR(pp)->maxlen) ++ ++#ifdef BCMDBG_POOL ++extern int pktpool_dbg_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); ++extern int pktpool_start_trigger(pktpool_t *pktp, void *p); ++extern int pktpool_dbg_dump(pktpool_t *pktp); ++extern int pktpool_dbg_notify(pktpool_t *pktp); ++extern int pktpool_stats_dump(pktpool_t *pktp, pktpool_stats_t *stats); ++#endif ++ ++ ++ ++struct ether_addr; ++ ++extern int ether_isbcast(const void *ea); ++extern int ether_isnulladdr(const void *ea); ++ ++ ++ ++#define pktq_psetmax(pq, prec, _max) ((pq)->q[prec].max = (_max)) ++#define pktq_pmax(pq, prec) ((pq)->q[prec].max) ++#define pktq_plen(pq, prec) ((pq)->q[prec].len) ++#define pktq_pavail(pq, prec) ((pq)->q[prec].max - (pq)->q[prec].len) ++#define pktq_pfull(pq, prec) ((pq)->q[prec].len >= (pq)->q[prec].max) ++#define pktq_pempty(pq, prec) ((pq)->q[prec].len == 0) ++ ++#define pktq_ppeek(pq, prec) ((pq)->q[prec].head) ++#define pktq_ppeek_tail(pq, prec) ((pq)->q[prec].tail) ++ ++extern void *pktq_penq(struct pktq *pq, int prec, void *p); ++extern void *pktq_penq_head(struct pktq *pq, int prec, void *p); ++extern void *pktq_pdeq(struct pktq *pq, int prec); ++extern void *pktq_pdeq_prev(struct pktq *pq, int prec, void *prev_p); ++extern void *pktq_pdeq_tail(struct pktq *pq, int prec); ++ ++extern void pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir, ++ ifpkt_cb_t fn, int arg); ++ ++extern bool pktq_pdel(struct pktq *pq, void *p, int prec); ++ ++ ++ ++extern int pktq_mlen(struct pktq *pq, uint prec_bmp); ++extern void *pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out); ++extern void *pktq_mpeek(struct pktq *pq, uint prec_bmp, int *prec_out); ++ ++ ++ ++#define pktq_len(pq) ((int)(pq)->len) ++#define pktq_max(pq) ((int)(pq)->max) ++#define pktq_avail(pq) ((int)((pq)->max - (pq)->len)) ++#define pktq_full(pq) ((pq)->len >= (pq)->max) ++#define pktq_empty(pq) ((pq)->len == 0) ++ ++ ++#define pktenq(pq, p) pktq_penq(((struct pktq *)(void *)pq), 0, (p)) ++#define pktenq_head(pq, p) pktq_penq_head(((struct pktq *)(void *)pq), 0, (p)) ++#define pktdeq(pq) pktq_pdeq(((struct pktq *)(void *)pq), 0) ++#define pktdeq_tail(pq) pktq_pdeq_tail(((struct pktq *)(void *)pq), 0) ++#define pktqinit(pq, len) pktq_init(((struct pktq *)(void *)pq), 1, len) ++ ++extern void pktq_init(struct pktq *pq, int num_prec, int max_len); ++extern void pktq_set_max_plen(struct pktq *pq, int prec, int max_len); ++ ++ ++extern void *pktq_deq(struct pktq *pq, int *prec_out); ++extern void *pktq_deq_tail(struct pktq *pq, int *prec_out); ++extern void *pktq_peek(struct pktq *pq, int *prec_out); ++extern void *pktq_peek_tail(struct pktq *pq, int *prec_out); ++extern void pktq_flush(osl_t *osh, struct pktq *pq, bool dir, ifpkt_cb_t fn, int arg); ++ ++ ++ ++extern uint pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf); ++extern uint pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf); ++extern uint pkttotlen(osl_t *osh, void *p); ++extern void *pktlast(osl_t *osh, void *p); ++extern uint pktsegcnt(osl_t *osh, void *p); ++extern uint pktsegcnt_war(osl_t *osh, void *p); ++extern uint8 *pktoffset(osl_t *osh, void *p, uint offset); ++ ++ ++#define PKTPRIO_VDSCP 0x100 ++#define PKTPRIO_VLAN 0x200 ++#define PKTPRIO_UPD 0x400 ++#define PKTPRIO_DSCP 0x800 ++ ++extern uint pktsetprio(void *pkt, bool update_vtag); ++ ++ ++extern int bcm_atoi(const char *s); ++extern ulong bcm_strtoul(const char *cp, char **endp, uint base); ++extern char *bcmstrstr(const char *haystack, const char *needle); ++extern char *bcmstrcat(char *dest, const char *src); ++extern char *bcmstrncat(char *dest, const char *src, uint size); ++extern ulong wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen); ++char* bcmstrtok(char **string, const char *delimiters, char *tokdelim); ++int bcmstricmp(const char *s1, const char *s2); ++int bcmstrnicmp(const char* s1, const char* s2, int cnt); ++ ++ ++ ++extern char *bcm_ether_ntoa(const struct ether_addr *ea, char *buf); ++extern int bcm_ether_atoe(const char *p, struct ether_addr *ea); ++ ++ ++struct ipv4_addr; ++extern char *bcm_ip_ntoa(struct ipv4_addr *ia, char *buf); ++ ++ ++extern void bcm_mdelay(uint ms); ++ ++#define NVRAM_RECLAIM_CHECK(name) ++ ++extern char *getvar(char *vars, const char *name); ++extern int getintvar(char *vars, const char *name); ++extern int getintvararray(char *vars, const char *name, int index); ++extern int getintvararraysize(char *vars, const char *name); ++extern uint getgpiopin(char *vars, char *pin_name, uint def_pin); ++#define bcm_perf_enable() ++#define bcmstats(fmt) ++#define bcmlog(fmt, a1, a2) ++#define bcmdumplog(buf, size) *buf = '\0' ++#define bcmdumplogent(buf, idx) -1 ++ ++#define bcmtslog(tstamp, fmt, a1, a2) ++#define bcmprinttslogs() ++#define bcmprinttstamp(us) ++#define bcmdumptslog(buf, size) ++ ++extern char *bcm_nvram_vars(uint *length); ++extern int bcm_nvram_cache(void *sih); ++ ++ ++ ++ ++typedef struct bcm_iovar { ++ const char *name; ++ uint16 varid; ++ uint16 flags; ++ uint16 type; ++ uint16 minlen; ++} bcm_iovar_t; ++ ++ ++ ++ ++#define IOV_GET 0 ++#define IOV_SET 1 ++ ++ ++#define IOV_GVAL(id) ((id) * 2) ++#define IOV_SVAL(id) ((id) * 2 + IOV_SET) ++#define IOV_ISSET(actionid) ((actionid & IOV_SET) == IOV_SET) ++#define IOV_ID(actionid) (actionid >> 1) ++ ++ ++ ++extern const bcm_iovar_t *bcm_iovar_lookup(const bcm_iovar_t *table, const char *name); ++extern int bcm_iovar_lencheck(const bcm_iovar_t *table, void *arg, int len, bool set); ++#if defined(WLTINYDUMP) || defined(WLMSG_INFORM) || defined(WLMSG_ASSOC) || \ ++ defined(WLMSG_PRPKT) || defined(WLMSG_WSEC) ++extern int bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len); ++#endif ++#endif ++ ++ ++#define IOVT_VOID 0 ++#define IOVT_BOOL 1 ++#define IOVT_INT8 2 ++#define IOVT_UINT8 3 ++#define IOVT_INT16 4 ++#define IOVT_UINT16 5 ++#define IOVT_INT32 6 ++#define IOVT_UINT32 7 ++#define IOVT_BUFFER 8 ++#define BCM_IOVT_VALID(type) (((unsigned int)(type)) <= IOVT_BUFFER) ++ ++ ++#define BCM_IOV_TYPE_INIT { \ ++ "void", \ ++ "bool", \ ++ "int8", \ ++ "uint8", \ ++ "int16", \ ++ "uint16", \ ++ "int32", \ ++ "uint32", \ ++ "buffer", \ ++ "" } ++ ++#define BCM_IOVT_IS_INT(type) (\ ++ (type == IOVT_BOOL) || \ ++ (type == IOVT_INT8) || \ ++ (type == IOVT_UINT8) || \ ++ (type == IOVT_INT16) || \ ++ (type == IOVT_UINT16) || \ ++ (type == IOVT_INT32) || \ ++ (type == IOVT_UINT32)) ++ ++ ++ ++#define BCME_STRLEN 64 ++#define VALID_BCMERROR(e) ((e <= 0) && (e >= BCME_LAST)) ++ ++ ++ ++ ++#define BCME_OK 0 ++#define BCME_ERROR -1 ++#define BCME_BADARG -2 ++#define BCME_BADOPTION -3 ++#define BCME_NOTUP -4 ++#define BCME_NOTDOWN -5 ++#define BCME_NOTAP -6 ++#define BCME_NOTSTA -7 ++#define BCME_BADKEYIDX -8 ++#define BCME_RADIOOFF -9 ++#define BCME_NOTBANDLOCKED -10 ++#define BCME_NOCLK -11 ++#define BCME_BADRATESET -12 ++#define BCME_BADBAND -13 ++#define BCME_BUFTOOSHORT -14 ++#define BCME_BUFTOOLONG -15 ++#define BCME_BUSY -16 ++#define BCME_NOTASSOCIATED -17 ++#define BCME_BADSSIDLEN -18 ++#define BCME_OUTOFRANGECHAN -19 ++#define BCME_BADCHAN -20 ++#define BCME_BADADDR -21 ++#define BCME_NORESOURCE -22 ++#define BCME_UNSUPPORTED -23 ++#define BCME_BADLEN -24 ++#define BCME_NOTREADY -25 ++#define BCME_EPERM -26 ++#define BCME_NOMEM -27 ++#define BCME_ASSOCIATED -28 ++#define BCME_RANGE -29 ++#define BCME_NOTFOUND -30 ++#define BCME_WME_NOT_ENABLED -31 ++#define BCME_TSPEC_NOTFOUND -32 ++#define BCME_ACM_NOTSUPPORTED -33 ++#define BCME_NOT_WME_ASSOCIATION -34 ++#define BCME_SDIO_ERROR -35 ++#define BCME_DONGLE_DOWN -36 ++#define BCME_VERSION -37 ++#define BCME_TXFAIL -38 ++#define BCME_RXFAIL -39 ++#define BCME_NODEVICE -40 ++#define BCME_NMODE_DISABLED -41 ++#define BCME_NONRESIDENT -42 ++#define BCME_LAST BCME_NONRESIDENT ++ ++ ++#define BCMERRSTRINGTABLE { \ ++ "OK", \ ++ "Undefined error", \ ++ "Bad Argument", \ ++ "Bad Option", \ ++ "Not up", \ ++ "Not down", \ ++ "Not AP", \ ++ "Not STA", \ ++ "Bad Key Index", \ ++ "Radio Off", \ ++ "Not band locked", \ ++ "No clock", \ ++ "Bad Rate valueset", \ ++ "Bad Band", \ ++ "Buffer too short", \ ++ "Buffer too long", \ ++ "Busy", \ ++ "Not Associated", \ ++ "Bad SSID len", \ ++ "Out of Range Channel", \ ++ "Bad Channel", \ ++ "Bad Address", \ ++ "Not Enough Resources", \ ++ "Unsupported", \ ++ "Bad length", \ ++ "Not Ready", \ ++ "Not Permitted", \ ++ "No Memory", \ ++ "Associated", \ ++ "Not In Range", \ ++ "Not Found", \ ++ "WME Not Enabled", \ ++ "TSPEC Not Found", \ ++ "ACM Not Supported", \ ++ "Not WME Association", \ ++ "SDIO Bus Error", \ ++ "Dongle Not Accessible", \ ++ "Incorrect version", \ ++ "TX Failure", \ ++ "RX Failure", \ ++ "Device Not Present", \ ++ "NMODE Disabled", \ ++ "Nonresident overlay access", \ ++} ++ ++#ifndef ABS ++#define ABS(a) (((a) < 0) ? -(a) : (a)) ++#endif ++ ++#ifndef MIN ++#define MIN(a, b) (((a) < (b)) ? (a) : (b)) ++#endif ++ ++#ifndef MAX ++#define MAX(a, b) (((a) > (b)) ? (a) : (b)) ++#endif ++ ++#define CEIL(x, y) (((x) + ((y) - 1)) / (y)) ++#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) ++#define ISALIGNED(a, x) (((uintptr)(a) & ((x) - 1)) == 0) ++#define ALIGN_ADDR(addr, boundary) (void *)(((uintptr)(addr) + (boundary) - 1) \ ++ & ~((boundary) - 1)) ++#define ALIGN_SIZE(size, boundary) (((size) + (boundary) - 1) \ ++ & ~((boundary) - 1)) ++#define ISPOWEROF2(x) ((((x) - 1) & (x)) == 0) ++#define VALID_MASK(mask) !((mask) & ((mask) + 1)) ++ ++#ifndef OFFSETOF ++#ifdef __ARMCC_VERSION ++ ++#include ++#define OFFSETOF(type, member) offsetof(type, member) ++#else ++#define OFFSETOF(type, member) ((uint)(uintptr)&((type *)0)->member) ++#endif ++#endif ++ ++#ifndef ARRAYSIZE ++#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0])) ++#endif ++ ++ ++extern void *_bcmutils_dummy_fn; ++#define REFERENCE_FUNCTION(f) (_bcmutils_dummy_fn = (void *)(f)) ++ ++ ++#ifndef setbit ++#ifndef NBBY ++#define NBBY 8 ++#endif ++#define setbit(a, i) (((uint8 *)a)[(i) / NBBY] |= 1 << ((i) % NBBY)) ++#define clrbit(a, i) (((uint8 *)a)[(i) / NBBY] &= ~(1 << ((i) % NBBY))) ++#define isset(a, i) (((const uint8 *)a)[(i) / NBBY] & (1 << ((i) % NBBY))) ++#define isclr(a, i) ((((const uint8 *)a)[(i) / NBBY] & (1 << ((i) % NBBY))) == 0) ++#endif ++ ++#define NBITS(type) (sizeof(type) * 8) ++#define NBITVAL(nbits) (1 << (nbits)) ++#define MAXBITVAL(nbits) ((1 << (nbits)) - 1) ++#define NBITMASK(nbits) MAXBITVAL(nbits) ++#define MAXNBVAL(nbyte) MAXBITVAL((nbyte) * 8) ++ ++ ++#define MUX(pred, true, false) ((pred) ? (true) : (false)) ++ ++ ++#define MODDEC(x, bound) MUX((x) == 0, (bound) - 1, (x) - 1) ++#define MODINC(x, bound) MUX((x) == (bound) - 1, 0, (x) + 1) ++ ++ ++#define MODDEC_POW2(x, bound) (((x) - 1) & ((bound) - 1)) ++#define MODINC_POW2(x, bound) (((x) + 1) & ((bound) - 1)) ++ ++ ++#define MODADD(x, y, bound) \ ++ MUX((x) + (y) >= (bound), (x) + (y) - (bound), (x) + (y)) ++#define MODSUB(x, y, bound) \ ++ MUX(((int)(x)) - ((int)(y)) < 0, (x) - (y) + (bound), (x) - (y)) ++ ++ ++#define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1)) ++#define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1)) ++ ++ ++#define CRC8_INIT_VALUE 0xff ++#define CRC8_GOOD_VALUE 0x9f ++#define CRC16_INIT_VALUE 0xffff ++#define CRC16_GOOD_VALUE 0xf0b8 ++#define CRC32_INIT_VALUE 0xffffffff ++#define CRC32_GOOD_VALUE 0xdebb20e3 ++ ++ ++#define MACF "%02x:%02x:%02x:%02x:%02x:%02x" ++#define ETHERP_TO_MACF(ea) ((struct ether_addr *) (ea))->octet[0], \ ++ ((struct ether_addr *) (ea))->octet[1], \ ++ ((struct ether_addr *) (ea))->octet[2], \ ++ ((struct ether_addr *) (ea))->octet[3], \ ++ ((struct ether_addr *) (ea))->octet[4], \ ++ ((struct ether_addr *) (ea))->octet[5] ++ ++#define ETHER_TO_MACF(ea) (ea).octet[0], \ ++ (ea).octet[1], \ ++ (ea).octet[2], \ ++ (ea).octet[3], \ ++ (ea).octet[4], \ ++ (ea).octet[5] ++#if !defined(SIMPLE_MAC_PRINT) ++#define MACDBG "%02x:%02x:%02x:%02x:%02x:%02x" ++#define MAC2STRDBG(ea) (ea)[0], (ea)[1], (ea)[2], (ea)[3], (ea)[4], (ea)[5] ++#else ++#define MACDBG "%02x:%02x:%02x" ++#define MAC2STRDBG(ea) (ea)[0], (ea)[4], (ea)[5] ++#endif /* SIMPLE_MAC_PRINT */ ++ ++typedef struct bcm_bit_desc { ++ uint32 bit; ++ const char* name; ++} bcm_bit_desc_t; ++ ++ ++typedef struct bcm_tlv { ++ uint8 id; ++ uint8 len; ++ uint8 data[1]; ++} bcm_tlv_t; ++ ++ ++#define bcm_valid_tlv(elt, buflen) ((buflen) >= 2 && (int)(buflen) >= (int)(2 + (elt)->len)) ++ ++ ++#define ETHER_ADDR_STR_LEN 18 ++ ++ ++ ++static INLINE void ++xor_128bit_block(const uint8 *src1, const uint8 *src2, uint8 *dst) ++{ ++ if ( ++#ifdef __i386__ ++ 1 || ++#endif ++ (((uintptr)src1 | (uintptr)src2 | (uintptr)dst) & 3) == 0) { ++ ++ ++ ((uint32 *)dst)[0] = ((const uint32 *)src1)[0] ^ ((const uint32 *)src2)[0]; ++ ((uint32 *)dst)[1] = ((const uint32 *)src1)[1] ^ ((const uint32 *)src2)[1]; ++ ((uint32 *)dst)[2] = ((const uint32 *)src1)[2] ^ ((const uint32 *)src2)[2]; ++ ((uint32 *)dst)[3] = ((const uint32 *)src1)[3] ^ ((const uint32 *)src2)[3]; ++ } else { ++ ++ int k; ++ for (k = 0; k < 16; k++) ++ dst[k] = src1[k] ^ src2[k]; ++ } ++} ++ ++ ++ ++extern uint8 hndcrc8(uint8 *p, uint nbytes, uint8 crc); ++extern uint16 hndcrc16(uint8 *p, uint nbytes, uint16 crc); ++extern uint32 hndcrc32(uint8 *p, uint nbytes, uint32 crc); ++ ++ ++#if defined(DHD_DEBUG) || defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || \ ++ defined(WLMSG_ASSOC) ++extern int bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char* buf, int len); ++#endif ++ ++#if defined(DHD_DEBUG) || defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || \ ++ defined(WLMSG_ASSOC) || defined(WLMEDIA_PEAKRATE) ++extern int bcm_format_hex(char *str, const void *bytes, int len); ++#endif ++ ++extern const char *bcm_crypto_algo_name(uint algo); ++extern char *bcm_chipname(uint chipid, char *buf, uint len); ++extern char *bcm_brev_str(uint32 brev, char *buf); ++extern void printbig(char *buf); ++extern void prhex(const char *msg, uchar *buf, uint len); ++ ++ ++extern bcm_tlv_t *bcm_next_tlv(bcm_tlv_t *elt, int *buflen); ++extern bcm_tlv_t *bcm_parse_tlvs(void *buf, int buflen, uint key); ++extern bcm_tlv_t *bcm_parse_ordered_tlvs(void *buf, int buflen, uint key); ++ ++ ++extern const char *bcmerrorstr(int bcmerror); ++extern bcm_tlv_t *bcm_parse_tlvs(void *buf, int buflen, uint key); ++ ++ ++typedef uint32 mbool; ++#define mboolset(mb, bit) ((mb) |= (bit)) ++#define mboolclr(mb, bit) ((mb) &= ~(bit)) ++#define mboolisset(mb, bit) (((mb) & (bit)) != 0) ++#define mboolmaskset(mb, mask, val) ((mb) = (((mb) & ~(mask)) | (val))) ++ ++ ++struct fielddesc { ++ const char *nameandfmt; ++ uint32 offset; ++ uint32 len; ++}; ++ ++extern void bcm_binit(struct bcmstrbuf *b, char *buf, uint size); ++extern void bcm_bprhex(struct bcmstrbuf *b, const char *msg, bool newline, uint8 *buf, int len); ++ ++extern void bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount); ++extern int bcm_cmp_bytes(const uchar *arg1, const uchar *arg2, uint8 nbytes); ++extern void bcm_print_bytes(const char *name, const uchar *cdata, int len); ++ ++typedef uint32 (*bcmutl_rdreg_rtn)(void *arg0, uint arg1, uint32 offset); ++extern uint bcmdumpfields(bcmutl_rdreg_rtn func_ptr, void *arg0, uint arg1, struct fielddesc *str, ++ char *buf, uint32 bufsize); ++extern uint bcm_bitcount(uint8 *bitmap, uint bytelength); ++ ++extern int bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...); ++ ++ ++extern uint16 bcm_qdbm_to_mw(uint8 qdbm); ++extern uint8 bcm_mw_to_qdbm(uint16 mw); ++extern uint bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint len); ++ ++unsigned int process_nvram_vars(char *varbuf, unsigned int len); ++ ++#ifdef __cplusplus ++ } ++#endif ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/bcmwifi_channels.h b/drivers/net/wireless/bcmdhd/include/bcmwifi_channels.h +new file mode 100644 +index 00000000..bc57aca2 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmwifi_channels.h +@@ -0,0 +1,490 @@ ++/* ++ * Misc utility routines for WL and Apps ++ * This header file housing the define and function prototype use by ++ * both the wl driver, tools & Apps. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmwifi_channels.h 309193 2012-01-19 00:03:57Z $ ++ */ ++ ++#ifndef _bcmwifi_channels_h_ ++#define _bcmwifi_channels_h_ ++ ++ ++/* A chanspec holds the channel number, band, bandwidth and control sideband */ ++typedef uint16 chanspec_t; ++ ++/* channel defines */ ++#define CH_UPPER_SB 0x01 ++#define CH_LOWER_SB 0x02 ++#define CH_EWA_VALID 0x04 ++#define CH_80MHZ_APART 16 ++#define CH_40MHZ_APART 8 ++#define CH_20MHZ_APART 4 ++#define CH_10MHZ_APART 2 ++#define CH_5MHZ_APART 1 /* 2G band channels are 5 Mhz apart */ ++#define CH_MAX_2G_CHANNEL 14 /* Max channel in 2G band */ ++#define MAXCHANNEL 224 /* max # supported channels. The max channel no is 216, ++ * this is that + 1 rounded up to a multiple of NBBY (8). ++ * DO NOT MAKE it > 255: channels are uint8's all over ++ */ ++#define CHSPEC_CTLOVLP(sp1, sp2, sep) ABS(wf_chspec_ctlchan(sp1) - wf_chspec_ctlchan(sp2)) < (sep) ++ ++/* All builds use the new 11ac ratespec/chanspec */ ++#undef D11AC_IOTYPES ++#define D11AC_IOTYPES ++ ++#ifndef D11AC_IOTYPES ++ ++#define WL_CHANSPEC_CHAN_MASK 0x00ff ++#define WL_CHANSPEC_CHAN_SHIFT 0 ++ ++#define WL_CHANSPEC_CTL_SB_MASK 0x0300 ++#define WL_CHANSPEC_CTL_SB_SHIFT 8 ++#define WL_CHANSPEC_CTL_SB_LOWER 0x0100 ++#define WL_CHANSPEC_CTL_SB_UPPER 0x0200 ++#define WL_CHANSPEC_CTL_SB_NONE 0x0300 ++ ++#define WL_CHANSPEC_BW_MASK 0x0C00 ++#define WL_CHANSPEC_BW_SHIFT 10 ++#define WL_CHANSPEC_BW_10 0x0400 ++#define WL_CHANSPEC_BW_20 0x0800 ++#define WL_CHANSPEC_BW_40 0x0C00 ++ ++#define WL_CHANSPEC_BAND_MASK 0xf000 ++#define WL_CHANSPEC_BAND_SHIFT 12 ++#ifdef WL_CHANSPEC_BAND_5G ++#undef WL_CHANSPEC_BAND_5G ++#endif ++#ifdef WL_CHANSPEC_BAND_2G ++#undef WL_CHANSPEC_BAND_2G ++#endif ++#define WL_CHANSPEC_BAND_5G 0x1000 ++#define WL_CHANSPEC_BAND_2G 0x2000 ++#define INVCHANSPEC 255 ++ ++/* channel defines */ ++#define LOWER_20_SB(channel) (((channel) > CH_10MHZ_APART) ? ((channel) - CH_10MHZ_APART) : 0) ++#define UPPER_20_SB(channel) (((channel) < (MAXCHANNEL - CH_10MHZ_APART)) ? \ ++ ((channel) + CH_10MHZ_APART) : 0) ++#define CHSPEC_WLCBANDUNIT(chspec) (CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX) ++#define CH20MHZ_CHSPEC(channel) (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_20 | \ ++ WL_CHANSPEC_CTL_SB_NONE | (((channel) <= CH_MAX_2G_CHANNEL) ? \ ++ WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G)) ++#define NEXT_20MHZ_CHAN(channel) (((channel) < (MAXCHANNEL - CH_20MHZ_APART)) ? \ ++ ((channel) + CH_20MHZ_APART) : 0) ++#define CH40MHZ_CHSPEC(channel, ctlsb) (chanspec_t) \ ++ ((channel) | (ctlsb) | WL_CHANSPEC_BW_40 | \ ++ ((channel) <= CH_MAX_2G_CHANNEL ? WL_CHANSPEC_BAND_2G : \ ++ WL_CHANSPEC_BAND_5G)) ++#define CHSPEC_CHANNEL(chspec) ((uint8)((chspec) & WL_CHANSPEC_CHAN_MASK)) ++#define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK) ++ ++/* chanspec stores radio channel & flags to indicate control channel location, i.e. upper/lower */ ++#define CHSPEC_CTL_SB(chspec) ((chspec) & WL_CHANSPEC_CTL_SB_MASK) ++#define CHSPEC_BW(chspec) ((chspec) & WL_CHANSPEC_BW_MASK) ++ ++#ifdef WL11N_20MHZONLY ++ ++#define CHSPEC_IS10(chspec) 0 ++#define CHSPEC_IS20(chspec) 1 ++#ifndef CHSPEC_IS40 ++#define CHSPEC_IS40(chspec) 0 ++#endif ++ ++#else /* !WL11N_20MHZONLY */ ++ ++#define CHSPEC_IS10(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10) ++#define CHSPEC_IS20(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20) ++#ifndef CHSPEC_IS40 ++#define CHSPEC_IS40(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40) ++#endif ++ ++#endif /* !WL11N_20MHZONLY */ ++ ++#define CHSPEC_IS5G(chspec) (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G) ++#define CHSPEC_IS2G(chspec) (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G) ++#define CHSPEC_SB_NONE(chspec) (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE) ++#define CHSPEC_SB_UPPER(chspec) (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER) ++#define CHSPEC_SB_LOWER(chspec) (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER) ++#define CHSPEC_CTL_CHAN(chspec) ((CHSPEC_SB_LOWER(chspec)) ? \ ++ (LOWER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \ ++ (UPPER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK)))) ++#define CHSPEC2WLC_BAND(chspec) (CHSPEC_IS5G(chspec) ? WLC_BAND_5G : WLC_BAND_2G) ++ ++#define CHANSPEC_STR_LEN 8 ++ ++#else /* D11AC_IOTYPES */ ++ ++#define WL_CHANSPEC_CHAN_MASK 0x00ff ++#define WL_CHANSPEC_CHAN_SHIFT 0 ++#define WL_CHANSPEC_CHAN1_MASK 0x000f ++#define WL_CHANSPEC_CHAN1_SHIFT 0 ++#define WL_CHANSPEC_CHAN2_MASK 0x00f0 ++#define WL_CHANSPEC_CHAN2_SHIFT 4 ++ ++#define WL_CHANSPEC_CTL_SB_MASK 0x0700 ++#define WL_CHANSPEC_CTL_SB_SHIFT 8 ++#define WL_CHANSPEC_CTL_SB_LLL 0x0000 ++#define WL_CHANSPEC_CTL_SB_LLU 0x0100 ++#define WL_CHANSPEC_CTL_SB_LUL 0x0200 ++#define WL_CHANSPEC_CTL_SB_LUU 0x0300 ++#define WL_CHANSPEC_CTL_SB_ULL 0x0400 ++#define WL_CHANSPEC_CTL_SB_ULU 0x0500 ++#define WL_CHANSPEC_CTL_SB_UUL 0x0600 ++#define WL_CHANSPEC_CTL_SB_UUU 0x0700 ++#define WL_CHANSPEC_CTL_SB_LL WL_CHANSPEC_CTL_SB_LLL ++#define WL_CHANSPEC_CTL_SB_LU WL_CHANSPEC_CTL_SB_LLU ++#define WL_CHANSPEC_CTL_SB_UL WL_CHANSPEC_CTL_SB_LUL ++#define WL_CHANSPEC_CTL_SB_UU WL_CHANSPEC_CTL_SB_LUU ++#define WL_CHANSPEC_CTL_SB_L WL_CHANSPEC_CTL_SB_LLL ++#define WL_CHANSPEC_CTL_SB_U WL_CHANSPEC_CTL_SB_LLU ++#define WL_CHANSPEC_CTL_SB_LOWER WL_CHANSPEC_CTL_SB_LLL ++#define WL_CHANSPEC_CTL_SB_UPPER WL_CHANSPEC_CTL_SB_LLU ++ ++#define WL_CHANSPEC_BW_MASK 0x3800 ++#define WL_CHANSPEC_BW_SHIFT 11 ++#define WL_CHANSPEC_BW_5 0x0000 ++#define WL_CHANSPEC_BW_10 0x0800 ++#define WL_CHANSPEC_BW_20 0x1000 ++#define WL_CHANSPEC_BW_40 0x1800 ++#define WL_CHANSPEC_BW_80 0x2000 ++#define WL_CHANSPEC_BW_160 0x2800 ++#define WL_CHANSPEC_BW_8080 0x3000 ++ ++#define WL_CHANSPEC_BAND_MASK 0xc000 ++#define WL_CHANSPEC_BAND_SHIFT 14 ++#define WL_CHANSPEC_BAND_2G 0x0000 ++#define WL_CHANSPEC_BAND_3G 0x4000 ++#define WL_CHANSPEC_BAND_4G 0x8000 ++#define WL_CHANSPEC_BAND_5G 0xc000 ++#define INVCHANSPEC 255 ++ ++/* channel defines */ ++#define LOWER_20_SB(channel) (((channel) > CH_10MHZ_APART) ? \ ++ ((channel) - CH_10MHZ_APART) : 0) ++#define UPPER_20_SB(channel) (((channel) < (MAXCHANNEL - CH_10MHZ_APART)) ? \ ++ ((channel) + CH_10MHZ_APART) : 0) ++#define LOWER_40_SB(channel) ((channel) - CH_20MHZ_APART) ++#define UPPER_40_SB(channel) ((channel) + CH_20MHZ_APART) ++#define CHSPEC_WLCBANDUNIT(chspec) (CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX) ++#define CH20MHZ_CHSPEC(channel) (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_20 | \ ++ (((channel) <= CH_MAX_2G_CHANNEL) ? \ ++ WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G)) ++#define NEXT_20MHZ_CHAN(channel) (((channel) < (MAXCHANNEL - CH_20MHZ_APART)) ? \ ++ ((channel) + CH_20MHZ_APART) : 0) ++#define CH40MHZ_CHSPEC(channel, ctlsb) (chanspec_t) \ ++ ((channel) | (ctlsb) | WL_CHANSPEC_BW_40 | \ ++ ((channel) <= CH_MAX_2G_CHANNEL ? WL_CHANSPEC_BAND_2G : \ ++ WL_CHANSPEC_BAND_5G)) ++#define CH80MHZ_CHSPEC(channel, ctlsb) (chanspec_t) \ ++ ((channel) | (ctlsb) | \ ++ WL_CHANSPEC_BW_80 | WL_CHANSPEC_BAND_5G) ++#define CH160MHZ_CHSPEC(channel, ctlsb) (chanspec_t) \ ++ ((channel) | (ctlsb) | \ ++ WL_CHANSPEC_BW_160 | WL_CHANSPEC_BAND_5G) ++ ++/* simple MACROs to get different fields of chanspec */ ++#define CHSPEC_CHANNEL(chspec) ((uint8)((chspec) & WL_CHANSPEC_CHAN_MASK)) ++#define CHSPEC_CHAN1(chspec) ((chspec) & WL_CHANSPEC_CHAN1_MASK) ++#define CHSPEC_CHAN2(chspec) ((chspec) & WL_CHANSPEC_CHAN2_MASK) ++#define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK) ++#define CHSPEC_CTL_SB(chspec) ((chspec) & WL_CHANSPEC_CTL_SB_MASK) ++#define CHSPEC_BW(chspec) ((chspec) & WL_CHANSPEC_BW_MASK) ++ ++#ifdef WL11N_20MHZONLY ++ ++#define CHSPEC_IS10(chspec) 0 ++#define CHSPEC_IS20(chspec) 1 ++#ifndef CHSPEC_IS40 ++#define CHSPEC_IS40(chspec) 0 ++#endif ++#ifndef CHSPEC_IS80 ++#define CHSPEC_IS80(chspec) 0 ++#endif ++#ifndef CHSPEC_IS160 ++#define CHSPEC_IS160(chspec) 0 ++#endif ++#ifndef CHSPEC_IS8080 ++#define CHSPEC_IS8080(chspec) 0 ++#endif ++ ++#else /* !WL11N_20MHZONLY */ ++ ++#define CHSPEC_IS10(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10) ++#define CHSPEC_IS20(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20) ++#ifndef CHSPEC_IS40 ++#define CHSPEC_IS40(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40) ++#endif ++#ifndef CHSPEC_IS80 ++#define CHSPEC_IS80(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_80) ++#endif ++#ifndef CHSPEC_IS160 ++#define CHSPEC_IS160(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_160) ++#endif ++#ifndef CHSPEC_IS8080 ++#define CHSPEC_IS8080(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_8080) ++#endif ++ ++#endif /* !WL11N_20MHZONLY */ ++ ++#define CHSPEC_IS5G(chspec) (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G) ++#define CHSPEC_IS2G(chspec) (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G) ++#define CHSPEC_SB_UPPER(chspec) \ ++ ((((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER) && \ ++ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)) ++#define CHSPEC_SB_LOWER(chspec) \ ++ ((((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER) && \ ++ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)) ++#define CHSPEC2WLC_BAND(chspec) (CHSPEC_IS5G(chspec) ? WLC_BAND_5G : WLC_BAND_2G) ++ ++/** ++ * Number of chars needed for wf_chspec_ntoa() destination character buffer. ++ */ ++#define CHANSPEC_STR_LEN 20 ++ ++ ++/* Legacy Chanspec defines ++ * These are the defines for the previous format of the chanspec_t ++ */ ++#define WL_LCHANSPEC_CHAN_MASK 0x00ff ++#define WL_LCHANSPEC_CHAN_SHIFT 0 ++ ++#define WL_LCHANSPEC_CTL_SB_MASK 0x0300 ++#define WL_LCHANSPEC_CTL_SB_SHIFT 8 ++#define WL_LCHANSPEC_CTL_SB_LOWER 0x0100 ++#define WL_LCHANSPEC_CTL_SB_UPPER 0x0200 ++#define WL_LCHANSPEC_CTL_SB_NONE 0x0300 ++ ++#define WL_LCHANSPEC_BW_MASK 0x0C00 ++#define WL_LCHANSPEC_BW_SHIFT 10 ++#define WL_LCHANSPEC_BW_10 0x0400 ++#define WL_LCHANSPEC_BW_20 0x0800 ++#define WL_LCHANSPEC_BW_40 0x0C00 ++ ++#define WL_LCHANSPEC_BAND_MASK 0xf000 ++#define WL_LCHANSPEC_BAND_SHIFT 12 ++#define WL_LCHANSPEC_BAND_5G 0x1000 ++#define WL_LCHANSPEC_BAND_2G 0x2000 ++ ++#define LCHSPEC_CHANNEL(chspec) ((uint8)((chspec) & WL_LCHANSPEC_CHAN_MASK)) ++#define LCHSPEC_BAND(chspec) ((chspec) & WL_LCHANSPEC_BAND_MASK) ++#define LCHSPEC_CTL_SB(chspec) ((chspec) & WL_LCHANSPEC_CTL_SB_MASK) ++#define LCHSPEC_BW(chspec) ((chspec) & WL_LCHANSPEC_BW_MASK) ++#define LCHSPEC_IS10(chspec) (((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_10) ++#define LCHSPEC_IS20(chspec) (((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_20) ++#define LCHSPEC_IS40(chspec) (((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_40) ++#define LCHSPEC_IS5G(chspec) (((chspec) & WL_LCHANSPEC_BAND_MASK) == WL_LCHANSPEC_BAND_5G) ++#define LCHSPEC_IS2G(chspec) (((chspec) & WL_LCHANSPEC_BAND_MASK) == WL_LCHANSPEC_BAND_2G) ++ ++#define LCHSPEC_CREATE(chan, band, bw, sb) ((uint16)((chan) | (sb) | (bw) | (band))) ++ ++#endif /* D11AC_IOTYPES */ ++ ++/* ++ * WF_CHAN_FACTOR_* constants are used to calculate channel frequency ++ * given a channel number. ++ * chan_freq = chan_factor * 500Mhz + chan_number * 5 ++ */ ++ ++/** ++ * Channel Factor for the starting frequence of 2.4 GHz channels. ++ * The value corresponds to 2407 MHz. ++ */ ++#define WF_CHAN_FACTOR_2_4_G 4814 /* 2.4 GHz band, 2407 MHz */ ++ ++/** ++ * Channel Factor for the starting frequence of 5 GHz channels. ++ * The value corresponds to 5000 MHz. ++ */ ++#define WF_CHAN_FACTOR_5_G 10000 /* 5 GHz band, 5000 MHz */ ++ ++/** ++ * Channel Factor for the starting frequence of 4.9 GHz channels. ++ * The value corresponds to 4000 MHz. ++ */ ++#define WF_CHAN_FACTOR_4_G 8000 /* 4.9 GHz band for Japan */ ++ ++/* defined rate in 500kbps */ ++#define WLC_MAXRATE 108 /* in 500kbps units */ ++#define WLC_RATE_1M 2 /* in 500kbps units */ ++#define WLC_RATE_2M 4 /* in 500kbps units */ ++#define WLC_RATE_5M5 11 /* in 500kbps units */ ++#define WLC_RATE_11M 22 /* in 500kbps units */ ++#define WLC_RATE_6M 12 /* in 500kbps units */ ++#define WLC_RATE_9M 18 /* in 500kbps units */ ++#define WLC_RATE_12M 24 /* in 500kbps units */ ++#define WLC_RATE_18M 36 /* in 500kbps units */ ++#define WLC_RATE_24M 48 /* in 500kbps units */ ++#define WLC_RATE_36M 72 /* in 500kbps units */ ++#define WLC_RATE_48M 96 /* in 500kbps units */ ++#define WLC_RATE_54M 108 /* in 500kbps units */ ++ ++#define WLC_2G_25MHZ_OFFSET 5 /* 2.4GHz band channel offset */ ++ ++/** ++ * Convert chanspec to ascii string ++ * ++ * @param chspec chanspec format ++ * @param buf ascii string of chanspec ++ * ++ * @return pointer to buf with room for at least CHANSPEC_STR_LEN bytes ++ * ++ * @see CHANSPEC_STR_LEN ++ */ ++extern char * wf_chspec_ntoa(chanspec_t chspec, char *buf); ++ ++/** ++ * Convert ascii string to chanspec ++ * ++ * @param a pointer to input string ++ * ++ * @return >= 0 if successful or 0 otherwise ++ */ ++extern chanspec_t wf_chspec_aton(const char *a); ++ ++/** ++ * Verify the chanspec fields are valid. ++ * ++ * Verify the chanspec is using a legal set field values, i.e. that the chanspec ++ * specified a band, bw, ctl_sb and channel and that the combination could be ++ * legal given some set of circumstances. ++ * ++ * @param chanspec input chanspec to verify ++ * ++ * @return TRUE if the chanspec is malformed, FALSE if it looks good. ++ */ ++extern bool wf_chspec_malformed(chanspec_t chanspec); ++ ++/** ++ * Verify the chanspec specifies a valid channel according to 802.11. ++ * ++ * @param chanspec input chanspec to verify ++ * ++ * @return TRUE if the chanspec is a valid 802.11 channel ++ */ ++extern bool wf_chspec_valid(chanspec_t chanspec); ++ ++/** ++ * Return the primary (control) channel. ++ * ++ * This function returns the channel number of the primary 20MHz channel. For ++ * 20MHz channels this is just the channel number. For 40MHz or wider channels ++ * it is the primary 20MHz channel specified by the chanspec. ++ * ++ * @param chspec input chanspec ++ * ++ * @return Returns the channel number of the primary 20MHz channel ++ */ ++extern uint8 wf_chspec_ctlchan(chanspec_t chspec); ++ ++/** ++ * Return the primary (control) chanspec. ++ * ++ * This function returns the chanspec of the primary 20MHz channel. For 20MHz ++ * channels this is just the chanspec. For 40MHz or wider channels it is the ++ * chanspec of the primary 20MHZ channel specified by the chanspec. ++ * ++ * @param chspec input chanspec ++ * ++ * @return Returns the chanspec of the primary 20MHz channel ++ */ ++extern chanspec_t wf_chspec_ctlchspec(chanspec_t chspec); ++ ++/** ++ * Return a channel number corresponding to a frequency. ++ * ++ * This function returns the chanspec for the primary 40MHz of an 80MHz channel. ++ * The control sideband specifies the same 20MHz channel that the 80MHz channel is using ++ * as the primary 20MHz channel. ++ */ ++extern chanspec_t wf_chspec_primary40_chspec(chanspec_t chspec); ++ ++/* ++ * Return the channel number for a given frequency and base frequency. ++ * The returned channel number is relative to the given base frequency. ++ * If the given base frequency is zero, a base frequency of 5 GHz is assumed for ++ * frequencies from 5 - 6 GHz, and 2.407 GHz is assumed for 2.4 - 2.5 GHz. ++ * ++ * Frequency is specified in MHz. ++ * The base frequency is specified as (start_factor * 500 kHz). ++ * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G are defined for ++ * 2.4 GHz and 5 GHz bands. ++ * ++ * The returned channel will be in the range [1, 14] in the 2.4 GHz band ++ * and [0, 200] otherwise. ++ * -1 is returned if the start_factor is WF_CHAN_FACTOR_2_4_G and the ++ * frequency is not a 2.4 GHz channel, or if the frequency is not and even ++ * multiple of 5 MHz from the base frequency to the base plus 1 GHz. ++ * ++ * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2 ++ * ++ * @param freq frequency in MHz ++ * @param start_factor base frequency in 500 kHz units, e.g. 10000 for 5 GHz ++ * ++ * @return Returns a channel number ++ * ++ * @see WF_CHAN_FACTOR_2_4_G ++ * @see WF_CHAN_FACTOR_5_G ++ */ ++extern int wf_mhz2channel(uint freq, uint start_factor); ++ ++/** ++ * Return the center frequency in MHz of the given channel and base frequency. ++ * ++ * Return the center frequency in MHz of the given channel and base frequency. ++ * The channel number is interpreted relative to the given base frequency. ++ * ++ * The valid channel range is [1, 14] in the 2.4 GHz band and [0, 200] otherwise. ++ * The base frequency is specified as (start_factor * 500 kHz). ++ * Constants WF_CHAN_FACTOR_2_4_G, WF_CHAN_FACTOR_5_G are defined for ++ * 2.4 GHz and 5 GHz bands. ++ * The channel range of [1, 14] is only checked for a start_factor of ++ * WF_CHAN_FACTOR_2_4_G (4814). ++ * Odd start_factors produce channels on .5 MHz boundaries, in which case ++ * the answer is rounded down to an integral MHz. ++ * -1 is returned for an out of range channel. ++ * ++ * Reference 802.11 REVma, section 17.3.8.3, and 802.11B section 18.4.6.2 ++ * ++ * @param channel input channel number ++ * @param start_factor base frequency in 500 kHz units, e.g. 10000 for 5 GHz ++ * ++ * @return Returns a frequency in MHz ++ * ++ * @see WF_CHAN_FACTOR_2_4_G ++ * @see WF_CHAN_FACTOR_5_G ++ */ ++extern int wf_channel2mhz(uint channel, uint start_factor); ++ ++/** ++ * Convert ctl chan and bw to chanspec ++ * ++ * @param ctl_ch channel ++ * @param bw bandwidth ++ * ++ * @return > 0 if successful or 0 otherwise ++ * ++ */ ++extern uint16 wf_channel2chspec(uint ctl_ch, uint bw); ++ ++#endif /* _bcmwifi_channels_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/bcmwifi_rates.h b/drivers/net/wireless/bcmdhd/include/bcmwifi_rates.h +new file mode 100644 +index 00000000..9896b236 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/bcmwifi_rates.h +@@ -0,0 +1,306 @@ ++/* ++ * Indices for 802.11 a/b/g/n/ac 1-3 chain symmetric transmit rates ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmwifi_rates.h 252708 2011-04-12 06:45:56Z $ ++ */ ++ ++#ifndef _bcmwifi_rates_h_ ++#define _bcmwifi_rates_h_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++#define WL_RATESET_SZ_DSSS 4 ++#define WL_RATESET_SZ_OFDM 8 ++#define WL_RATESET_SZ_HT_MCS 8 ++#define WL_RATESET_SZ_VHT_MCS 10 ++ ++#define WL_TX_CHAINS_MAX 3 ++ ++#define WL_RATE_DISABLED (-128) ++ ++ ++typedef enum wl_tx_bw { ++ WL_TX_BW_20, ++ WL_TX_BW_40, ++ WL_TX_BW_80, ++ WL_TX_BW_20IN40, ++ WL_TX_BW_20IN80, ++ WL_TX_BW_40IN80, ++ WL_TX_BW_ALL ++} wl_tx_bw_t; ++ ++ ++ ++typedef enum wl_tx_mode { ++ WL_TX_MODE_NONE, ++ WL_TX_MODE_STBC, ++ WL_TX_MODE_CDD, ++ WL_TX_MODE_SDM ++} wl_tx_mode_t; ++ ++ ++ ++typedef enum wl_tx_chains { ++ WL_TX_CHAINS_1 = 1, ++ WL_TX_CHAINS_2, ++ WL_TX_CHAINS_3 ++} wl_tx_chains_t; ++ ++ ++ ++typedef enum wl_tx_nss { ++ WL_TX_NSS_1 = 1, ++ WL_TX_NSS_2, ++ WL_TX_NSS_3 ++} wl_tx_nss_t; ++ ++ ++typedef enum clm_rates { ++ ++ ++ ++ WL_RATE_1X1_DSSS_1 = 0, ++ WL_RATE_1X1_DSSS_2 = 1, ++ WL_RATE_1X1_DSSS_5_5 = 2, ++ WL_RATE_1X1_DSSS_11 = 3, ++ ++ WL_RATE_1X1_OFDM_6 = 4, ++ WL_RATE_1X1_OFDM_9 = 5, ++ WL_RATE_1X1_OFDM_12 = 6, ++ WL_RATE_1X1_OFDM_18 = 7, ++ WL_RATE_1X1_OFDM_24 = 8, ++ WL_RATE_1X1_OFDM_36 = 9, ++ WL_RATE_1X1_OFDM_48 = 10, ++ WL_RATE_1X1_OFDM_54 = 11, ++ ++ WL_RATE_1X1_MCS0 = 12, ++ WL_RATE_1X1_MCS1 = 13, ++ WL_RATE_1X1_MCS2 = 14, ++ WL_RATE_1X1_MCS3 = 15, ++ WL_RATE_1X1_MCS4 = 16, ++ WL_RATE_1X1_MCS5 = 17, ++ WL_RATE_1X1_MCS6 = 18, ++ WL_RATE_1X1_MCS7 = 19, ++ ++ WL_RATE_1X1_VHT0SS1 = 12, ++ WL_RATE_1X1_VHT1SS1 = 13, ++ WL_RATE_1X1_VHT2SS1 = 14, ++ WL_RATE_1X1_VHT3SS1 = 15, ++ WL_RATE_1X1_VHT4SS1 = 16, ++ WL_RATE_1X1_VHT5SS1 = 17, ++ WL_RATE_1X1_VHT6SS1 = 18, ++ WL_RATE_1X1_VHT7SS1 = 19, ++ WL_RATE_1X1_VHT8SS1 = 20, ++ WL_RATE_1X1_VHT9SS1 = 21, ++ ++ ++ ++ ++ ++ WL_RATE_1X2_DSSS_1 = 22, ++ WL_RATE_1X2_DSSS_2 = 23, ++ WL_RATE_1X2_DSSS_5_5 = 24, ++ WL_RATE_1X2_DSSS_11 = 25, ++ ++ WL_RATE_1X2_CDD_OFDM_6 = 26, ++ WL_RATE_1X2_CDD_OFDM_9 = 27, ++ WL_RATE_1X2_CDD_OFDM_12 = 28, ++ WL_RATE_1X2_CDD_OFDM_18 = 29, ++ WL_RATE_1X2_CDD_OFDM_24 = 30, ++ WL_RATE_1X2_CDD_OFDM_36 = 31, ++ WL_RATE_1X2_CDD_OFDM_48 = 32, ++ WL_RATE_1X2_CDD_OFDM_54 = 33, ++ ++ WL_RATE_1X2_CDD_MCS0 = 34, ++ WL_RATE_1X2_CDD_MCS1 = 35, ++ WL_RATE_1X2_CDD_MCS2 = 36, ++ WL_RATE_1X2_CDD_MCS3 = 37, ++ WL_RATE_1X2_CDD_MCS4 = 38, ++ WL_RATE_1X2_CDD_MCS5 = 39, ++ WL_RATE_1X2_CDD_MCS6 = 40, ++ WL_RATE_1X2_CDD_MCS7 = 41, ++ ++ WL_RATE_1X2_VHT0SS1 = 34, ++ WL_RATE_1X2_VHT1SS1 = 35, ++ WL_RATE_1X2_VHT2SS1 = 36, ++ WL_RATE_1X2_VHT3SS1 = 37, ++ WL_RATE_1X2_VHT4SS1 = 38, ++ WL_RATE_1X2_VHT5SS1 = 39, ++ WL_RATE_1X2_VHT6SS1 = 40, ++ WL_RATE_1X2_VHT7SS1 = 41, ++ WL_RATE_1X2_VHT8SS1 = 42, ++ WL_RATE_1X2_VHT9SS1 = 43, ++ ++ ++ WL_RATE_2X2_STBC_MCS0 = 44, ++ WL_RATE_2X2_STBC_MCS1 = 45, ++ WL_RATE_2X2_STBC_MCS2 = 46, ++ WL_RATE_2X2_STBC_MCS3 = 47, ++ WL_RATE_2X2_STBC_MCS4 = 48, ++ WL_RATE_2X2_STBC_MCS5 = 49, ++ WL_RATE_2X2_STBC_MCS6 = 50, ++ WL_RATE_2X2_STBC_MCS7 = 51, ++ ++ WL_RATE_2X2_STBC_VHT0SS1 = 44, ++ WL_RATE_2X2_STBC_VHT1SS1 = 45, ++ WL_RATE_2X2_STBC_VHT2SS1 = 46, ++ WL_RATE_2X2_STBC_VHT3SS1 = 47, ++ WL_RATE_2X2_STBC_VHT4SS1 = 48, ++ WL_RATE_2X2_STBC_VHT5SS1 = 49, ++ WL_RATE_2X2_STBC_VHT6SS1 = 50, ++ WL_RATE_2X2_STBC_VHT7SS1 = 51, ++ WL_RATE_2X2_STBC_VHT8SS1 = 52, ++ WL_RATE_2X2_STBC_VHT9SS1 = 53, ++ ++ WL_RATE_2X2_SDM_MCS8 = 54, ++ WL_RATE_2X2_SDM_MCS9 = 55, ++ WL_RATE_2X2_SDM_MCS10 = 56, ++ WL_RATE_2X2_SDM_MCS11 = 57, ++ WL_RATE_2X2_SDM_MCS12 = 58, ++ WL_RATE_2X2_SDM_MCS13 = 59, ++ WL_RATE_2X2_SDM_MCS14 = 60, ++ WL_RATE_2X2_SDM_MCS15 = 61, ++ ++ WL_RATE_2X2_VHT0SS2 = 54, ++ WL_RATE_2X2_VHT1SS2 = 55, ++ WL_RATE_2X2_VHT2SS2 = 56, ++ WL_RATE_2X2_VHT3SS2 = 57, ++ WL_RATE_2X2_VHT4SS2 = 58, ++ WL_RATE_2X2_VHT5SS2 = 59, ++ WL_RATE_2X2_VHT6SS2 = 60, ++ WL_RATE_2X2_VHT7SS2 = 61, ++ WL_RATE_2X2_VHT8SS2 = 62, ++ WL_RATE_2X2_VHT9SS2 = 63, ++ ++ ++ ++ ++ ++ WL_RATE_1X3_DSSS_1 = 64, ++ WL_RATE_1X3_DSSS_2 = 65, ++ WL_RATE_1X3_DSSS_5_5 = 66, ++ WL_RATE_1X3_DSSS_11 = 67, ++ ++ WL_RATE_1X3_CDD_OFDM_6 = 68, ++ WL_RATE_1X3_CDD_OFDM_9 = 69, ++ WL_RATE_1X3_CDD_OFDM_12 = 70, ++ WL_RATE_1X3_CDD_OFDM_18 = 71, ++ WL_RATE_1X3_CDD_OFDM_24 = 72, ++ WL_RATE_1X3_CDD_OFDM_36 = 73, ++ WL_RATE_1X3_CDD_OFDM_48 = 74, ++ WL_RATE_1X3_CDD_OFDM_54 = 75, ++ ++ WL_RATE_1X3_CDD_MCS0 = 76, ++ WL_RATE_1X3_CDD_MCS1 = 77, ++ WL_RATE_1X3_CDD_MCS2 = 78, ++ WL_RATE_1X3_CDD_MCS3 = 79, ++ WL_RATE_1X3_CDD_MCS4 = 80, ++ WL_RATE_1X3_CDD_MCS5 = 81, ++ WL_RATE_1X3_CDD_MCS6 = 82, ++ WL_RATE_1X3_CDD_MCS7 = 83, ++ ++ WL_RATE_1X3_VHT0SS1 = 76, ++ WL_RATE_1X3_VHT1SS1 = 77, ++ WL_RATE_1X3_VHT2SS1 = 78, ++ WL_RATE_1X3_VHT3SS1 = 79, ++ WL_RATE_1X3_VHT4SS1 = 80, ++ WL_RATE_1X3_VHT5SS1 = 81, ++ WL_RATE_1X3_VHT6SS1 = 82, ++ WL_RATE_1X3_VHT7SS1 = 83, ++ WL_RATE_1X3_VHT8SS1 = 84, ++ WL_RATE_1X3_VHT9SS1 = 85, ++ ++ ++ WL_RATE_2X3_STBC_MCS0 = 86, ++ WL_RATE_2X3_STBC_MCS1 = 87, ++ WL_RATE_2X3_STBC_MCS2 = 88, ++ WL_RATE_2X3_STBC_MCS3 = 89, ++ WL_RATE_2X3_STBC_MCS4 = 90, ++ WL_RATE_2X3_STBC_MCS5 = 91, ++ WL_RATE_2X3_STBC_MCS6 = 92, ++ WL_RATE_2X3_STBC_MCS7 = 93, ++ ++ WL_RATE_2X3_STBC_VHT0SS1 = 86, ++ WL_RATE_2X3_STBC_VHT1SS1 = 87, ++ WL_RATE_2X3_STBC_VHT2SS1 = 88, ++ WL_RATE_2X3_STBC_VHT3SS1 = 89, ++ WL_RATE_2X3_STBC_VHT4SS1 = 90, ++ WL_RATE_2X3_STBC_VHT5SS1 = 91, ++ WL_RATE_2X3_STBC_VHT6SS1 = 92, ++ WL_RATE_2X3_STBC_VHT7SS1 = 93, ++ WL_RATE_2X3_STBC_VHT8SS1 = 94, ++ WL_RATE_2X3_STBC_VHT9SS1 = 95, ++ ++ WL_RATE_2X3_SDM_MCS8 = 96, ++ WL_RATE_2X3_SDM_MCS9 = 97, ++ WL_RATE_2X3_SDM_MCS10 = 98, ++ WL_RATE_2X3_SDM_MCS11 = 99, ++ WL_RATE_2X3_SDM_MCS12 = 100, ++ WL_RATE_2X3_SDM_MCS13 = 101, ++ WL_RATE_2X3_SDM_MCS14 = 102, ++ WL_RATE_2X3_SDM_MCS15 = 103, ++ ++ WL_RATE_2X3_VHT0SS2 = 96, ++ WL_RATE_2X3_VHT1SS2 = 97, ++ WL_RATE_2X3_VHT2SS2 = 98, ++ WL_RATE_2X3_VHT3SS2 = 99, ++ WL_RATE_2X3_VHT4SS2 = 100, ++ WL_RATE_2X3_VHT5SS2 = 101, ++ WL_RATE_2X3_VHT6SS2 = 102, ++ WL_RATE_2X3_VHT7SS2 = 103, ++ WL_RATE_2X3_VHT8SS2 = 104, ++ WL_RATE_2X3_VHT9SS2 = 105, ++ ++ ++ WL_RATE_3X3_SDM_MCS16 = 106, ++ WL_RATE_3X3_SDM_MCS17 = 107, ++ WL_RATE_3X3_SDM_MCS18 = 108, ++ WL_RATE_3X3_SDM_MCS19 = 109, ++ WL_RATE_3X3_SDM_MCS20 = 110, ++ WL_RATE_3X3_SDM_MCS21 = 111, ++ WL_RATE_3X3_SDM_MCS22 = 112, ++ WL_RATE_3X3_SDM_MCS23 = 113, ++ ++ WL_RATE_3X3_VHT0SS3 = 106, ++ WL_RATE_3X3_VHT1SS3 = 107, ++ WL_RATE_3X3_VHT2SS3 = 108, ++ WL_RATE_3X3_VHT3SS3 = 109, ++ WL_RATE_3X3_VHT4SS3 = 110, ++ WL_RATE_3X3_VHT5SS3 = 111, ++ WL_RATE_3X3_VHT6SS3 = 112, ++ WL_RATE_3X3_VHT7SS3 = 113, ++ WL_RATE_3X3_VHT8SS3 = 114, ++ WL_RATE_3X3_VHT9SS3 = 115, ++ ++ ++ WL_NUMRATES = 116 ++} clm_rates_t; ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/dhdioctl.h b/drivers/net/wireless/bcmdhd/include/dhdioctl.h +new file mode 100644 +index 00000000..11fff555 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/dhdioctl.h +@@ -0,0 +1,136 @@ ++/* ++ * Definitions for ioctls to access DHD iovars. ++ * Based on wlioctl.h (for Broadcom 802.11abg driver). ++ * (Moves towards generic ioctls for BCM drivers/iovars.) ++ * ++ * Definitions subject to change without notice. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhdioctl.h 354894 2012-09-04 12:34:07Z $ ++ */ ++ ++#ifndef _dhdioctl_h_ ++#define _dhdioctl_h_ ++ ++#include ++ ++ ++/* require default structure packing */ ++#define BWL_DEFAULT_PACKING ++#include ++ ++ ++/* Linux network driver ioctl encoding */ ++typedef struct dhd_ioctl { ++ uint cmd; /* common ioctl definition */ ++ void *buf; /* pointer to user buffer */ ++ uint len; /* length of user buffer */ ++ bool set; /* get or set request (optional) */ ++ uint used; /* bytes read or written (optional) */ ++ uint needed; /* bytes needed (optional) */ ++ uint driver; /* to identify target driver */ ++} dhd_ioctl_t; ++ ++/* Underlying BUS definition */ ++enum { ++ BUS_TYPE_USB = 0, /* for USB dongles */ ++ BUS_TYPE_SDIO /* for SDIO dongles */ ++}; ++ ++/* per-driver magic numbers */ ++#define DHD_IOCTL_MAGIC 0x00444944 ++ ++/* bump this number if you change the ioctl interface */ ++#define DHD_IOCTL_VERSION 1 ++ ++#define DHD_IOCTL_MAXLEN 8192 /* max length ioctl buffer required */ ++#define DHD_IOCTL_SMLEN 256 /* "small" length ioctl buffer required */ ++ ++/* common ioctl definitions */ ++#define DHD_GET_MAGIC 0 ++#define DHD_GET_VERSION 1 ++#define DHD_GET_VAR 2 ++#define DHD_SET_VAR 3 ++ ++/* message levels */ ++#define DHD_ERROR_VAL 0x0001 ++#define DHD_TRACE_VAL 0x0002 ++#define DHD_INFO_VAL 0x0004 ++#define DHD_DATA_VAL 0x0008 ++#define DHD_CTL_VAL 0x0010 ++#define DHD_TIMER_VAL 0x0020 ++#define DHD_HDRS_VAL 0x0040 ++#define DHD_BYTES_VAL 0x0080 ++#define DHD_INTR_VAL 0x0100 ++#define DHD_LOG_VAL 0x0200 ++#define DHD_GLOM_VAL 0x0400 ++#define DHD_EVENT_VAL 0x0800 ++#define DHD_BTA_VAL 0x1000 ++#if 0 && (NDISVER >= 0x0630) && 1 ++#define DHD_SCAN_VAL 0x2000 ++#else ++#define DHD_ISCAN_VAL 0x2000 ++#endif ++#define DHD_ARPOE_VAL 0x4000 ++#define DHD_REORDER_VAL 0x8000 ++#define DHD_WL_VAL 0x10000 ++#define DHD_WL_VAL2 0x20000 ++ ++#ifdef SDTEST ++/* For pktgen iovar */ ++typedef struct dhd_pktgen { ++ uint version; /* To allow structure change tracking */ ++ uint freq; /* Max ticks between tx/rx attempts */ ++ uint count; /* Test packets to send/rcv each attempt */ ++ uint print; /* Print counts every attempts */ ++ uint total; /* Total packets (or bursts) */ ++ uint minlen; /* Minimum length of packets to send */ ++ uint maxlen; /* Maximum length of packets to send */ ++ uint numsent; /* Count of test packets sent */ ++ uint numrcvd; /* Count of test packets received */ ++ uint numfail; /* Count of test send failures */ ++ uint mode; /* Test mode (type of test packets) */ ++ uint stop; /* Stop after this many tx failures */ ++} dhd_pktgen_t; ++ ++/* Version in case structure changes */ ++#define DHD_PKTGEN_VERSION 2 ++ ++/* Type of test packets to use */ ++#define DHD_PKTGEN_ECHO 1 /* Send echo requests */ ++#define DHD_PKTGEN_SEND 2 /* Send discard packets */ ++#define DHD_PKTGEN_RXBURST 3 /* Request dongle send N packets */ ++#define DHD_PKTGEN_RECV 4 /* Continuous rx from continuous tx dongle */ ++#endif /* SDTEST */ ++ ++/* Enter idle immediately (no timeout) */ ++#define DHD_IDLE_IMMEDIATE (-1) ++ ++/* Values for idleclock iovar: other values are the sd_divisor to use when idle */ ++#define DHD_IDLE_ACTIVE 0 /* Do not request any SD clock change when idle */ ++#define DHD_IDLE_STOP (-1) /* Request SD clock be stopped (and use SD1 mode) */ ++ ++ ++/* require default structure packing */ ++#include ++ ++#endif /* _dhdioctl_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h +new file mode 100644 +index 00000000..7f5a9710 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/epivers.h +@@ -0,0 +1,56 @@ ++/* ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: epivers.h.in,v 13.33 2010-09-08 22:08:53 $ ++ * ++*/ ++ ++#ifndef _epivers_h_ ++#define _epivers_h_ ++ ++#define EPI_MAJOR_VERSION 1 ++ ++#define EPI_MINOR_VERSION 28 ++ ++#define EPI_RC_NUMBER 27 ++ ++#define EPI_INCREMENTAL_NUMBER 1 ++ ++#define EPI_BUILD_NUMBER 0 ++ ++#define EPI_VERSION 1, 28, 27, 1 ++ ++#define EPI_VERSION_NUM 0x011c1b01 ++ ++#define EPI_VERSION_DEV 1.28.27 ++ ++/* Driver Version String, ASCII, 32 chars max */ ++#ifdef BCMINTERNAL ++#define EPI_VERSION_STR "1.28.27.1 (r BCMINT)" ++#else ++#ifdef WLTEST ++#define EPI_VERSION_STR "1.28.27.1 (r WLTEST)" ++#else ++#define EPI_VERSION_STR "1.28.27.1 (r)" ++#endif ++#endif /* BCMINTERNAL */ ++ ++#endif /* _epivers_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/hndpmu.h b/drivers/net/wireless/bcmdhd/include/hndpmu.h +new file mode 100644 +index 00000000..c41def6c +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/hndpmu.h +@@ -0,0 +1,36 @@ ++/* ++ * HND SiliconBackplane PMU support. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: hndpmu.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++#ifndef _hndpmu_h_ ++#define _hndpmu_h_ ++ ++ ++extern void si_pmu_otp_power(si_t *sih, osl_t *osh, bool on); ++extern void si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength); ++ ++extern void si_pmu_minresmask_htavail_set(si_t *sih, osl_t *osh, bool set_clear); ++ ++#endif /* _hndpmu_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h b/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h +new file mode 100644 +index 00000000..90d97992 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h +@@ -0,0 +1,88 @@ ++/* ++ * HNDRTE arm trap handling. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: hndrte_armtrap.h 261365 2011-05-24 20:42:23Z $ ++ */ ++ ++#ifndef _hndrte_armtrap_h ++#define _hndrte_armtrap_h ++ ++ ++/* ARM trap handling */ ++ ++/* Trap types defined by ARM (see arminc.h) */ ++ ++/* Trap locations in lo memory */ ++#define TRAP_STRIDE 4 ++#define FIRST_TRAP TR_RST ++#define LAST_TRAP (TR_FIQ * TRAP_STRIDE) ++ ++#if defined(__ARM_ARCH_4T__) ++#define MAX_TRAP_TYPE (TR_FIQ + 1) ++#elif defined(__ARM_ARCH_7M__) ++#define MAX_TRAP_TYPE (TR_ISR + ARMCM3_NUMINTS) ++#endif /* __ARM_ARCH_7M__ */ ++ ++/* The trap structure is defined here as offsets for assembly */ ++#define TR_TYPE 0x00 ++#define TR_EPC 0x04 ++#define TR_CPSR 0x08 ++#define TR_SPSR 0x0c ++#define TR_REGS 0x10 ++#define TR_REG(n) (TR_REGS + (n) * 4) ++#define TR_SP TR_REG(13) ++#define TR_LR TR_REG(14) ++#define TR_PC TR_REG(15) ++ ++#define TRAP_T_SIZE 80 ++ ++#ifndef _LANGUAGE_ASSEMBLY ++ ++#include ++ ++typedef struct _trap_struct { ++ uint32 type; ++ uint32 epc; ++ uint32 cpsr; ++ uint32 spsr; ++ uint32 r0; /* a1 */ ++ uint32 r1; /* a2 */ ++ uint32 r2; /* a3 */ ++ uint32 r3; /* a4 */ ++ uint32 r4; /* v1 */ ++ uint32 r5; /* v2 */ ++ uint32 r6; /* v3 */ ++ uint32 r7; /* v4 */ ++ uint32 r8; /* v5 */ ++ uint32 r9; /* sb/v6 */ ++ uint32 r10; /* sl/v7 */ ++ uint32 r11; /* fp/v8 */ ++ uint32 r12; /* ip */ ++ uint32 r13; /* sp */ ++ uint32 r14; /* lr */ ++ uint32 pc; /* r15 */ ++} trap_t; ++ ++#endif /* !_LANGUAGE_ASSEMBLY */ ++ ++#endif /* _hndrte_armtrap_h */ +diff --git a/drivers/net/wireless/bcmdhd/include/hndrte_cons.h b/drivers/net/wireless/bcmdhd/include/hndrte_cons.h +new file mode 100644 +index 00000000..57abbbd5 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/hndrte_cons.h +@@ -0,0 +1,67 @@ ++/* ++ * Console support for hndrte. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: hndrte_cons.h 300516 2011-12-04 17:39:44Z $ ++ */ ++#ifndef _HNDRTE_CONS_H ++#define _HNDRTE_CONS_H ++ ++#include ++ ++#define CBUF_LEN (128) ++ ++#define LOG_BUF_LEN 1024 ++ ++typedef struct { ++ uint32 buf; /* Can't be pointer on (64-bit) hosts */ ++ uint buf_size; ++ uint idx; ++ char *_buf_compat; /* redundant pointer for backward compat. */ ++} hndrte_log_t; ++ ++typedef struct { ++ /* Virtual UART ++ * When there is no UART (e.g. Quickturn), the host should write a complete ++ * input line directly into cbuf and then write the length into vcons_in. ++ * This may also be used when there is a real UART (at risk of conflicting with ++ * the real UART). vcons_out is currently unused. ++ */ ++ volatile uint vcons_in; ++ volatile uint vcons_out; ++ ++ /* Output (logging) buffer ++ * Console output is written to a ring buffer log_buf at index log_idx. ++ * The host may read the output when it sees log_idx advance. ++ * Output will be lost if the output wraps around faster than the host polls. ++ */ ++ hndrte_log_t log; ++ ++ /* Console input line buffer ++ * Characters are read one at a time into cbuf until is received, then ++ * the buffer is processed as a command line. Also used for virtual UART. ++ */ ++ uint cbuf_idx; ++ char cbuf[CBUF_LEN]; ++} hndrte_cons_t; ++ ++#endif /* _HNDRTE_CONS_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/hndsoc.h b/drivers/net/wireless/bcmdhd/include/hndsoc.h +new file mode 100644 +index 00000000..66640c3b +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/hndsoc.h +@@ -0,0 +1,235 @@ ++/* ++ * Broadcom HND chip & on-chip-interconnect-related definitions. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: hndsoc.h 309193 2012-01-19 00:03:57Z $ ++ */ ++ ++#ifndef _HNDSOC_H ++#define _HNDSOC_H ++ ++/* Include the soci specific files */ ++#include ++#include ++ ++/* ++ * SOC Interconnect Address Map. ++ * All regions may not exist on all chips. ++ */ ++#define SI_SDRAM_BASE 0x00000000 /* Physical SDRAM */ ++#define SI_PCI_MEM 0x08000000 /* Host Mode sb2pcitranslation0 (64 MB) */ ++#define SI_PCI_MEM_SZ (64 * 1024 * 1024) ++#define SI_PCI_CFG 0x0c000000 /* Host Mode sb2pcitranslation1 (64 MB) */ ++#define SI_SDRAM_SWAPPED 0x10000000 /* Byteswapped Physical SDRAM */ ++#define SI_SDRAM_R2 0x80000000 /* Region 2 for sdram (512 MB) */ ++ ++#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */ ++ ++#define SI_WRAP_BASE 0x18100000 /* Wrapper space base */ ++#define SI_CORE_SIZE 0x1000 /* each core gets 4Kbytes for registers */ ++#define SI_MAXCORES 16 /* Max cores (this is arbitrary, for software ++ * convenience and could be changed if we ++ * make any larger chips ++ */ ++ ++#define SI_FASTRAM 0x19000000 /* On-chip RAM on chips that also have DDR */ ++#define SI_FASTRAM_SWAPPED 0x19800000 ++ ++#define SI_FLASH2 0x1c000000 /* Flash Region 2 (region 1 shadowed here) */ ++#define SI_FLASH2_SZ 0x02000000 /* Size of Flash Region 2 */ ++#define SI_ARMCM3_ROM 0x1e000000 /* ARM Cortex-M3 ROM */ ++#define SI_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */ ++#define SI_FLASH1_SZ 0x00400000 /* MIPS Size of Flash Region 1 */ ++#define SI_ARM7S_ROM 0x20000000 /* ARM7TDMI-S ROM */ ++#define SI_ARMCR4_ROM 0x000f0000 /* ARM Cortex-R4 ROM */ ++#define SI_ARMCM3_SRAM2 0x60000000 /* ARM Cortex-M3 SRAM Region 2 */ ++#define SI_ARM7S_SRAM2 0x80000000 /* ARM7TDMI-S SRAM Region 2 */ ++#define SI_ARM_FLASH1 0xffff0000 /* ARM Flash Region 1 */ ++#define SI_ARM_FLASH1_SZ 0x00010000 /* ARM Size of Flash Region 1 */ ++ ++#define SI_PCI_DMA 0x40000000 /* Client Mode sb2pcitranslation2 (1 GB) */ ++#define SI_PCI_DMA2 0x80000000 /* Client Mode sb2pcitranslation2 (1 GB) */ ++#define SI_PCI_DMA_SZ 0x40000000 /* Client Mode sb2pcitranslation2 size in bytes */ ++#define SI_PCIE_DMA_L32 0x00000000 /* PCIE Client Mode sb2pcitranslation2 ++ * (2 ZettaBytes), low 32 bits ++ */ ++#define SI_PCIE_DMA_H32 0x80000000 /* PCIE Client Mode sb2pcitranslation2 ++ * (2 ZettaBytes), high 32 bits ++ */ ++ ++/* core codes */ ++#define NODEV_CORE_ID 0x700 /* Invalid coreid */ ++#define CC_CORE_ID 0x800 /* chipcommon core */ ++#define ILINE20_CORE_ID 0x801 /* iline20 core */ ++#define SRAM_CORE_ID 0x802 /* sram core */ ++#define SDRAM_CORE_ID 0x803 /* sdram core */ ++#define PCI_CORE_ID 0x804 /* pci core */ ++#define MIPS_CORE_ID 0x805 /* mips core */ ++#define ENET_CORE_ID 0x806 /* enet mac core */ ++#define CODEC_CORE_ID 0x807 /* v90 codec core */ ++#define USB_CORE_ID 0x808 /* usb 1.1 host/device core */ ++#define ADSL_CORE_ID 0x809 /* ADSL core */ ++#define ILINE100_CORE_ID 0x80a /* iline100 core */ ++#define IPSEC_CORE_ID 0x80b /* ipsec core */ ++#define UTOPIA_CORE_ID 0x80c /* utopia core */ ++#define PCMCIA_CORE_ID 0x80d /* pcmcia core */ ++#define SOCRAM_CORE_ID 0x80e /* internal memory core */ ++#define MEMC_CORE_ID 0x80f /* memc sdram core */ ++#define OFDM_CORE_ID 0x810 /* OFDM phy core */ ++#define EXTIF_CORE_ID 0x811 /* external interface core */ ++#define D11_CORE_ID 0x812 /* 802.11 MAC core */ ++#define APHY_CORE_ID 0x813 /* 802.11a phy core */ ++#define BPHY_CORE_ID 0x814 /* 802.11b phy core */ ++#define GPHY_CORE_ID 0x815 /* 802.11g phy core */ ++#define MIPS33_CORE_ID 0x816 /* mips3302 core */ ++#define USB11H_CORE_ID 0x817 /* usb 1.1 host core */ ++#define USB11D_CORE_ID 0x818 /* usb 1.1 device core */ ++#define USB20H_CORE_ID 0x819 /* usb 2.0 host core */ ++#define USB20D_CORE_ID 0x81a /* usb 2.0 device core */ ++#define SDIOH_CORE_ID 0x81b /* sdio host core */ ++#define ROBO_CORE_ID 0x81c /* roboswitch core */ ++#define ATA100_CORE_ID 0x81d /* parallel ATA core */ ++#define SATAXOR_CORE_ID 0x81e /* serial ATA & XOR DMA core */ ++#define GIGETH_CORE_ID 0x81f /* gigabit ethernet core */ ++#define PCIE_CORE_ID 0x820 /* pci express core */ ++#define NPHY_CORE_ID 0x821 /* 802.11n 2x2 phy core */ ++#define SRAMC_CORE_ID 0x822 /* SRAM controller core */ ++#define MINIMAC_CORE_ID 0x823 /* MINI MAC/phy core */ ++#define ARM11_CORE_ID 0x824 /* ARM 1176 core */ ++#define ARM7S_CORE_ID 0x825 /* ARM7tdmi-s core */ ++#define LPPHY_CORE_ID 0x826 /* 802.11a/b/g phy core */ ++#define PMU_CORE_ID 0x827 /* PMU core */ ++#define SSNPHY_CORE_ID 0x828 /* 802.11n single-stream phy core */ ++#define SDIOD_CORE_ID 0x829 /* SDIO device core */ ++#define ARMCM3_CORE_ID 0x82a /* ARM Cortex M3 core */ ++#define HTPHY_CORE_ID 0x82b /* 802.11n 4x4 phy core */ ++#define MIPS74K_CORE_ID 0x82c /* mips 74k core */ ++#define GMAC_CORE_ID 0x82d /* Gigabit MAC core */ ++#define DMEMC_CORE_ID 0x82e /* DDR1/2 memory controller core */ ++#define PCIERC_CORE_ID 0x82f /* PCIE Root Complex core */ ++#define OCP_CORE_ID 0x830 /* OCP2OCP bridge core */ ++#define SC_CORE_ID 0x831 /* shared common core */ ++#define AHB_CORE_ID 0x832 /* OCP2AHB bridge core */ ++#define SPIH_CORE_ID 0x833 /* SPI host core */ ++#define I2S_CORE_ID 0x834 /* I2S core */ ++#define DMEMS_CORE_ID 0x835 /* SDR/DDR1 memory controller core */ ++#define DEF_SHIM_COMP 0x837 /* SHIM component in ubus/6362 */ ++ ++#define ACPHY_CORE_ID 0x83b /* Dot11 ACPHY */ ++#define PCIE2_CORE_ID 0x83c /* pci express Gen2 core */ ++#define USB30D_CORE_ID 0x83d /* usb 3.0 device core */ ++#define ARMCR4_CORE_ID 0x83e /* ARM CR4 CPU */ ++#define APB_BRIDGE_CORE_ID 0x135 /* APB bridge core ID */ ++#define AXI_CORE_ID 0x301 /* AXI/GPV core ID */ ++#define EROM_CORE_ID 0x366 /* EROM core ID */ ++#define OOB_ROUTER_CORE_ID 0x367 /* OOB router core ID */ ++#define DEF_AI_COMP 0xfff /* Default component, in ai chips it maps all ++ * unused address ranges ++ */ ++ ++#define CC_4706_CORE_ID 0x500 /* chipcommon core */ ++#define SOCRAM_4706_CORE_ID 0x50e /* internal memory core */ ++#define GMAC_COMMON_4706_CORE_ID 0x5dc /* Gigabit MAC core */ ++#define GMAC_4706_CORE_ID 0x52d /* Gigabit MAC core */ ++#define AMEMC_CORE_ID 0x52e /* DDR1/2 memory controller core */ ++#define ALTA_CORE_ID 0x534 /* I2S core */ ++#define DDR23_PHY_CORE_ID 0x5dd ++ ++#define SI_PCI1_MEM 0x40000000 /* Host Mode sb2pcitranslation0 (64 MB) */ ++#define SI_PCI1_CFG 0x44000000 /* Host Mode sb2pcitranslation1 (64 MB) */ ++#define SI_PCIE1_DMA_H32 0xc0000000 /* PCIE Client Mode sb2pcitranslation2 ++ * (2 ZettaBytes), high 32 bits ++ */ ++#define CC_4706B0_CORE_REV 0x8000001f /* chipcommon core */ ++#define SOCRAM_4706B0_CORE_REV 0x80000005 /* internal memory core */ ++#define GMAC_4706B0_CORE_REV 0x80000000 /* Gigabit MAC core */ ++ ++/* There are TWO constants on all HND chips: SI_ENUM_BASE above, ++ * and chipcommon being the first core: ++ */ ++#define SI_CC_IDX 0 ++ ++/* SOC Interconnect types (aka chip types) */ ++#define SOCI_SB 0 ++#define SOCI_AI 1 ++#define SOCI_UBUS 2 ++ ++/* Common core control flags */ ++#define SICF_BIST_EN 0x8000 ++#define SICF_PME_EN 0x4000 ++#define SICF_CORE_BITS 0x3ffc ++#define SICF_FGC 0x0002 ++#define SICF_CLOCK_EN 0x0001 ++ ++/* Common core status flags */ ++#define SISF_BIST_DONE 0x8000 ++#define SISF_BIST_ERROR 0x4000 ++#define SISF_GATED_CLK 0x2000 ++#define SISF_DMA64 0x1000 ++#define SISF_CORE_BITS 0x0fff ++ ++/* A register that is common to all cores to ++ * communicate w/PMU regarding clock control. ++ */ ++#define SI_CLK_CTL_ST 0x1e0 /* clock control and status */ ++ ++/* clk_ctl_st register */ ++#define CCS_FORCEALP 0x00000001 /* force ALP request */ ++#define CCS_FORCEHT 0x00000002 /* force HT request */ ++#define CCS_FORCEILP 0x00000004 /* force ILP request */ ++#define CCS_ALPAREQ 0x00000008 /* ALP Avail Request */ ++#define CCS_HTAREQ 0x00000010 /* HT Avail Request */ ++#define CCS_FORCEHWREQOFF 0x00000020 /* Force HW Clock Request Off */ ++#define CCS_HQCLKREQ 0x00000040 /* HQ Clock Required */ ++#define CCS_USBCLKREQ 0x00000100 /* USB Clock Req */ ++#define CCS_ERSRC_REQ_MASK 0x00000700 /* external resource requests */ ++#define CCS_ERSRC_REQ_SHIFT 8 ++#define CCS_ALPAVAIL 0x00010000 /* ALP is available */ ++#define CCS_HTAVAIL 0x00020000 /* HT is available */ ++#define CCS_BP_ON_APL 0x00040000 /* RO: Backplane is running on ALP clock */ ++#define CCS_BP_ON_HT 0x00080000 /* RO: Backplane is running on HT clock */ ++#define CCS_ERSRC_STS_MASK 0x07000000 /* external resource status */ ++#define CCS_ERSRC_STS_SHIFT 24 ++ ++#define CCS0_HTAVAIL 0x00010000 /* HT avail in chipc and pcmcia on 4328a0 */ ++#define CCS0_ALPAVAIL 0x00020000 /* ALP avail in chipc and pcmcia on 4328a0 */ ++ ++/* Not really related to SOC Interconnect, but a couple of software ++ * conventions for the use the flash space: ++ */ ++ ++/* Minumum amount of flash we support */ ++#define FLASH_MIN 0x00020000 /* Minimum flash size */ ++ ++/* A boot/binary may have an embedded block that describes its size */ ++#define BISZ_OFFSET 0x3e0 /* At this offset into the binary */ ++#define BISZ_MAGIC 0x4249535a /* Marked with this value: 'BISZ' */ ++#define BISZ_MAGIC_IDX 0 /* Word 0: magic */ ++#define BISZ_TXTST_IDX 1 /* 1: text start */ ++#define BISZ_TXTEND_IDX 2 /* 2: text end */ ++#define BISZ_DATAST_IDX 3 /* 3: data start */ ++#define BISZ_DATAEND_IDX 4 /* 4: data end */ ++#define BISZ_BSSST_IDX 5 /* 5: bss start */ ++#define BISZ_BSSEND_IDX 6 /* 6: bss end */ ++#define BISZ_SIZE 7 /* descriptor size in 32-bit integers */ ++ ++#endif /* _HNDSOC_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/linux_osl.h b/drivers/net/wireless/bcmdhd/include/linux_osl.h +new file mode 100644 +index 00000000..d2f3a987 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/linux_osl.h +@@ -0,0 +1,425 @@ ++/* ++ * Linux OS Independent Layer ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: linux_osl.h 354452 2012-08-31 04:59:17Z $ ++ */ ++ ++#ifndef _linux_osl_h_ ++#define _linux_osl_h_ ++ ++#include ++ ++ ++extern void * osl_os_open_image(char * filename); ++extern int osl_os_get_image_block(char * buf, int len, void * image); ++extern void osl_os_close_image(void * image); ++extern int osl_os_image_size(void *image); ++ ++ ++#ifdef BCMDRIVER ++ ++ ++extern osl_t *osl_attach(void *pdev, uint bustype, bool pkttag); ++extern void osl_detach(osl_t *osh); ++ ++ ++extern uint32 g_assert_type; ++ ++ ++#if defined(BCMASSERT_LOG) ++ #define ASSERT(exp) \ ++ do { if (!(exp)) osl_assert(#exp, __FILE__, __LINE__); } while (0) ++extern void osl_assert(const char *exp, const char *file, int line); ++#else ++ #ifdef __GNUC__ ++ #define GCC_VERSION \ ++ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) ++ #if GCC_VERSION > 30100 ++ #define ASSERT(exp) do {} while (0) ++ #else ++ ++ #define ASSERT(exp) ++ #endif ++ #endif ++#endif ++ ++ ++#define OSL_DELAY(usec) osl_delay(usec) ++extern void osl_delay(uint usec); ++ ++#define OSL_PCMCIA_READ_ATTR(osh, offset, buf, size) \ ++ osl_pcmcia_read_attr((osh), (offset), (buf), (size)) ++#define OSL_PCMCIA_WRITE_ATTR(osh, offset, buf, size) \ ++ osl_pcmcia_write_attr((osh), (offset), (buf), (size)) ++extern void osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size); ++extern void osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size); ++ ++ ++#define OSL_PCI_READ_CONFIG(osh, offset, size) \ ++ osl_pci_read_config((osh), (offset), (size)) ++#define OSL_PCI_WRITE_CONFIG(osh, offset, size, val) \ ++ osl_pci_write_config((osh), (offset), (size), (val)) ++extern uint32 osl_pci_read_config(osl_t *osh, uint offset, uint size); ++extern void osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val); ++ ++ ++#define OSL_PCI_BUS(osh) osl_pci_bus(osh) ++#define OSL_PCI_SLOT(osh) osl_pci_slot(osh) ++extern uint osl_pci_bus(osl_t *osh); ++extern uint osl_pci_slot(osl_t *osh); ++extern struct pci_dev *osl_pci_device(osl_t *osh); ++ ++ ++typedef struct { ++ bool pkttag; ++ uint pktalloced; ++ bool mmbus; ++ pktfree_cb_fn_t tx_fn; ++ void *tx_ctx; ++ void *unused[3]; ++} osl_pubinfo_t; ++ ++#define PKTFREESETCB(osh, _tx_fn, _tx_ctx) \ ++ do { \ ++ ((osl_pubinfo_t*)osh)->tx_fn = _tx_fn; \ ++ ((osl_pubinfo_t*)osh)->tx_ctx = _tx_ctx; \ ++ } while (0) ++ ++ ++ ++#define BUS_SWAP32(v) (v) ++ ++ #define MALLOC(osh, size) osl_malloc((osh), (size)) ++ #define MFREE(osh, addr, size) osl_mfree((osh), (addr), (size)) ++ #define MALLOCED(osh) osl_malloced((osh)) ++ extern void *osl_malloc(osl_t *osh, uint size); ++ extern void osl_mfree(osl_t *osh, void *addr, uint size); ++ extern uint osl_malloced(osl_t *osh); ++ ++#define NATIVE_MALLOC(osh, size) kmalloc(size, GFP_ATOMIC) ++#define NATIVE_MFREE(osh, addr, size) kfree(addr) ++ ++#define MALLOC_FAILED(osh) osl_malloc_failed((osh)) ++extern uint osl_malloc_failed(osl_t *osh); ++ ++ ++#define DMA_CONSISTENT_ALIGN osl_dma_consistent_align() ++#define DMA_ALLOC_CONSISTENT(osh, size, align, tot, pap, dmah) \ ++ osl_dma_alloc_consistent((osh), (size), (align), (tot), (pap)) ++#define DMA_FREE_CONSISTENT(osh, va, size, pa, dmah) \ ++ osl_dma_free_consistent((osh), (void*)(va), (size), (pa)) ++extern uint osl_dma_consistent_align(void); ++extern void *osl_dma_alloc_consistent(osl_t *osh, uint size, uint16 align, uint *tot, ulong *pap); ++extern void osl_dma_free_consistent(osl_t *osh, void *va, uint size, ulong pa); ++ ++ ++#define DMA_TX 1 ++#define DMA_RX 2 ++ ++ ++#define DMA_UNMAP(osh, pa, size, direction, p, dmah) \ ++ osl_dma_unmap((osh), (pa), (size), (direction)) ++extern uint osl_dma_map(osl_t *osh, void *va, uint size, int direction); ++extern void osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction); ++ ++ ++#define OSL_DMADDRWIDTH(osh, addrwidth) do {} while (0) ++ ++ ++ #include ++ #define OSL_WRITE_REG(osh, r, v) (bcmsdh_reg_write(NULL, (uintptr)(r), sizeof(*(r)), (v))) ++ #define OSL_READ_REG(osh, r) (bcmsdh_reg_read(NULL, (uintptr)(r), sizeof(*(r)))) ++ ++ #define SELECT_BUS_WRITE(osh, mmap_op, bus_op) if (((osl_pubinfo_t*)(osh))->mmbus) \ ++ mmap_op else bus_op ++ #define SELECT_BUS_READ(osh, mmap_op, bus_op) (((osl_pubinfo_t*)(osh))->mmbus) ? \ ++ mmap_op : bus_op ++ ++#define OSL_ERROR(bcmerror) osl_error(bcmerror) ++extern int osl_error(int bcmerror); ++ ++ ++#define PKTBUFSZ 2048 ++ ++ ++#include ++#include ++#include ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 29) ++#define OSL_SYSUPTIME() ((uint32)jiffies_to_msecs(jiffies)) ++#else ++#define OSL_SYSUPTIME() ((uint32)jiffies * (1000 / HZ)) ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 29) */ ++#define printf(fmt, args...) printk(fmt , ## args) ++#include ++#include ++ ++#define bcopy(src, dst, len) memcpy((dst), (src), (len)) ++#define bcmp(b1, b2, len) memcmp((b1), (b2), (len)) ++#define bzero(b, len) memset((b), '\0', (len)) ++ ++ ++ ++#define R_REG(osh, r) (\ ++ SELECT_BUS_READ(osh, \ ++ ({ \ ++ __typeof(*(r)) __osl_v; \ ++ BCM_REFERENCE(osh); \ ++ switch (sizeof(*(r))) { \ ++ case sizeof(uint8): __osl_v = \ ++ readb((volatile uint8*)(r)); break; \ ++ case sizeof(uint16): __osl_v = \ ++ readw((volatile uint16*)(r)); break; \ ++ case sizeof(uint32): __osl_v = \ ++ readl((volatile uint32*)(r)); break; \ ++ } \ ++ __osl_v; \ ++ }), \ ++ OSL_READ_REG(osh, r)) \ ++) ++ ++#define W_REG(osh, r, v) do { \ ++ BCM_REFERENCE(osh); \ ++ SELECT_BUS_WRITE(osh, \ ++ switch (sizeof(*(r))) { \ ++ case sizeof(uint8): writeb((uint8)(v), (volatile uint8*)(r)); break; \ ++ case sizeof(uint16): writew((uint16)(v), (volatile uint16*)(r)); break; \ ++ case sizeof(uint32): writel((uint32)(v), (volatile uint32*)(r)); break; \ ++ }, \ ++ (OSL_WRITE_REG(osh, r, v))); \ ++ } while (0) ++ ++#define AND_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) & (v)) ++#define OR_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) | (v)) ++ ++ ++#define bcopy(src, dst, len) memcpy((dst), (src), (len)) ++#define bcmp(b1, b2, len) memcmp((b1), (b2), (len)) ++#define bzero(b, len) memset((b), '\0', (len)) ++ ++ ++#define OSL_UNCACHED(va) ((void *)va) ++#define OSL_CACHED(va) ((void *)va) ++ ++#define OSL_PREF_RANGE_LD(va, sz) ++#define OSL_PREF_RANGE_ST(va, sz) ++ ++ ++#if defined(__i386__) ++#define OSL_GETCYCLES(x) rdtscl((x)) ++#else ++#define OSL_GETCYCLES(x) ((x) = 0) ++#endif ++ ++ ++#define BUSPROBE(val, addr) ({ (val) = R_REG(NULL, (addr)); 0; }) ++ ++ ++#if !defined(CONFIG_MMC_MSM7X00A) ++#define REG_MAP(pa, size) ioremap_nocache((unsigned long)(pa), (unsigned long)(size)) ++#else ++#define REG_MAP(pa, size) (void *)(0) ++#endif ++#define REG_UNMAP(va) iounmap((va)) ++ ++ ++#define R_SM(r) *(r) ++#define W_SM(r, v) (*(r) = (v)) ++#define BZERO_SM(r, len) memset((r), '\0', (len)) ++ ++ ++#include ++ ++ ++#define PKTGET(osh, len, send) osl_pktget((osh), (len)) ++#define PKTDUP(osh, skb) osl_pktdup((osh), (skb)) ++#define PKTLIST_DUMP(osh, buf) ++#define PKTDBG_TRACE(osh, pkt, bit) ++#define PKTFREE(osh, skb, send) osl_pktfree((osh), (skb), (send)) ++#ifdef CONFIG_DHD_USE_STATIC_BUF ++#define PKTGET_STATIC(osh, len, send) osl_pktget_static((osh), (len)) ++#define PKTFREE_STATIC(osh, skb, send) osl_pktfree_static((osh), (skb), (send)) ++#endif ++#define PKTDATA(osh, skb) (((struct sk_buff*)(skb))->data) ++#define PKTLEN(osh, skb) (((struct sk_buff*)(skb))->len) ++#define PKTHEADROOM(osh, skb) (PKTDATA(osh, skb)-(((struct sk_buff*)(skb))->head)) ++#define PKTTAILROOM(osh, skb) ((((struct sk_buff*)(skb))->end)-(((struct sk_buff*)(skb))->tail)) ++#define PKTNEXT(osh, skb) (((struct sk_buff*)(skb))->next) ++#define PKTSETNEXT(osh, skb, x) (((struct sk_buff*)(skb))->next = (struct sk_buff*)(x)) ++#define PKTSETLEN(osh, skb, len) __skb_trim((struct sk_buff*)(skb), (len)) ++#define PKTPUSH(osh, skb, bytes) skb_push((struct sk_buff*)(skb), (bytes)) ++#define PKTPULL(osh, skb, bytes) skb_pull((struct sk_buff*)(skb), (bytes)) ++#define PKTTAG(skb) ((void*)(((struct sk_buff*)(skb))->cb)) ++#define PKTALLOCED(osh) ((osl_pubinfo_t *)(osh))->pktalloced ++#define PKTSETPOOL(osh, skb, x, y) do {} while (0) ++#define PKTPOOL(osh, skb) FALSE ++#define PKTSHRINK(osh, m) (m) ++ ++#ifdef CTFPOOL ++#define CTFPOOL_REFILL_THRESH 3 ++typedef struct ctfpool { ++ void *head; ++ spinlock_t lock; ++ uint max_obj; ++ uint curr_obj; ++ uint obj_size; ++ uint refills; ++ uint fast_allocs; ++ uint fast_frees; ++ uint slow_allocs; ++} ctfpool_t; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) ++#define FASTBUF (1 << 16) ++#define CTFBUF (1 << 17) ++#define PKTSETFAST(osh, skb) ((((struct sk_buff*)(skb))->mac_len) |= FASTBUF) ++#define PKTCLRFAST(osh, skb) ((((struct sk_buff*)(skb))->mac_len) &= (~FASTBUF)) ++#define PKTSETCTF(osh, skb) ((((struct sk_buff*)(skb))->mac_len) |= CTFBUF) ++#define PKTCLRCTF(osh, skb) ((((struct sk_buff*)(skb))->mac_len) &= (~CTFBUF)) ++#define PKTISFAST(osh, skb) ((((struct sk_buff*)(skb))->mac_len) & FASTBUF) ++#define PKTISCTF(osh, skb) ((((struct sk_buff*)(skb))->mac_len) & CTFBUF) ++#define PKTFAST(osh, skb) (((struct sk_buff*)(skb))->mac_len) ++#else ++#define FASTBUF (1 << 0) ++#define CTFBUF (1 << 1) ++#define PKTSETFAST(osh, skb) ((((struct sk_buff*)(skb))->__unused) |= FASTBUF) ++#define PKTCLRFAST(osh, skb) ((((struct sk_buff*)(skb))->__unused) &= (~FASTBUF)) ++#define PKTSETCTF(osh, skb) ((((struct sk_buff*)(skb))->__unused) |= CTFBUF) ++#define PKTCLRCTF(osh, skb) ((((struct sk_buff*)(skb))->__unused) &= (~CTFBUF)) ++#define PKTISFAST(osh, skb) ((((struct sk_buff*)(skb))->__unused) & FASTBUF) ++#define PKTISCTF(osh, skb) ((((struct sk_buff*)(skb))->__unused) & CTFBUF) ++#define PKTFAST(osh, skb) (((struct sk_buff*)(skb))->__unused) ++#endif ++ ++#define CTFPOOLPTR(osh, skb) (((struct sk_buff*)(skb))->sk) ++#define CTFPOOLHEAD(osh, skb) (((ctfpool_t *)((struct sk_buff*)(skb))->sk)->head) ++ ++extern void *osl_ctfpool_add(osl_t *osh); ++extern void osl_ctfpool_replenish(osl_t *osh, uint thresh); ++extern int32 osl_ctfpool_init(osl_t *osh, uint numobj, uint size); ++extern void osl_ctfpool_cleanup(osl_t *osh); ++extern void osl_ctfpool_stats(osl_t *osh, void *b); ++#endif ++ ++ ++#ifdef HNDCTF ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) ++#define SKIPCT (1 << 18) ++#define PKTSETSKIPCT(osh, skb) (((struct sk_buff*)(skb))->mac_len |= SKIPCT) ++#define PKTCLRSKIPCT(osh, skb) (((struct sk_buff*)(skb))->mac_len &= (~SKIPCT)) ++#define PKTSKIPCT(osh, skb) (((struct sk_buff*)(skb))->mac_len & SKIPCT) ++#else ++#define SKIPCT (1 << 2) ++#define PKTSETSKIPCT(osh, skb) (((struct sk_buff*)(skb))->__unused |= SKIPCT) ++#define PKTCLRSKIPCT(osh, skb) (((struct sk_buff*)(skb))->__unused &= (~SKIPCT)) ++#define PKTSKIPCT(osh, skb) (((struct sk_buff*)(skb))->__unused & SKIPCT) ++#endif ++#else ++#define PKTSETSKIPCT(osh, skb) ++#define PKTCLRSKIPCT(osh, skb) ++#define PKTSKIPCT(osh, skb) ++#endif ++ ++extern void osl_pktfree(osl_t *osh, void *skb, bool send); ++extern void *osl_pktget_static(osl_t *osh, uint len); ++extern void osl_pktfree_static(osl_t *osh, void *skb, bool send); ++ ++extern void *osl_pkt_frmnative(osl_t *osh, void *skb); ++extern void *osl_pktget(osl_t *osh, uint len); ++extern void *osl_pktdup(osl_t *osh, void *skb); ++extern struct sk_buff *osl_pkt_tonative(osl_t *osh, void *pkt); ++#define PKTFRMNATIVE(osh, skb) osl_pkt_frmnative(((osl_t *)osh), (struct sk_buff*)(skb)) ++#define PKTTONATIVE(osh, pkt) osl_pkt_tonative((osl_t *)(osh), (pkt)) ++ ++#define PKTLINK(skb) (((struct sk_buff*)(skb))->prev) ++#define PKTSETLINK(skb, x) (((struct sk_buff*)(skb))->prev = (struct sk_buff*)(x)) ++#define PKTPRIO(skb) (((struct sk_buff*)(skb))->priority) ++#define PKTSETPRIO(skb, x) (((struct sk_buff*)(skb))->priority = (x)) ++#define PKTSUMNEEDED(skb) (((struct sk_buff*)(skb))->ip_summed == CHECKSUM_HW) ++#define PKTSETSUMGOOD(skb, x) (((struct sk_buff*)(skb))->ip_summed = \ ++ ((x) ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE)) ++ ++#define PKTSHARED(skb) (((struct sk_buff*)(skb))->cloned) ++ ++#define DMA_MAP(osh, va, size, direction, p, dmah) \ ++ osl_dma_map((osh), (va), (size), (direction)) ++ ++#ifdef PKTC ++ ++struct chain_node { ++ struct sk_buff *link; ++ unsigned int flags:3, pkts:9, bytes:20; ++}; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) ++#define CHAIN_NODE(skb) ((struct chain_node*)&(((struct sk_buff*)skb)->tstamp)) ++#else ++#define CHAIN_NODE(skb) ((struct chain_node*)&(((struct sk_buff*)skb)->stamp)) ++#endif ++ ++#define PKTCCNT(skb) (CHAIN_NODE(skb)->pkts) ++#define PKTCLEN(skb) (CHAIN_NODE(skb)->bytes) ++#define PKTCFLAGS(skb) (CHAIN_NODE(skb)->flags) ++#define PKTCSETCNT(skb, c) (CHAIN_NODE(skb)->pkts = (c) & ((1 << 9) - 1)) ++#define PKTCSETLEN(skb, l) (CHAIN_NODE(skb)->bytes = (l) & ((1 << 20) - 1)) ++#define PKTCSETFLAG(skb, fb) (CHAIN_NODE(skb)->flags |= (fb)) ++#define PKTCCLRFLAG(skb, fb) (CHAIN_NODE(skb)->flags &= ~(fb)) ++#define PKTCLINK(skb) (CHAIN_NODE(skb)->link) ++#define PKTSETCLINK(skb, x) (CHAIN_NODE(skb)->link = (struct sk_buff*)(x)) ++#define PKTISCHAINED(skb) (PKTCLINK(skb) != NULL) ++#define FOREACH_CHAINED_PKT(skb, nskb) \ ++ for (; (skb) != NULL; (skb) = (nskb)) \ ++ if ((nskb) = PKTCLINK(skb), PKTSETCLINK((skb), NULL), 1) ++#define PKTCFREE(osh, skb, send) \ ++do { \ ++ void *nskb; \ ++ ASSERT((skb) != NULL); \ ++ FOREACH_CHAINED_PKT((skb), nskb) { \ ++ PKTFREE((osh), (skb), (send)); \ ++ } \ ++} while (0) ++#endif ++ ++#else ++ ++ ++ ++ #define ASSERT(exp) do {} while (0) ++ ++ ++#define MALLOC(o, l) malloc(l) ++#define MFREE(o, p, l) free(p) ++#include ++ ++ ++#include ++ ++ ++#include ++ ++ ++extern void bcopy(const void *src, void *dst, size_t len); ++extern int bcmp(const void *b1, const void *b2, size_t len); ++extern void bzero(void *b, size_t len); ++#endif ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/linuxver.h b/drivers/net/wireless/bcmdhd/include/linuxver.h +new file mode 100644 +index 00000000..f242aad4 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/linuxver.h +@@ -0,0 +1,630 @@ ++/* ++ * Linux-specific abstractions to gain some independence from linux kernel versions. ++ * Pave over some 2.2 versus 2.4 versus 2.6 kernel differences. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: linuxver.h 353905 2012-08-29 07:33:08Z $ ++ */ ++ ++#ifndef _linuxver_h_ ++#define _linuxver_h_ ++ ++#include ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) ++#include ++#else ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) ++#include ++#else ++#include ++#endif ++#endif ++#include ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)) ++ ++#ifdef __UNDEF_NO_VERSION__ ++#undef __NO_VERSION__ ++#else ++#define __NO_VERSION__ ++#endif ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) ++#define module_param(_name_, _type_, _perm_) MODULE_PARM(_name_, "i") ++#define module_param_string(_name_, _string_, _size_, _perm_) \ ++ MODULE_PARM(_string_, "c" __MODULE_STRING(_size_)) ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 9)) ++#include ++#else ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++#include ++#else ++#include ++#endif ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) ++#undef IP_TOS ++#endif ++#include ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 41)) ++#include ++#else ++#include ++#ifndef work_struct ++#define work_struct tq_struct ++#endif ++#ifndef INIT_WORK ++#define INIT_WORK(_work, _func, _data) INIT_TQUEUE((_work), (_func), (_data)) ++#endif ++#ifndef schedule_work ++#define schedule_work(_work) schedule_task((_work)) ++#endif ++#ifndef flush_scheduled_work ++#define flush_scheduled_work() flush_scheduled_tasks() ++#endif ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) ++#define DAEMONIZE(a) daemonize(a); \ ++ allow_signal(SIGKILL); \ ++ allow_signal(SIGTERM); ++#else ++#define RAISE_RX_SOFTIRQ() \ ++ cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ) ++#define DAEMONIZE(a) daemonize(); \ ++ do { if (a) \ ++ strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a)))); \ ++ } while (0); ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ++#define MY_INIT_WORK(_work, _func) INIT_WORK(_work, _func) ++#else ++#define MY_INIT_WORK(_work, _func) INIT_WORK(_work, _func, _work) ++#if !(LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 18) && defined(RHEL_MAJOR) && \ ++ (RHEL_MAJOR == 5)) ++ ++typedef void (*work_func_t)(void *work); ++#endif ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) ++ ++#ifndef IRQ_NONE ++typedef void irqreturn_t; ++#define IRQ_NONE ++#define IRQ_HANDLED ++#define IRQ_RETVAL(x) ++#endif ++#else ++typedef irqreturn_t(*FN_ISR) (int irq, void *dev_id, struct pt_regs *ptregs); ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) ++#define IRQF_SHARED SA_SHIRQ ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17) ++#ifdef CONFIG_NET_RADIO ++#define CONFIG_WIRELESS_EXT ++#endif ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 67) ++#define MOD_INC_USE_COUNT ++#define MOD_DEC_USE_COUNT ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) ++#include ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) ++#include ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) ++#include ++#else ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) ++#include ++#endif ++#endif ++ ++ ++#ifndef __exit ++#define __exit ++#endif ++#ifndef __devexit ++#define __devexit ++#endif ++#ifndef __devinit ++#define __devinit __init ++#endif ++#ifndef __devinitdata ++#define __devinitdata ++#endif ++#ifndef __devexit_p ++#define __devexit_p(x) x ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)) ++ ++#define pci_get_drvdata(dev) (dev)->sysdata ++#define pci_set_drvdata(dev, value) (dev)->sysdata = (value) ++ ++ ++ ++struct pci_device_id { ++ unsigned int vendor, device; ++ unsigned int subvendor, subdevice; ++ unsigned int class, class_mask; ++ unsigned long driver_data; ++}; ++ ++struct pci_driver { ++ struct list_head node; ++ char *name; ++ const struct pci_device_id *id_table; ++ int (*probe)(struct pci_dev *dev, ++ const struct pci_device_id *id); ++ void (*remove)(struct pci_dev *dev); ++ void (*suspend)(struct pci_dev *dev); ++ void (*resume)(struct pci_dev *dev); ++}; ++ ++#define MODULE_DEVICE_TABLE(type, name) ++#define PCI_ANY_ID (~0) ++ ++ ++#define pci_module_init pci_register_driver ++extern int pci_register_driver(struct pci_driver *drv); ++extern void pci_unregister_driver(struct pci_driver *drv); ++ ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)) ++#define pci_module_init pci_register_driver ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)) ++#ifdef MODULE ++#define module_init(x) int init_module(void) { return x(); } ++#define module_exit(x) void cleanup_module(void) { x(); } ++#else ++#define module_init(x) __initcall(x); ++#define module_exit(x) __exitcall(x); ++#endif ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) ++#define WL_USE_NETDEV_OPS ++#else ++#undef WL_USE_NETDEV_OPS ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) && defined(CONFIG_RFKILL) ++#define WL_CONFIG_RFKILL ++#else ++#undef WL_CONFIG_RFKILL ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 48)) ++#define list_for_each(pos, head) \ ++ for (pos = (head)->next; pos != (head); pos = pos->next) ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 13)) ++#define pci_resource_start(dev, bar) ((dev)->base_address[(bar)]) ++#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 44)) ++#define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start) ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 23)) ++#define pci_enable_device(dev) do { } while (0) ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 14)) ++#define net_device device ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 42)) ++ ++ ++ ++#ifndef PCI_DMA_TODEVICE ++#define PCI_DMA_TODEVICE 1 ++#define PCI_DMA_FROMDEVICE 2 ++#endif ++ ++typedef u32 dma_addr_t; ++ ++ ++static inline int get_order(unsigned long size) ++{ ++ int order; ++ ++ size = (size-1) >> (PAGE_SHIFT-1); ++ order = -1; ++ do { ++ size >>= 1; ++ order++; ++ } while (size); ++ return order; ++} ++ ++static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, ++ dma_addr_t *dma_handle) ++{ ++ void *ret; ++ int gfp = GFP_ATOMIC | GFP_DMA; ++ ++ ret = (void *)__get_free_pages(gfp, get_order(size)); ++ ++ if (ret != NULL) { ++ memset(ret, 0, size); ++ *dma_handle = virt_to_bus(ret); ++ } ++ return ret; ++} ++static inline void pci_free_consistent(struct pci_dev *hwdev, size_t size, ++ void *vaddr, dma_addr_t dma_handle) ++{ ++ free_pages((unsigned long)vaddr, get_order(size)); ++} ++#define pci_map_single(cookie, address, size, dir) virt_to_bus(address) ++#define pci_unmap_single(cookie, address, size, dir) ++ ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 43)) ++ ++#define dev_kfree_skb_any(a) dev_kfree_skb(a) ++#define netif_down(dev) do { (dev)->start = 0; } while (0) ++ ++ ++#ifndef _COMPAT_NETDEVICE_H ++ ++ ++ ++#define dev_kfree_skb_irq(a) dev_kfree_skb(a) ++#define netif_wake_queue(dev) \ ++ do { clear_bit(0, &(dev)->tbusy); mark_bh(NET_BH); } while (0) ++#define netif_stop_queue(dev) set_bit(0, &(dev)->tbusy) ++ ++static inline void netif_start_queue(struct net_device *dev) ++{ ++ dev->tbusy = 0; ++ dev->interrupt = 0; ++ dev->start = 1; ++} ++ ++#define netif_queue_stopped(dev) (dev)->tbusy ++#define netif_running(dev) (dev)->start ++ ++#endif ++ ++#define netif_device_attach(dev) netif_start_queue(dev) ++#define netif_device_detach(dev) netif_stop_queue(dev) ++ ++ ++#define tasklet_struct tq_struct ++static inline void tasklet_schedule(struct tasklet_struct *tasklet) ++{ ++ queue_task(tasklet, &tq_immediate); ++ mark_bh(IMMEDIATE_BH); ++} ++ ++static inline void tasklet_init(struct tasklet_struct *tasklet, ++ void (*func)(unsigned long), ++ unsigned long data) ++{ ++ tasklet->next = NULL; ++ tasklet->sync = 0; ++ tasklet->routine = (void (*)(void *))func; ++ tasklet->data = (void *)data; ++} ++#define tasklet_kill(tasklet) { do {} while (0); } ++ ++ ++#define del_timer_sync(timer) del_timer(timer) ++ ++#else ++ ++#define netif_down(dev) ++ ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 3)) ++ ++ ++#define PREPARE_TQUEUE(_tq, _routine, _data) \ ++ do { \ ++ (_tq)->routine = _routine; \ ++ (_tq)->data = _data; \ ++ } while (0) ++ ++ ++#define INIT_TQUEUE(_tq, _routine, _data) \ ++ do { \ ++ INIT_LIST_HEAD(&(_tq)->list); \ ++ (_tq)->sync = 0; \ ++ PREPARE_TQUEUE((_tq), (_routine), (_data)); \ ++ } while (0) ++ ++#endif ++ ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 9) ++#define PCI_SAVE_STATE(a, b) pci_save_state(a) ++#define PCI_RESTORE_STATE(a, b) pci_restore_state(a) ++#else ++#define PCI_SAVE_STATE(a, b) pci_save_state(a, b) ++#define PCI_RESTORE_STATE(a, b) pci_restore_state(a, b) ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6)) ++static inline int ++pci_save_state(struct pci_dev *dev, u32 *buffer) ++{ ++ int i; ++ if (buffer) { ++ for (i = 0; i < 16; i++) ++ pci_read_config_dword(dev, i * 4, &buffer[i]); ++ } ++ return 0; ++} ++ ++static inline int ++pci_restore_state(struct pci_dev *dev, u32 *buffer) ++{ ++ int i; ++ ++ if (buffer) { ++ for (i = 0; i < 16; i++) ++ pci_write_config_dword(dev, i * 4, buffer[i]); ++ } ++ ++ else { ++ for (i = 0; i < 6; i ++) ++ pci_write_config_dword(dev, ++ PCI_BASE_ADDRESS_0 + (i * 4), ++ pci_resource_start(dev, i)); ++ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); ++ } ++ return 0; ++} ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 19)) ++#define read_c0_count() read_32bit_cp0_register(CP0_COUNT) ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)) ++#ifndef SET_MODULE_OWNER ++#define SET_MODULE_OWNER(dev) do {} while (0) ++#define OLD_MOD_INC_USE_COUNT MOD_INC_USE_COUNT ++#define OLD_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT ++#else ++#define OLD_MOD_INC_USE_COUNT do {} while (0) ++#define OLD_MOD_DEC_USE_COUNT do {} while (0) ++#endif ++#else ++#ifndef SET_MODULE_OWNER ++#define SET_MODULE_OWNER(dev) do {} while (0) ++#endif ++#ifndef MOD_INC_USE_COUNT ++#define MOD_INC_USE_COUNT do {} while (0) ++#endif ++#ifndef MOD_DEC_USE_COUNT ++#define MOD_DEC_USE_COUNT do {} while (0) ++#endif ++#define OLD_MOD_INC_USE_COUNT MOD_INC_USE_COUNT ++#define OLD_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT ++#endif ++ ++#ifndef SET_NETDEV_DEV ++#define SET_NETDEV_DEV(net, pdev) do {} while (0) ++#endif ++ ++#ifndef HAVE_FREE_NETDEV ++#define free_netdev(dev) kfree(dev) ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) ++ ++#define af_packet_priv data ++#endif ++ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) ++#define DRV_SUSPEND_STATE_TYPE pm_message_t ++#else ++#define DRV_SUSPEND_STATE_TYPE uint32 ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ++#define CHECKSUM_HW CHECKSUM_PARTIAL ++#endif ++ ++typedef struct { ++ void *parent; ++ struct task_struct *p_task; ++ long thr_pid; ++ int prio; ++ struct semaphore sema; ++ int terminated; ++ struct completion completed; ++} tsk_ctl_t; ++ ++ ++ ++ ++#ifdef DHD_DEBUG ++#define DBG_THR(x) printk x ++#else ++#define DBG_THR(x) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) ++#define SMP_RD_BARRIER_DEPENDS(x) smp_read_barrier_depends(x) ++#else ++#define SMP_RD_BARRIER_DEPENDS(x) smp_rmb(x) ++#endif ++ ++ ++#define PROC_START(thread_func, owner, tsk_ctl, flags) \ ++{ \ ++ sema_init(&((tsk_ctl)->sema), 0); \ ++ init_completion(&((tsk_ctl)->completed)); \ ++ (tsk_ctl)->parent = owner; \ ++ (tsk_ctl)->terminated = FALSE; \ ++ (tsk_ctl)->thr_pid = kernel_thread(thread_func, tsk_ctl, flags); \ ++ DBG_THR(("%s thr:%lx created\n", __FUNCTION__, (tsk_ctl)->thr_pid)); \ ++ if ((tsk_ctl)->thr_pid > 0) \ ++ wait_for_completion(&((tsk_ctl)->completed)); \ ++ DBG_THR(("%s thr:%lx started\n", __FUNCTION__, (tsk_ctl)->thr_pid)); \ ++} ++ ++#ifdef USE_KTHREAD_API ++#define PROC_START2(thread_func, owner, tsk_ctl, flags, name) \ ++{ \ ++ sema_init(&((tsk_ctl)->sema), 0); \ ++ init_completion(&((tsk_ctl)->completed)); \ ++ (tsk_ctl)->parent = owner; \ ++ (tsk_ctl)->terminated = FALSE; \ ++ (tsk_ctl)->p_task = kthread_run(thread_func, tsk_ctl, (char*)name); \ ++ (tsk_ctl)->thr_pid = (tsk_ctl)->p_task->pid; \ ++ DBG_THR(("%s thr:%lx created\n", __FUNCTION__, (tsk_ctl)->thr_pid)); \ ++} ++#endif ++ ++#define PROC_STOP(tsk_ctl) \ ++{ \ ++ (tsk_ctl)->terminated = TRUE; \ ++ smp_wmb(); \ ++ up(&((tsk_ctl)->sema)); \ ++ wait_for_completion(&((tsk_ctl)->completed)); \ ++ DBG_THR(("%s thr:%lx terminated OK\n", __FUNCTION__, (tsk_ctl)->thr_pid)); \ ++ (tsk_ctl)->thr_pid = -1; \ ++} ++ ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) ++#define KILL_PROC(nr, sig) \ ++{ \ ++struct task_struct *tsk; \ ++struct pid *pid; \ ++pid = find_get_pid((pid_t)nr); \ ++tsk = pid_task(pid, PIDTYPE_PID); \ ++if (tsk) send_sig(sig, tsk, 1); \ ++} ++#else ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (LINUX_VERSION_CODE <= \ ++ KERNEL_VERSION(2, 6, 30)) ++#define KILL_PROC(pid, sig) \ ++{ \ ++ struct task_struct *tsk; \ ++ tsk = find_task_by_vpid(pid); \ ++ if (tsk) send_sig(sig, tsk, 1); \ ++} ++#else ++#define KILL_PROC(pid, sig) \ ++{ \ ++ kill_proc(pid, sig, 1); \ ++} ++#endif ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) ++#include ++#include ++#else ++#include ++ ++#define __wait_event_interruptible_timeout(wq, condition, ret) \ ++do { \ ++ wait_queue_t __wait; \ ++ init_waitqueue_entry(&__wait, current); \ ++ \ ++ add_wait_queue(&wq, &__wait); \ ++ for (;;) { \ ++ set_current_state(TASK_INTERRUPTIBLE); \ ++ if (condition) \ ++ break; \ ++ if (!signal_pending(current)) { \ ++ ret = schedule_timeout(ret); \ ++ if (!ret) \ ++ break; \ ++ continue; \ ++ } \ ++ ret = -ERESTARTSYS; \ ++ break; \ ++ } \ ++ current->state = TASK_RUNNING; \ ++ remove_wait_queue(&wq, &__wait); \ ++} while (0) ++ ++#define wait_event_interruptible_timeout(wq, condition, timeout) \ ++({ \ ++ long __ret = timeout; \ ++ if (!(condition)) \ ++ __wait_event_interruptible_timeout(wq, condition, __ret); \ ++ __ret; \ ++}) ++ ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)) ++#define DEV_PRIV(dev) (dev->priv) ++#else ++#define DEV_PRIV(dev) netdev_priv(dev) ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) ++#define WL_ISR(i, d, p) wl_isr((i), (d)) ++#else ++#define WL_ISR(i, d, p) wl_isr((i), (d), (p)) ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) ++#define netdev_priv(dev) dev->priv ++#endif ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/miniopt.h b/drivers/net/wireless/bcmdhd/include/miniopt.h +new file mode 100644 +index 00000000..c1eca68a +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/miniopt.h +@@ -0,0 +1,77 @@ ++/* ++ * Command line options parser. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * $Id: miniopt.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++ ++#ifndef MINI_OPT_H ++#define MINI_OPT_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* ---- Include Files ---------------------------------------------------- */ ++/* ---- Constants and Types ---------------------------------------------- */ ++ ++#define MINIOPT_MAXKEY 128 /* Max options */ ++typedef struct miniopt { ++ ++ /* These are persistent after miniopt_init() */ ++ const char* name; /* name for prompt in error strings */ ++ const char* flags; /* option chars that take no args */ ++ bool longflags; /* long options may be flags */ ++ bool opt_end; /* at end of options (passed a "--") */ ++ ++ /* These are per-call to miniopt() */ ++ ++ int consumed; /* number of argv entries cosumed in ++ * the most recent call to miniopt() ++ */ ++ bool positional; ++ bool good_int; /* 'val' member is the result of a sucessful ++ * strtol conversion of the option value ++ */ ++ char opt; ++ char key[MINIOPT_MAXKEY]; ++ char* valstr; /* positional param, or value for the option, ++ * or null if the option had ++ * no accompanying value ++ */ ++ uint uval; /* strtol translation of valstr */ ++ int val; /* strtol translation of valstr */ ++} miniopt_t; ++ ++void miniopt_init(miniopt_t *t, const char* name, const char* flags, bool longflags); ++int miniopt(miniopt_t *t, char **argv); ++ ++ ++/* ---- Variable Externs ------------------------------------------------- */ ++/* ---- Function Prototypes ---------------------------------------------- */ ++ ++ ++#ifdef __cplusplus ++ } ++#endif ++ ++#endif /* MINI_OPT_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/msgtrace.h b/drivers/net/wireless/bcmdhd/include/msgtrace.h +new file mode 100644 +index 00000000..7c5fd810 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/msgtrace.h +@@ -0,0 +1,74 @@ ++/* ++ * Trace messages sent over HBUS ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: msgtrace.h 281527 2011-09-02 17:12:53Z $ ++ */ ++ ++#ifndef _MSGTRACE_H ++#define _MSGTRACE_H ++ ++#ifndef _TYPEDEFS_H_ ++#include ++#endif ++ ++ ++/* This marks the start of a packed structure section. */ ++#include ++ ++#define MSGTRACE_VERSION 1 ++ ++/* Message trace header */ ++typedef BWL_PRE_PACKED_STRUCT struct msgtrace_hdr { ++ uint8 version; ++ uint8 spare; ++ uint16 len; /* Len of the trace */ ++ uint32 seqnum; /* Sequence number of message. Useful if the messsage has been lost ++ * because of DMA error or a bus reset (ex: SDIO Func2) ++ */ ++ uint32 discarded_bytes; /* Number of discarded bytes because of trace overflow */ ++ uint32 discarded_printf; /* Number of discarded printf because of trace overflow */ ++} BWL_POST_PACKED_STRUCT msgtrace_hdr_t; ++ ++#define MSGTRACE_HDRLEN sizeof(msgtrace_hdr_t) ++ ++/* The hbus driver generates traces when sending a trace message. This causes endless traces. ++ * This flag must be set to TRUE in any hbus traces. The flag is reset in the function msgtrace_put. ++ * This prevents endless traces but generates hasardous lost of traces only in bus device code. ++ * It is recommendat to set this flag in macro SD_TRACE but not in SD_ERROR for avoiding missing ++ * hbus error traces. hbus error trace should not generates endless traces. ++ */ ++extern bool msgtrace_hbus_trace; ++ ++typedef void (*msgtrace_func_send_t)(void *hdl1, void *hdl2, uint8 *hdr, ++ uint16 hdrlen, uint8 *buf, uint16 buflen); ++extern void msgtrace_start(void); ++extern void msgtrace_stop(void); ++extern void msgtrace_sent(void); ++extern void msgtrace_put(char *buf, int count); ++extern void msgtrace_init(void *hdl1, void *hdl2, msgtrace_func_send_t func_send); ++extern bool msgtrace_event_enabled(void); ++ ++/* This marks the end of a packed structure section. */ ++#include ++ ++#endif /* _MSGTRACE_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/osl.h b/drivers/net/wireless/bcmdhd/include/osl.h +new file mode 100644 +index 00000000..ca171d8a +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/osl.h +@@ -0,0 +1,88 @@ ++/* ++ * OS Abstraction Layer ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: osl.h 320905 2012-03-13 15:33:25Z $ ++ */ ++ ++#ifndef _osl_h_ ++#define _osl_h_ ++ ++ ++typedef struct osl_info osl_t; ++typedef struct osl_dmainfo osldma_t; ++ ++#define OSL_PKTTAG_SZ 32 ++ ++ ++typedef void (*pktfree_cb_fn_t)(void *ctx, void *pkt, unsigned int status); ++ ++ ++typedef unsigned int (*osl_rreg_fn_t)(void *ctx, volatile void *reg, unsigned int size); ++typedef void (*osl_wreg_fn_t)(void *ctx, volatile void *reg, unsigned int val, unsigned int size); ++ ++ ++#include ++ ++#ifndef PKTDBG_TRACE ++#define PKTDBG_TRACE(osh, pkt, bit) ++#endif ++ ++#define PKTCTFMAP(osh, p) ++ ++ ++ ++#define SET_REG(osh, r, mask, val) W_REG((osh), (r), ((R_REG((osh), r) & ~(mask)) | (val))) ++ ++#ifndef AND_REG ++#define AND_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) & (v)) ++#endif ++ ++#ifndef OR_REG ++#define OR_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) | (v)) ++#endif ++ ++#if !defined(OSL_SYSUPTIME) ++#define OSL_SYSUPTIME() (0) ++#define OSL_SYSUPTIME_SUPPORT FALSE ++#else ++#define OSL_SYSUPTIME_SUPPORT TRUE ++#endif ++ ++#if !defined(PKTC) ++#define PKTCCNT(skb) (0) ++#define PKTCLEN(skb) (0) ++#define PKTCFLAGS(skb) (0) ++#define PKTCSETCNT(skb, c) ++#define PKTCSETLEN(skb, l) ++#define PKTCSETFLAG(skb, fb) ++#define PKTCCLRFLAG(skb, fb) ++#define PKTCLINK(skb) PKTLINK(skb) ++#define PKTSETCLINK(skb, x) PKTSETLINK((skb), (x)) ++#define PKTISCHAINED(skb) FALSE ++#define FOREACH_CHAINED_PKT(skb, nskb) \ ++ for ((nskb) = NULL; (skb) != NULL; (skb) = (nskb)) ++#define PKTCFREE PKTFREE ++#endif ++ ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/packed_section_end.h b/drivers/net/wireless/bcmdhd/include/packed_section_end.h +new file mode 100644 +index 00000000..24ff4672 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/packed_section_end.h +@@ -0,0 +1,53 @@ ++/* ++ * Declare directives for structure packing. No padding will be provided ++ * between the members of packed structures, and therefore, there is no ++ * guarantee that structure members will be aligned. ++ * ++ * Declaring packed structures is compiler specific. In order to handle all ++ * cases, packed structures should be delared as: ++ * ++ * #include ++ * ++ * typedef BWL_PRE_PACKED_STRUCT struct foobar_t { ++ * some_struct_members; ++ * } BWL_POST_PACKED_STRUCT foobar_t; ++ * ++ * #include ++ * ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * $Id: packed_section_end.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++ ++ ++#ifdef BWL_PACKED_SECTION ++ #undef BWL_PACKED_SECTION ++#else ++ #error "BWL_PACKED_SECTION is NOT defined!" ++#endif ++ ++ ++ ++ ++ ++#undef BWL_PRE_PACKED_STRUCT ++#undef BWL_POST_PACKED_STRUCT +diff --git a/drivers/net/wireless/bcmdhd/include/packed_section_start.h b/drivers/net/wireless/bcmdhd/include/packed_section_start.h +new file mode 100644 +index 00000000..7fce0ddf +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/packed_section_start.h +@@ -0,0 +1,60 @@ ++/* ++ * Declare directives for structure packing. No padding will be provided ++ * between the members of packed structures, and therefore, there is no ++ * guarantee that structure members will be aligned. ++ * ++ * Declaring packed structures is compiler specific. In order to handle all ++ * cases, packed structures should be delared as: ++ * ++ * #include ++ * ++ * typedef BWL_PRE_PACKED_STRUCT struct foobar_t { ++ * some_struct_members; ++ * } BWL_POST_PACKED_STRUCT foobar_t; ++ * ++ * #include ++ * ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * $Id: packed_section_start.h 286783 2011-09-29 06:18:57Z $ ++ */ ++ ++ ++ ++#ifdef BWL_PACKED_SECTION ++ #error "BWL_PACKED_SECTION is already defined!" ++#else ++ #define BWL_PACKED_SECTION ++#endif ++ ++ ++ ++ ++ ++#if defined(__GNUC__) || defined(__lint) ++ #define BWL_PRE_PACKED_STRUCT ++ #define BWL_POST_PACKED_STRUCT __attribute__ ((packed)) ++#elif defined(__CC_ARM) ++ #define BWL_PRE_PACKED_STRUCT __packed ++ #define BWL_POST_PACKED_STRUCT ++#else ++ #error "Unknown compiler!" ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/pcicfg.h b/drivers/net/wireless/bcmdhd/include/pcicfg.h +new file mode 100644 +index 00000000..5f7df6a7 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/pcicfg.h +@@ -0,0 +1,90 @@ ++/* ++ * pcicfg.h: PCI configuration constants and structures. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: pcicfg.h 309193 2012-01-19 00:03:57Z $ ++ */ ++ ++#ifndef _h_pcicfg_ ++#define _h_pcicfg_ ++ ++ ++#define PCI_CFG_VID 0 ++#define PCI_CFG_DID 2 ++#define PCI_CFG_CMD 4 ++#define PCI_CFG_STAT 6 ++#define PCI_CFG_REV 8 ++#define PCI_CFG_PROGIF 9 ++#define PCI_CFG_SUBCL 0xa ++#define PCI_CFG_BASECL 0xb ++#define PCI_CFG_CLSZ 0xc ++#define PCI_CFG_LATTIM 0xd ++#define PCI_CFG_HDR 0xe ++#define PCI_CFG_BIST 0xf ++#define PCI_CFG_BAR0 0x10 ++#define PCI_CFG_BAR1 0x14 ++#define PCI_CFG_BAR2 0x18 ++#define PCI_CFG_BAR3 0x1c ++#define PCI_CFG_BAR4 0x20 ++#define PCI_CFG_BAR5 0x24 ++#define PCI_CFG_CIS 0x28 ++#define PCI_CFG_SVID 0x2c ++#define PCI_CFG_SSID 0x2e ++#define PCI_CFG_ROMBAR 0x30 ++#define PCI_CFG_CAPPTR 0x34 ++#define PCI_CFG_INT 0x3c ++#define PCI_CFG_PIN 0x3d ++#define PCI_CFG_MINGNT 0x3e ++#define PCI_CFG_MAXLAT 0x3f ++#define PCI_BAR0_WIN 0x80 ++#define PCI_BAR1_WIN 0x84 ++#define PCI_SPROM_CONTROL 0x88 ++#define PCI_BAR1_CONTROL 0x8c ++#define PCI_INT_STATUS 0x90 ++#define PCI_INT_MASK 0x94 ++#define PCI_TO_SB_MB 0x98 ++#define PCI_BACKPLANE_ADDR 0xa0 ++#define PCI_BACKPLANE_DATA 0xa4 ++#define PCI_CLK_CTL_ST 0xa8 ++#define PCI_BAR0_WIN2 0xac ++#define PCI_GPIO_IN 0xb0 ++#define PCI_GPIO_OUT 0xb4 ++#define PCI_GPIO_OUTEN 0xb8 ++ ++#define PCI_BAR0_SHADOW_OFFSET (2 * 1024) ++#define PCI_BAR0_SPROM_OFFSET (4 * 1024) ++#define PCI_BAR0_PCIREGS_OFFSET (6 * 1024) ++#define PCI_BAR0_PCISBR_OFFSET (4 * 1024) ++ ++#define PCIE2_BAR0_WIN2 0x70 ++#define PCIE2_BAR0_CORE2_WIN 0x74 ++#define PCIE2_BAR0_CORE2_WIN2 0x78 ++ ++#define PCI_BAR0_WINSZ (16 * 1024) ++ ++#define PCI_16KB0_PCIREGS_OFFSET (8 * 1024) ++#define PCI_16KB0_CCREGS_OFFSET (12 * 1024) ++#define PCI_16KBB0_WINSZ (16 * 1024) ++ ++ ++#define PCI_CONFIG_SPACE_SIZE 256 ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11.h b/drivers/net/wireless/bcmdhd/include/proto/802.11.h +new file mode 100644 +index 00000000..dc648eef +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/802.11.h +@@ -0,0 +1,2243 @@ ++/* ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * Fundamental types and constants relating to 802.11 ++ * ++ * $Id: 802.11.h 346820 2012-07-24 13:53:12Z $ ++ */ ++ ++#ifndef _802_11_H_ ++#define _802_11_H_ ++ ++#ifndef _TYPEDEFS_H_ ++#include ++#endif ++ ++#ifndef _NET_ETHERNET_H_ ++#include ++#endif ++ ++#include ++ ++ ++#include ++ ++ ++#define DOT11_TU_TO_US 1024 ++ ++ ++#define DOT11_A3_HDR_LEN 24 ++#define DOT11_A4_HDR_LEN 30 ++#define DOT11_MAC_HDR_LEN DOT11_A3_HDR_LEN ++#define DOT11_FCS_LEN 4 ++#define DOT11_ICV_LEN 4 ++#define DOT11_ICV_AES_LEN 8 ++#define DOT11_QOS_LEN 2 ++#define DOT11_HTC_LEN 4 ++ ++#define DOT11_KEY_INDEX_SHIFT 6 ++#define DOT11_IV_LEN 4 ++#define DOT11_IV_TKIP_LEN 8 ++#define DOT11_IV_AES_OCB_LEN 4 ++#define DOT11_IV_AES_CCM_LEN 8 ++#define DOT11_IV_MAX_LEN 8 ++ ++ ++#define DOT11_MAX_MPDU_BODY_LEN 2304 ++ ++#define DOT11_MAX_MPDU_LEN (DOT11_A4_HDR_LEN + \ ++ DOT11_QOS_LEN + \ ++ DOT11_IV_AES_CCM_LEN + \ ++ DOT11_MAX_MPDU_BODY_LEN + \ ++ DOT11_ICV_LEN + \ ++ DOT11_FCS_LEN) ++ ++#define DOT11_MAX_SSID_LEN 32 ++ ++ ++#define DOT11_DEFAULT_RTS_LEN 2347 ++#define DOT11_MAX_RTS_LEN 2347 ++ ++ ++#define DOT11_MIN_FRAG_LEN 256 ++#define DOT11_MAX_FRAG_LEN 2346 ++#define DOT11_DEFAULT_FRAG_LEN 2346 ++ ++ ++#define DOT11_MIN_BEACON_PERIOD 1 ++#define DOT11_MAX_BEACON_PERIOD 0xFFFF ++ ++ ++#define DOT11_MIN_DTIM_PERIOD 1 ++#define DOT11_MAX_DTIM_PERIOD 0xFF ++ ++ ++#define DOT11_LLC_SNAP_HDR_LEN 8 ++#define DOT11_OUI_LEN 3 ++BWL_PRE_PACKED_STRUCT struct dot11_llc_snap_header { ++ uint8 dsap; ++ uint8 ssap; ++ uint8 ctl; ++ uint8 oui[DOT11_OUI_LEN]; ++ uint16 type; ++} BWL_POST_PACKED_STRUCT; ++ ++ ++#define RFC1042_HDR_LEN (ETHER_HDR_LEN + DOT11_LLC_SNAP_HDR_LEN) ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_header { ++ uint16 fc; ++ uint16 durid; ++ struct ether_addr a1; ++ struct ether_addr a2; ++ struct ether_addr a3; ++ uint16 seq; ++ struct ether_addr a4; ++} BWL_POST_PACKED_STRUCT; ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_rts_frame { ++ uint16 fc; ++ uint16 durid; ++ struct ether_addr ra; ++ struct ether_addr ta; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_RTS_LEN 16 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_cts_frame { ++ uint16 fc; ++ uint16 durid; ++ struct ether_addr ra; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_CTS_LEN 10 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_ack_frame { ++ uint16 fc; ++ uint16 durid; ++ struct ether_addr ra; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_ACK_LEN 10 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_ps_poll_frame { ++ uint16 fc; ++ uint16 durid; ++ struct ether_addr bssid; ++ struct ether_addr ta; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_PS_POLL_LEN 16 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_cf_end_frame { ++ uint16 fc; ++ uint16 durid; ++ struct ether_addr ra; ++ struct ether_addr bssid; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_CS_END_LEN 16 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_action_wifi_vendor_specific { ++ uint8 category; ++ uint8 OUI[3]; ++ uint8 type; ++ uint8 subtype; ++ uint8 data[1040]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_action_wifi_vendor_specific dot11_action_wifi_vendor_specific_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_action_vs_frmhdr { ++ uint8 category; ++ uint8 OUI[3]; ++ uint8 type; ++ uint8 subtype; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_action_vs_frmhdr dot11_action_vs_frmhdr_t; ++#define DOT11_ACTION_VS_HDR_LEN 6 ++ ++#define BCM_ACTION_OUI_BYTE0 0x00 ++#define BCM_ACTION_OUI_BYTE1 0x90 ++#define BCM_ACTION_OUI_BYTE2 0x4c ++ ++ ++#define DOT11_BA_CTL_POLICY_NORMAL 0x0000 ++#define DOT11_BA_CTL_POLICY_NOACK 0x0001 ++#define DOT11_BA_CTL_POLICY_MASK 0x0001 ++ ++#define DOT11_BA_CTL_MTID 0x0002 ++#define DOT11_BA_CTL_COMPRESSED 0x0004 ++ ++#define DOT11_BA_CTL_NUMMSDU_MASK 0x0FC0 ++#define DOT11_BA_CTL_NUMMSDU_SHIFT 6 ++ ++#define DOT11_BA_CTL_TID_MASK 0xF000 ++#define DOT11_BA_CTL_TID_SHIFT 12 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_ctl_header { ++ uint16 fc; ++ uint16 durid; ++ struct ether_addr ra; ++ struct ether_addr ta; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_CTL_HDR_LEN 16 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_bar { ++ uint16 bar_control; ++ uint16 seqnum; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_BAR_LEN 4 ++ ++#define DOT11_BA_BITMAP_LEN 128 ++#define DOT11_BA_CMP_BITMAP_LEN 8 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_ba { ++ uint16 ba_control; ++ uint16 seqnum; ++ uint8 bitmap[DOT11_BA_BITMAP_LEN]; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_BA_LEN 4 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_management_header { ++ uint16 fc; ++ uint16 durid; ++ struct ether_addr da; ++ struct ether_addr sa; ++ struct ether_addr bssid; ++ uint16 seq; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_MGMT_HDR_LEN 24 ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_bcn_prb { ++ uint32 timestamp[2]; ++ uint16 beacon_interval; ++ uint16 capability; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_BCN_PRB_LEN 12 ++#define DOT11_BCN_PRB_FIXED_LEN 12 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_auth { ++ uint16 alg; ++ uint16 seq; ++ uint16 status; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_AUTH_FIXED_LEN 6 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_assoc_req { ++ uint16 capability; ++ uint16 listen; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_ASSOC_REQ_FIXED_LEN 4 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_reassoc_req { ++ uint16 capability; ++ uint16 listen; ++ struct ether_addr ap; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_REASSOC_REQ_FIXED_LEN 10 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_assoc_resp { ++ uint16 capability; ++ uint16 status; ++ uint16 aid; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_ASSOC_RESP_FIXED_LEN 6 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_action_measure { ++ uint8 category; ++ uint8 action; ++ uint8 token; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_ACTION_MEASURE_LEN 3 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_action_ht_ch_width { ++ uint8 category; ++ uint8 action; ++ uint8 ch_width; ++} BWL_POST_PACKED_STRUCT; ++ ++BWL_PRE_PACKED_STRUCT struct dot11_action_ht_mimops { ++ uint8 category; ++ uint8 action; ++ uint8 control; ++} BWL_POST_PACKED_STRUCT; ++ ++BWL_PRE_PACKED_STRUCT struct dot11_action_sa_query { ++ uint8 category; ++ uint8 action; ++ uint16 id; ++} BWL_POST_PACKED_STRUCT; ++ ++#define SM_PWRSAVE_ENABLE 1 ++#define SM_PWRSAVE_MODE 2 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_power_cnst { ++ uint8 id; ++ uint8 len; ++ uint8 power; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_power_cnst dot11_power_cnst_t; ++ ++BWL_PRE_PACKED_STRUCT struct dot11_power_cap { ++ uint8 min; ++ uint8 max; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_power_cap dot11_power_cap_t; ++ ++BWL_PRE_PACKED_STRUCT struct dot11_tpc_rep { ++ uint8 id; ++ uint8 len; ++ uint8 tx_pwr; ++ uint8 margin; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_tpc_rep dot11_tpc_rep_t; ++#define DOT11_MNG_IE_TPC_REPORT_LEN 2 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_supp_channels { ++ uint8 id; ++ uint8 len; ++ uint8 first_channel; ++ uint8 num_channels; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_supp_channels dot11_supp_channels_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_extch { ++ uint8 id; ++ uint8 len; ++ uint8 extch; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_extch dot11_extch_ie_t; ++ ++BWL_PRE_PACKED_STRUCT struct dot11_brcm_extch { ++ uint8 id; ++ uint8 len; ++ uint8 oui[3]; ++ uint8 type; ++ uint8 extch; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_brcm_extch dot11_brcm_extch_ie_t; ++ ++#define BRCM_EXTCH_IE_LEN 5 ++#define BRCM_EXTCH_IE_TYPE 53 ++#define DOT11_EXTCH_IE_LEN 1 ++#define DOT11_EXT_CH_MASK 0x03 ++#define DOT11_EXT_CH_UPPER 0x01 ++#define DOT11_EXT_CH_LOWER 0x03 ++#define DOT11_EXT_CH_NONE 0x00 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_action_frmhdr { ++ uint8 category; ++ uint8 action; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_ACTION_FRMHDR_LEN 2 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_channel_switch { ++ uint8 id; ++ uint8 len; ++ uint8 mode; ++ uint8 channel; ++ uint8 count; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_channel_switch dot11_chan_switch_ie_t; ++ ++#define DOT11_SWITCH_IE_LEN 3 ++ ++#define DOT11_CSA_MODE_ADVISORY 0 ++#define DOT11_CSA_MODE_NO_TX 1 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_action_switch_channel { ++ uint8 category; ++ uint8 action; ++ dot11_chan_switch_ie_t chan_switch_ie; ++ dot11_brcm_extch_ie_t extch_ie; ++} BWL_POST_PACKED_STRUCT; ++ ++BWL_PRE_PACKED_STRUCT struct dot11_csa_body { ++ uint8 mode; ++ uint8 reg; ++ uint8 channel; ++ uint8 count; ++} BWL_POST_PACKED_STRUCT; ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_ext_csa { ++ uint8 id; ++ uint8 len; ++ struct dot11_csa_body b; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_ext_csa dot11_ext_csa_ie_t; ++#define DOT11_EXT_CSA_IE_LEN 4 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_action_ext_csa { ++ uint8 category; ++ uint8 action; ++ dot11_ext_csa_ie_t chan_switch_ie; ++} BWL_POST_PACKED_STRUCT; ++ ++BWL_PRE_PACKED_STRUCT struct dot11y_action_ext_csa { ++ uint8 category; ++ uint8 action; ++ struct dot11_csa_body b; ++} BWL_POST_PACKED_STRUCT; ++ ++BWL_PRE_PACKED_STRUCT struct dot11_obss_coex { ++ uint8 id; ++ uint8 len; ++ uint8 info; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_obss_coex dot11_obss_coex_t; ++#define DOT11_OBSS_COEXINFO_LEN 1 ++ ++#define DOT11_OBSS_COEX_INFO_REQ 0x01 ++#define DOT11_OBSS_COEX_40MHZ_INTOLERANT 0x02 ++#define DOT11_OBSS_COEX_20MHZ_WIDTH_REQ 0x04 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_obss_chanlist { ++ uint8 id; ++ uint8 len; ++ uint8 regclass; ++ uint8 chanlist[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_obss_chanlist dot11_obss_chanlist_t; ++#define DOT11_OBSS_CHANLIST_FIXED_LEN 1 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_extcap_ie { ++ uint8 id; ++ uint8 len; ++ uint8 cap[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_extcap_ie dot11_extcap_ie_t; ++ ++#define DOT11_EXTCAP_LEN_MAX 7 ++#define DOT11_EXTCAP_LEN_COEX 1 ++#define DOT11_EXTCAP_LEN_BT 3 ++#define DOT11_EXTCAP_LEN_IW 4 ++#define DOT11_EXTCAP_LEN_SI 6 ++ ++#define DOT11_EXTCAP_LEN_TDLS 5 ++BWL_PRE_PACKED_STRUCT struct dot11_extcap { ++ uint8 extcap[DOT11_EXTCAP_LEN_TDLS]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_extcap dot11_extcap_t; ++ ++ ++#define TDLS_CAP_TDLS 37 ++#define TDLS_CAP_PU_BUFFER_STA 28 ++#define TDLS_CAP_PEER_PSM 20 ++#define TDLS_CAP_CH_SW 30 ++#define TDLS_CAP_PROH 38 ++#define TDLS_CAP_CH_SW_PROH 39 ++ ++#define TDLS_CAP_MAX_BIT 39 ++ ++ ++ ++#define DOT11_MEASURE_TYPE_BASIC 0 ++#define DOT11_MEASURE_TYPE_CCA 1 ++#define DOT11_MEASURE_TYPE_RPI 2 ++#define DOT11_MEASURE_TYPE_CHLOAD 3 ++#define DOT11_MEASURE_TYPE_NOISE 4 ++#define DOT11_MEASURE_TYPE_BEACON 5 ++#define DOT11_MEASURE_TYPE_FRAME 6 ++#define DOT11_MEASURE_TYPE_STATS 7 ++#define DOT11_MEASURE_TYPE_LCI 8 ++#define DOT11_MEASURE_TYPE_TXSTREAM 9 ++#define DOT11_MEASURE_TYPE_PAUSE 255 ++ ++ ++#define DOT11_MEASURE_MODE_PARALLEL (1<<0) ++#define DOT11_MEASURE_MODE_ENABLE (1<<1) ++#define DOT11_MEASURE_MODE_REQUEST (1<<2) ++#define DOT11_MEASURE_MODE_REPORT (1<<3) ++#define DOT11_MEASURE_MODE_DUR (1<<4) ++ ++#define DOT11_MEASURE_MODE_LATE (1<<0) ++#define DOT11_MEASURE_MODE_INCAPABLE (1<<1) ++#define DOT11_MEASURE_MODE_REFUSED (1<<2) ++ ++#define DOT11_MEASURE_BASIC_MAP_BSS ((uint8)(1<<0)) ++#define DOT11_MEASURE_BASIC_MAP_OFDM ((uint8)(1<<1)) ++#define DOT11_MEASURE_BASIC_MAP_UKNOWN ((uint8)(1<<2)) ++#define DOT11_MEASURE_BASIC_MAP_RADAR ((uint8)(1<<3)) ++#define DOT11_MEASURE_BASIC_MAP_UNMEAS ((uint8)(1<<4)) ++ ++BWL_PRE_PACKED_STRUCT struct dot11_meas_req { ++ uint8 id; ++ uint8 len; ++ uint8 token; ++ uint8 mode; ++ uint8 type; ++ uint8 channel; ++ uint8 start_time[8]; ++ uint16 duration; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_meas_req dot11_meas_req_t; ++#define DOT11_MNG_IE_MREQ_LEN 14 ++ ++#define DOT11_MNG_IE_MREQ_FIXED_LEN 3 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_meas_rep { ++ uint8 id; ++ uint8 len; ++ uint8 token; ++ uint8 mode; ++ uint8 type; ++ BWL_PRE_PACKED_STRUCT union ++ { ++ BWL_PRE_PACKED_STRUCT struct { ++ uint8 channel; ++ uint8 start_time[8]; ++ uint16 duration; ++ uint8 map; ++ } BWL_POST_PACKED_STRUCT basic; ++ uint8 data[1]; ++ } BWL_POST_PACKED_STRUCT rep; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_meas_rep dot11_meas_rep_t; ++ ++ ++#define DOT11_MNG_IE_MREP_FIXED_LEN 3 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_meas_rep_basic { ++ uint8 channel; ++ uint8 start_time[8]; ++ uint16 duration; ++ uint8 map; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_meas_rep_basic dot11_meas_rep_basic_t; ++#define DOT11_MEASURE_BASIC_REP_LEN 12 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_quiet { ++ uint8 id; ++ uint8 len; ++ uint8 count; ++ uint8 period; ++ uint16 duration; ++ uint16 offset; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_quiet dot11_quiet_t; ++ ++BWL_PRE_PACKED_STRUCT struct chan_map_tuple { ++ uint8 channel; ++ uint8 map; ++} BWL_POST_PACKED_STRUCT; ++typedef struct chan_map_tuple chan_map_tuple_t; ++ ++BWL_PRE_PACKED_STRUCT struct dot11_ibss_dfs { ++ uint8 id; ++ uint8 len; ++ uint8 eaddr[ETHER_ADDR_LEN]; ++ uint8 interval; ++ chan_map_tuple_t map[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_ibss_dfs dot11_ibss_dfs_t; ++ ++ ++#define WME_OUI "\x00\x50\xf2" ++#define WME_OUI_LEN 3 ++#define WME_OUI_TYPE 2 ++#define WME_TYPE 2 ++#define WME_SUBTYPE_IE 0 ++#define WME_SUBTYPE_PARAM_IE 1 ++#define WME_SUBTYPE_TSPEC 2 ++#define WME_VER 1 ++ ++ ++#define AC_BE 0 ++#define AC_BK 1 ++#define AC_VI 2 ++#define AC_VO 3 ++#define AC_COUNT 4 ++ ++typedef uint8 ac_bitmap_t; ++ ++#define AC_BITMAP_NONE 0x0 ++#define AC_BITMAP_ALL 0xf ++#define AC_BITMAP_TST(ab, ac) (((ab) & (1 << (ac))) != 0) ++#define AC_BITMAP_SET(ab, ac) (((ab) |= (1 << (ac)))) ++#define AC_BITMAP_RESET(ab, ac) (((ab) &= ~(1 << (ac)))) ++ ++ ++BWL_PRE_PACKED_STRUCT struct wme_ie { ++ uint8 oui[3]; ++ uint8 type; ++ uint8 subtype; ++ uint8 version; ++ uint8 qosinfo; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wme_ie wme_ie_t; ++#define WME_IE_LEN 7 ++ ++BWL_PRE_PACKED_STRUCT struct edcf_acparam { ++ uint8 ACI; ++ uint8 ECW; ++ uint16 TXOP; ++} BWL_POST_PACKED_STRUCT; ++typedef struct edcf_acparam edcf_acparam_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wme_param_ie { ++ uint8 oui[3]; ++ uint8 type; ++ uint8 subtype; ++ uint8 version; ++ uint8 qosinfo; ++ uint8 rsvd; ++ edcf_acparam_t acparam[AC_COUNT]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wme_param_ie wme_param_ie_t; ++#define WME_PARAM_IE_LEN 24 ++ ++ ++#define WME_QI_AP_APSD_MASK 0x80 ++#define WME_QI_AP_APSD_SHIFT 7 ++#define WME_QI_AP_COUNT_MASK 0x0f ++#define WME_QI_AP_COUNT_SHIFT 0 ++ ++ ++#define WME_QI_STA_MAXSPLEN_MASK 0x60 ++#define WME_QI_STA_MAXSPLEN_SHIFT 5 ++#define WME_QI_STA_APSD_ALL_MASK 0xf ++#define WME_QI_STA_APSD_ALL_SHIFT 0 ++#define WME_QI_STA_APSD_BE_MASK 0x8 ++#define WME_QI_STA_APSD_BE_SHIFT 3 ++#define WME_QI_STA_APSD_BK_MASK 0x4 ++#define WME_QI_STA_APSD_BK_SHIFT 2 ++#define WME_QI_STA_APSD_VI_MASK 0x2 ++#define WME_QI_STA_APSD_VI_SHIFT 1 ++#define WME_QI_STA_APSD_VO_MASK 0x1 ++#define WME_QI_STA_APSD_VO_SHIFT 0 ++ ++ ++#define EDCF_AIFSN_MIN 1 ++#define EDCF_AIFSN_MAX 15 ++#define EDCF_AIFSN_MASK 0x0f ++#define EDCF_ACM_MASK 0x10 ++#define EDCF_ACI_MASK 0x60 ++#define EDCF_ACI_SHIFT 5 ++#define EDCF_AIFSN_SHIFT 12 ++ ++ ++#define EDCF_ECW_MIN 0 ++#define EDCF_ECW_MAX 15 ++#define EDCF_ECW2CW(exp) ((1 << (exp)) - 1) ++#define EDCF_ECWMIN_MASK 0x0f ++#define EDCF_ECWMAX_MASK 0xf0 ++#define EDCF_ECWMAX_SHIFT 4 ++ ++ ++#define EDCF_TXOP_MIN 0 ++#define EDCF_TXOP_MAX 65535 ++#define EDCF_TXOP2USEC(txop) ((txop) << 5) ++ ++ ++#define NON_EDCF_AC_BE_ACI_STA 0x02 ++ ++ ++#define EDCF_AC_BE_ACI_STA 0x03 ++#define EDCF_AC_BE_ECW_STA 0xA4 ++#define EDCF_AC_BE_TXOP_STA 0x0000 ++#define EDCF_AC_BK_ACI_STA 0x27 ++#define EDCF_AC_BK_ECW_STA 0xA4 ++#define EDCF_AC_BK_TXOP_STA 0x0000 ++#define EDCF_AC_VI_ACI_STA 0x42 ++#define EDCF_AC_VI_ECW_STA 0x43 ++#define EDCF_AC_VI_TXOP_STA 0x005e ++#define EDCF_AC_VO_ACI_STA 0x62 ++#define EDCF_AC_VO_ECW_STA 0x32 ++#define EDCF_AC_VO_TXOP_STA 0x002f ++ ++ ++#define EDCF_AC_BE_ACI_AP 0x03 ++#define EDCF_AC_BE_ECW_AP 0x64 ++#define EDCF_AC_BE_TXOP_AP 0x0000 ++#define EDCF_AC_BK_ACI_AP 0x27 ++#define EDCF_AC_BK_ECW_AP 0xA4 ++#define EDCF_AC_BK_TXOP_AP 0x0000 ++#define EDCF_AC_VI_ACI_AP 0x41 ++#define EDCF_AC_VI_ECW_AP 0x43 ++#define EDCF_AC_VI_TXOP_AP 0x005e ++#define EDCF_AC_VO_ACI_AP 0x61 ++#define EDCF_AC_VO_ECW_AP 0x32 ++#define EDCF_AC_VO_TXOP_AP 0x002f ++ ++ ++BWL_PRE_PACKED_STRUCT struct edca_param_ie { ++ uint8 qosinfo; ++ uint8 rsvd; ++ edcf_acparam_t acparam[AC_COUNT]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct edca_param_ie edca_param_ie_t; ++#define EDCA_PARAM_IE_LEN 18 ++ ++ ++BWL_PRE_PACKED_STRUCT struct qos_cap_ie { ++ uint8 qosinfo; ++} BWL_POST_PACKED_STRUCT; ++typedef struct qos_cap_ie qos_cap_ie_t; ++ ++BWL_PRE_PACKED_STRUCT struct dot11_qbss_load_ie { ++ uint8 id; ++ uint8 length; ++ uint16 station_count; ++ uint8 channel_utilization; ++ uint16 aac; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_qbss_load_ie dot11_qbss_load_ie_t; ++#define BSS_LOAD_IE_SIZE 7 ++ ++ ++#define FIXED_MSDU_SIZE 0x8000 ++#define MSDU_SIZE_MASK 0x7fff ++ ++ ++ ++#define INTEGER_SHIFT 13 ++#define FRACTION_MASK 0x1FFF ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_management_notification { ++ uint8 category; ++ uint8 action; ++ uint8 token; ++ uint8 status; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT; ++#define DOT11_MGMT_NOTIFICATION_LEN 4 ++ ++ ++BWL_PRE_PACKED_STRUCT struct ti_ie { ++ uint8 ti_type; ++ uint32 ti_val; ++} BWL_POST_PACKED_STRUCT; ++typedef struct ti_ie ti_ie_t; ++#define TI_TYPE_REASSOC_DEADLINE 1 ++#define TI_TYPE_KEY_LIFETIME 2 ++ ++ ++#define WME_ADDTS_REQUEST 0 ++#define WME_ADDTS_RESPONSE 1 ++#define WME_DELTS_REQUEST 2 ++ ++ ++#define WME_ADMISSION_ACCEPTED 0 ++#define WME_INVALID_PARAMETERS 1 ++#define WME_ADMISSION_REFUSED 3 ++ ++ ++#define BCN_PRB_SSID(body) ((char*)(body) + DOT11_BCN_PRB_LEN) ++ ++ ++#define DOT11_OPEN_SYSTEM 0 ++#define DOT11_SHARED_KEY 1 ++#define DOT11_FAST_BSS 2 ++#define DOT11_CHALLENGE_LEN 128 ++ ++ ++#define FC_PVER_MASK 0x3 ++#define FC_PVER_SHIFT 0 ++#define FC_TYPE_MASK 0xC ++#define FC_TYPE_SHIFT 2 ++#define FC_SUBTYPE_MASK 0xF0 ++#define FC_SUBTYPE_SHIFT 4 ++#define FC_TODS 0x100 ++#define FC_TODS_SHIFT 8 ++#define FC_FROMDS 0x200 ++#define FC_FROMDS_SHIFT 9 ++#define FC_MOREFRAG 0x400 ++#define FC_MOREFRAG_SHIFT 10 ++#define FC_RETRY 0x800 ++#define FC_RETRY_SHIFT 11 ++#define FC_PM 0x1000 ++#define FC_PM_SHIFT 12 ++#define FC_MOREDATA 0x2000 ++#define FC_MOREDATA_SHIFT 13 ++#define FC_WEP 0x4000 ++#define FC_WEP_SHIFT 14 ++#define FC_ORDER 0x8000 ++#define FC_ORDER_SHIFT 15 ++ ++ ++#define SEQNUM_SHIFT 4 ++#define SEQNUM_MAX 0x1000 ++#define FRAGNUM_MASK 0xF ++ ++ ++ ++ ++#define FC_TYPE_MNG 0 ++#define FC_TYPE_CTL 1 ++#define FC_TYPE_DATA 2 ++ ++ ++#define FC_SUBTYPE_ASSOC_REQ 0 ++#define FC_SUBTYPE_ASSOC_RESP 1 ++#define FC_SUBTYPE_REASSOC_REQ 2 ++#define FC_SUBTYPE_REASSOC_RESP 3 ++#define FC_SUBTYPE_PROBE_REQ 4 ++#define FC_SUBTYPE_PROBE_RESP 5 ++#define FC_SUBTYPE_BEACON 8 ++#define FC_SUBTYPE_ATIM 9 ++#define FC_SUBTYPE_DISASSOC 10 ++#define FC_SUBTYPE_AUTH 11 ++#define FC_SUBTYPE_DEAUTH 12 ++#define FC_SUBTYPE_ACTION 13 ++#define FC_SUBTYPE_ACTION_NOACK 14 ++ ++ ++#define FC_SUBTYPE_CTL_WRAPPER 7 ++#define FC_SUBTYPE_BLOCKACK_REQ 8 ++#define FC_SUBTYPE_BLOCKACK 9 ++#define FC_SUBTYPE_PS_POLL 10 ++#define FC_SUBTYPE_RTS 11 ++#define FC_SUBTYPE_CTS 12 ++#define FC_SUBTYPE_ACK 13 ++#define FC_SUBTYPE_CF_END 14 ++#define FC_SUBTYPE_CF_END_ACK 15 ++ ++ ++#define FC_SUBTYPE_DATA 0 ++#define FC_SUBTYPE_DATA_CF_ACK 1 ++#define FC_SUBTYPE_DATA_CF_POLL 2 ++#define FC_SUBTYPE_DATA_CF_ACK_POLL 3 ++#define FC_SUBTYPE_NULL 4 ++#define FC_SUBTYPE_CF_ACK 5 ++#define FC_SUBTYPE_CF_POLL 6 ++#define FC_SUBTYPE_CF_ACK_POLL 7 ++#define FC_SUBTYPE_QOS_DATA 8 ++#define FC_SUBTYPE_QOS_DATA_CF_ACK 9 ++#define FC_SUBTYPE_QOS_DATA_CF_POLL 10 ++#define FC_SUBTYPE_QOS_DATA_CF_ACK_POLL 11 ++#define FC_SUBTYPE_QOS_NULL 12 ++#define FC_SUBTYPE_QOS_CF_POLL 14 ++#define FC_SUBTYPE_QOS_CF_ACK_POLL 15 ++ ++ ++#define FC_SUBTYPE_ANY_QOS(s) (((s) & 8) != 0) ++#define FC_SUBTYPE_ANY_NULL(s) (((s) & 4) != 0) ++#define FC_SUBTYPE_ANY_CF_POLL(s) (((s) & 2) != 0) ++#define FC_SUBTYPE_ANY_CF_ACK(s) (((s) & 1) != 0) ++ ++ ++#define FC_KIND_MASK (FC_TYPE_MASK | FC_SUBTYPE_MASK) ++ ++#define FC_KIND(t, s) (((t) << FC_TYPE_SHIFT) | ((s) << FC_SUBTYPE_SHIFT)) ++ ++#define FC_SUBTYPE(fc) (((fc) & FC_SUBTYPE_MASK) >> FC_SUBTYPE_SHIFT) ++#define FC_TYPE(fc) (((fc) & FC_TYPE_MASK) >> FC_TYPE_SHIFT) ++ ++#define FC_ASSOC_REQ FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ASSOC_REQ) ++#define FC_ASSOC_RESP FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ASSOC_RESP) ++#define FC_REASSOC_REQ FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_REASSOC_REQ) ++#define FC_REASSOC_RESP FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_REASSOC_RESP) ++#define FC_PROBE_REQ FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_PROBE_REQ) ++#define FC_PROBE_RESP FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_PROBE_RESP) ++#define FC_BEACON FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_BEACON) ++#define FC_DISASSOC FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_DISASSOC) ++#define FC_AUTH FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_AUTH) ++#define FC_DEAUTH FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_DEAUTH) ++#define FC_ACTION FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ACTION) ++#define FC_ACTION_NOACK FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ACTION_NOACK) ++ ++#define FC_CTL_WRAPPER FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CTL_WRAPPER) ++#define FC_BLOCKACK_REQ FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_BLOCKACK_REQ) ++#define FC_BLOCKACK FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_BLOCKACK) ++#define FC_PS_POLL FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_PS_POLL) ++#define FC_RTS FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_RTS) ++#define FC_CTS FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CTS) ++#define FC_ACK FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_ACK) ++#define FC_CF_END FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CF_END) ++#define FC_CF_END_ACK FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CF_END_ACK) ++ ++#define FC_DATA FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_DATA) ++#define FC_NULL_DATA FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_NULL) ++#define FC_DATA_CF_ACK FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_DATA_CF_ACK) ++#define FC_QOS_DATA FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_QOS_DATA) ++#define FC_QOS_NULL FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_QOS_NULL) ++ ++ ++ ++ ++#define QOS_PRIO_SHIFT 0 ++#define QOS_PRIO_MASK 0x0007 ++#define QOS_PRIO(qos) (((qos) & QOS_PRIO_MASK) >> QOS_PRIO_SHIFT) ++ ++ ++#define QOS_TID_SHIFT 0 ++#define QOS_TID_MASK 0x000f ++#define QOS_TID(qos) (((qos) & QOS_TID_MASK) >> QOS_TID_SHIFT) ++ ++ ++#define QOS_EOSP_SHIFT 4 ++#define QOS_EOSP_MASK 0x0010 ++#define QOS_EOSP(qos) (((qos) & QOS_EOSP_MASK) >> QOS_EOSP_SHIFT) ++ ++ ++#define QOS_ACK_NORMAL_ACK 0 ++#define QOS_ACK_NO_ACK 1 ++#define QOS_ACK_NO_EXP_ACK 2 ++#define QOS_ACK_BLOCK_ACK 3 ++#define QOS_ACK_SHIFT 5 ++#define QOS_ACK_MASK 0x0060 ++#define QOS_ACK(qos) (((qos) & QOS_ACK_MASK) >> QOS_ACK_SHIFT) ++ ++ ++#define QOS_AMSDU_SHIFT 7 ++#define QOS_AMSDU_MASK 0x0080 ++ ++ ++ ++ ++ ++ ++#define DOT11_MNG_AUTH_ALGO_LEN 2 ++#define DOT11_MNG_AUTH_SEQ_LEN 2 ++#define DOT11_MNG_BEACON_INT_LEN 2 ++#define DOT11_MNG_CAP_LEN 2 ++#define DOT11_MNG_AP_ADDR_LEN 6 ++#define DOT11_MNG_LISTEN_INT_LEN 2 ++#define DOT11_MNG_REASON_LEN 2 ++#define DOT11_MNG_AID_LEN 2 ++#define DOT11_MNG_STATUS_LEN 2 ++#define DOT11_MNG_TIMESTAMP_LEN 8 ++ ++ ++#define DOT11_AID_MASK 0x3fff ++ ++ ++#define DOT11_RC_RESERVED 0 ++#define DOT11_RC_UNSPECIFIED 1 ++#define DOT11_RC_AUTH_INVAL 2 ++#define DOT11_RC_DEAUTH_LEAVING 3 ++#define DOT11_RC_INACTIVITY 4 ++#define DOT11_RC_BUSY 5 ++#define DOT11_RC_INVAL_CLASS_2 6 ++#define DOT11_RC_INVAL_CLASS_3 7 ++#define DOT11_RC_DISASSOC_LEAVING 8 ++#define DOT11_RC_NOT_AUTH 9 ++#define DOT11_RC_BAD_PC 10 ++#define DOT11_RC_BAD_CHANNELS 11 ++ ++ ++ ++#define DOT11_RC_UNSPECIFIED_QOS 32 ++#define DOT11_RC_INSUFFCIENT_BW 33 ++#define DOT11_RC_EXCESSIVE_FRAMES 34 ++#define DOT11_RC_TX_OUTSIDE_TXOP 35 ++#define DOT11_RC_LEAVING_QBSS 36 ++#define DOT11_RC_BAD_MECHANISM 37 ++#define DOT11_RC_SETUP_NEEDED 38 ++#define DOT11_RC_TIMEOUT 39 ++ ++#define DOT11_RC_MAX 23 ++ ++#define DOT11_RC_TDLS_PEER_UNREACH 25 ++#define DOT11_RC_TDLS_DOWN_UNSPECIFIED 26 ++ ++ ++#define DOT11_SC_SUCCESS 0 ++#define DOT11_SC_FAILURE 1 ++#define DOT11_SC_TDLS_WAKEUP_SCH_ALT 2 ++ ++#define DOT11_SC_TDLS_WAKEUP_SCH_REJ 3 ++#define DOT11_SC_TDLS_SEC_DISABLED 5 ++#define DOT11_SC_LIFETIME_REJ 6 ++#define DOT11_SC_NOT_SAME_BSS 7 ++#define DOT11_SC_CAP_MISMATCH 10 ++#define DOT11_SC_REASSOC_FAIL 11 ++#define DOT11_SC_ASSOC_FAIL 12 ++#define DOT11_SC_AUTH_MISMATCH 13 ++#define DOT11_SC_AUTH_SEQ 14 ++#define DOT11_SC_AUTH_CHALLENGE_FAIL 15 ++#define DOT11_SC_AUTH_TIMEOUT 16 ++#define DOT11_SC_ASSOC_BUSY_FAIL 17 ++#define DOT11_SC_ASSOC_RATE_MISMATCH 18 ++#define DOT11_SC_ASSOC_SHORT_REQUIRED 19 ++#define DOT11_SC_ASSOC_PBCC_REQUIRED 20 ++#define DOT11_SC_ASSOC_AGILITY_REQUIRED 21 ++#define DOT11_SC_ASSOC_SPECTRUM_REQUIRED 22 ++#define DOT11_SC_ASSOC_BAD_POWER_CAP 23 ++#define DOT11_SC_ASSOC_BAD_SUP_CHANNELS 24 ++#define DOT11_SC_ASSOC_SHORTSLOT_REQUIRED 25 ++#define DOT11_SC_ASSOC_ERPBCC_REQUIRED 26 ++#define DOT11_SC_ASSOC_DSSOFDM_REQUIRED 27 ++#define DOT11_SC_ASSOC_R0KH_UNREACHABLE 28 ++#define DOT11_SC_ASSOC_TRY_LATER 30 ++#define DOT11_SC_ASSOC_MFP_VIOLATION 31 ++ ++#define DOT11_SC_DECLINED 37 ++#define DOT11_SC_INVALID_PARAMS 38 ++#define DOT11_SC_INVALID_PAIRWISE_CIPHER 42 ++#define DOT11_SC_INVALID_AKMP 43 ++#define DOT11_SC_INVALID_RSNIE_CAP 45 ++#define DOT11_SC_DLS_NOT_ALLOWED 48 ++#define DOT11_SC_INVALID_PMKID 53 ++#define DOT11_SC_INVALID_MDID 54 ++#define DOT11_SC_INVALID_FTIE 55 ++ ++#define DOT11_SC_UNEXP_MSG 70 ++#define DOT11_SC_INVALID_SNONCE 71 ++#define DOT11_SC_INVALID_RSNIE 72 ++ ++ ++#define DOT11_MNG_DS_PARAM_LEN 1 ++#define DOT11_MNG_IBSS_PARAM_LEN 2 ++ ++ ++#define DOT11_MNG_TIM_FIXED_LEN 3 ++#define DOT11_MNG_TIM_DTIM_COUNT 0 ++#define DOT11_MNG_TIM_DTIM_PERIOD 1 ++#define DOT11_MNG_TIM_BITMAP_CTL 2 ++#define DOT11_MNG_TIM_PVB 3 ++ ++ ++#define TLV_TAG_OFF 0 ++#define TLV_LEN_OFF 1 ++#define TLV_HDR_LEN 2 ++#define TLV_BODY_OFF 2 ++ ++ ++#define DOT11_MNG_SSID_ID 0 ++#define DOT11_MNG_RATES_ID 1 ++#define DOT11_MNG_FH_PARMS_ID 2 ++#define DOT11_MNG_DS_PARMS_ID 3 ++#define DOT11_MNG_CF_PARMS_ID 4 ++#define DOT11_MNG_TIM_ID 5 ++#define DOT11_MNG_IBSS_PARMS_ID 6 ++#define DOT11_MNG_COUNTRY_ID 7 ++#define DOT11_MNG_HOPPING_PARMS_ID 8 ++#define DOT11_MNG_HOPPING_TABLE_ID 9 ++#define DOT11_MNG_REQUEST_ID 10 ++#define DOT11_MNG_QBSS_LOAD_ID 11 ++#define DOT11_MNG_EDCA_PARAM_ID 12 ++#define DOT11_MNG_CHALLENGE_ID 16 ++#define DOT11_MNG_PWR_CONSTRAINT_ID 32 ++#define DOT11_MNG_PWR_CAP_ID 33 ++#define DOT11_MNG_TPC_REQUEST_ID 34 ++#define DOT11_MNG_TPC_REPORT_ID 35 ++#define DOT11_MNG_SUPP_CHANNELS_ID 36 ++#define DOT11_MNG_CHANNEL_SWITCH_ID 37 ++#define DOT11_MNG_MEASURE_REQUEST_ID 38 ++#define DOT11_MNG_MEASURE_REPORT_ID 39 ++#define DOT11_MNG_QUIET_ID 40 ++#define DOT11_MNG_IBSS_DFS_ID 41 ++#define DOT11_MNG_ERP_ID 42 ++#define DOT11_MNG_TS_DELAY_ID 43 ++#define DOT11_MNG_HT_CAP 45 ++#define DOT11_MNG_QOS_CAP_ID 46 ++#define DOT11_MNG_NONERP_ID 47 ++#define DOT11_MNG_RSN_ID 48 ++#define DOT11_MNG_EXT_RATES_ID 50 ++#define DOT11_MNG_AP_CHREP_ID 51 ++#define DOT11_MNG_NBR_REP_ID 52 ++#define DOT11_MNG_MDIE_ID 54 ++#define DOT11_MNG_FTIE_ID 55 ++#define DOT11_MNG_FT_TI_ID 56 ++#define DOT11_MNG_REGCLASS_ID 59 ++#define DOT11_MNG_EXT_CSA_ID 60 ++#define DOT11_MNG_HT_ADD 61 ++#define DOT11_MNG_EXT_CHANNEL_OFFSET 62 ++#define DOT11_MNG_WAPI_ID 68 ++#define DOT11_MNG_TIME_ADVERTISE_ID 69 ++#define DOT11_MNG_RRM_CAP_ID 70 ++#define DOT11_MNG_HT_BSS_COEXINFO_ID 72 ++#define DOT11_MNG_HT_BSS_CHANNEL_REPORT_ID 73 ++#define DOT11_MNG_HT_OBSS_ID 74 ++#define DOT11_MNG_CHANNEL_USAGE 97 ++#define DOT11_MNG_TIME_ZONE_ID 98 ++#define DOT11_MNG_LINK_IDENTIFIER_ID 101 ++#define DOT11_MNG_WAKEUP_SCHEDULE_ID 102 ++#define DOT11_MNG_CHANNEL_SWITCH_TIMING_ID 104 ++#define DOT11_MNG_PTI_CONTROL_ID 105 ++#define DOT11_MNG_PU_BUFFER_STATUS_ID 106 ++#define DOT11_MNG_INTERWORKING_ID 107 ++#define DOT11_MNG_ADVERTISEMENT_ID 108 ++#define DOT11_MNG_EXP_BW_REQ_ID 109 ++#define DOT11_MNG_QOS_MAP_ID 110 ++#define DOT11_MNG_ROAM_CONSORT_ID 111 ++#define DOT11_MNG_EMERGCY_ALERT_ID 112 ++#define DOT11_MNG_EXT_CAP_ID 127 ++#define DOT11_MNG_VHT_CAP_ID 191 ++#define DOT11_MNG_VHT_OPERATION_ID 192 ++ ++#define DOT11_MNG_WPA_ID 221 ++#define DOT11_MNG_PROPR_ID 221 ++ ++#define DOT11_MNG_VS_ID 221 ++ ++ ++#define DOT11_RATE_BASIC 0x80 ++#define DOT11_RATE_MASK 0x7F ++ ++ ++#define DOT11_MNG_ERP_LEN 1 ++#define DOT11_MNG_NONERP_PRESENT 0x01 ++#define DOT11_MNG_USE_PROTECTION 0x02 ++#define DOT11_MNG_BARKER_PREAMBLE 0x04 ++ ++#define DOT11_MGN_TS_DELAY_LEN 4 ++#define TS_DELAY_FIELD_SIZE 4 ++ ++ ++#define DOT11_CAP_ESS 0x0001 ++#define DOT11_CAP_IBSS 0x0002 ++#define DOT11_CAP_POLLABLE 0x0004 ++#define DOT11_CAP_POLL_RQ 0x0008 ++#define DOT11_CAP_PRIVACY 0x0010 ++#define DOT11_CAP_SHORT 0x0020 ++#define DOT11_CAP_PBCC 0x0040 ++#define DOT11_CAP_AGILITY 0x0080 ++#define DOT11_CAP_SPECTRUM 0x0100 ++#define DOT11_CAP_SHORTSLOT 0x0400 ++#define DOT11_CAP_RRM 0x1000 ++#define DOT11_CAP_CCK_OFDM 0x2000 ++ ++ ++ ++#define DOT11_EXT_CAP_OBSS_COEX_MGMT 0 ++ ++#define DOT11_EXT_CAP_SPSMP 6 ++ ++#define DOT11_EXT_CAP_BSS_TRANSITION_MGMT 19 ++ ++#define DOT11_EXT_CAP_IW 31 ++ ++#define DOT11_EXT_CAP_SI 41 ++#define DOT11_EXT_CAP_SI_MASK 0x0E ++ ++ ++#define DOT11_ACTION_HDR_LEN 2 ++#define DOT11_ACTION_CAT_OFF 0 ++#define DOT11_ACTION_ACT_OFF 1 ++ ++ ++#define DOT11_ACTION_CAT_ERR_MASK 0x80 ++#define DOT11_ACTION_CAT_MASK 0x7F ++#define DOT11_ACTION_CAT_SPECT_MNG 0 ++#define DOT11_ACTION_CAT_QOS 1 ++#define DOT11_ACTION_CAT_DLS 2 ++#define DOT11_ACTION_CAT_BLOCKACK 3 ++#define DOT11_ACTION_CAT_PUBLIC 4 ++#define DOT11_ACTION_CAT_RRM 5 ++#define DOT11_ACTION_CAT_FBT 6 ++#define DOT11_ACTION_CAT_HT 7 ++#define DOT11_ACTION_CAT_SA_QUERY 8 ++#define DOT11_ACTION_CAT_PDPA 9 ++#define DOT11_ACTION_CAT_BSSMGMT 10 ++#define DOT11_ACTION_NOTIFICATION 17 ++#define DOT11_ACTION_CAT_VSP 126 ++#define DOT11_ACTION_CAT_VS 127 ++ ++ ++#define DOT11_SM_ACTION_M_REQ 0 ++#define DOT11_SM_ACTION_M_REP 1 ++#define DOT11_SM_ACTION_TPC_REQ 2 ++#define DOT11_SM_ACTION_TPC_REP 3 ++#define DOT11_SM_ACTION_CHANNEL_SWITCH 4 ++#define DOT11_SM_ACTION_EXT_CSA 5 ++ ++ ++#define DOT11_ACTION_ID_HT_CH_WIDTH 0 ++#define DOT11_ACTION_ID_HT_MIMO_PS 1 ++ ++ ++#define DOT11_PUB_ACTION_BSS_COEX_MNG 0 ++#define DOT11_PUB_ACTION_CHANNEL_SWITCH 4 ++ ++ ++#define DOT11_BA_ACTION_ADDBA_REQ 0 ++#define DOT11_BA_ACTION_ADDBA_RESP 1 ++#define DOT11_BA_ACTION_DELBA 2 ++ ++ ++#define DOT11_ADDBA_PARAM_AMSDU_SUP 0x0001 ++#define DOT11_ADDBA_PARAM_POLICY_MASK 0x0002 ++#define DOT11_ADDBA_PARAM_POLICY_SHIFT 1 ++#define DOT11_ADDBA_PARAM_TID_MASK 0x003c ++#define DOT11_ADDBA_PARAM_TID_SHIFT 2 ++#define DOT11_ADDBA_PARAM_BSIZE_MASK 0xffc0 ++#define DOT11_ADDBA_PARAM_BSIZE_SHIFT 6 ++ ++#define DOT11_ADDBA_POLICY_DELAYED 0 ++#define DOT11_ADDBA_POLICY_IMMEDIATE 1 ++ ++ ++#define DOT11_FT_ACTION_FT_RESERVED 0 ++#define DOT11_FT_ACTION_FT_REQ 1 ++#define DOT11_FT_ACTION_FT_RES 2 ++#define DOT11_FT_ACTION_FT_CON 3 ++#define DOT11_FT_ACTION_FT_ACK 4 ++ ++ ++#define DOT11_DLS_ACTION_REQ 0 ++#define DOT11_DLS_ACTION_RESP 1 ++#define DOT11_DLS_ACTION_TD 2 ++ ++ ++#define DOT11_WNM_ACTION_EVENT_REQ 0 ++#define DOT11_WNM_ACTION_EVENT_REP 1 ++#define DOT11_WNM_ACTION_DIAG_REQ 2 ++#define DOT11_WNM_ACTION_DIAG_REP 3 ++#define DOT11_WNM_ACTION_LOC_CFG_REQ 4 ++#define DOT11_WNM_ACTION_LOC_RFG_RESP 5 ++#define DOT11_WNM_ACTION_BSS_TRANS_QURY 6 ++#define DOT11_WNM_ACTION_BSS_TRANS_REQ 7 ++#define DOT11_WNM_ACTION_BSS_TRANS_RESP 8 ++#define DOT11_WNM_ACTION_FMS_REQ 9 ++#define DOT11_WNM_ACTION_FMS_RESP 10 ++#define DOT11_WNM_ACTION_COL_INTRFRNCE_REQ 11 ++#define DOT11_WNM_ACTION_COL_INTRFRNCE_REP 12 ++#define DOT11_WNM_ACTION_TFS_REQ 13 ++#define DOT11_WNM_ACTION_TFS_RESP 14 ++#define DOT11_WNM_ACTION_TFS_NOTIFY 15 ++#define DOT11_WNM_ACTION_WNM_SLEEP_REQ 16 ++#define DOT11_WNM_ACTION_WNM_SLEEP_RESP 17 ++#define DOT11_WNM_ACTION_TIM_BCAST_REQ 18 ++#define DOT11_WNM_ACTION_TIM_BCAST_RESP 19 ++#define DOT11_WNM_ACTION_QOS_TRFC_CAP_UPD 20 ++#define DOT11_WNM_ACTION_CHAN_USAGE_REQ 21 ++#define DOT11_WNM_ACTION_CHAN_USAGE_RESP 22 ++#define DOT11_WNM_ACTION_DMS_REQ 23 ++#define DOT11_WNM_ACTION_DMS_RESP 24 ++#define DOT11_WNM_ACTION_TMNG_MEASUR_REQ 25 ++#define DOT11_WNM_ACTION_NOTFCTN_REQ 26 ++#define DOT11_WNM_ACTION_NOTFCTN_RES 27 ++ ++#define DOT11_MNG_COUNTRY_ID_LEN 3 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_dls_req { ++ uint8 category; ++ uint8 action; ++ struct ether_addr da; ++ struct ether_addr sa; ++ uint16 cap; ++ uint16 timeout; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_dls_req dot11_dls_req_t; ++#define DOT11_DLS_REQ_LEN 18 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_dls_resp { ++ uint8 category; ++ uint8 action; ++ uint16 status; ++ struct ether_addr da; ++ struct ether_addr sa; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_dls_resp dot11_dls_resp_t; ++#define DOT11_DLS_RESP_LEN 16 ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_query { ++ uint8 category; ++ uint8 action; ++ uint8 token; ++ uint8 reason; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_bss_trans_query dot11_bss_trans_query_t; ++#define DOT11_BSS_TRANS_QUERY_LEN 4 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_req { ++ uint8 category; ++ uint8 action; ++ uint8 token; ++ uint8 reqmode; ++ uint16 disassoc_tmr; ++ uint8 validity_intrvl; ++ uint8 data[1]; ++ ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_bss_trans_req dot11_bss_trans_req_t; ++#define DOT11_BSS_TRANS_REQ_LEN 7 ++ ++#define DOT11_BSS_TERM_DUR_LEN 12 ++ ++ ++ ++#define DOT11_BSS_TRNS_REQMODE_PREF_LIST_INCL 0x01 ++#define DOT11_BSS_TRNS_REQMODE_ABRIDGED 0x02 ++#define DOT11_BSS_TRNS_REQMODE_DISASSOC_IMMINENT 0x04 ++#define DOT11_BSS_TRNS_REQMODE_BSS_TERM_INCL 0x08 ++#define DOT11_BSS_TRNS_REQMODE_ESS_DISASSOC_IMNT 0x10 ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_res { ++ uint8 category; ++ uint8 action; ++ uint8 token; ++ uint8 status; ++ uint8 term_delay; ++ uint8 data[1]; ++ ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_bss_trans_res dot11_bss_trans_res_t; ++#define DOT11_BSS_TRANS_RES_LEN 5 ++ ++ ++#define DOT11_BSS_TRNS_RES_STATUS_ACCEPT 0 ++#define DOT11_BSS_TRNS_RES_STATUS_REJECT 1 ++#define DOT11_BSS_TRNS_RES_STATUS_REJ_INSUFF_BCN 2 ++#define DOT11_BSS_TRNS_RES_STATUS_REJ_INSUFF_CAP 3 ++#define DOT11_BSS_TRNS_RES_STATUS_REJ_TERM_UNDESIRED 4 ++#define DOT11_BSS_TRNS_RES_STATUS_REJ_TERM_DELAY_REQ 5 ++#define DOT11_BSS_TRNS_RES_STATUS_REJ_BSS_LIST_PROVIDED 6 ++#define DOT11_BSS_TRNS_RES_STATUS_REJ_NO_SUITABLE_BSS 7 ++#define DOT11_BSS_TRNS_RES_STATUS_REJ_LEAVING_ESS 8 ++ ++ ++ ++#define DOT11_NBR_RPRT_BSSID_INFO_REACHABILTY 0x0003 ++#define DOT11_NBR_RPRT_BSSID_INFO_SEC 0x0004 ++#define DOT11_NBR_RPRT_BSSID_INFO_KEY_SCOPE 0x0008 ++#define DOT11_NBR_RPRT_BSSID_INFO_CAP 0x03f0 ++ ++#define DOT11_NBR_RPRT_BSSID_INFO_CAP_SPEC_MGMT 0x0010 ++#define DOT11_NBR_RPRT_BSSID_INFO_CAP_QOS 0x0020 ++#define DOT11_NBR_RPRT_BSSID_INFO_CAP_APSD 0x0040 ++#define DOT11_NBR_RPRT_BSSID_INFO_CAP_RDIO_MSMT 0x0080 ++#define DOT11_NBR_RPRT_BSSID_INFO_CAP_DEL_BA 0x0100 ++#define DOT11_NBR_RPRT_BSSID_INFO_CAP_IMM_BA 0x0200 ++ ++ ++#define DOT11_NBR_RPRT_SUBELEM_BSS_CANDDT_PREF_ID 3 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_addba_req { ++ uint8 category; ++ uint8 action; ++ uint8 token; ++ uint16 addba_param_set; ++ uint16 timeout; ++ uint16 start_seqnum; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_addba_req dot11_addba_req_t; ++#define DOT11_ADDBA_REQ_LEN 9 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_addba_resp { ++ uint8 category; ++ uint8 action; ++ uint8 token; ++ uint16 status; ++ uint16 addba_param_set; ++ uint16 timeout; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_addba_resp dot11_addba_resp_t; ++#define DOT11_ADDBA_RESP_LEN 9 ++ ++ ++#define DOT11_DELBA_PARAM_INIT_MASK 0x0800 ++#define DOT11_DELBA_PARAM_INIT_SHIFT 11 ++#define DOT11_DELBA_PARAM_TID_MASK 0xf000 ++#define DOT11_DELBA_PARAM_TID_SHIFT 12 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_delba { ++ uint8 category; ++ uint8 action; ++ uint16 delba_param_set; ++ uint16 reason; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_delba dot11_delba_t; ++#define DOT11_DELBA_LEN 6 ++ ++ ++#define SA_QUERY_REQUEST 0 ++#define SA_QUERY_RESPONSE 1 ++ ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_ft_req { ++ uint8 category; ++ uint8 action; ++ uint8 sta_addr[ETHER_ADDR_LEN]; ++ uint8 tgt_ap_addr[ETHER_ADDR_LEN]; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_ft_req dot11_ft_req_t; ++#define DOT11_FT_REQ_FIXED_LEN 14 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_ft_res { ++ uint8 category; ++ uint8 action; ++ uint8 sta_addr[ETHER_ADDR_LEN]; ++ uint8 tgt_ap_addr[ETHER_ADDR_LEN]; ++ uint16 status; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_ft_res dot11_ft_res_t; ++#define DOT11_FT_RES_FIXED_LEN 16 ++ ++ ++ ++ ++ ++ ++#define DOT11_RRM_CAP_LEN 5 ++BWL_PRE_PACKED_STRUCT struct dot11_rrm_cap_ie { ++ uint8 cap[DOT11_RRM_CAP_LEN]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_rrm_cap_ie dot11_rrm_cap_ie_t; ++ ++ ++#define DOT11_RRM_CAP_LINK 0 ++#define DOT11_RRM_CAP_NEIGHBOR_REPORT 1 ++#define DOT11_RRM_CAP_PARALLEL 2 ++#define DOT11_RRM_CAP_REPEATED 3 ++#define DOT11_RRM_CAP_BCN_PASSIVE 4 ++#define DOT11_RRM_CAP_BCN_ACTIVE 5 ++#define DOT11_RRM_CAP_BCN_TABLE 6 ++#define DOT11_RRM_CAP_BCN_REP_COND 7 ++#define DOT11_RRM_CAP_AP_CHANREP 16 ++ ++ ++ ++#define DOT11_OP_CLASS_NONE 255 ++ ++ ++ ++#define DOT11_RM_ACTION_RM_REQ 0 ++#define DOT11_RM_ACTION_RM_REP 1 ++#define DOT11_RM_ACTION_LM_REQ 2 ++#define DOT11_RM_ACTION_LM_REP 3 ++#define DOT11_RM_ACTION_NR_REQ 4 ++#define DOT11_RM_ACTION_NR_REP 5 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_rm_action { ++ uint8 category; ++ uint8 action; ++ uint8 token; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_rm_action dot11_rm_action_t; ++#define DOT11_RM_ACTION_LEN 3 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_rmreq { ++ uint8 category; ++ uint8 action; ++ uint8 token; ++ uint16 reps; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_rmreq dot11_rmreq_t; ++#define DOT11_RMREQ_LEN 5 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_rm_ie { ++ uint8 id; ++ uint8 len; ++ uint8 token; ++ uint8 mode; ++ uint8 type; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_rm_ie dot11_rm_ie_t; ++#define DOT11_RM_IE_LEN 5 ++ ++ ++#define DOT11_RMREQ_MODE_PARALLEL 1 ++#define DOT11_RMREQ_MODE_ENABLE 2 ++#define DOT11_RMREQ_MODE_REQUEST 4 ++#define DOT11_RMREQ_MODE_REPORT 8 ++#define DOT11_RMREQ_MODE_DURMAND 0x10 ++ ++ ++#define DOT11_RMREP_MODE_LATE 1 ++#define DOT11_RMREP_MODE_INCAPABLE 2 ++#define DOT11_RMREP_MODE_REFUSED 4 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_rmreq_bcn { ++ uint8 id; ++ uint8 len; ++ uint8 token; ++ uint8 mode; ++ uint8 type; ++ uint8 reg; ++ uint8 channel; ++ uint16 interval; ++ uint16 duration; ++ uint8 bcn_mode; ++ struct ether_addr bssid; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_rmreq_bcn dot11_rmreq_bcn_t; ++#define DOT11_RMREQ_BCN_LEN 18 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_rmrep_bcn { ++ uint8 reg; ++ uint8 channel; ++ uint32 starttime[2]; ++ uint16 duration; ++ uint8 frame_info; ++ uint8 rcpi; ++ uint8 rsni; ++ struct ether_addr bssid; ++ uint8 antenna_id; ++ uint32 parent_tsf; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_rmrep_bcn dot11_rmrep_bcn_t; ++#define DOT11_RMREP_BCN_LEN 26 ++ ++ ++#define DOT11_RMREQ_BCN_PASSIVE 0 ++#define DOT11_RMREQ_BCN_ACTIVE 1 ++#define DOT11_RMREQ_BCN_TABLE 2 ++ ++ ++#define DOT11_RMREQ_BCN_SSID_ID 0 ++#define DOT11_RMREQ_BCN_REPINFO_ID 1 ++#define DOT11_RMREQ_BCN_REPDET_ID 2 ++#define DOT11_RMREQ_BCN_REQUEST_ID 10 ++#define DOT11_RMREQ_BCN_APCHREP_ID 51 ++ ++ ++#define DOT11_RMREQ_BCN_REPDET_FIXED 0 ++#define DOT11_RMREQ_BCN_REPDET_REQUEST 1 ++#define DOT11_RMREQ_BCN_REPDET_ALL 2 ++ ++ ++#define DOT11_RMREP_BCN_FRM_BODY 1 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_rmrep_nbr { ++ struct ether_addr bssid; ++ uint32 bssid_info; ++ uint8 reg; ++ uint8 channel; ++ uint8 phytype; ++ uchar sub_elements[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_rmrep_nbr dot11_rmrep_nbr_t; ++#define DOT11_RMREP_NBR_LEN 13 ++ ++ ++#define DOT11_BSSTYPE_INFRASTRUCTURE 0 ++#define DOT11_BSSTYPE_INDEPENDENT 1 ++#define DOT11_BSSTYPE_ANY 2 ++#define DOT11_SCANTYPE_ACTIVE 0 ++#define DOT11_SCANTYPE_PASSIVE 1 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_lmreq { ++ uint8 category; ++ uint8 action; ++ uint8 token; ++ uint8 txpwr; ++ uint8 maxtxpwr; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_lmreq dot11_lmreq_t; ++#define DOT11_LMREQ_LEN 5 ++ ++BWL_PRE_PACKED_STRUCT struct dot11_lmrep { ++ uint8 category; ++ uint8 action; ++ uint8 token; ++ dot11_tpc_rep_t tpc; ++ uint8 rxant; ++ uint8 txant; ++ uint8 rcpi; ++ uint8 rsni; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_lmrep dot11_lmrep_t; ++#define DOT11_LMREP_LEN 11 ++ ++ ++#define PREN_PREAMBLE 24 ++#define PREN_MM_EXT 12 ++#define PREN_PREAMBLE_EXT 4 ++ ++ ++#define RIFS_11N_TIME 2 ++ ++ ++ ++#define HT_SIG1_MCS_MASK 0x00007F ++#define HT_SIG1_CBW 0x000080 ++#define HT_SIG1_HT_LENGTH 0xFFFF00 ++ ++ ++#define HT_SIG2_SMOOTHING 0x000001 ++#define HT_SIG2_NOT_SOUNDING 0x000002 ++#define HT_SIG2_RESERVED 0x000004 ++#define HT_SIG2_AGGREGATION 0x000008 ++#define HT_SIG2_STBC_MASK 0x000030 ++#define HT_SIG2_STBC_SHIFT 4 ++#define HT_SIG2_FEC_CODING 0x000040 ++#define HT_SIG2_SHORT_GI 0x000080 ++#define HT_SIG2_ESS_MASK 0x000300 ++#define HT_SIG2_ESS_SHIFT 8 ++#define HT_SIG2_CRC 0x03FC00 ++#define HT_SIG2_TAIL 0x1C0000 ++ ++ ++#define APHY_SLOT_TIME 9 ++#define APHY_SIFS_TIME 16 ++#define APHY_DIFS_TIME (APHY_SIFS_TIME + (2 * APHY_SLOT_TIME)) ++#define APHY_PREAMBLE_TIME 16 ++#define APHY_SIGNAL_TIME 4 ++#define APHY_SYMBOL_TIME 4 ++#define APHY_SERVICE_NBITS 16 ++#define APHY_TAIL_NBITS 6 ++#define APHY_CWMIN 15 ++ ++ ++#define BPHY_SLOT_TIME 20 ++#define BPHY_SIFS_TIME 10 ++#define BPHY_DIFS_TIME 50 ++#define BPHY_PLCP_TIME 192 ++#define BPHY_PLCP_SHORT_TIME 96 ++#define BPHY_CWMIN 31 ++ ++ ++#define DOT11_OFDM_SIGNAL_EXTENSION 6 ++ ++#define PHY_CWMAX 1023 ++ ++#define DOT11_MAXNUMFRAGS 16 ++ ++ ++ ++typedef int vht_group_id_t; ++ ++ ++ ++#define VHT_SIGA1_CONST_MASK 0x800004 ++ ++#define VHT_SIGA1_20MHZ_VAL 0x000000 ++#define VHT_SIGA1_40MHZ_VAL 0x000001 ++#define VHT_SIGA1_80MHZ_VAL 0x000002 ++#define VHT_SIGA1_160MHZ_VAL 0x000003 ++ ++#define VHT_SIGA1_STBC 0x000008 ++ ++#define VHT_SIGA1_GID_MAX_GID 0x3f ++#define VHT_SIGA1_GID_SHIFT 4 ++#define VHT_SIGA1_GID_TO_AP 0x00 ++#define VHT_SIGA1_GID_NOT_TO_AP 0x3f ++ ++#define VHT_SIGA1_NSTS_SHIFT 10 ++#define VHT_SIGA1_NSTS_SHIFT_MASK_USER0 0x001C00 ++ ++#define VHT_SIGA1_PARTIAL_AID_SHIFT 13 ++ ++ ++#define VHT_SIGA2_GI_NONE 0x000000 ++#define VHT_SIGA2_GI_SHORT 0x000001 ++#define VHT_SIGA2_GI_W_MOD10 0x000002 ++#define VHT_SIGA2_CODING_LDPC 0x000004 ++#define VHT_SIGA2_BEAMFORM_ENABLE 0x000100 ++#define VHT_SIGA2_MCS_SHIFT 4 ++ ++#define VHT_SIGA2_B9_RESERVED 0x000200 ++#define VHT_SIGA2_TAIL_MASK 0xfc0000 ++#define VHT_SIGA2_TAIL_VALUE 0x000000 ++ ++#define VHT_SIGA2_SVC_BITS 16 ++#define VHT_SIGA2_TAIL_BITS 6 ++ ++ ++ ++typedef struct d11cnt { ++ uint32 txfrag; ++ uint32 txmulti; ++ uint32 txfail; ++ uint32 txretry; ++ uint32 txretrie; ++ uint32 rxdup; ++ uint32 txrts; ++ uint32 txnocts; ++ uint32 txnoack; ++ uint32 rxfrag; ++ uint32 rxmulti; ++ uint32 rxcrc; ++ uint32 txfrmsnt; ++ uint32 rxundec; ++} d11cnt_t; ++ ++ ++#define BRCM_PROP_OUI "\x00\x90\x4C" ++ ++ ++ ++#define BRCM_OUI "\x00\x10\x18" ++ ++ ++BWL_PRE_PACKED_STRUCT struct brcm_ie { ++ uint8 id; ++ uint8 len; ++ uint8 oui[3]; ++ uint8 ver; ++ uint8 assoc; ++ uint8 flags; ++ uint8 flags1; ++ uint16 amsdu_mtu_pref; ++} BWL_POST_PACKED_STRUCT; ++typedef struct brcm_ie brcm_ie_t; ++#define BRCM_IE_LEN 11 ++#define BRCM_IE_VER 2 ++#define BRCM_IE_LEGACY_AES_VER 1 ++ ++ ++#define BRF_LZWDS 0x4 ++#define BRF_BLOCKACK 0x8 ++ ++ ++#define BRF1_AMSDU 0x1 ++#define BRF1_WMEPS 0x4 ++#define BRF1_PSOFIX 0x8 ++#define BRF1_RX_LARGE_AGG 0x10 ++#define BRF1_RFAWARE_DCS 0x20 ++#define BRF1_SOFTAP 0x40 ++ ++ ++BWL_PRE_PACKED_STRUCT struct vndr_ie { ++ uchar id; ++ uchar len; ++ uchar oui [3]; ++ uchar data [1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct vndr_ie vndr_ie_t; ++ ++#define VNDR_IE_HDR_LEN 2 ++#define VNDR_IE_MIN_LEN 3 ++#define VNDR_IE_FIXED_LEN (VNDR_IE_HDR_LEN + VNDR_IE_MIN_LEN) ++#define VNDR_IE_MAX_LEN 256 ++ ++ ++#define MCSSET_LEN 16 ++#define MAX_MCS_NUM (128) ++ ++BWL_PRE_PACKED_STRUCT struct ht_cap_ie { ++ uint16 cap; ++ uint8 params; ++ uint8 supp_mcs[MCSSET_LEN]; ++ uint16 ext_htcap; ++ uint32 txbf_cap; ++ uint8 as_cap; ++} BWL_POST_PACKED_STRUCT; ++typedef struct ht_cap_ie ht_cap_ie_t; ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct ht_prop_cap_ie { ++ uint8 id; ++ uint8 len; ++ uint8 oui[3]; ++ uint8 type; ++ ht_cap_ie_t cap_ie; ++} BWL_POST_PACKED_STRUCT; ++typedef struct ht_prop_cap_ie ht_prop_cap_ie_t; ++ ++#define HT_PROP_IE_OVERHEAD 4 ++#define HT_CAP_IE_LEN 26 ++#define HT_CAP_IE_TYPE 51 ++ ++#define HT_CAP_LDPC_CODING 0x0001 ++#define HT_CAP_40MHZ 0x0002 ++#define HT_CAP_MIMO_PS_MASK 0x000C ++#define HT_CAP_MIMO_PS_SHIFT 0x0002 ++#define HT_CAP_MIMO_PS_OFF 0x0003 ++#define HT_CAP_MIMO_PS_RTS 0x0001 ++#define HT_CAP_MIMO_PS_ON 0x0000 ++#define HT_CAP_GF 0x0010 ++#define HT_CAP_SHORT_GI_20 0x0020 ++#define HT_CAP_SHORT_GI_40 0x0040 ++#define HT_CAP_TX_STBC 0x0080 ++#define HT_CAP_RX_STBC_MASK 0x0300 ++#define HT_CAP_RX_STBC_SHIFT 8 ++#define HT_CAP_DELAYED_BA 0x0400 ++#define HT_CAP_MAX_AMSDU 0x0800 ++ ++#define HT_CAP_DSSS_CCK 0x1000 ++#define HT_CAP_PSMP 0x2000 ++#define HT_CAP_40MHZ_INTOLERANT 0x4000 ++#define HT_CAP_LSIG_TXOP 0x8000 ++ ++#define HT_CAP_RX_STBC_NO 0x0 ++#define HT_CAP_RX_STBC_ONE_STREAM 0x1 ++#define HT_CAP_RX_STBC_TWO_STREAM 0x2 ++#define HT_CAP_RX_STBC_THREE_STREAM 0x3 ++ ++#define VHT_MAX_MPDU 11454 ++#define VHT_MPDU_MSDU_DELTA 56 ++ ++#define VHT_MAX_AMSDU (VHT_MAX_MPDU - VHT_MPDU_MSDU_DELTA) ++ ++#define HT_MAX_AMSDU 7935 ++#define HT_MIN_AMSDU 3835 ++ ++#define HT_PARAMS_RX_FACTOR_MASK 0x03 ++#define HT_PARAMS_DENSITY_MASK 0x1C ++#define HT_PARAMS_DENSITY_SHIFT 2 ++ ++ ++#define AMPDU_MAX_MPDU_DENSITY 7 ++#define AMPDU_DENSITY_NONE 0 ++#define AMPDU_DENSITY_1over4_US 1 ++#define AMPDU_DENSITY_1over2_US 2 ++#define AMPDU_DENSITY_1_US 3 ++#define AMPDU_DENSITY_2_US 4 ++#define AMPDU_DENSITY_4_US 5 ++#define AMPDU_DENSITY_8_US 6 ++#define AMPDU_DENSITY_16_US 7 ++#define AMPDU_RX_FACTOR_8K 0 ++#define AMPDU_RX_FACTOR_16K 1 ++#define AMPDU_RX_FACTOR_32K 2 ++#define AMPDU_RX_FACTOR_64K 3 ++#define AMPDU_RX_FACTOR_BASE 8*1024 ++ ++#define AMPDU_DELIMITER_LEN 4 ++#define AMPDU_DELIMITER_LEN_MAX 63 ++ ++#define HT_CAP_EXT_PCO 0x0001 ++#define HT_CAP_EXT_PCO_TTIME_MASK 0x0006 ++#define HT_CAP_EXT_PCO_TTIME_SHIFT 1 ++#define HT_CAP_EXT_MCS_FEEDBACK_MASK 0x0300 ++#define HT_CAP_EXT_MCS_FEEDBACK_SHIFT 8 ++#define HT_CAP_EXT_HTC 0x0400 ++#define HT_CAP_EXT_RD_RESP 0x0800 ++ ++BWL_PRE_PACKED_STRUCT struct ht_add_ie { ++ uint8 ctl_ch; ++ uint8 byte1; ++ uint16 opmode; ++ uint16 misc_bits; ++ uint8 basic_mcs[MCSSET_LEN]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct ht_add_ie ht_add_ie_t; ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct ht_prop_add_ie { ++ uint8 id; ++ uint8 len; ++ uint8 oui[3]; ++ uint8 type; ++ ht_add_ie_t add_ie; ++} BWL_POST_PACKED_STRUCT; ++typedef struct ht_prop_add_ie ht_prop_add_ie_t; ++ ++#define HT_ADD_IE_LEN 22 ++#define HT_ADD_IE_TYPE 52 ++ ++ ++#define HT_BW_ANY 0x04 ++#define HT_RIFS_PERMITTED 0x08 ++ ++ ++#define HT_OPMODE_MASK 0x0003 ++#define HT_OPMODE_SHIFT 0 ++#define HT_OPMODE_PURE 0x0000 ++#define HT_OPMODE_OPTIONAL 0x0001 ++#define HT_OPMODE_HT20IN40 0x0002 ++#define HT_OPMODE_MIXED 0x0003 ++#define HT_OPMODE_NONGF 0x0004 ++#define DOT11N_TXBURST 0x0008 ++#define DOT11N_OBSS_NONHT 0x0010 ++ ++ ++#define HT_BASIC_STBC_MCS 0x007f ++#define HT_DUAL_STBC_PROT 0x0080 ++#define HT_SECOND_BCN 0x0100 ++#define HT_LSIG_TXOP 0x0200 ++#define HT_PCO_ACTIVE 0x0400 ++#define HT_PCO_PHASE 0x0800 ++#define HT_DUALCTS_PROTECTION 0x0080 ++ ++ ++#define DOT11N_2G_TXBURST_LIMIT 6160 ++#define DOT11N_5G_TXBURST_LIMIT 3080 ++ ++ ++#define GET_HT_OPMODE(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \ ++ >> HT_OPMODE_SHIFT) ++#define HT_MIXEDMODE_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \ ++ == HT_OPMODE_MIXED) ++#define HT_HT20_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \ ++ == HT_OPMODE_HT20IN40) ++#define HT_OPTIONAL_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \ ++ == HT_OPMODE_OPTIONAL) ++#define HT_USE_PROTECTION(add_ie) (HT_HT20_PRESENT((add_ie)) || \ ++ HT_MIXEDMODE_PRESENT((add_ie))) ++#define HT_NONGF_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_NONGF) \ ++ == HT_OPMODE_NONGF) ++#define DOT11N_TXBURST_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & DOT11N_TXBURST) \ ++ == DOT11N_TXBURST) ++#define DOT11N_OBSS_NONHT_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & DOT11N_OBSS_NONHT) \ ++ == DOT11N_OBSS_NONHT) ++ ++BWL_PRE_PACKED_STRUCT struct obss_params { ++ uint16 passive_dwell; ++ uint16 active_dwell; ++ uint16 bss_widthscan_interval; ++ uint16 passive_total; ++ uint16 active_total; ++ uint16 chanwidth_transition_dly; ++ uint16 activity_threshold; ++} BWL_POST_PACKED_STRUCT; ++typedef struct obss_params obss_params_t; ++ ++BWL_PRE_PACKED_STRUCT struct dot11_obss_ie { ++ uint8 id; ++ uint8 len; ++ obss_params_t obss_params; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_obss_ie dot11_obss_ie_t; ++#define DOT11_OBSS_SCAN_IE_LEN sizeof(obss_params_t) ++ ++ ++#define HT_CTRL_LA_TRQ 0x00000002 ++#define HT_CTRL_LA_MAI 0x0000003C ++#define HT_CTRL_LA_MAI_SHIFT 2 ++#define HT_CTRL_LA_MAI_MRQ 0x00000004 ++#define HT_CTRL_LA_MAI_MSI 0x00000038 ++#define HT_CTRL_LA_MFSI 0x000001C0 ++#define HT_CTRL_LA_MFSI_SHIFT 6 ++#define HT_CTRL_LA_MFB_ASELC 0x0000FE00 ++#define HT_CTRL_LA_MFB_ASELC_SH 9 ++#define HT_CTRL_LA_ASELC_CMD 0x00000C00 ++#define HT_CTRL_LA_ASELC_DATA 0x0000F000 ++#define HT_CTRL_CAL_POS 0x00030000 ++#define HT_CTRL_CAL_SEQ 0x000C0000 ++#define HT_CTRL_CSI_STEERING 0x00C00000 ++#define HT_CTRL_CSI_STEER_SHIFT 22 ++#define HT_CTRL_CSI_STEER_NFB 0 ++#define HT_CTRL_CSI_STEER_CSI 1 ++#define HT_CTRL_CSI_STEER_NCOM 2 ++#define HT_CTRL_CSI_STEER_COM 3 ++#define HT_CTRL_NDP_ANNOUNCE 0x01000000 ++#define HT_CTRL_AC_CONSTRAINT 0x40000000 ++#define HT_CTRL_RDG_MOREPPDU 0x80000000 ++ ++#define HT_OPMODE_OPTIONAL 0x0001 ++#define HT_OPMODE_HT20IN40 0x0002 ++#define HT_OPMODE_MIXED 0x0003 ++#define HT_OPMODE_NONGF 0x0004 ++#define DOT11N_TXBURST 0x0008 ++#define DOT11N_OBSS_NONHT 0x0010 ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct vht_cap_ie { ++ uint32 vht_cap_info; ++ ++ uint16 rx_mcs_map; ++ uint16 rx_max_rate; ++ uint16 tx_mcs_map; ++ uint16 tx_max_rate; ++} BWL_POST_PACKED_STRUCT; ++typedef struct vht_cap_ie vht_cap_ie_t; ++ ++#define VHT_CAP_IE_LEN 12 ++ ++#define VHT_CAP_INFO_MAX_MPDU_LEN_MASK 0x00000003 ++#define VHT_CAP_INFO_SUPP_CHAN_WIDTH_MASK 0x0000000c ++#define VHT_CAP_INFO_LDPC 0x00000010 ++#define VHT_CAP_INFO_SGI_80MHZ 0x00000020 ++ ++#define VHT_CAP_INFO_SGI_160MHZ 0x00000040 ++#define VHT_CAP_INFO_TX_STBC 0x00000080 ++ ++#define VHT_CAP_INFO_RX_STBC_MASK 0x00000700 ++#define VHT_CAP_INFO_RX_STBC_SHIFT 8 ++#define VHT_CAP_INFO_SU_BEAMFMR 0x00000800 ++#define VHT_CAP_INFO_SU_BEAMFMEE 0x00001000 ++#define VHT_CAP_INFO_NUM_BMFMR_ANT_MASK 0x0000e000 ++#define VHT_CAP_INFO_NUM_BMFMR_ANT_SHIFT 13 ++ ++#define VHT_CAP_INFO_NUM_SOUNDING_DIM_MASK 0x00070000 ++#define VHT_CAP_INFO_NUM_SOUNDING_DIM_SHIFT 16 ++#define VHT_CAP_INFO_MU_BEAMFMR 0x00080000 ++#define VHT_CAP_INFO_MU_BEAMFMEE 0x00100000 ++#define VHT_CAP_INFO_TXOPPS 0x00200000 ++#define VHT_CAP_INFO_HTCVHT 0x00400000 ++#define VHT_CAP_INFO_AMPDU_MAXLEN_EXP_MASK 0x03800000 ++#define VHT_CAP_INFO_AMPDU_MAXLEN_EXP_SHIFT 23 ++ ++#define VHT_CAP_INFO_LINK_ADAPT_CAP_MASK 0x0c000000 ++#define VHT_CAP_INFO_LINK_ADAPT_CAP_SHIFT 26 ++ ++ ++#define VHT_CAP_SUPP_MCS_RX_HIGHEST_RATE_MASK 0x1fff ++#define VHT_CAP_SUPP_MCS_RX_HIGHEST_RATE_SHIFT 0 ++ ++#define VHT_CAP_SUPP_MCS_TX_HIGHEST_RATE_MASK 0x1fff ++#define VHT_CAP_SUPP_MCS_TX_HIGHEST_RATE_SHIFT 0 ++ ++#define VHT_CAP_MCS_MAP_0_7 0 ++#define VHT_CAP_MCS_MAP_0_8 1 ++#define VHT_CAP_MCS_MAP_0_9 2 ++#define VHT_CAP_MCS_MAP_NONE 3 ++ ++#define VHT_CAP_MCS_MAP_NSS_MAX 8 ++ ++ ++typedef enum vht_cap_chan_width { ++ VHT_CAP_CHAN_WIDTH_20_40 = 0x00, ++ VHT_CAP_CHAN_WIDTH_80 = 0x04, ++ VHT_CAP_CHAN_WIDTH_160 = 0x08 ++} vht_cap_chan_width_t; ++ ++ ++typedef enum vht_cap_max_mpdu_len { ++ VHT_CAP_MPDU_MAX_4K = 0x00, ++ VHT_CAP_MPDU_MAX_8K = 0x01, ++ VHT_CAP_MPDU_MAX_11K = 0x02 ++} vht_cap_max_mpdu_len_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct vht_op_ie { ++ uint8 chan_width; ++ uint8 chan1; ++ uint8 chan2; ++ uint16 supp_mcs; ++} BWL_POST_PACKED_STRUCT; ++typedef struct vht_op_ie vht_op_ie_t; ++ ++#define VHT_OP_IE_LEN 5 ++ ++typedef enum vht_op_chan_width { ++ VHT_OP_CHAN_WIDTH_20_40 = 0, ++ VHT_OP_CHAN_WIDTH_80 = 1, ++ VHT_OP_CHAN_WIDTH_160 = 2, ++ VHT_OP_CHAN_WIDTH_80_80 = 3 ++} vht_op_chan_width_t; ++ ++ ++#define VHT_MCS_MAP_GET_SS_IDX(nss) (((nss)-1)*2) ++#define VHT_MCS_MAP_GET_MCS_PER_SS(nss, mcsMap) \ ++ (((mcsMap) >> VHT_MCS_MAP_GET_SS_IDX(nss)) & 0x3) ++#define VHT_MCS_MAP_SET_MCS_PER_SS(nss, numMcs, mcsMap) \ ++ ((mcsMap) |= (((numMcs) & 0x3) << VHT_MCS_MAP_GET_SS_IDX(nss))) ++ ++ ++#define WPA_OUI "\x00\x50\xF2" ++#define WPA_OUI_LEN 3 ++#define WPA_OUI_TYPE 1 ++#define WPA_VERSION 1 ++#define WPA2_OUI "\x00\x0F\xAC" ++#define WPA2_OUI_LEN 3 ++#define WPA2_VERSION 1 ++#define WPA2_VERSION_LEN 2 ++ ++ ++#define WPS_OUI "\x00\x50\xF2" ++#define WPS_OUI_LEN 3 ++#define WPS_OUI_TYPE 4 ++ ++ ++ ++#ifdef P2P_IE_OVRD ++#define WFA_OUI MAC_OUI ++#else ++#define WFA_OUI "\x50\x6F\x9A" ++#endif ++#define WFA_OUI_LEN 3 ++#ifdef P2P_IE_OVRD ++#define WFA_OUI_TYPE_P2P MAC_OUI_TYPE_P2P ++#else ++#define WFA_OUI_TYPE_P2P 9 ++#endif ++ ++#define WFA_OUI_TYPE_TPC 8 ++#ifdef WLTDLS ++#define WFA_OUI_TYPE_WFD 10 ++#endif ++ ++ ++#define RSN_AKM_NONE 0 ++#define RSN_AKM_UNSPECIFIED 1 ++#define RSN_AKM_PSK 2 ++#define RSN_AKM_FBT_1X 3 ++#define RSN_AKM_FBT_PSK 4 ++#define RSN_AKM_MFP_1X 5 ++#define RSN_AKM_MFP_PSK 6 ++#define RSN_AKM_TPK 7 ++ ++ ++#define DOT11_MAX_DEFAULT_KEYS 4 ++#define DOT11_MAX_KEY_SIZE 32 ++#define DOT11_MAX_IV_SIZE 16 ++#define DOT11_EXT_IV_FLAG (1<<5) ++#define DOT11_WPA_KEY_RSC_LEN 8 ++ ++#define WEP1_KEY_SIZE 5 ++#define WEP1_KEY_HEX_SIZE 10 ++#define WEP128_KEY_SIZE 13 ++#define WEP128_KEY_HEX_SIZE 26 ++#define TKIP_MIC_SIZE 8 ++#define TKIP_EOM_SIZE 7 ++#define TKIP_EOM_FLAG 0x5a ++#define TKIP_KEY_SIZE 32 ++#define TKIP_MIC_AUTH_TX 16 ++#define TKIP_MIC_AUTH_RX 24 ++#define TKIP_MIC_SUP_RX TKIP_MIC_AUTH_TX ++#define TKIP_MIC_SUP_TX TKIP_MIC_AUTH_RX ++#define AES_KEY_SIZE 16 ++#define AES_MIC_SIZE 8 ++#define BIP_KEY_SIZE 16 ++ ++ ++#define WCN_OUI "\x00\x50\xf2" ++#define WCN_TYPE 4 ++ ++ ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_mdid_ie { ++ uint8 id; ++ uint8 len; ++ uint16 mdid; ++ uint8 cap; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_mdid_ie dot11_mdid_ie_t; ++ ++#define FBT_MDID_CAP_OVERDS 0x01 ++#define FBT_MDID_CAP_RRP 0x02 ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_ft_ie { ++ uint8 id; ++ uint8 len; ++ uint16 mic_control; ++ uint8 mic[16]; ++ uint8 anonce[32]; ++ uint8 snonce[32]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_ft_ie dot11_ft_ie_t; ++ ++#define TIE_TYPE_RESERVED 0 ++#define TIE_TYPE_REASSOC_DEADLINE 1 ++#define TIE_TYPE_KEY_LIEFTIME 2 ++#define TIE_TYPE_ASSOC_COMEBACK 3 ++BWL_PRE_PACKED_STRUCT struct dot11_timeout_ie { ++ uint8 id; ++ uint8 len; ++ uint8 type; ++ uint32 value; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_timeout_ie dot11_timeout_ie_t; ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct dot11_gtk_ie { ++ uint8 id; ++ uint8 len; ++ uint16 key_info; ++ uint8 key_len; ++ uint8 rsc[8]; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct dot11_gtk_ie dot11_gtk_ie_t; ++ ++#define BSSID_INVALID "\x00\x00\x00\x00\x00\x00" ++#define BSSID_BROADCAST "\xFF\xFF\xFF\xFF\xFF\xFF" ++ ++ ++ ++#define WMM_OUI "\x00\x50\xF2" ++#define WMM_OUI_LEN 3 ++#define WMM_OUI_TYPE 2 ++#define WMM_VERSION 1 ++#define WMM_VERSION_LEN 1 ++ ++ ++#define WMM_OUI_SUBTYPE_PARAMETER 1 ++#define WMM_PARAMETER_IE_LEN 24 ++ ++ ++BWL_PRE_PACKED_STRUCT struct link_id_ie { ++ uint8 id; ++ uint8 len; ++ struct ether_addr bssid; ++ struct ether_addr tdls_init_mac; ++ struct ether_addr tdls_resp_mac; ++} BWL_POST_PACKED_STRUCT; ++typedef struct link_id_ie link_id_ie_t; ++#define TDLS_LINK_ID_IE_LEN 18 ++ ++ ++BWL_PRE_PACKED_STRUCT struct wakeup_sch_ie { ++ uint8 id; ++ uint8 len; ++ uint32 offset; ++ uint32 interval; ++ uint32 awake_win_slots; ++ uint32 max_wake_win; ++ uint16 idle_cnt; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wakeup_sch_ie wakeup_sch_ie_t; ++#define TDLS_WAKEUP_SCH_IE_LEN 18 ++ ++ ++BWL_PRE_PACKED_STRUCT struct channel_switch_timing_ie { ++ uint8 id; ++ uint8 len; ++ uint16 switch_time; ++ uint16 switch_timeout; ++} BWL_POST_PACKED_STRUCT; ++typedef struct channel_switch_timing_ie channel_switch_timing_ie_t; ++#define TDLS_CHANNEL_SWITCH_TIMING_IE_LEN 4 ++ ++ ++BWL_PRE_PACKED_STRUCT struct pti_control_ie { ++ uint8 id; ++ uint8 len; ++ uint8 tid; ++ uint16 seq_control; ++} BWL_POST_PACKED_STRUCT; ++typedef struct pti_control_ie pti_control_ie_t; ++#define TDLS_PTI_CONTROL_IE_LEN 3 ++ ++ ++BWL_PRE_PACKED_STRUCT struct pu_buffer_status_ie { ++ uint8 id; ++ uint8 len; ++ uint8 status; ++} BWL_POST_PACKED_STRUCT; ++typedef struct pu_buffer_status_ie pu_buffer_status_ie_t; ++#define TDLS_PU_BUFFER_STATUS_IE_LEN 1 ++#define TDLS_PU_BUFFER_STATUS_AC_BK 1 ++#define TDLS_PU_BUFFER_STATUS_AC_BE 2 ++#define TDLS_PU_BUFFER_STATUS_AC_VI 4 ++#define TDLS_PU_BUFFER_STATUS_AC_VO 8 ++ ++ ++#include ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h b/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h +new file mode 100644 +index 00000000..3ee5a748 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h +@@ -0,0 +1,45 @@ ++/* ++ * BT-AMP (BlueTooth Alternate Mac and Phy) 802.11 PAL (Protocol Adaptation Layer) ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: 802.11_bta.h 294267 2011-11-04 23:41:52Z $ ++*/ ++ ++#ifndef _802_11_BTA_H_ ++#define _802_11_BTA_H_ ++ ++#define BT_SIG_SNAP_MPROT "\xAA\xAA\x03\x00\x19\x58" ++ ++/* BT-AMP 802.11 PAL Protocols */ ++#define BTA_PROT_L2CAP 1 ++#define BTA_PROT_ACTIVITY_REPORT 2 ++#define BTA_PROT_SECURITY 3 ++#define BTA_PROT_LINK_SUPERVISION_REQUEST 4 ++#define BTA_PROT_LINK_SUPERVISION_REPLY 5 ++ ++/* BT-AMP 802.11 PAL AMP_ASSOC Type IDs */ ++#define BTA_TYPE_ID_MAC_ADDRESS 1 ++#define BTA_TYPE_ID_PREFERRED_CHANNELS 2 ++#define BTA_TYPE_ID_CONNECTED_CHANNELS 3 ++#define BTA_TYPE_ID_CAPABILITIES 4 ++#define BTA_TYPE_ID_VERSION 5 ++#endif /* _802_11_bta_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11e.h b/drivers/net/wireless/bcmdhd/include/proto/802.11e.h +new file mode 100644 +index 00000000..f391e68c +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/802.11e.h +@@ -0,0 +1,131 @@ ++/* ++ * 802.11e protocol header file ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: 802.11e.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++#ifndef _802_11e_H_ ++#define _802_11e_H_ ++ ++#ifndef _TYPEDEFS_H_ ++#include ++#endif ++ ++/* This marks the start of a packed structure section. */ ++#include ++ ++ ++/* WME Traffic Specification (TSPEC) element */ ++#define WME_TSPEC_HDR_LEN 2 /* WME TSPEC header length */ ++#define WME_TSPEC_BODY_OFF 2 /* WME TSPEC body offset */ ++ ++#define WME_CATEGORY_CODE_OFFSET 0 /* WME Category code offset */ ++#define WME_ACTION_CODE_OFFSET 1 /* WME Action code offset */ ++#define WME_TOKEN_CODE_OFFSET 2 /* WME Token code offset */ ++#define WME_STATUS_CODE_OFFSET 3 /* WME Status code offset */ ++ ++BWL_PRE_PACKED_STRUCT struct tsinfo { ++ uint8 octets[3]; ++} BWL_POST_PACKED_STRUCT; ++ ++typedef struct tsinfo tsinfo_t; ++ ++/* 802.11e TSPEC IE */ ++typedef BWL_PRE_PACKED_STRUCT struct tspec { ++ uint8 oui[DOT11_OUI_LEN]; /* WME_OUI */ ++ uint8 type; /* WME_TYPE */ ++ uint8 subtype; /* WME_SUBTYPE_TSPEC */ ++ uint8 version; /* WME_VERSION */ ++ tsinfo_t tsinfo; /* TS Info bit field */ ++ uint16 nom_msdu_size; /* (Nominal or fixed) MSDU Size (bytes) */ ++ uint16 max_msdu_size; /* Maximum MSDU Size (bytes) */ ++ uint32 min_srv_interval; /* Minimum Service Interval (us) */ ++ uint32 max_srv_interval; /* Maximum Service Interval (us) */ ++ uint32 inactivity_interval; /* Inactivity Interval (us) */ ++ uint32 suspension_interval; /* Suspension Interval (us) */ ++ uint32 srv_start_time; /* Service Start Time (us) */ ++ uint32 min_data_rate; /* Minimum Data Rate (bps) */ ++ uint32 mean_data_rate; /* Mean Data Rate (bps) */ ++ uint32 peak_data_rate; /* Peak Data Rate (bps) */ ++ uint32 max_burst_size; /* Maximum Burst Size (bytes) */ ++ uint32 delay_bound; /* Delay Bound (us) */ ++ uint32 min_phy_rate; /* Minimum PHY Rate (bps) */ ++ uint16 surplus_bw; /* Surplus Bandwidth Allowance (range 1.0-8.0) */ ++ uint16 medium_time; /* Medium Time (32 us/s periods) */ ++} BWL_POST_PACKED_STRUCT tspec_t; ++ ++#define WME_TSPEC_LEN (sizeof(tspec_t)) /* not including 2-bytes of header */ ++ ++/* ts_info */ ++/* 802.1D priority is duplicated - bits 13-11 AND bits 3-1 */ ++#define TS_INFO_TID_SHIFT 1 /* TS info. TID shift */ ++#define TS_INFO_TID_MASK (0xf << TS_INFO_TID_SHIFT) /* TS info. TID mask */ ++#define TS_INFO_CONTENTION_SHIFT 7 /* TS info. contention shift */ ++#define TS_INFO_CONTENTION_MASK (0x1 << TS_INFO_CONTENTION_SHIFT) /* TS info. contention mask */ ++#define TS_INFO_DIRECTION_SHIFT 5 /* TS info. direction shift */ ++#define TS_INFO_DIRECTION_MASK (0x3 << TS_INFO_DIRECTION_SHIFT) /* TS info. direction mask */ ++#define TS_INFO_PSB_SHIFT 2 /* TS info. PSB bit Shift */ ++#define TS_INFO_PSB_MASK (1 << TS_INFO_PSB_SHIFT) /* TS info. PSB mask */ ++#define TS_INFO_UPLINK (0 << TS_INFO_DIRECTION_SHIFT) /* TS info. uplink */ ++#define TS_INFO_DOWNLINK (1 << TS_INFO_DIRECTION_SHIFT) /* TS info. downlink */ ++#define TS_INFO_BIDIRECTIONAL (3 << TS_INFO_DIRECTION_SHIFT) /* TS info. bidirectional */ ++#define TS_INFO_USER_PRIO_SHIFT 3 /* TS info. user priority shift */ ++/* TS info. user priority mask */ ++#define TS_INFO_USER_PRIO_MASK (0x7 << TS_INFO_USER_PRIO_SHIFT) ++ ++/* Macro to get/set bit(s) field in TSINFO */ ++#define WLC_CAC_GET_TID(pt) ((((pt).octets[0]) & TS_INFO_TID_MASK) >> TS_INFO_TID_SHIFT) ++#define WLC_CAC_GET_DIR(pt) ((((pt).octets[0]) & \ ++ TS_INFO_DIRECTION_MASK) >> TS_INFO_DIRECTION_SHIFT) ++#define WLC_CAC_GET_PSB(pt) ((((pt).octets[1]) & TS_INFO_PSB_MASK) >> TS_INFO_PSB_SHIFT) ++#define WLC_CAC_GET_USER_PRIO(pt) ((((pt).octets[1]) & \ ++ TS_INFO_USER_PRIO_MASK) >> TS_INFO_USER_PRIO_SHIFT) ++ ++#define WLC_CAC_SET_TID(pt, id) ((((pt).octets[0]) & (~TS_INFO_TID_MASK)) | \ ++ ((id) << TS_INFO_TID_SHIFT)) ++#define WLC_CAC_SET_USER_PRIO(pt, prio) ((((pt).octets[0]) & (~TS_INFO_USER_PRIO_MASK)) | \ ++ ((prio) << TS_INFO_USER_PRIO_SHIFT)) ++ ++/* 802.11e QBSS Load IE */ ++#define QBSS_LOAD_IE_LEN 5 /* QBSS Load IE length */ ++#define QBSS_LOAD_AAC_OFF 3 /* AAC offset in IE */ ++ ++#define CAC_ADDTS_RESP_TIMEOUT 300 /* default ADDTS response timeout in ms */ ++ ++/* 802.11e ADDTS status code */ ++#define DOT11E_STATUS_ADMISSION_ACCEPTED 0 /* TSPEC Admission accepted status */ ++#define DOT11E_STATUS_ADDTS_INVALID_PARAM 1 /* TSPEC invalid parameter status */ ++#define DOT11E_STATUS_ADDTS_REFUSED_NSBW 3 /* ADDTS refused (non-sufficient BW) */ ++#define DOT11E_STATUS_ADDTS_REFUSED_AWHILE 47 /* ADDTS refused but could retry later */ ++ ++/* 802.11e DELTS status code */ ++#define DOT11E_STATUS_QSTA_LEAVE_QBSS 36 /* STA leave QBSS */ ++#define DOT11E_STATUS_END_TS 37 /* END TS */ ++#define DOT11E_STATUS_UNKNOWN_TS 38 /* UNKNOWN TS */ ++#define DOT11E_STATUS_QSTA_REQ_TIMEOUT 39 /* STA ADDTS request timeout */ ++ ++ ++/* This marks the end of a packed structure section. */ ++#include ++ ++#endif /* _802_11e_CAC_H_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.1d.h b/drivers/net/wireless/bcmdhd/include/proto/802.1d.h +new file mode 100644 +index 00000000..116a226b +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/802.1d.h +@@ -0,0 +1,48 @@ ++/* ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * Fundamental types and constants relating to 802.1D ++ * ++ * $Id: 802.1d.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++#ifndef _802_1_D_ ++#define _802_1_D_ ++ ++ ++#define PRIO_8021D_NONE 2 ++#define PRIO_8021D_BK 1 ++#define PRIO_8021D_BE 0 ++#define PRIO_8021D_EE 3 ++#define PRIO_8021D_CL 4 ++#define PRIO_8021D_VI 5 ++#define PRIO_8021D_VO 6 ++#define PRIO_8021D_NC 7 ++#define MAXPRIO 7 ++#define NUMPRIO (MAXPRIO + 1) ++ ++#define ALLPRIO -1 ++ ++ ++#define PRIO2PREC(prio) \ ++ (((prio) == PRIO_8021D_NONE || (prio) == PRIO_8021D_BE) ? ((prio^2)) : (prio)) ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h b/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h +new file mode 100644 +index 00000000..e54b2e38 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h +@@ -0,0 +1,82 @@ ++/* ++ * Broadcom Ethernettype protocol definitions ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bcmeth.h 294352 2011-11-06 19:23:00Z $ ++ */ ++ ++ ++ ++#ifndef _BCMETH_H_ ++#define _BCMETH_H_ ++ ++#ifndef _TYPEDEFS_H_ ++#include ++#endif ++ ++ ++#include ++ ++ ++ ++ ++ ++ ++ ++#define BCMILCP_SUBTYPE_RATE 1 ++#define BCMILCP_SUBTYPE_LINK 2 ++#define BCMILCP_SUBTYPE_CSA 3 ++#define BCMILCP_SUBTYPE_LARQ 4 ++#define BCMILCP_SUBTYPE_VENDOR 5 ++#define BCMILCP_SUBTYPE_FLH 17 ++ ++#define BCMILCP_SUBTYPE_VENDOR_LONG 32769 ++#define BCMILCP_SUBTYPE_CERT 32770 ++#define BCMILCP_SUBTYPE_SES 32771 ++ ++ ++#define BCMILCP_BCM_SUBTYPE_RESERVED 0 ++#define BCMILCP_BCM_SUBTYPE_EVENT 1 ++#define BCMILCP_BCM_SUBTYPE_SES 2 ++ ++ ++#define BCMILCP_BCM_SUBTYPE_DPT 4 ++ ++#define BCMILCP_BCM_SUBTYPEHDR_MINLENGTH 8 ++#define BCMILCP_BCM_SUBTYPEHDR_VERSION 0 ++ ++ ++typedef BWL_PRE_PACKED_STRUCT struct bcmeth_hdr ++{ ++ uint16 subtype; ++ uint16 length; ++ uint8 version; ++ uint8 oui[3]; ++ ++ uint16 usr_subtype; ++} BWL_POST_PACKED_STRUCT bcmeth_hdr_t; ++ ++ ++ ++#include ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h +new file mode 100644 +index 00000000..c4397074 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h +@@ -0,0 +1,368 @@ ++/* ++ * Broadcom Event protocol definitions ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * Dependencies: proto/bcmeth.h ++ * ++ * $Id: bcmevent.h 374275 2012-12-12 11:44:18Z $ ++ * ++ */ ++ ++/* ++ * Broadcom Ethernet Events protocol defines ++ * ++ */ ++ ++#ifndef _BCMEVENT_H_ ++#define _BCMEVENT_H_ ++ ++#ifndef _TYPEDEFS_H_ ++#include ++#endif ++ ++/* This marks the start of a packed structure section. */ ++#include ++ ++#define BCM_EVENT_MSG_VERSION 2 /* wl_event_msg_t struct version */ ++#define BCM_MSG_IFNAME_MAX 16 /* max length of interface name */ ++ ++/* flags */ ++#define WLC_EVENT_MSG_LINK 0x01 /* link is up */ ++#define WLC_EVENT_MSG_FLUSHTXQ 0x02 /* flush tx queue on MIC error */ ++#define WLC_EVENT_MSG_GROUP 0x04 /* group MIC error */ ++#define WLC_EVENT_MSG_UNKBSS 0x08 /* unknown source bsscfg */ ++#define WLC_EVENT_MSG_UNKIF 0x10 /* unknown source OS i/f */ ++ ++/* these fields are stored in network order */ ++ ++/* version 1 */ ++typedef BWL_PRE_PACKED_STRUCT struct ++{ ++ uint16 version; ++ uint16 flags; /* see flags below */ ++ uint32 event_type; /* Message (see below) */ ++ uint32 status; /* Status code (see below) */ ++ uint32 reason; /* Reason code (if applicable) */ ++ uint32 auth_type; /* WLC_E_AUTH */ ++ uint32 datalen; /* data buf */ ++ struct ether_addr addr; /* Station address (if applicable) */ ++ char ifname[BCM_MSG_IFNAME_MAX]; /* name of the packet incoming interface */ ++} BWL_POST_PACKED_STRUCT wl_event_msg_v1_t; ++ ++/* the current version */ ++typedef BWL_PRE_PACKED_STRUCT struct ++{ ++ uint16 version; ++ uint16 flags; /* see flags below */ ++ uint32 event_type; /* Message (see below) */ ++ uint32 status; /* Status code (see below) */ ++ uint32 reason; /* Reason code (if applicable) */ ++ uint32 auth_type; /* WLC_E_AUTH */ ++ uint32 datalen; /* data buf */ ++ struct ether_addr addr; /* Station address (if applicable) */ ++ char ifname[BCM_MSG_IFNAME_MAX]; /* name of the packet incoming interface */ ++ uint8 ifidx; /* destination OS i/f index */ ++ uint8 bsscfgidx; /* source bsscfg index */ ++} BWL_POST_PACKED_STRUCT wl_event_msg_t; ++ ++/* used by driver msgs */ ++typedef BWL_PRE_PACKED_STRUCT struct bcm_event { ++ struct ether_header eth; ++ bcmeth_hdr_t bcm_hdr; ++ wl_event_msg_t event; ++ /* data portion follows */ ++} BWL_POST_PACKED_STRUCT bcm_event_t; ++ ++#define BCM_MSG_LEN (sizeof(bcm_event_t) - sizeof(bcmeth_hdr_t) - sizeof(struct ether_header)) ++ ++/* Event messages */ ++#define WLC_E_SET_SSID 0 /* indicates status of set SSID */ ++#define WLC_E_JOIN 1 /* differentiates join IBSS from found (WLC_E_START) IBSS */ ++#define WLC_E_START 2 /* STA founded an IBSS or AP started a BSS */ ++#define WLC_E_AUTH 3 /* 802.11 AUTH request */ ++#define WLC_E_AUTH_IND 4 /* 802.11 AUTH indication */ ++#define WLC_E_DEAUTH 5 /* 802.11 DEAUTH request */ ++#define WLC_E_DEAUTH_IND 6 /* 802.11 DEAUTH indication */ ++#define WLC_E_ASSOC 7 /* 802.11 ASSOC request */ ++#define WLC_E_ASSOC_IND 8 /* 802.11 ASSOC indication */ ++#define WLC_E_REASSOC 9 /* 802.11 REASSOC request */ ++#define WLC_E_REASSOC_IND 10 /* 802.11 REASSOC indication */ ++#define WLC_E_DISASSOC 11 /* 802.11 DISASSOC request */ ++#define WLC_E_DISASSOC_IND 12 /* 802.11 DISASSOC indication */ ++#define WLC_E_QUIET_START 13 /* 802.11h Quiet period started */ ++#define WLC_E_QUIET_END 14 /* 802.11h Quiet period ended */ ++#define WLC_E_BEACON_RX 15 /* BEACONS received/lost indication */ ++#define WLC_E_LINK 16 /* generic link indication */ ++#define WLC_E_MIC_ERROR 17 /* TKIP MIC error occurred */ ++#define WLC_E_NDIS_LINK 18 /* NDIS style link indication */ ++#define WLC_E_ROAM 19 /* roam attempt occurred: indicate status & reason */ ++#define WLC_E_TXFAIL 20 /* change in dot11FailedCount (txfail) */ ++#define WLC_E_PMKID_CACHE 21 /* WPA2 pmkid cache indication */ ++#define WLC_E_RETROGRADE_TSF 22 /* current AP's TSF value went backward */ ++#define WLC_E_PRUNE 23 /* AP was pruned from join list for reason */ ++#define WLC_E_AUTOAUTH 24 /* report AutoAuth table entry match for join attempt */ ++#define WLC_E_EAPOL_MSG 25 /* Event encapsulating an EAPOL message */ ++#define WLC_E_SCAN_COMPLETE 26 /* Scan results are ready or scan was aborted */ ++#define WLC_E_ADDTS_IND 27 /* indicate to host addts fail/success */ ++#define WLC_E_DELTS_IND 28 /* indicate to host delts fail/success */ ++#define WLC_E_BCNSENT_IND 29 /* indicate to host of beacon transmit */ ++#define WLC_E_BCNRX_MSG 30 /* Send the received beacon up to the host */ ++#define WLC_E_BCNLOST_MSG 31 /* indicate to host loss of beacon */ ++#define WLC_E_ROAM_PREP 32 /* before attempting to roam */ ++#define WLC_E_PFN_NET_FOUND 33 /* PFN network found event */ ++#define WLC_E_PFN_NET_LOST 34 /* PFN network lost event */ ++#define WLC_E_RESET_COMPLETE 35 ++#define WLC_E_JOIN_START 36 ++#define WLC_E_ROAM_START 37 ++#define WLC_E_ASSOC_START 38 ++#define WLC_E_IBSS_ASSOC 39 ++#define WLC_E_RADIO 40 ++#define WLC_E_PSM_WATCHDOG 41 /* PSM microcode watchdog fired */ ++#define WLC_E_PROBREQ_MSG 44 /* probe request received */ ++#define WLC_E_SCAN_CONFIRM_IND 45 ++#define WLC_E_PSK_SUP 46 /* WPA Handshake fail */ ++#define WLC_E_COUNTRY_CODE_CHANGED 47 ++#define WLC_E_EXCEEDED_MEDIUM_TIME 48 /* WMMAC excedded medium time */ ++#define WLC_E_ICV_ERROR 49 /* WEP ICV error occurred */ ++#define WLC_E_UNICAST_DECODE_ERROR 50 /* Unsupported unicast encrypted frame */ ++#define WLC_E_MULTICAST_DECODE_ERROR 51 /* Unsupported multicast encrypted frame */ ++#define WLC_E_TRACE 52 ++#ifdef WLBTAMP ++#define WLC_E_BTA_HCI_EVENT 53 /* BT-AMP HCI event */ ++#endif ++#define WLC_E_IF 54 /* I/F change (for dongle host notification) */ ++#define WLC_E_P2P_DISC_LISTEN_COMPLETE 55 /* listen state expires */ ++#define WLC_E_RSSI 56 /* indicate RSSI change based on configured levels */ ++#define WLC_E_PFN_SCAN_COMPLETE 57 /* PFN completed scan of network list */ ++#define WLC_E_EXTLOG_MSG 58 ++#define WLC_E_ACTION_FRAME 59 /* Action frame Rx */ ++#define WLC_E_ACTION_FRAME_COMPLETE 60 /* Action frame Tx complete */ ++#define WLC_E_PRE_ASSOC_IND 61 /* assoc request received */ ++#define WLC_E_PRE_REASSOC_IND 62 /* re-assoc request received */ ++#define WLC_E_CHANNEL_ADOPTED 63 ++#define WLC_E_AP_STARTED 64 /* AP started */ ++#define WLC_E_DFS_AP_STOP 65 /* AP stopped due to DFS */ ++#define WLC_E_DFS_AP_RESUME 66 /* AP resumed due to DFS */ ++#define WLC_E_WAI_STA_EVENT 67 /* WAI stations event */ ++#define WLC_E_WAI_MSG 68 /* event encapsulating an WAI message */ ++#define WLC_E_ESCAN_RESULT 69 /* escan result event */ ++#define WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE 70 /* action frame off channel complete */ ++#define WLC_E_PROBRESP_MSG 71 /* probe response received */ ++#define WLC_E_P2P_PROBREQ_MSG 72 /* P2P Probe request received */ ++#define WLC_E_DCS_REQUEST 73 ++ ++#define WLC_E_FIFO_CREDIT_MAP 74 /* credits for D11 FIFOs. [AC0,AC1,AC2,AC3,BC_MC,ATIM] */ ++ ++#define WLC_E_ACTION_FRAME_RX 75 /* Received action frame event WITH ++ * wl_event_rx_frame_data_t header ++ */ ++#define WLC_E_WAKE_EVENT 76 /* Wake Event timer fired, used for wake WLAN test mode */ ++#define WLC_E_RM_COMPLETE 77 /* Radio measurement complete */ ++#define WLC_E_HTSFSYNC 78 /* Synchronize TSF with the host */ ++#define WLC_E_OVERLAY_REQ 79 /* request an overlay IOCTL/iovar from the host */ ++#define WLC_E_CSA_COMPLETE_IND 80 /* 802.11 CHANNEL SWITCH ACTION completed */ ++#define WLC_E_EXCESS_PM_WAKE_EVENT 81 /* excess PM Wake Event to inform host */ ++#define WLC_E_PFN_SCAN_NONE 82 /* no PFN networks around */ ++#define WLC_E_PFN_SCAN_ALLGONE 83 /* last found PFN network gets lost */ ++#define WLC_E_GTK_PLUMBED 84 ++#define WLC_E_ASSOC_IND_NDIS 85 /* 802.11 ASSOC indication for NDIS only */ ++#define WLC_E_REASSOC_IND_NDIS 86 /* 802.11 REASSOC indication for NDIS only */ ++#define WLC_E_ASSOC_REQ_IE 87 ++#define WLC_E_ASSOC_RESP_IE 88 ++#define WLC_E_ASSOC_RECREATED 89 /* association recreated on resume */ ++#define WLC_E_ACTION_FRAME_RX_NDIS 90 /* rx action frame event for NDIS only */ ++#define WLC_E_AUTH_REQ 91 /* authentication request received */ ++#define WLC_E_TDLS_PEER_EVENT 92 /* discovered peer, connected or disconnected peer */ ++#define WLC_E_SPEEDY_RECREATE_FAIL 93 /* fast assoc recreation failed */ ++#define WLC_E_SERVICE_FOUND 102 /* desired service found */ ++#define WLC_E_GAS_FRAGMENT_RX 103 /* GAS fragment received */ ++#define WLC_E_GAS_COMPLETE 104 /* GAS sessions all complete */ ++#define WLC_E_P2PO_ADD_DEVICE 105 /* New device found by p2p offload */ ++#define WLC_E_P2PO_DEL_DEVICE 106 /* device has been removed by p2p offload */ ++#define WLC_E_LAST 107 /* highest val + 1 for range checking */ ++ ++ ++/* Table of event name strings for UIs and debugging dumps */ ++typedef struct { ++ uint event; ++ const char *name; ++} bcmevent_name_t; ++ ++extern const bcmevent_name_t bcmevent_names[]; ++extern const int bcmevent_names_size; ++ ++/* Event status codes */ ++#define WLC_E_STATUS_SUCCESS 0 /* operation was successful */ ++#define WLC_E_STATUS_FAIL 1 /* operation failed */ ++#define WLC_E_STATUS_TIMEOUT 2 /* operation timed out */ ++#define WLC_E_STATUS_NO_NETWORKS 3 /* failed due to no matching network found */ ++#define WLC_E_STATUS_ABORT 4 /* operation was aborted */ ++#define WLC_E_STATUS_NO_ACK 5 /* protocol failure: packet not ack'd */ ++#define WLC_E_STATUS_UNSOLICITED 6 /* AUTH or ASSOC packet was unsolicited */ ++#define WLC_E_STATUS_ATTEMPT 7 /* attempt to assoc to an auto auth configuration */ ++#define WLC_E_STATUS_PARTIAL 8 /* scan results are incomplete */ ++#define WLC_E_STATUS_NEWSCAN 9 /* scan aborted by another scan */ ++#define WLC_E_STATUS_NEWASSOC 10 /* scan aborted due to assoc in progress */ ++#define WLC_E_STATUS_11HQUIET 11 /* 802.11h quiet period started */ ++#define WLC_E_STATUS_SUPPRESS 12 /* user disabled scanning (WLC_SET_SCANSUPPRESS) */ ++#define WLC_E_STATUS_NOCHANS 13 /* no allowable channels to scan */ ++#define WLC_E_STATUS_CS_ABORT 15 /* abort channel select */ ++#define WLC_E_STATUS_ERROR 16 /* request failed due to error */ ++ ++/* roam reason codes */ ++#define WLC_E_REASON_INITIAL_ASSOC 0 /* initial assoc */ ++#define WLC_E_REASON_LOW_RSSI 1 /* roamed due to low RSSI */ ++#define WLC_E_REASON_DEAUTH 2 /* roamed due to DEAUTH indication */ ++#define WLC_E_REASON_DISASSOC 3 /* roamed due to DISASSOC indication */ ++#define WLC_E_REASON_BCNS_LOST 4 /* roamed due to lost beacons */ ++#define WLC_E_REASON_MINTXRATE 9 /* roamed because at mintxrate for too long */ ++#define WLC_E_REASON_TXFAIL 10 /* We can hear AP, but AP can't hear us */ ++ ++/* Roam codes used primarily by CCX */ ++#define WLC_E_REASON_FAST_ROAM_FAILED 5 /* roamed due to fast roam failure */ ++#define WLC_E_REASON_DIRECTED_ROAM 6 /* roamed due to request by AP */ ++#define WLC_E_REASON_TSPEC_REJECTED 7 /* roamed due to TSPEC rejection */ ++#define WLC_E_REASON_BETTER_AP 8 /* roamed due to finding better AP */ ++ ++ ++#define WLC_E_REASON_REQUESTED_ROAM 11 /* roamed due to BSS Mgmt Transition request by AP */ ++ ++/* prune reason codes */ ++#define WLC_E_PRUNE_ENCR_MISMATCH 1 /* encryption mismatch */ ++#define WLC_E_PRUNE_BCAST_BSSID 2 /* AP uses a broadcast BSSID */ ++#define WLC_E_PRUNE_MAC_DENY 3 /* STA's MAC addr is in AP's MAC deny list */ ++#define WLC_E_PRUNE_MAC_NA 4 /* STA's MAC addr is not in AP's MAC allow list */ ++#define WLC_E_PRUNE_REG_PASSV 5 /* AP not allowed due to regulatory restriction */ ++#define WLC_E_PRUNE_SPCT_MGMT 6 /* AP does not support STA locale spectrum mgmt */ ++#define WLC_E_PRUNE_RADAR 7 /* AP is on a radar channel of STA locale */ ++#define WLC_E_RSN_MISMATCH 8 /* STA does not support AP's RSN */ ++#define WLC_E_PRUNE_NO_COMMON_RATES 9 /* No rates in common with AP */ ++#define WLC_E_PRUNE_BASIC_RATES 10 /* STA does not support all basic rates of BSS */ ++#define WLC_E_PRUNE_CIPHER_NA 12 /* BSS's cipher not supported */ ++#define WLC_E_PRUNE_KNOWN_STA 13 /* AP is already known to us as a STA */ ++#define WLC_E_PRUNE_WDS_PEER 15 /* AP is already known to us as a WDS peer */ ++#define WLC_E_PRUNE_QBSS_LOAD 16 /* QBSS LOAD - AAC is too low */ ++#define WLC_E_PRUNE_HOME_AP 17 /* prune home AP */ ++ ++/* WPA failure reason codes carried in the WLC_E_PSK_SUP event */ ++#define WLC_E_SUP_OTHER 0 /* Other reason */ ++#define WLC_E_SUP_DECRYPT_KEY_DATA 1 /* Decryption of key data failed */ ++#define WLC_E_SUP_BAD_UCAST_WEP128 2 /* Illegal use of ucast WEP128 */ ++#define WLC_E_SUP_BAD_UCAST_WEP40 3 /* Illegal use of ucast WEP40 */ ++#define WLC_E_SUP_UNSUP_KEY_LEN 4 /* Unsupported key length */ ++#define WLC_E_SUP_PW_KEY_CIPHER 5 /* Unicast cipher mismatch in pairwise key */ ++#define WLC_E_SUP_MSG3_TOO_MANY_IE 6 /* WPA IE contains > 1 RSN IE in key msg 3 */ ++#define WLC_E_SUP_MSG3_IE_MISMATCH 7 /* WPA IE mismatch in key message 3 */ ++#define WLC_E_SUP_NO_INSTALL_FLAG 8 /* INSTALL flag unset in 4-way msg */ ++#define WLC_E_SUP_MSG3_NO_GTK 9 /* encapsulated GTK missing from msg 3 */ ++#define WLC_E_SUP_GRP_KEY_CIPHER 10 /* Multicast cipher mismatch in group key */ ++#define WLC_E_SUP_GRP_MSG1_NO_GTK 11 /* encapsulated GTK missing from group msg 1 */ ++#define WLC_E_SUP_GTK_DECRYPT_FAIL 12 /* GTK decrypt failure */ ++#define WLC_E_SUP_SEND_FAIL 13 /* message send failure */ ++#define WLC_E_SUP_DEAUTH 14 /* received FC_DEAUTH */ ++#define WLC_E_SUP_WPA_PSK_TMO 15 /* WPA PSK 4-way handshake timeout */ ++ ++/* Event data for events that include frames received over the air */ ++/* WLC_E_PROBRESP_MSG ++ * WLC_E_P2P_PROBREQ_MSG ++ * WLC_E_ACTION_FRAME_RX ++ */ ++typedef BWL_PRE_PACKED_STRUCT struct wl_event_rx_frame_data { ++ uint16 version; ++ uint16 channel; /* Matches chanspec_t format from bcmwifi_channels.h */ ++ int32 rssi; ++ uint32 mactime; ++ uint32 rate; ++} BWL_POST_PACKED_STRUCT wl_event_rx_frame_data_t; ++ ++#define BCM_RX_FRAME_DATA_VERSION 1 ++ ++/* WLC_E_IF event data */ ++typedef struct wl_event_data_if { ++ uint8 ifidx; /* RTE virtual device index (for dongle) */ ++ uint8 opcode; /* see I/F opcode */ ++ uint8 reserved; ++ uint8 bssidx; /* bsscfg index */ ++ uint8 role; /* see I/F role */ ++} wl_event_data_if_t; ++ ++/* opcode in WLC_E_IF event */ ++#define WLC_E_IF_ADD 1 /* bsscfg add */ ++#define WLC_E_IF_DEL 2 /* bsscfg delete */ ++#define WLC_E_IF_CHANGE 3 /* bsscfg role change */ ++ ++/* I/F role code in WLC_E_IF event */ ++#define WLC_E_IF_ROLE_STA 0 /* Infra STA */ ++#define WLC_E_IF_ROLE_AP 1 /* Access Point */ ++#define WLC_E_IF_ROLE_WDS 2 /* WDS link */ ++#define WLC_E_IF_ROLE_P2P_GO 3 /* P2P Group Owner */ ++#define WLC_E_IF_ROLE_P2P_CLIENT 4 /* P2P Client */ ++#ifdef WLBTAMP ++#define WLC_E_IF_ROLE_BTA_CREATOR 5 /* BT-AMP Creator */ ++#define WLC_E_IF_ROLE_BTA_ACCEPTOR 6 /* BT-AMP Acceptor */ ++#endif ++ ++/* Reason codes for LINK */ ++#define WLC_E_LINK_BCN_LOSS 1 /* Link down because of beacon loss */ ++#define WLC_E_LINK_DISASSOC 2 /* Link down because of disassoc */ ++#define WLC_E_LINK_ASSOC_REC 3 /* Link down because assoc recreate failed */ ++#define WLC_E_LINK_BSSCFG_DIS 4 /* Link down due to bsscfg down */ ++ ++/* reason codes for WLC_E_OVERLAY_REQ event */ ++#define WLC_E_OVL_DOWNLOAD 0 /* overlay download request */ ++#define WLC_E_OVL_UPDATE_IND 1 /* device indication of host overlay update */ ++ ++/* reason codes for WLC_E_TDLS_PEER_EVENT event */ ++#define WLC_E_TDLS_PEER_DISCOVERED 0 /* peer is ready to establish TDLS */ ++#define WLC_E_TDLS_PEER_CONNECTED 1 ++#define WLC_E_TDLS_PEER_DISCONNECTED 2 ++ ++/* GAS event data */ ++typedef BWL_PRE_PACKED_STRUCT struct wl_event_gas { ++ uint16 channel; /* channel of GAS protocol */ ++ uint8 dialog_token; /* GAS dialog token */ ++ uint8 fragment_id; /* fragment id */ ++ uint16 status_code; /* status code on GAS completion */ ++ uint16 data_len; /* length of data to follow */ ++ uint8 data[1]; /* variable length specified by data_len */ ++} BWL_POST_PACKED_STRUCT wl_event_gas_t; ++ ++/* service discovery TLV */ ++typedef BWL_PRE_PACKED_STRUCT struct wl_sd_tlv { ++ uint16 length; /* length of response_data */ ++ uint8 protocol; /* service protocol type */ ++ uint8 transaction_id; /* service transaction id */ ++ uint8 status_code; /* status code */ ++ uint8 data[1]; /* response data */ ++} BWL_POST_PACKED_STRUCT wl_sd_tlv_t; ++ ++/* service discovery event data */ ++typedef BWL_PRE_PACKED_STRUCT struct wl_event_sd { ++ uint16 channel; /* channel */ ++ uint8 count; /* number of tlvs */ ++ wl_sd_tlv_t tlv[1]; /* service discovery TLV */ ++} BWL_POST_PACKED_STRUCT wl_event_sd_t; ++ ++/* This marks the end of a packed structure section. */ ++#include ++ ++#endif /* _BCMEVENT_H_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmip.h b/drivers/net/wireless/bcmdhd/include/proto/bcmip.h +new file mode 100644 +index 00000000..d5c3b76d +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/bcmip.h +@@ -0,0 +1,210 @@ ++/* ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * Fundamental constants relating to IP Protocol ++ * ++ * $Id: bcmip.h 290206 2011-10-17 19:13:51Z $ ++ */ ++ ++#ifndef _bcmip_h_ ++#define _bcmip_h_ ++ ++#ifndef _TYPEDEFS_H_ ++#include ++#endif ++ ++ ++#include ++ ++ ++ ++#define IP_VER_OFFSET 0x0 ++#define IP_VER_MASK 0xf0 ++#define IP_VER_SHIFT 4 ++#define IP_VER_4 4 ++#define IP_VER_6 6 ++ ++#define IP_VER(ip_body) \ ++ ((((uint8 *)(ip_body))[IP_VER_OFFSET] & IP_VER_MASK) >> IP_VER_SHIFT) ++ ++#define IP_PROT_ICMP 0x1 ++#define IP_PROT_IGMP 0x2 ++#define IP_PROT_TCP 0x6 ++#define IP_PROT_UDP 0x11 ++#define IP_PROT_ICMP6 0x3a ++ ++ ++#define IPV4_VER_HL_OFFSET 0 ++#define IPV4_TOS_OFFSET 1 ++#define IPV4_PKTLEN_OFFSET 2 ++#define IPV4_PKTFLAG_OFFSET 6 ++#define IPV4_PROT_OFFSET 9 ++#define IPV4_CHKSUM_OFFSET 10 ++#define IPV4_SRC_IP_OFFSET 12 ++#define IPV4_DEST_IP_OFFSET 16 ++#define IPV4_OPTIONS_OFFSET 20 ++ ++ ++#define IPV4_VER_MASK 0xf0 ++#define IPV4_VER_SHIFT 4 ++ ++#define IPV4_HLEN_MASK 0x0f ++#define IPV4_HLEN(ipv4_body) (4 * (((uint8 *)(ipv4_body))[IPV4_VER_HL_OFFSET] & IPV4_HLEN_MASK)) ++ ++#define IPV4_ADDR_LEN 4 ++ ++#define IPV4_ADDR_NULL(a) ((((uint8 *)(a))[0] | ((uint8 *)(a))[1] | \ ++ ((uint8 *)(a))[2] | ((uint8 *)(a))[3]) == 0) ++ ++#define IPV4_ADDR_BCAST(a) ((((uint8 *)(a))[0] & ((uint8 *)(a))[1] & \ ++ ((uint8 *)(a))[2] & ((uint8 *)(a))[3]) == 0xff) ++ ++#define IPV4_TOS_DSCP_MASK 0xfc ++#define IPV4_TOS_DSCP_SHIFT 2 ++ ++#define IPV4_TOS(ipv4_body) (((uint8 *)(ipv4_body))[IPV4_TOS_OFFSET]) ++ ++#define IPV4_TOS_PREC_MASK 0xe0 ++#define IPV4_TOS_PREC_SHIFT 5 ++ ++#define IPV4_TOS_LOWDELAY 0x10 ++#define IPV4_TOS_THROUGHPUT 0x8 ++#define IPV4_TOS_RELIABILITY 0x4 ++ ++#define IPV4_PROT(ipv4_body) (((uint8 *)(ipv4_body))[IPV4_PROT_OFFSET]) ++ ++#define IPV4_FRAG_RESV 0x8000 ++#define IPV4_FRAG_DONT 0x4000 ++#define IPV4_FRAG_MORE 0x2000 ++#define IPV4_FRAG_OFFSET_MASK 0x1fff ++ ++#define IPV4_ADDR_STR_LEN 16 ++ ++ ++BWL_PRE_PACKED_STRUCT struct ipv4_addr { ++ uint8 addr[IPV4_ADDR_LEN]; ++} BWL_POST_PACKED_STRUCT; ++ ++BWL_PRE_PACKED_STRUCT struct ipv4_hdr { ++ uint8 version_ihl; ++ uint8 tos; ++ uint16 tot_len; ++ uint16 id; ++ uint16 frag; ++ uint8 ttl; ++ uint8 prot; ++ uint16 hdr_chksum; ++ uint8 src_ip[IPV4_ADDR_LEN]; ++ uint8 dst_ip[IPV4_ADDR_LEN]; ++} BWL_POST_PACKED_STRUCT; ++ ++ ++#define IPV6_PAYLOAD_LEN_OFFSET 4 ++#define IPV6_NEXT_HDR_OFFSET 6 ++#define IPV6_HOP_LIMIT_OFFSET 7 ++#define IPV6_SRC_IP_OFFSET 8 ++#define IPV6_DEST_IP_OFFSET 24 ++ ++ ++#define IPV6_TRAFFIC_CLASS(ipv6_body) \ ++ (((((uint8 *)(ipv6_body))[0] & 0x0f) << 4) | \ ++ ((((uint8 *)(ipv6_body))[1] & 0xf0) >> 4)) ++ ++#define IPV6_FLOW_LABEL(ipv6_body) \ ++ (((((uint8 *)(ipv6_body))[1] & 0x0f) << 16) | \ ++ (((uint8 *)(ipv6_body))[2] << 8) | \ ++ (((uint8 *)(ipv6_body))[3])) ++ ++#define IPV6_PAYLOAD_LEN(ipv6_body) \ ++ ((((uint8 *)(ipv6_body))[IPV6_PAYLOAD_LEN_OFFSET + 0] << 8) | \ ++ ((uint8 *)(ipv6_body))[IPV6_PAYLOAD_LEN_OFFSET + 1]) ++ ++#define IPV6_NEXT_HDR(ipv6_body) \ ++ (((uint8 *)(ipv6_body))[IPV6_NEXT_HDR_OFFSET]) ++ ++#define IPV6_PROT(ipv6_body) IPV6_NEXT_HDR(ipv6_body) ++ ++#define IPV6_ADDR_LEN 16 ++ ++ ++#define IP_TOS46(ip_body) \ ++ (IP_VER(ip_body) == IP_VER_4 ? IPV4_TOS(ip_body) : \ ++ IP_VER(ip_body) == IP_VER_6 ? IPV6_TRAFFIC_CLASS(ip_body) : 0) ++ ++ ++#define IPV6_EXTHDR_HOP 0 ++#define IPV6_EXTHDR_ROUTING 43 ++#define IPV6_EXTHDR_FRAGMENT 44 ++#define IPV6_EXTHDR_AUTH 51 ++#define IPV6_EXTHDR_NONE 59 ++#define IPV6_EXTHDR_DEST 60 ++ ++#define IPV6_EXTHDR(prot) (((prot) == IPV6_EXTHDR_HOP) || \ ++ ((prot) == IPV6_EXTHDR_ROUTING) || \ ++ ((prot) == IPV6_EXTHDR_FRAGMENT) || \ ++ ((prot) == IPV6_EXTHDR_AUTH) || \ ++ ((prot) == IPV6_EXTHDR_NONE) || \ ++ ((prot) == IPV6_EXTHDR_DEST)) ++ ++#define IPV6_MIN_HLEN 40 ++ ++#define IPV6_EXTHDR_LEN(eh) ((((struct ipv6_exthdr *)(eh))->hdrlen + 1) << 3) ++ ++BWL_PRE_PACKED_STRUCT struct ipv6_exthdr { ++ uint8 nexthdr; ++ uint8 hdrlen; ++} BWL_POST_PACKED_STRUCT; ++ ++BWL_PRE_PACKED_STRUCT struct ipv6_exthdr_frag { ++ uint8 nexthdr; ++ uint8 rsvd; ++ uint16 frag_off; ++ uint32 ident; ++} BWL_POST_PACKED_STRUCT; ++ ++static INLINE int32 ++ipv6_exthdr_len(uint8 *h, uint8 *proto) ++{ ++ uint16 len = 0, hlen; ++ struct ipv6_exthdr *eh = (struct ipv6_exthdr *)h; ++ ++ while (IPV6_EXTHDR(eh->nexthdr)) { ++ if (eh->nexthdr == IPV6_EXTHDR_NONE) ++ return -1; ++ else if (eh->nexthdr == IPV6_EXTHDR_FRAGMENT) ++ hlen = 8; ++ else if (eh->nexthdr == IPV6_EXTHDR_AUTH) ++ hlen = (eh->hdrlen + 2) << 2; ++ else ++ hlen = IPV6_EXTHDR_LEN(eh); ++ ++ len += hlen; ++ eh = (struct ipv6_exthdr *)(h + len); ++ } ++ ++ *proto = eh->nexthdr; ++ return len; ++} ++ ++ ++#include ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h b/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h +new file mode 100644 +index 00000000..8617985d +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h +@@ -0,0 +1,441 @@ ++/* ++ * BT-AMP (BlueTooth Alternate Mac and Phy) HCI (Host/Controller Interface) ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: bt_amp_hci.h 294267 2011-11-04 23:41:52Z $ ++*/ ++ ++#ifndef _bt_amp_hci_h ++#define _bt_amp_hci_h ++ ++/* This marks the start of a packed structure section. */ ++#include ++ ++ ++/* AMP HCI CMD packet format */ ++typedef BWL_PRE_PACKED_STRUCT struct amp_hci_cmd { ++ uint16 opcode; ++ uint8 plen; ++ uint8 parms[1]; ++} BWL_POST_PACKED_STRUCT amp_hci_cmd_t; ++ ++#define HCI_CMD_PREAMBLE_SIZE OFFSETOF(amp_hci_cmd_t, parms) ++#define HCI_CMD_DATA_SIZE 255 ++ ++/* AMP HCI CMD opcode layout */ ++#define HCI_CMD_OPCODE(ogf, ocf) ((((ogf) & 0x3F) << 10) | ((ocf) & 0x03FF)) ++#define HCI_CMD_OGF(opcode) ((uint8)(((opcode) >> 10) & 0x3F)) ++#define HCI_CMD_OCF(opcode) ((opcode) & 0x03FF) ++ ++/* AMP HCI command opcodes */ ++#define HCI_Read_Failed_Contact_Counter HCI_CMD_OPCODE(0x05, 0x0001) ++#define HCI_Reset_Failed_Contact_Counter HCI_CMD_OPCODE(0x05, 0x0002) ++#define HCI_Read_Link_Quality HCI_CMD_OPCODE(0x05, 0x0003) ++#define HCI_Read_Local_AMP_Info HCI_CMD_OPCODE(0x05, 0x0009) ++#define HCI_Read_Local_AMP_ASSOC HCI_CMD_OPCODE(0x05, 0x000A) ++#define HCI_Write_Remote_AMP_ASSOC HCI_CMD_OPCODE(0x05, 0x000B) ++#define HCI_Create_Physical_Link HCI_CMD_OPCODE(0x01, 0x0035) ++#define HCI_Accept_Physical_Link_Request HCI_CMD_OPCODE(0x01, 0x0036) ++#define HCI_Disconnect_Physical_Link HCI_CMD_OPCODE(0x01, 0x0037) ++#define HCI_Create_Logical_Link HCI_CMD_OPCODE(0x01, 0x0038) ++#define HCI_Accept_Logical_Link HCI_CMD_OPCODE(0x01, 0x0039) ++#define HCI_Disconnect_Logical_Link HCI_CMD_OPCODE(0x01, 0x003A) ++#define HCI_Logical_Link_Cancel HCI_CMD_OPCODE(0x01, 0x003B) ++#define HCI_Flow_Spec_Modify HCI_CMD_OPCODE(0x01, 0x003C) ++#define HCI_Write_Flow_Control_Mode HCI_CMD_OPCODE(0x01, 0x0067) ++#define HCI_Read_Best_Effort_Flush_Timeout HCI_CMD_OPCODE(0x01, 0x0069) ++#define HCI_Write_Best_Effort_Flush_Timeout HCI_CMD_OPCODE(0x01, 0x006A) ++#define HCI_Short_Range_Mode HCI_CMD_OPCODE(0x01, 0x006B) ++#define HCI_Reset HCI_CMD_OPCODE(0x03, 0x0003) ++#define HCI_Read_Connection_Accept_Timeout HCI_CMD_OPCODE(0x03, 0x0015) ++#define HCI_Write_Connection_Accept_Timeout HCI_CMD_OPCODE(0x03, 0x0016) ++#define HCI_Read_Link_Supervision_Timeout HCI_CMD_OPCODE(0x03, 0x0036) ++#define HCI_Write_Link_Supervision_Timeout HCI_CMD_OPCODE(0x03, 0x0037) ++#define HCI_Enhanced_Flush HCI_CMD_OPCODE(0x03, 0x005F) ++#define HCI_Read_Logical_Link_Accept_Timeout HCI_CMD_OPCODE(0x03, 0x0061) ++#define HCI_Write_Logical_Link_Accept_Timeout HCI_CMD_OPCODE(0x03, 0x0062) ++#define HCI_Set_Event_Mask_Page_2 HCI_CMD_OPCODE(0x03, 0x0063) ++#define HCI_Read_Location_Data_Command HCI_CMD_OPCODE(0x03, 0x0064) ++#define HCI_Write_Location_Data_Command HCI_CMD_OPCODE(0x03, 0x0065) ++#define HCI_Read_Local_Version_Info HCI_CMD_OPCODE(0x04, 0x0001) ++#define HCI_Read_Local_Supported_Commands HCI_CMD_OPCODE(0x04, 0x0002) ++#define HCI_Read_Buffer_Size HCI_CMD_OPCODE(0x04, 0x0005) ++#define HCI_Read_Data_Block_Size HCI_CMD_OPCODE(0x04, 0x000A) ++ ++/* AMP HCI command parameters */ ++typedef BWL_PRE_PACKED_STRUCT struct read_local_cmd_parms { ++ uint8 plh; ++ uint8 offset[2]; /* length so far */ ++ uint8 max_remote[2]; ++} BWL_POST_PACKED_STRUCT read_local_cmd_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct write_remote_cmd_parms { ++ uint8 plh; ++ uint8 offset[2]; ++ uint8 len[2]; ++ uint8 frag[1]; ++} BWL_POST_PACKED_STRUCT write_remote_cmd_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct phy_link_cmd_parms { ++ uint8 plh; ++ uint8 key_length; ++ uint8 key_type; ++ uint8 key[1]; ++} BWL_POST_PACKED_STRUCT phy_link_cmd_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct dis_phy_link_cmd_parms { ++ uint8 plh; ++ uint8 reason; ++} BWL_POST_PACKED_STRUCT dis_phy_link_cmd_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct log_link_cmd_parms { ++ uint8 plh; ++ uint8 txflow[16]; ++ uint8 rxflow[16]; ++} BWL_POST_PACKED_STRUCT log_link_cmd_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct ext_flow_spec { ++ uint8 id; ++ uint8 service_type; ++ uint8 max_sdu[2]; ++ uint8 sdu_ia_time[4]; ++ uint8 access_latency[4]; ++ uint8 flush_timeout[4]; ++} BWL_POST_PACKED_STRUCT ext_flow_spec_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct log_link_cancel_cmd_parms { ++ uint8 plh; ++ uint8 tx_fs_ID; ++} BWL_POST_PACKED_STRUCT log_link_cancel_cmd_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct flow_spec_mod_cmd_parms { ++ uint8 llh[2]; ++ uint8 txflow[16]; ++ uint8 rxflow[16]; ++} BWL_POST_PACKED_STRUCT flow_spec_mod_cmd_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct plh_pad { ++ uint8 plh; ++ uint8 pad; ++} BWL_POST_PACKED_STRUCT plh_pad_t; ++ ++typedef BWL_PRE_PACKED_STRUCT union hci_handle { ++ uint16 bredr; ++ plh_pad_t amp; ++} BWL_POST_PACKED_STRUCT hci_handle_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct ls_to_cmd_parms { ++ hci_handle_t handle; ++ uint8 timeout[2]; ++} BWL_POST_PACKED_STRUCT ls_to_cmd_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct befto_cmd_parms { ++ uint8 llh[2]; ++ uint8 befto[4]; ++} BWL_POST_PACKED_STRUCT befto_cmd_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct srm_cmd_parms { ++ uint8 plh; ++ uint8 srm; ++} BWL_POST_PACKED_STRUCT srm_cmd_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct ld_cmd_parms { ++ uint8 ld_aware; ++ uint8 ld[2]; ++ uint8 ld_opts; ++ uint8 l_opts; ++} BWL_POST_PACKED_STRUCT ld_cmd_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct eflush_cmd_parms { ++ uint8 llh[2]; ++ uint8 packet_type; ++} BWL_POST_PACKED_STRUCT eflush_cmd_parms_t; ++ ++/* Generic AMP extended flow spec service types */ ++#define EFS_SVCTYPE_NO_TRAFFIC 0 ++#define EFS_SVCTYPE_BEST_EFFORT 1 ++#define EFS_SVCTYPE_GUARANTEED 2 ++ ++/* AMP HCI event packet format */ ++typedef BWL_PRE_PACKED_STRUCT struct amp_hci_event { ++ uint8 ecode; ++ uint8 plen; ++ uint8 parms[1]; ++} BWL_POST_PACKED_STRUCT amp_hci_event_t; ++ ++#define HCI_EVT_PREAMBLE_SIZE OFFSETOF(amp_hci_event_t, parms) ++ ++/* AMP HCI event codes */ ++#define HCI_Command_Complete 0x0E ++#define HCI_Command_Status 0x0F ++#define HCI_Flush_Occurred 0x11 ++#define HCI_Enhanced_Flush_Complete 0x39 ++#define HCI_Physical_Link_Complete 0x40 ++#define HCI_Channel_Select 0x41 ++#define HCI_Disconnect_Physical_Link_Complete 0x42 ++#define HCI_Logical_Link_Complete 0x45 ++#define HCI_Disconnect_Logical_Link_Complete 0x46 ++#define HCI_Flow_Spec_Modify_Complete 0x47 ++#define HCI_Number_of_Completed_Data_Blocks 0x48 ++#define HCI_Short_Range_Mode_Change_Complete 0x4C ++#define HCI_Status_Change_Event 0x4D ++#define HCI_Vendor_Specific 0xFF ++ ++/* AMP HCI event mask bit positions */ ++#define HCI_Physical_Link_Complete_Event_Mask 0x0001 ++#define HCI_Channel_Select_Event_Mask 0x0002 ++#define HCI_Disconnect_Physical_Link_Complete_Event_Mask 0x0004 ++#define HCI_Logical_Link_Complete_Event_Mask 0x0020 ++#define HCI_Disconnect_Logical_Link_Complete_Event_Mask 0x0040 ++#define HCI_Flow_Spec_Modify_Complete_Event_Mask 0x0080 ++#define HCI_Number_of_Completed_Data_Blocks_Event_Mask 0x0100 ++#define HCI_Short_Range_Mode_Change_Complete_Event_Mask 0x1000 ++#define HCI_Status_Change_Event_Mask 0x2000 ++#define HCI_All_Event_Mask 0x31e7 ++/* AMP HCI event parameters */ ++typedef BWL_PRE_PACKED_STRUCT struct cmd_status_parms { ++ uint8 status; ++ uint8 cmdpkts; ++ uint16 opcode; ++} BWL_POST_PACKED_STRUCT cmd_status_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct cmd_complete_parms { ++ uint8 cmdpkts; ++ uint16 opcode; ++ uint8 parms[1]; ++} BWL_POST_PACKED_STRUCT cmd_complete_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct flush_occurred_evt_parms { ++ uint16 handle; ++} BWL_POST_PACKED_STRUCT flush_occurred_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct write_remote_evt_parms { ++ uint8 status; ++ uint8 plh; ++} BWL_POST_PACKED_STRUCT write_remote_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct read_local_evt_parms { ++ uint8 status; ++ uint8 plh; ++ uint16 len; ++ uint8 frag[1]; ++} BWL_POST_PACKED_STRUCT read_local_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct read_local_info_evt_parms { ++ uint8 status; ++ uint8 AMP_status; ++ uint32 bandwidth; ++ uint32 gbandwidth; ++ uint32 latency; ++ uint32 PDU_size; ++ uint8 ctrl_type; ++ uint16 PAL_cap; ++ uint16 AMP_ASSOC_len; ++ uint32 max_flush_timeout; ++ uint32 be_flush_timeout; ++} BWL_POST_PACKED_STRUCT read_local_info_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct log_link_evt_parms { ++ uint8 status; ++ uint16 llh; ++ uint8 plh; ++ uint8 tx_fs_ID; ++} BWL_POST_PACKED_STRUCT log_link_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct disc_log_link_evt_parms { ++ uint8 status; ++ uint16 llh; ++ uint8 reason; ++} BWL_POST_PACKED_STRUCT disc_log_link_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct log_link_cancel_evt_parms { ++ uint8 status; ++ uint8 plh; ++ uint8 tx_fs_ID; ++} BWL_POST_PACKED_STRUCT log_link_cancel_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct flow_spec_mod_evt_parms { ++ uint8 status; ++ uint16 llh; ++} BWL_POST_PACKED_STRUCT flow_spec_mod_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct phy_link_evt_parms { ++ uint8 status; ++ uint8 plh; ++} BWL_POST_PACKED_STRUCT phy_link_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct dis_phy_link_evt_parms { ++ uint8 status; ++ uint8 plh; ++ uint8 reason; ++} BWL_POST_PACKED_STRUCT dis_phy_link_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct read_ls_to_evt_parms { ++ uint8 status; ++ hci_handle_t handle; ++ uint16 timeout; ++} BWL_POST_PACKED_STRUCT read_ls_to_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct read_lla_ca_to_evt_parms { ++ uint8 status; ++ uint16 timeout; ++} BWL_POST_PACKED_STRUCT read_lla_ca_to_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct read_data_block_size_evt_parms { ++ uint8 status; ++ uint16 ACL_pkt_len; ++ uint16 data_block_len; ++ uint16 data_block_num; ++} BWL_POST_PACKED_STRUCT read_data_block_size_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct data_blocks { ++ uint16 handle; ++ uint16 pkts; ++ uint16 blocks; ++} BWL_POST_PACKED_STRUCT data_blocks_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct num_completed_data_blocks_evt_parms { ++ uint16 num_blocks; ++ uint8 num_handles; ++ data_blocks_t completed[1]; ++} BWL_POST_PACKED_STRUCT num_completed_data_blocks_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct befto_evt_parms { ++ uint8 status; ++ uint32 befto; ++} BWL_POST_PACKED_STRUCT befto_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct srm_evt_parms { ++ uint8 status; ++ uint8 plh; ++ uint8 srm; ++} BWL_POST_PACKED_STRUCT srm_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct contact_counter_evt_parms { ++ uint8 status; ++ uint8 llh[2]; ++ uint16 counter; ++} BWL_POST_PACKED_STRUCT contact_counter_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct contact_counter_reset_evt_parms { ++ uint8 status; ++ uint8 llh[2]; ++} BWL_POST_PACKED_STRUCT contact_counter_reset_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct read_linkq_evt_parms { ++ uint8 status; ++ hci_handle_t handle; ++ uint8 link_quality; ++} BWL_POST_PACKED_STRUCT read_linkq_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct ld_evt_parms { ++ uint8 status; ++ uint8 ld_aware; ++ uint8 ld[2]; ++ uint8 ld_opts; ++ uint8 l_opts; ++} BWL_POST_PACKED_STRUCT ld_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct eflush_complete_evt_parms { ++ uint16 handle; ++} BWL_POST_PACKED_STRUCT eflush_complete_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct vendor_specific_evt_parms { ++ uint8 len; ++ uint8 parms[1]; ++} BWL_POST_PACKED_STRUCT vendor_specific_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct local_version_info_evt_parms { ++ uint8 status; ++ uint8 hci_version; ++ uint16 hci_revision; ++ uint8 pal_version; ++ uint16 mfg_name; ++ uint16 pal_subversion; ++} BWL_POST_PACKED_STRUCT local_version_info_evt_parms_t; ++ ++#define MAX_SUPPORTED_CMD_BYTE 64 ++typedef BWL_PRE_PACKED_STRUCT struct local_supported_cmd_evt_parms { ++ uint8 status; ++ uint8 cmd[MAX_SUPPORTED_CMD_BYTE]; ++} BWL_POST_PACKED_STRUCT local_supported_cmd_evt_parms_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct status_change_evt_parms { ++ uint8 status; ++ uint8 amp_status; ++} BWL_POST_PACKED_STRUCT status_change_evt_parms_t; ++ ++/* AMP HCI error codes */ ++#define HCI_SUCCESS 0x00 ++#define HCI_ERR_ILLEGAL_COMMAND 0x01 ++#define HCI_ERR_NO_CONNECTION 0x02 ++#define HCI_ERR_MEMORY_FULL 0x07 ++#define HCI_ERR_CONNECTION_TIMEOUT 0x08 ++#define HCI_ERR_MAX_NUM_OF_CONNECTIONS 0x09 ++#define HCI_ERR_CONNECTION_EXISTS 0x0B ++#define HCI_ERR_CONNECTION_DISALLOWED 0x0C ++#define HCI_ERR_CONNECTION_ACCEPT_TIMEOUT 0x10 ++#define HCI_ERR_UNSUPPORTED_VALUE 0x11 ++#define HCI_ERR_ILLEGAL_PARAMETER_FMT 0x12 ++#define HCI_ERR_CONN_TERM_BY_LOCAL_HOST 0x16 ++#define HCI_ERR_UNSPECIFIED 0x1F ++#define HCI_ERR_UNIT_KEY_USED 0x26 ++#define HCI_ERR_QOS_REJECTED 0x2D ++#define HCI_ERR_PARAM_OUT_OF_RANGE 0x30 ++#define HCI_ERR_NO_SUITABLE_CHANNEL 0x39 ++#define HCI_ERR_CHANNEL_MOVE 0xFF ++ ++/* AMP HCI ACL Data packet format */ ++typedef BWL_PRE_PACKED_STRUCT struct amp_hci_ACL_data { ++ uint16 handle; /* 12-bit connection handle + 2-bit PB and 2-bit BC flags */ ++ uint16 dlen; /* data total length */ ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT amp_hci_ACL_data_t; ++ ++#define HCI_ACL_DATA_PREAMBLE_SIZE OFFSETOF(amp_hci_ACL_data_t, data) ++ ++#define HCI_ACL_DATA_BC_FLAGS (0x0 << 14) ++#define HCI_ACL_DATA_PB_FLAGS (0x3 << 12) ++ ++#define HCI_ACL_DATA_HANDLE(handle) ((handle) & 0x0fff) ++#define HCI_ACL_DATA_FLAGS(handle) ((handle) >> 12) ++ ++/* AMP Activity Report packet formats */ ++typedef BWL_PRE_PACKED_STRUCT struct amp_hci_activity_report { ++ uint8 ScheduleKnown; ++ uint8 NumReports; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT amp_hci_activity_report_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct amp_hci_activity_report_triple { ++ uint32 StartTime; ++ uint32 Duration; ++ uint32 Periodicity; ++} BWL_POST_PACKED_STRUCT amp_hci_activity_report_triple_t; ++ ++#define HCI_AR_SCHEDULE_KNOWN 0x01 ++ ++ ++/* This marks the end of a packed structure section. */ ++#include ++ ++#endif /* _bt_amp_hci_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/proto/eapol.h b/drivers/net/wireless/bcmdhd/include/proto/eapol.h +new file mode 100644 +index 00000000..8936d164 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/eapol.h +@@ -0,0 +1,193 @@ ++/* ++ * 802.1x EAPOL definitions ++ * ++ * See ++ * IEEE Std 802.1X-2001 ++ * IEEE 802.1X RADIUS Usage Guidelines ++ * ++ * Copyright (C) 2002 Broadcom Corporation ++ * ++ * $Id: eapol.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++#ifndef _eapol_h_ ++#define _eapol_h_ ++ ++#ifndef _TYPEDEFS_H_ ++#include ++#endif ++ ++/* This marks the start of a packed structure section. */ ++#include ++ ++#include ++ ++/* EAPOL for 802.3/Ethernet */ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ struct ether_header eth; /* 802.3/Ethernet header */ ++ unsigned char version; /* EAPOL protocol version */ ++ unsigned char type; /* EAPOL type */ ++ unsigned short length; /* Length of body */ ++ unsigned char body[1]; /* Body (optional) */ ++} BWL_POST_PACKED_STRUCT eapol_header_t; ++ ++#define EAPOL_HEADER_LEN 18 ++ ++typedef struct { ++ unsigned char version; /* EAPOL protocol version */ ++ unsigned char type; /* EAPOL type */ ++ unsigned short length; /* Length of body */ ++} eapol_hdr_t; ++ ++#define EAPOL_HDR_LEN 4 ++ ++/* EAPOL version */ ++#define WPA2_EAPOL_VERSION 2 ++#define WPA_EAPOL_VERSION 1 ++#define LEAP_EAPOL_VERSION 1 ++#define SES_EAPOL_VERSION 1 ++ ++/* EAPOL types */ ++#define EAP_PACKET 0 ++#define EAPOL_START 1 ++#define EAPOL_LOGOFF 2 ++#define EAPOL_KEY 3 ++#define EAPOL_ASF 4 ++ ++/* EAPOL-Key types */ ++#define EAPOL_RC4_KEY 1 ++#define EAPOL_WPA2_KEY 2 /* 802.11i/WPA2 */ ++#define EAPOL_WPA_KEY 254 /* WPA */ ++ ++/* RC4 EAPOL-Key header field sizes */ ++#define EAPOL_KEY_REPLAY_LEN 8 ++#define EAPOL_KEY_IV_LEN 16 ++#define EAPOL_KEY_SIG_LEN 16 ++ ++/* RC4 EAPOL-Key */ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ unsigned char type; /* Key Descriptor Type */ ++ unsigned short length; /* Key Length (unaligned) */ ++ unsigned char replay[EAPOL_KEY_REPLAY_LEN]; /* Replay Counter */ ++ unsigned char iv[EAPOL_KEY_IV_LEN]; /* Key IV */ ++ unsigned char index; /* Key Flags & Index */ ++ unsigned char signature[EAPOL_KEY_SIG_LEN]; /* Key Signature */ ++ unsigned char key[1]; /* Key (optional) */ ++} BWL_POST_PACKED_STRUCT eapol_key_header_t; ++ ++#define EAPOL_KEY_HEADER_LEN 44 ++ ++/* RC4 EAPOL-Key flags */ ++#define EAPOL_KEY_FLAGS_MASK 0x80 ++#define EAPOL_KEY_BROADCAST 0 ++#define EAPOL_KEY_UNICAST 0x80 ++ ++/* RC4 EAPOL-Key index */ ++#define EAPOL_KEY_INDEX_MASK 0x7f ++ ++/* WPA/802.11i/WPA2 EAPOL-Key header field sizes */ ++#define EAPOL_WPA_KEY_REPLAY_LEN 8 ++#define EAPOL_WPA_KEY_NONCE_LEN 32 ++#define EAPOL_WPA_KEY_IV_LEN 16 ++#define EAPOL_WPA_KEY_RSC_LEN 8 ++#define EAPOL_WPA_KEY_ID_LEN 8 ++#define EAPOL_WPA_KEY_MIC_LEN 16 ++#define EAPOL_WPA_KEY_DATA_LEN (EAPOL_WPA_MAX_KEY_SIZE + AKW_BLOCK_LEN) ++#define EAPOL_WPA_MAX_KEY_SIZE 32 ++ ++/* WPA EAPOL-Key */ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ unsigned char type; /* Key Descriptor Type */ ++ unsigned short key_info; /* Key Information (unaligned) */ ++ unsigned short key_len; /* Key Length (unaligned) */ ++ unsigned char replay[EAPOL_WPA_KEY_REPLAY_LEN]; /* Replay Counter */ ++ unsigned char nonce[EAPOL_WPA_KEY_NONCE_LEN]; /* Nonce */ ++ unsigned char iv[EAPOL_WPA_KEY_IV_LEN]; /* Key IV */ ++ unsigned char rsc[EAPOL_WPA_KEY_RSC_LEN]; /* Key RSC */ ++ unsigned char id[EAPOL_WPA_KEY_ID_LEN]; /* WPA:Key ID, 802.11i/WPA2: Reserved */ ++ unsigned char mic[EAPOL_WPA_KEY_MIC_LEN]; /* Key MIC */ ++ unsigned short data_len; /* Key Data Length */ ++ unsigned char data[EAPOL_WPA_KEY_DATA_LEN]; /* Key data */ ++} BWL_POST_PACKED_STRUCT eapol_wpa_key_header_t; ++ ++#define EAPOL_WPA_KEY_LEN 95 ++ ++/* WPA/802.11i/WPA2 KEY KEY_INFO bits */ ++#define WPA_KEY_DESC_V1 0x01 ++#define WPA_KEY_DESC_V2 0x02 ++#define WPA_KEY_DESC_V3 0x03 ++#define WPA_KEY_PAIRWISE 0x08 ++#define WPA_KEY_INSTALL 0x40 ++#define WPA_KEY_ACK 0x80 ++#define WPA_KEY_MIC 0x100 ++#define WPA_KEY_SECURE 0x200 ++#define WPA_KEY_ERROR 0x400 ++#define WPA_KEY_REQ 0x800 ++ ++#define WPA_KEY_DESC_V2_OR_V3 WPA_KEY_DESC_V2 ++ ++/* WPA-only KEY KEY_INFO bits */ ++#define WPA_KEY_INDEX_0 0x00 ++#define WPA_KEY_INDEX_1 0x10 ++#define WPA_KEY_INDEX_2 0x20 ++#define WPA_KEY_INDEX_3 0x30 ++#define WPA_KEY_INDEX_MASK 0x30 ++#define WPA_KEY_INDEX_SHIFT 0x04 ++ ++/* 802.11i/WPA2-only KEY KEY_INFO bits */ ++#define WPA_KEY_ENCRYPTED_DATA 0x1000 ++ ++/* Key Data encapsulation */ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ uint8 type; ++ uint8 length; ++ uint8 oui[3]; ++ uint8 subtype; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT eapol_wpa2_encap_data_t; ++ ++#define EAPOL_WPA2_ENCAP_DATA_HDR_LEN 6 ++ ++#define WPA2_KEY_DATA_SUBTYPE_GTK 1 ++#define WPA2_KEY_DATA_SUBTYPE_STAKEY 2 ++#define WPA2_KEY_DATA_SUBTYPE_MAC 3 ++#define WPA2_KEY_DATA_SUBTYPE_PMKID 4 ++#define WPA2_KEY_DATA_SUBTYPE_IGTK 9 ++ ++/* GTK encapsulation */ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ uint8 flags; ++ uint8 reserved; ++ uint8 gtk[EAPOL_WPA_MAX_KEY_SIZE]; ++} BWL_POST_PACKED_STRUCT eapol_wpa2_key_gtk_encap_t; ++ ++#define EAPOL_WPA2_KEY_GTK_ENCAP_HDR_LEN 2 ++ ++#define WPA2_GTK_INDEX_MASK 0x03 ++#define WPA2_GTK_INDEX_SHIFT 0x00 ++ ++#define WPA2_GTK_TRANSMIT 0x04 ++ ++/* IGTK encapsulation */ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ uint16 key_id; ++ uint8 ipn[6]; ++ uint8 key[EAPOL_WPA_MAX_KEY_SIZE]; ++} BWL_POST_PACKED_STRUCT eapol_wpa2_key_igtk_encap_t; ++ ++#define EAPOL_WPA2_KEY_IGTK_ENCAP_HDR_LEN 8 ++ ++/* STAKey encapsulation */ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ uint8 reserved[2]; ++ uint8 mac[ETHER_ADDR_LEN]; ++ uint8 stakey[EAPOL_WPA_MAX_KEY_SIZE]; ++} BWL_POST_PACKED_STRUCT eapol_wpa2_key_stakey_encap_t; ++ ++#define WPA2_KEY_DATA_PAD 0xdd ++ ++ ++/* This marks the end of a packed structure section. */ ++#include ++ ++#endif /* _eapol_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/proto/ethernet.h b/drivers/net/wireless/bcmdhd/include/proto/ethernet.h +new file mode 100644 +index 00000000..e4551856 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/ethernet.h +@@ -0,0 +1,162 @@ ++/* ++ * From FreeBSD 2.2.7: Fundamental constants relating to ethernet. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: ethernet.h 309193 2012-01-19 00:03:57Z $ ++ */ ++ ++#ifndef _NET_ETHERNET_H_ ++#define _NET_ETHERNET_H_ ++ ++#ifndef _TYPEDEFS_H_ ++#include "typedefs.h" ++#endif ++ ++ ++#include ++ ++ ++ ++#define ETHER_ADDR_LEN 6 ++ ++ ++#define ETHER_TYPE_LEN 2 ++ ++ ++#define ETHER_CRC_LEN 4 ++ ++ ++#define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN) ++ ++ ++#define ETHER_MIN_LEN 64 ++ ++ ++#define ETHER_MIN_DATA 46 ++ ++ ++#define ETHER_MAX_LEN 1518 ++ ++ ++#define ETHER_MAX_DATA 1500 ++ ++ ++#define ETHER_TYPE_MIN 0x0600 ++#define ETHER_TYPE_IP 0x0800 ++#define ETHER_TYPE_ARP 0x0806 ++#define ETHER_TYPE_8021Q 0x8100 ++#define ETHER_TYPE_IPV6 0x86dd ++#define ETHER_TYPE_BRCM 0x886c ++#define ETHER_TYPE_802_1X 0x888e ++#define ETHER_TYPE_802_1X_PREAUTH 0x88c7 ++#define ETHER_TYPE_WAI 0x88b4 ++#define ETHER_TYPE_89_0D 0x890d ++ ++#define ETHER_TYPE_IPV6 0x86dd ++ ++ ++#define ETHER_BRCM_SUBTYPE_LEN 4 ++ ++ ++#define ETHER_DEST_OFFSET (0 * ETHER_ADDR_LEN) ++#define ETHER_SRC_OFFSET (1 * ETHER_ADDR_LEN) ++#define ETHER_TYPE_OFFSET (2 * ETHER_ADDR_LEN) ++ ++ ++#define ETHER_IS_VALID_LEN(foo) \ ++ ((foo) >= ETHER_MIN_LEN && (foo) <= ETHER_MAX_LEN) ++ ++#define ETHER_FILL_MCAST_ADDR_FROM_IP(ea, mgrp_ip) { \ ++ ((uint8 *)ea)[0] = 0x01; \ ++ ((uint8 *)ea)[1] = 0x00; \ ++ ((uint8 *)ea)[2] = 0x5e; \ ++ ((uint8 *)ea)[3] = ((mgrp_ip) >> 16) & 0x7f; \ ++ ((uint8 *)ea)[4] = ((mgrp_ip) >> 8) & 0xff; \ ++ ((uint8 *)ea)[5] = ((mgrp_ip) >> 0) & 0xff; \ ++} ++ ++#ifndef __INCif_etherh ++ ++BWL_PRE_PACKED_STRUCT struct ether_header { ++ uint8 ether_dhost[ETHER_ADDR_LEN]; ++ uint8 ether_shost[ETHER_ADDR_LEN]; ++ uint16 ether_type; ++} BWL_POST_PACKED_STRUCT; ++ ++ ++BWL_PRE_PACKED_STRUCT struct ether_addr { ++ uint8 octet[ETHER_ADDR_LEN]; ++} BWL_POST_PACKED_STRUCT; ++#endif ++ ++ ++#define ETHER_SET_LOCALADDR(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] | 2)) ++#define ETHER_IS_LOCALADDR(ea) (((uint8 *)(ea))[0] & 2) ++#define ETHER_CLR_LOCALADDR(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] & 0xfd)) ++#define ETHER_TOGGLE_LOCALADDR(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] ^ 2)) ++ ++ ++#define ETHER_SET_UNICAST(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] & ~1)) ++ ++ ++#define ETHER_ISMULTI(ea) (((const uint8 *)(ea))[0] & 1) ++ ++ ++ ++#define ether_cmp(a, b) (!(((short*)(a))[0] == ((short*)(b))[0]) | \ ++ !(((short*)(a))[1] == ((short*)(b))[1]) | \ ++ !(((short*)(a))[2] == ((short*)(b))[2])) ++ ++ ++#define ether_copy(s, d) { \ ++ ((short*)(d))[0] = ((const short*)(s))[0]; \ ++ ((short*)(d))[1] = ((const short*)(s))[1]; \ ++ ((short*)(d))[2] = ((const short*)(s))[2]; } ++ ++ ++static const struct ether_addr ether_bcast = {{255, 255, 255, 255, 255, 255}}; ++static const struct ether_addr ether_null = {{0, 0, 0, 0, 0, 0}}; ++ ++#define ETHER_ISBCAST(ea) ((((uint8 *)(ea))[0] & \ ++ ((uint8 *)(ea))[1] & \ ++ ((uint8 *)(ea))[2] & \ ++ ((uint8 *)(ea))[3] & \ ++ ((uint8 *)(ea))[4] & \ ++ ((uint8 *)(ea))[5]) == 0xff) ++#define ETHER_ISNULLADDR(ea) ((((uint8 *)(ea))[0] | \ ++ ((uint8 *)(ea))[1] | \ ++ ((uint8 *)(ea))[2] | \ ++ ((uint8 *)(ea))[3] | \ ++ ((uint8 *)(ea))[4] | \ ++ ((uint8 *)(ea))[5]) == 0) ++ ++#define ETHER_MOVE_HDR(d, s) \ ++do { \ ++ struct ether_header t; \ ++ t = *(struct ether_header *)(s); \ ++ *(struct ether_header *)(d) = t; \ ++} while (0) ++ ++ ++#include ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/proto/p2p.h b/drivers/net/wireless/bcmdhd/include/proto/p2p.h +new file mode 100644 +index 00000000..1bfe73bc +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/p2p.h +@@ -0,0 +1,564 @@ ++/* ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * Fundamental types and constants relating to WFA P2P (aka WiFi Direct) ++ * ++ * $Id: p2p.h 326276 2012-04-06 23:16:42Z $ ++ */ ++ ++#ifndef _P2P_H_ ++#define _P2P_H_ ++ ++#ifndef _TYPEDEFS_H_ ++#include ++#endif ++#include ++#include ++ ++ ++#include ++ ++ ++ ++#define P2P_OUI WFA_OUI ++#define P2P_VER WFA_OUI_TYPE_P2P ++ ++#define P2P_IE_ID 0xdd ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_ie { ++ uint8 id; ++ uint8 len; ++ uint8 OUI[3]; ++ uint8 oui_type; ++ uint8 subelts[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_ie wifi_p2p_ie_t; ++ ++#define P2P_IE_FIXED_LEN 6 ++ ++#define P2P_ATTR_ID_OFF 0 ++#define P2P_ATTR_LEN_OFF 1 ++#define P2P_ATTR_DATA_OFF 3 ++ ++#define P2P_ATTR_ID_LEN 1 ++#define P2P_ATTR_LEN_LEN 2 ++#define P2P_ATTR_HDR_LEN 3 ++ ++ ++#define P2P_SEID_STATUS 0 ++#define P2P_SEID_MINOR_RC 1 ++#define P2P_SEID_P2P_INFO 2 ++#define P2P_SEID_DEV_ID 3 ++#define P2P_SEID_INTENT 4 ++#define P2P_SEID_CFG_TIMEOUT 5 ++#define P2P_SEID_CHANNEL 6 ++#define P2P_SEID_GRP_BSSID 7 ++#define P2P_SEID_XT_TIMING 8 ++#define P2P_SEID_INTINTADDR 9 ++#define P2P_SEID_P2P_MGBTY 10 ++#define P2P_SEID_CHAN_LIST 11 ++#define P2P_SEID_ABSENCE 12 ++#define P2P_SEID_DEV_INFO 13 ++#define P2P_SEID_GROUP_INFO 14 ++#define P2P_SEID_GROUP_ID 15 ++#define P2P_SEID_P2P_IF 16 ++#define P2P_SEID_OP_CHANNEL 17 ++#define P2P_SEID_INVITE_FLAGS 18 ++#define P2P_SEID_VNDR 221 ++ ++#define P2P_SE_VS_ID_SERVICES 0x1b ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_info_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 dev; ++ uint8 group; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_info_se_s wifi_p2p_info_se_t; ++ ++ ++#define P2P_CAPSE_DEV_SERVICE_DIS 0x1 ++#define P2P_CAPSE_DEV_CLIENT_DIS 0x2 ++#define P2P_CAPSE_DEV_CONCURRENT 0x4 ++#define P2P_CAPSE_DEV_INFRA_MAN 0x8 ++#define P2P_CAPSE_DEV_LIMIT 0x10 ++#define P2P_CAPSE_INVITE_PROC 0x20 ++ ++ ++#define P2P_CAPSE_GRP_OWNER 0x1 ++#define P2P_CAPSE_PERSIST_GRP 0x2 ++#define P2P_CAPSE_GRP_LIMIT 0x4 ++#define P2P_CAPSE_GRP_INTRA_BSS 0x8 ++#define P2P_CAPSE_GRP_X_CONNECT 0x10 ++#define P2P_CAPSE_GRP_PERSISTENT 0x20 ++#define P2P_CAPSE_GRP_FORMATION 0x40 ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_intent_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 intent; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_intent_se_s wifi_p2p_intent_se_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_cfg_tmo_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 go_tmo; ++ uint8 client_tmo; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_cfg_tmo_se_s wifi_p2p_cfg_tmo_se_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_listen_channel_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 country[3]; ++ uint8 op_class; ++ uint8 channel; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_listen_channel_se_s wifi_p2p_listen_channel_se_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_grp_bssid_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 mac[6]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_grp_bssid_se_s wifi_p2p_grp_bssid_se_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_grp_id_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 mac[6]; ++ uint8 ssid[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_grp_id_se_s wifi_p2p_grp_id_se_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_intf_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 mac[6]; ++ uint8 ifaddrs; ++ uint8 ifaddr[1][6]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_intf_se_s wifi_p2p_intf_se_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_status_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 status; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_status_se_s wifi_p2p_status_se_t; ++ ++ ++#define P2P_STATSE_SUCCESS 0 ++ ++#define P2P_STATSE_FAIL_INFO_CURR_UNAVAIL 1 ++ ++#define P2P_STATSE_PASSED_UP P2P_STATSE_FAIL_INFO_CURR_UNAVAIL ++ ++#define P2P_STATSE_FAIL_INCOMPAT_PARAMS 2 ++ ++#define P2P_STATSE_FAIL_LIMIT_REACHED 3 ++ ++#define P2P_STATSE_FAIL_INVALID_PARAMS 4 ++ ++#define P2P_STATSE_FAIL_UNABLE_TO_ACCOM 5 ++ ++#define P2P_STATSE_FAIL_PROTO_ERROR 6 ++ ++#define P2P_STATSE_FAIL_NO_COMMON_CHAN 7 ++ ++#define P2P_STATSE_FAIL_UNKNOWN_GROUP 8 ++ ++#define P2P_STATSE_FAIL_INTENT 9 ++ ++#define P2P_STATSE_FAIL_INCOMPAT_PROVIS 10 ++ ++#define P2P_STATSE_FAIL_USER_REJECT 11 ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_ext_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 avail[2]; ++ uint8 interval[2]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_ext_se_s wifi_p2p_ext_se_t; ++ ++#define P2P_EXT_MIN 10 ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_intintad_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 mac[6]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_intintad_se_s wifi_p2p_intintad_se_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_channel_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 band; ++ uint8 channel; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_channel_se_s wifi_p2p_channel_se_t; ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_chanlist_entry_s { ++ uint8 band; ++ uint8 num_channels; ++ uint8 channels[WL_NUMCHANNELS]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_chanlist_entry_s wifi_p2p_chanlist_entry_t; ++#define WIFI_P2P_CHANLIST_SE_MAX_ENTRIES 2 ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_chanlist_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 country[3]; ++ uint8 num_entries; ++ wifi_p2p_chanlist_entry_t entries[WIFI_P2P_CHANLIST_SE_MAX_ENTRIES]; ++ ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_chanlist_se_s wifi_p2p_chanlist_se_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_pri_devtype_s { ++ uint16 cat_id; ++ uint8 OUI[3]; ++ uint8 oui_type; ++ uint16 sub_cat_id; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_pri_devtype_s wifi_p2p_pri_devtype_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_devinfo_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 mac[6]; ++ uint16 wps_cfg_meths; ++ uint8 pri_devtype[8]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_devinfo_se_s wifi_p2p_devinfo_se_t; ++ ++#define P2P_DEV_TYPE_LEN 8 ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_cid_fixed_s { ++ uint8 len; ++ uint8 devaddr[ETHER_ADDR_LEN]; ++ uint8 ifaddr[ETHER_ADDR_LEN]; ++ uint8 devcap; ++ uint8 cfg_meths[2]; ++ uint8 pridt[P2P_DEV_TYPE_LEN]; ++ uint8 secdts; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_cid_fixed_s wifi_p2p_cid_fixed_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_devid_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ struct ether_addr addr; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_devid_se_s wifi_p2p_devid_se_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_mgbt_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 mg_bitmap; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_mgbt_se_s wifi_p2p_mgbt_se_t; ++ ++#define P2P_MGBTSE_P2PDEVMGMT_FLAG 0x1 ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_grpinfo_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_grpinfo_se_s wifi_p2p_grpinfo_se_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_op_channel_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 country[3]; ++ uint8 op_class; ++ uint8 channel; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_op_channel_se_s wifi_p2p_op_channel_se_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_invite_flags_se_s { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 flags; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_invite_flags_se_s wifi_p2p_invite_flags_se_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_action_frame { ++ uint8 category; ++ uint8 OUI[3]; ++ uint8 type; ++ uint8 subtype; ++ uint8 dialog_token; ++ uint8 elts[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_action_frame wifi_p2p_action_frame_t; ++#define P2P_AF_CATEGORY 0x7f ++ ++#define P2P_AF_FIXED_LEN 7 ++ ++ ++#define P2P_AF_NOTICE_OF_ABSENCE 0 ++#define P2P_AF_PRESENCE_REQ 1 ++#define P2P_AF_PRESENCE_RSP 2 ++#define P2P_AF_GO_DISC_REQ 3 ++ ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_pub_act_frame { ++ uint8 category; ++ uint8 action; ++ uint8 oui[3]; ++ uint8 oui_type; ++ uint8 subtype; ++ uint8 dialog_token; ++ uint8 elts[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_pub_act_frame wifi_p2p_pub_act_frame_t; ++#define P2P_PUB_AF_FIXED_LEN 8 ++#define P2P_PUB_AF_CATEGORY 0x04 ++#define P2P_PUB_AF_ACTION 0x09 ++ ++ ++#define P2P_PAF_GON_REQ 0 ++#define P2P_PAF_GON_RSP 1 ++#define P2P_PAF_GON_CONF 2 ++#define P2P_PAF_INVITE_REQ 3 ++#define P2P_PAF_INVITE_RSP 4 ++#define P2P_PAF_DEVDIS_REQ 5 ++#define P2P_PAF_DEVDIS_RSP 6 ++#define P2P_PAF_PROVDIS_REQ 7 ++#define P2P_PAF_PROVDIS_RSP 8 ++#define P2P_PAF_SUBTYPE_INVALID 255 /* Invalid Subtype */ ++ ++#define P2P_TYPE_MNREQ P2P_PAF_GON_REQ ++#define P2P_TYPE_MNRSP P2P_PAF_GON_RSP ++#define P2P_TYPE_MNCONF P2P_PAF_GON_CONF ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_noa_desc { ++ uint8 cnt_type; ++ uint32 duration; ++ uint32 interval; ++ uint32 start; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_noa_desc wifi_p2p_noa_desc_t; ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2p_noa_se { ++ uint8 eltId; ++ uint8 len[2]; ++ uint8 index; ++ uint8 ops_ctw_parms; ++ wifi_p2p_noa_desc_t desc[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2p_noa_se wifi_p2p_noa_se_t; ++ ++#define P2P_NOA_SE_FIXED_LEN 5 ++ ++ ++#define P2P_NOA_DESC_CNT_RESERVED 0 ++#define P2P_NOA_DESC_CNT_REPEAT 255 ++#define P2P_NOA_DESC_TYPE_PREFERRED 1 ++#define P2P_NOA_DESC_TYPE_ACCEPTABLE 2 ++ ++ ++#define P2P_NOA_CTW_MASK 0x7f ++#define P2P_NOA_OPS_MASK 0x80 ++#define P2P_NOA_OPS_SHIFT 7 ++ ++#define P2P_CTW_MIN 10 ++ ++ ++#define P2PSD_ACTION_CATEGORY 0x04 ++ ++#define P2PSD_ACTION_ID_GAS_IREQ 0x0a ++ ++#define P2PSD_ACTION_ID_GAS_IRESP 0x0b ++ ++#define P2PSD_ACTION_ID_GAS_CREQ 0x0c ++ ++#define P2PSD_ACTION_ID_GAS_CRESP 0x0d ++ ++#define P2PSD_AD_EID 0x6c ++ ++#define P2PSD_ADP_TUPLE_QLMT_PAMEBI 0x00 ++ ++#define P2PSD_ADP_PROTO_ID 0x00 ++ ++#define P2PSD_GAS_OUI P2P_OUI ++ ++#define P2PSD_GAS_OUI_SUBTYPE P2P_VER ++ ++#define P2PSD_GAS_NQP_INFOID 0xDDDD ++ ++#define P2PSD_GAS_COMEBACKDEALY 0x00 ++ ++ ++ ++typedef enum p2psd_svc_protype { ++ SVC_RPOTYPE_ALL = 0, ++ SVC_RPOTYPE_BONJOUR = 1, ++ SVC_RPOTYPE_UPNP = 2, ++ SVC_RPOTYPE_WSD = 3, ++ SVC_RPOTYPE_VENDOR = 255 ++} p2psd_svc_protype_t; ++ ++ ++typedef enum { ++ P2PSD_RESP_STATUS_SUCCESS = 0, ++ P2PSD_RESP_STATUS_PROTYPE_NA = 1, ++ P2PSD_RESP_STATUS_DATA_NA = 2, ++ P2PSD_RESP_STATUS_BAD_REQUEST = 3 ++} p2psd_resp_status_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2psd_adp_tpl { ++ uint8 llm_pamebi; ++ uint8 adp_id; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2psd_adp_tpl wifi_p2psd_adp_tpl_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2psd_adp_ie { ++ uint8 id; ++ uint8 len; ++ wifi_p2psd_adp_tpl_t adp_tpl; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2psd_adp_ie wifi_p2psd_adp_ie_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2psd_nqp_query_vsc { ++ uint8 oui_subtype; ++ uint16 svc_updi; ++ uint8 svc_tlvs[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2psd_nqp_query_vsc wifi_p2psd_nqp_query_vsc_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2psd_qreq_tlv { ++ uint16 len; ++ uint8 svc_prot; ++ uint8 svc_tscid; ++ uint8 query_data[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2psd_qreq_tlv wifi_p2psd_qreq_tlv_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2psd_qreq_frame { ++ uint16 info_id; ++ uint16 len; ++ uint8 oui[3]; ++ uint8 qreq_vsc[1]; ++ ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2psd_qreq_frame wifi_p2psd_qreq_frame_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2psd_gas_ireq_frame { ++ wifi_p2psd_adp_ie_t adp_ie; ++ uint16 qreq_len; ++ uint8 qreq_frm[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2psd_gas_ireq_frame wifi_p2psd_gas_ireq_frame_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2psd_qresp_tlv { ++ uint16 len; ++ uint8 svc_prot; ++ uint8 svc_tscid; ++ uint8 status; ++ uint8 query_data[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2psd_qresp_tlv wifi_p2psd_qresp_tlv_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2psd_qresp_frame { ++ uint16 info_id; ++ uint16 len; ++ uint8 oui[3]; ++ uint8 qresp_vsc[1]; ++ ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2psd_qresp_frame wifi_p2psd_qresp_frame_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2psd_gas_iresp_frame { ++ uint16 status; ++ uint16 cb_delay; ++ wifi_p2psd_adp_ie_t adp_ie; ++ uint16 qresp_len; ++ uint8 qresp_frm[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2psd_gas_iresp_frame wifi_p2psd_gas_iresp_frame_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2psd_gas_cresp_frame { ++ uint16 status; ++ uint8 fragment_id; ++ uint16 cb_delay; ++ wifi_p2psd_adp_ie_t adp_ie; ++ uint16 qresp_len; ++ uint8 qresp_frm[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2psd_gas_cresp_frame wifi_p2psd_gas_cresp_frame_t; ++ ++ ++BWL_PRE_PACKED_STRUCT struct wifi_p2psd_gas_pub_act_frame { ++ uint8 category; ++ uint8 action; ++ uint8 dialog_token; ++ uint8 query_data[1]; ++} BWL_POST_PACKED_STRUCT; ++typedef struct wifi_p2psd_gas_pub_act_frame wifi_p2psd_gas_pub_act_frame_t; ++ ++ ++#include ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/proto/sdspi.h b/drivers/net/wireless/bcmdhd/include/proto/sdspi.h +new file mode 100644 +index 00000000..a4900edd +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/sdspi.h +@@ -0,0 +1,75 @@ ++/* ++ * SD-SPI Protocol Standard ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: sdspi.h 241182 2011-02-17 21:50:03Z $ ++ */ ++#ifndef _SD_SPI_H ++#define _SD_SPI_H ++ ++#define SPI_START_M BITFIELD_MASK(1) /* Bit [31] - Start Bit */ ++#define SPI_START_S 31 ++#define SPI_DIR_M BITFIELD_MASK(1) /* Bit [30] - Direction */ ++#define SPI_DIR_S 30 ++#define SPI_CMD_INDEX_M BITFIELD_MASK(6) /* Bits [29:24] - Command number */ ++#define SPI_CMD_INDEX_S 24 ++#define SPI_RW_M BITFIELD_MASK(1) /* Bit [23] - Read=0, Write=1 */ ++#define SPI_RW_S 23 ++#define SPI_FUNC_M BITFIELD_MASK(3) /* Bits [22:20] - Function Number */ ++#define SPI_FUNC_S 20 ++#define SPI_RAW_M BITFIELD_MASK(1) /* Bit [19] - Read After Wr */ ++#define SPI_RAW_S 19 ++#define SPI_STUFF_M BITFIELD_MASK(1) /* Bit [18] - Stuff bit */ ++#define SPI_STUFF_S 18 ++#define SPI_BLKMODE_M BITFIELD_MASK(1) /* Bit [19] - Blockmode 1=blk */ ++#define SPI_BLKMODE_S 19 ++#define SPI_OPCODE_M BITFIELD_MASK(1) /* Bit [18] - OP Code */ ++#define SPI_OPCODE_S 18 ++#define SPI_ADDR_M BITFIELD_MASK(17) /* Bits [17:1] - Address */ ++#define SPI_ADDR_S 1 ++#define SPI_STUFF0_M BITFIELD_MASK(1) /* Bit [0] - Stuff bit */ ++#define SPI_STUFF0_S 0 ++ ++#define SPI_RSP_START_M BITFIELD_MASK(1) /* Bit [7] - Start Bit (always 0) */ ++#define SPI_RSP_START_S 7 ++#define SPI_RSP_PARAM_ERR_M BITFIELD_MASK(1) /* Bit [6] - Parameter Error */ ++#define SPI_RSP_PARAM_ERR_S 6 ++#define SPI_RSP_RFU5_M BITFIELD_MASK(1) /* Bit [5] - RFU (Always 0) */ ++#define SPI_RSP_RFU5_S 5 ++#define SPI_RSP_FUNC_ERR_M BITFIELD_MASK(1) /* Bit [4] - Function number error */ ++#define SPI_RSP_FUNC_ERR_S 4 ++#define SPI_RSP_CRC_ERR_M BITFIELD_MASK(1) /* Bit [3] - COM CRC Error */ ++#define SPI_RSP_CRC_ERR_S 3 ++#define SPI_RSP_ILL_CMD_M BITFIELD_MASK(1) /* Bit [2] - Illegal Command error */ ++#define SPI_RSP_ILL_CMD_S 2 ++#define SPI_RSP_RFU1_M BITFIELD_MASK(1) /* Bit [1] - RFU (Always 0) */ ++#define SPI_RSP_RFU1_S 1 ++#define SPI_RSP_IDLE_M BITFIELD_MASK(1) /* Bit [0] - In idle state */ ++#define SPI_RSP_IDLE_S 0 ++ ++/* SD-SPI Protocol Definitions */ ++#define SDSPI_COMMAND_LEN 6 /* Number of bytes in an SD command */ ++#define SDSPI_START_BLOCK 0xFE /* SD Start Block Token */ ++#define SDSPI_IDLE_PAD 0xFF /* SD-SPI idle value for MOSI */ ++#define SDSPI_START_BIT_MASK 0x80 ++ ++#endif /* _SD_SPI_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/proto/vlan.h b/drivers/net/wireless/bcmdhd/include/proto/vlan.h +new file mode 100644 +index 00000000..9c94985c +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/vlan.h +@@ -0,0 +1,69 @@ ++/* ++ * 802.1Q VLAN protocol definitions ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: vlan.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++#ifndef _vlan_h_ ++#define _vlan_h_ ++ ++#ifndef _TYPEDEFS_H_ ++#include ++#endif ++ ++ ++#include ++ ++#define VLAN_VID_MASK 0xfff ++#define VLAN_CFI_SHIFT 12 ++#define VLAN_PRI_SHIFT 13 ++ ++#define VLAN_PRI_MASK 7 ++ ++#define VLAN_TAG_LEN 4 ++#define VLAN_TAG_OFFSET (2 * ETHER_ADDR_LEN) ++ ++#define VLAN_TPID 0x8100 ++ ++struct ethervlan_header { ++ uint8 ether_dhost[ETHER_ADDR_LEN]; ++ uint8 ether_shost[ETHER_ADDR_LEN]; ++ uint16 vlan_type; ++ uint16 vlan_tag; ++ uint16 ether_type; ++}; ++ ++#define ETHERVLAN_HDR_LEN (ETHER_HDR_LEN + VLAN_TAG_LEN) ++ ++ ++ ++#include ++ ++#define ETHERVLAN_MOVE_HDR(d, s) \ ++do { \ ++ struct ethervlan_header t; \ ++ t = *(struct ethervlan_header *)(s); \ ++ *(struct ethervlan_header *)(d) = t; \ ++} while (0) ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/proto/wpa.h b/drivers/net/wireless/bcmdhd/include/proto/wpa.h +new file mode 100644 +index 00000000..5b640ec3 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/proto/wpa.h +@@ -0,0 +1,173 @@ ++/* ++ * Fundamental types and constants relating to WPA ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wpa.h 261155 2011-05-23 23:51:32Z $ ++ */ ++ ++#ifndef _proto_wpa_h_ ++#define _proto_wpa_h_ ++ ++#include ++#include ++ ++ ++ ++#include ++ ++ ++ ++ ++#define DOT11_RC_INVALID_WPA_IE 13 ++#define DOT11_RC_MIC_FAILURE 14 ++#define DOT11_RC_4WH_TIMEOUT 15 ++#define DOT11_RC_GTK_UPDATE_TIMEOUT 16 ++#define DOT11_RC_WPA_IE_MISMATCH 17 ++#define DOT11_RC_INVALID_MC_CIPHER 18 ++#define DOT11_RC_INVALID_UC_CIPHER 19 ++#define DOT11_RC_INVALID_AKMP 20 ++#define DOT11_RC_BAD_WPA_VERSION 21 ++#define DOT11_RC_INVALID_WPA_CAP 22 ++#define DOT11_RC_8021X_AUTH_FAIL 23 ++ ++#define WPA2_PMKID_LEN 16 ++ ++ ++typedef BWL_PRE_PACKED_STRUCT struct ++{ ++ uint8 tag; ++ uint8 length; ++ uint8 oui[3]; ++ uint8 oui_type; ++ BWL_PRE_PACKED_STRUCT struct { ++ uint8 low; ++ uint8 high; ++ } BWL_POST_PACKED_STRUCT version; ++} BWL_POST_PACKED_STRUCT wpa_ie_fixed_t; ++#define WPA_IE_OUITYPE_LEN 4 ++#define WPA_IE_FIXED_LEN 8 ++#define WPA_IE_TAG_FIXED_LEN 6 ++ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ uint8 tag; ++ uint8 length; ++ BWL_PRE_PACKED_STRUCT struct { ++ uint8 low; ++ uint8 high; ++ } BWL_POST_PACKED_STRUCT version; ++} BWL_POST_PACKED_STRUCT wpa_rsn_ie_fixed_t; ++#define WPA_RSN_IE_FIXED_LEN 4 ++#define WPA_RSN_IE_TAG_FIXED_LEN 2 ++typedef uint8 wpa_pmkid_t[WPA2_PMKID_LEN]; ++ ++ ++typedef BWL_PRE_PACKED_STRUCT struct ++{ ++ uint8 oui[3]; ++ uint8 type; ++} BWL_POST_PACKED_STRUCT wpa_suite_t, wpa_suite_mcast_t; ++#define WPA_SUITE_LEN 4 ++ ++ ++typedef BWL_PRE_PACKED_STRUCT struct ++{ ++ BWL_PRE_PACKED_STRUCT struct { ++ uint8 low; ++ uint8 high; ++ } BWL_POST_PACKED_STRUCT count; ++ wpa_suite_t list[1]; ++} BWL_POST_PACKED_STRUCT wpa_suite_ucast_t, wpa_suite_auth_key_mgmt_t; ++#define WPA_IE_SUITE_COUNT_LEN 2 ++typedef BWL_PRE_PACKED_STRUCT struct ++{ ++ BWL_PRE_PACKED_STRUCT struct { ++ uint8 low; ++ uint8 high; ++ } BWL_POST_PACKED_STRUCT count; ++ wpa_pmkid_t list[1]; ++} BWL_POST_PACKED_STRUCT wpa_pmkid_list_t; ++ ++ ++#define WPA_CIPHER_NONE 0 ++#define WPA_CIPHER_WEP_40 1 ++#define WPA_CIPHER_TKIP 2 ++#define WPA_CIPHER_AES_OCB 3 ++#define WPA_CIPHER_AES_CCM 4 ++#define WPA_CIPHER_WEP_104 5 ++#define WPA_CIPHER_BIP 6 ++#define WPA_CIPHER_TPK 7 ++ ++ ++#define IS_WPA_CIPHER(cipher) ((cipher) == WPA_CIPHER_NONE || \ ++ (cipher) == WPA_CIPHER_WEP_40 || \ ++ (cipher) == WPA_CIPHER_WEP_104 || \ ++ (cipher) == WPA_CIPHER_TKIP || \ ++ (cipher) == WPA_CIPHER_AES_OCB || \ ++ (cipher) == WPA_CIPHER_AES_CCM || \ ++ (cipher) == WPA_CIPHER_TPK) ++ ++ ++ ++#define WPA_TKIP_CM_DETECT 60 ++#define WPA_TKIP_CM_BLOCK 60 ++ ++ ++#define RSN_CAP_LEN 2 ++ ++ ++#define RSN_CAP_PREAUTH 0x0001 ++#define RSN_CAP_NOPAIRWISE 0x0002 ++#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C ++#define RSN_CAP_PTK_REPLAY_CNTR_SHIFT 2 ++#define RSN_CAP_GTK_REPLAY_CNTR_MASK 0x0030 ++#define RSN_CAP_GTK_REPLAY_CNTR_SHIFT 4 ++#define RSN_CAP_1_REPLAY_CNTR 0 ++#define RSN_CAP_2_REPLAY_CNTRS 1 ++#define RSN_CAP_4_REPLAY_CNTRS 2 ++#define RSN_CAP_16_REPLAY_CNTRS 3 ++#ifdef MFP ++#define RSN_CAP_MFPR 0x0040 ++#define RSN_CAP_MFPC 0x0080 ++#endif ++ ++ ++#define WPA_CAP_4_REPLAY_CNTRS RSN_CAP_4_REPLAY_CNTRS ++#define WPA_CAP_16_REPLAY_CNTRS RSN_CAP_16_REPLAY_CNTRS ++#define WPA_CAP_REPLAY_CNTR_SHIFT RSN_CAP_PTK_REPLAY_CNTR_SHIFT ++#define WPA_CAP_REPLAY_CNTR_MASK RSN_CAP_PTK_REPLAY_CNTR_MASK ++ ++ ++#define WPA_CAP_PEER_KEY_ENABLE (0x1 << 1) ++ ++ ++#define WPA_CAP_LEN RSN_CAP_LEN ++#define WPA_PMKID_CNT_LEN 2 ++ ++#define WPA_CAP_WPA2_PREAUTH RSN_CAP_PREAUTH ++ ++#define WPA2_PMKID_COUNT_LEN 2 ++ ++ ++ ++#include ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/sbchipc.h b/drivers/net/wireless/bcmdhd/include/sbchipc.h +new file mode 100644 +index 00000000..a605d012 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/sbchipc.h +@@ -0,0 +1,2380 @@ ++/* ++ * SiliconBackplane Chipcommon core hardware definitions. ++ * ++ * The chipcommon core provides chip identification, SB control, ++ * JTAG, 0/1/2 UARTs, clock frequency control, a watchdog interrupt timer, ++ * GPIO interface, extbus, and support for serial and parallel flashes. ++ * ++ * $Id: sbchipc.h 347614 2012-07-27 10:24:51Z $ ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ */ ++ ++#ifndef _SBCHIPC_H ++#define _SBCHIPC_H ++ ++#ifndef _LANGUAGE_ASSEMBLY ++ ++ ++#ifndef PAD ++#define _PADLINE(line) pad ## line ++#define _XSTR(line) _PADLINE(line) ++#define PAD _XSTR(__LINE__) ++#endif ++ ++typedef struct eci_prerev35 { ++ uint32 eci_output; ++ uint32 eci_control; ++ uint32 eci_inputlo; ++ uint32 eci_inputmi; ++ uint32 eci_inputhi; ++ uint32 eci_inputintpolaritylo; ++ uint32 eci_inputintpolaritymi; ++ uint32 eci_inputintpolarityhi; ++ uint32 eci_intmasklo; ++ uint32 eci_intmaskmi; ++ uint32 eci_intmaskhi; ++ uint32 eci_eventlo; ++ uint32 eci_eventmi; ++ uint32 eci_eventhi; ++ uint32 eci_eventmasklo; ++ uint32 eci_eventmaskmi; ++ uint32 eci_eventmaskhi; ++ uint32 PAD[3]; ++} eci_prerev35_t; ++ ++typedef struct eci_rev35 { ++ uint32 eci_outputlo; ++ uint32 eci_outputhi; ++ uint32 eci_controllo; ++ uint32 eci_controlhi; ++ uint32 eci_inputlo; ++ uint32 eci_inputhi; ++ uint32 eci_inputintpolaritylo; ++ uint32 eci_inputintpolarityhi; ++ uint32 eci_intmasklo; ++ uint32 eci_intmaskhi; ++ uint32 eci_eventlo; ++ uint32 eci_eventhi; ++ uint32 eci_eventmasklo; ++ uint32 eci_eventmaskhi; ++ uint32 eci_auxtx; ++ uint32 eci_auxrx; ++ uint32 eci_datatag; ++ uint32 eci_uartescvalue; ++ uint32 eci_autobaudctr; ++ uint32 eci_uartfifolevel; ++} eci_rev35_t; ++ ++typedef struct flash_config { ++ uint32 PAD[19]; ++ ++ uint32 flashstrconfig; ++} flash_config_t; ++ ++typedef volatile struct { ++ uint32 chipid; ++ uint32 capabilities; ++ uint32 corecontrol; ++ uint32 bist; ++ ++ ++ uint32 otpstatus; ++ uint32 otpcontrol; ++ uint32 otpprog; ++ uint32 otplayout; ++ ++ ++ uint32 intstatus; ++ uint32 intmask; ++ ++ ++ uint32 chipcontrol; ++ uint32 chipstatus; ++ ++ ++ uint32 jtagcmd; ++ uint32 jtagir; ++ uint32 jtagdr; ++ uint32 jtagctrl; ++ ++ ++ uint32 flashcontrol; ++ uint32 flashaddress; ++ uint32 flashdata; ++ uint32 otplayoutextension; ++ ++ ++ uint32 broadcastaddress; ++ uint32 broadcastdata; ++ ++ ++ uint32 gpiopullup; ++ uint32 gpiopulldown; ++ uint32 gpioin; ++ uint32 gpioout; ++ uint32 gpioouten; ++ uint32 gpiocontrol; ++ uint32 gpiointpolarity; ++ uint32 gpiointmask; ++ ++ ++ uint32 gpioevent; ++ uint32 gpioeventintmask; ++ ++ ++ uint32 watchdog; ++ ++ ++ uint32 gpioeventintpolarity; ++ ++ ++ uint32 gpiotimerval; ++ uint32 gpiotimeroutmask; ++ ++ ++ uint32 clockcontrol_n; ++ uint32 clockcontrol_sb; ++ uint32 clockcontrol_pci; ++ uint32 clockcontrol_m2; ++ uint32 clockcontrol_m3; ++ uint32 clkdiv; ++ uint32 gpiodebugsel; ++ uint32 capabilities_ext; ++ ++ ++ uint32 pll_on_delay; ++ uint32 fref_sel_delay; ++ uint32 slow_clk_ctl; ++ uint32 PAD; ++ ++ ++ uint32 system_clk_ctl; ++ uint32 clkstatestretch; ++ uint32 PAD[2]; ++ ++ ++ uint32 bp_addrlow; ++ uint32 bp_addrhigh; ++ uint32 bp_data; ++ uint32 PAD; ++ uint32 bp_indaccess; ++ ++ uint32 gsioctrl; ++ uint32 gsioaddress; ++ uint32 gsiodata; ++ ++ ++ uint32 clkdiv2; ++ ++ uint32 otpcontrol1; ++ uint32 fabid; ++ ++ ++ uint32 eromptr; ++ ++ ++ uint32 pcmcia_config; ++ uint32 pcmcia_memwait; ++ uint32 pcmcia_attrwait; ++ uint32 pcmcia_iowait; ++ uint32 ide_config; ++ uint32 ide_memwait; ++ uint32 ide_attrwait; ++ uint32 ide_iowait; ++ uint32 prog_config; ++ uint32 prog_waitcount; ++ uint32 flash_config; ++ uint32 flash_waitcount; ++ uint32 SECI_config; ++ uint32 SECI_status; ++ uint32 SECI_statusmask; ++ uint32 SECI_rxnibchanged; ++ ++ uint32 PAD[20]; ++ ++ ++ uint32 sromcontrol; ++ uint32 sromaddress; ++ uint32 sromdata; ++ uint32 PAD[1]; ++ ++ uint32 nflashctrl; ++ uint32 nflashconf; ++ uint32 nflashcoladdr; ++ uint32 nflashrowaddr; ++ uint32 nflashdata; ++ uint32 nflashwaitcnt0; ++ uint32 PAD[2]; ++ ++ uint32 seci_uart_data; ++ uint32 seci_uart_bauddiv; ++ uint32 seci_uart_fcr; ++ uint32 seci_uart_lcr; ++ uint32 seci_uart_mcr; ++ uint32 seci_uart_lsr; ++ uint32 seci_uart_msr; ++ uint32 seci_uart_baudadj; ++ ++ uint32 clk_ctl_st; ++ uint32 hw_war; ++ uint32 PAD[70]; ++ ++ ++ uint8 uart0data; ++ uint8 uart0imr; ++ uint8 uart0fcr; ++ uint8 uart0lcr; ++ uint8 uart0mcr; ++ uint8 uart0lsr; ++ uint8 uart0msr; ++ uint8 uart0scratch; ++ uint8 PAD[248]; ++ ++ uint8 uart1data; ++ uint8 uart1imr; ++ uint8 uart1fcr; ++ uint8 uart1lcr; ++ uint8 uart1mcr; ++ uint8 uart1lsr; ++ uint8 uart1msr; ++ uint8 uart1scratch; ++ uint32 PAD[126]; ++ ++ ++ ++ uint32 pmucontrol; ++ uint32 pmucapabilities; ++ uint32 pmustatus; ++ uint32 res_state; ++ uint32 res_pending; ++ uint32 pmutimer; ++ uint32 min_res_mask; ++ uint32 max_res_mask; ++ uint32 res_table_sel; ++ uint32 res_dep_mask; ++ uint32 res_updn_timer; ++ uint32 res_timer; ++ uint32 clkstretch; ++ uint32 pmuwatchdog; ++ uint32 gpiosel; ++ uint32 gpioenable; ++ uint32 res_req_timer_sel; ++ uint32 res_req_timer; ++ uint32 res_req_mask; ++ uint32 PAD; ++ uint32 chipcontrol_addr; ++ uint32 chipcontrol_data; ++ uint32 regcontrol_addr; ++ uint32 regcontrol_data; ++ uint32 pllcontrol_addr; ++ uint32 pllcontrol_data; ++ uint32 pmustrapopt; ++ uint32 pmu_xtalfreq; ++ uint32 retention_ctl; ++ uint32 PAD[3]; ++ uint32 retention_grpidx; ++ uint32 retention_grpctl; ++ uint32 PAD[94]; ++ uint16 sromotp[512]; ++#ifdef NFLASH_SUPPORT ++ ++ uint32 nand_revision; ++ uint32 nand_cmd_start; ++ uint32 nand_cmd_addr_x; ++ uint32 nand_cmd_addr; ++ uint32 nand_cmd_end_addr; ++ uint32 nand_cs_nand_select; ++ uint32 nand_cs_nand_xor; ++ uint32 PAD; ++ uint32 nand_spare_rd0; ++ uint32 nand_spare_rd4; ++ uint32 nand_spare_rd8; ++ uint32 nand_spare_rd12; ++ uint32 nand_spare_wr0; ++ uint32 nand_spare_wr4; ++ uint32 nand_spare_wr8; ++ uint32 nand_spare_wr12; ++ uint32 nand_acc_control; ++ uint32 PAD; ++ uint32 nand_config; ++ uint32 PAD; ++ uint32 nand_timing_1; ++ uint32 nand_timing_2; ++ uint32 nand_semaphore; ++ uint32 PAD; ++ uint32 nand_devid; ++ uint32 nand_devid_x; ++ uint32 nand_block_lock_status; ++ uint32 nand_intfc_status; ++ uint32 nand_ecc_corr_addr_x; ++ uint32 nand_ecc_corr_addr; ++ uint32 nand_ecc_unc_addr_x; ++ uint32 nand_ecc_unc_addr; ++ uint32 nand_read_error_count; ++ uint32 nand_corr_stat_threshold; ++ uint32 PAD[2]; ++ uint32 nand_read_addr_x; ++ uint32 nand_read_addr; ++ uint32 nand_page_program_addr_x; ++ uint32 nand_page_program_addr; ++ uint32 nand_copy_back_addr_x; ++ uint32 nand_copy_back_addr; ++ uint32 nand_block_erase_addr_x; ++ uint32 nand_block_erase_addr; ++ uint32 nand_inv_read_addr_x; ++ uint32 nand_inv_read_addr; ++ uint32 PAD[2]; ++ uint32 nand_blk_wr_protect; ++ uint32 PAD[3]; ++ uint32 nand_acc_control_cs1; ++ uint32 nand_config_cs1; ++ uint32 nand_timing_1_cs1; ++ uint32 nand_timing_2_cs1; ++ uint32 PAD[20]; ++ uint32 nand_spare_rd16; ++ uint32 nand_spare_rd20; ++ uint32 nand_spare_rd24; ++ uint32 nand_spare_rd28; ++ uint32 nand_cache_addr; ++ uint32 nand_cache_data; ++ uint32 nand_ctrl_config; ++ uint32 nand_ctrl_status; ++#endif ++ uint32 gci_corecaps0; ++ uint32 gci_corecaps1; ++ uint32 gci_corecaps2; ++ uint32 gci_corectrl; ++ uint32 gci_corestat; ++ uint32 PAD[11]; ++ uint32 gci_indirect_addr; ++ uint32 PAD[111]; ++ uint32 gci_chipctrl; ++} chipcregs_t; ++ ++#endif ++ ++ ++#define CC_CHIPID 0 ++#define CC_CAPABILITIES 4 ++#define CC_CHIPST 0x2c ++#define CC_EROMPTR 0xfc ++ ++#define CC_OTPST 0x10 ++#define CC_JTAGCMD 0x30 ++#define CC_JTAGIR 0x34 ++#define CC_JTAGDR 0x38 ++#define CC_JTAGCTRL 0x3c ++#define CC_GPIOPU 0x58 ++#define CC_GPIOPD 0x5c ++#define CC_GPIOIN 0x60 ++#define CC_GPIOOUT 0x64 ++#define CC_GPIOOUTEN 0x68 ++#define CC_GPIOCTRL 0x6c ++#define CC_GPIOPOL 0x70 ++#define CC_GPIOINTM 0x74 ++#define CC_WATCHDOG 0x80 ++#define CC_CLKC_N 0x90 ++#define CC_CLKC_M0 0x94 ++#define CC_CLKC_M1 0x98 ++#define CC_CLKC_M2 0x9c ++#define CC_CLKC_M3 0xa0 ++#define CC_CLKDIV 0xa4 ++#define CC_SYS_CLK_CTL 0xc0 ++#define CC_CLK_CTL_ST SI_CLK_CTL_ST ++#define PMU_CTL 0x600 ++#define PMU_CAP 0x604 ++#define PMU_ST 0x608 ++#define PMU_RES_STATE 0x60c ++#define PMU_TIMER 0x614 ++#define PMU_MIN_RES_MASK 0x618 ++#define PMU_MAX_RES_MASK 0x61c ++#define CC_CHIPCTL_ADDR 0x650 ++#define CC_CHIPCTL_DATA 0x654 ++#define PMU_REG_CONTROL_ADDR 0x658 ++#define PMU_REG_CONTROL_DATA 0x65C ++#define PMU_PLL_CONTROL_ADDR 0x660 ++#define PMU_PLL_CONTROL_DATA 0x664 ++#define CC_SROM_OTP 0x800 ++#define CC_GCI_INDIRECT_ADDR_REG 0xC40 ++#define CC_GCI_CHIP_CTRL_REG 0xE00 ++#define CC_GCI_CC_OFFSET_2 2 ++#define CC_GCI_CC_OFFSET_5 5 ++ ++#ifdef NFLASH_SUPPORT ++ ++#define CC_NAND_REVISION 0xC00 ++#define CC_NAND_CMD_START 0xC04 ++#define CC_NAND_CMD_ADDR 0xC0C ++#define CC_NAND_SPARE_RD_0 0xC20 ++#define CC_NAND_SPARE_RD_4 0xC24 ++#define CC_NAND_SPARE_RD_8 0xC28 ++#define CC_NAND_SPARE_RD_C 0xC2C ++#define CC_NAND_CONFIG 0xC48 ++#define CC_NAND_DEVID 0xC60 ++#define CC_NAND_DEVID_EXT 0xC64 ++#define CC_NAND_INTFC_STATUS 0xC6C ++#endif ++ ++ ++#define CID_ID_MASK 0x0000ffff ++#define CID_REV_MASK 0x000f0000 ++#define CID_REV_SHIFT 16 ++#define CID_PKG_MASK 0x00f00000 ++#define CID_PKG_SHIFT 20 ++#define CID_CC_MASK 0x0f000000 ++#define CID_CC_SHIFT 24 ++#define CID_TYPE_MASK 0xf0000000 ++#define CID_TYPE_SHIFT 28 ++ ++ ++#define CC_CAP_UARTS_MASK 0x00000003 ++#define CC_CAP_MIPSEB 0x00000004 ++#define CC_CAP_UCLKSEL 0x00000018 ++#define CC_CAP_UINTCLK 0x00000008 ++#define CC_CAP_UARTGPIO 0x00000020 ++#define CC_CAP_EXTBUS_MASK 0x000000c0 ++#define CC_CAP_EXTBUS_NONE 0x00000000 ++#define CC_CAP_EXTBUS_FULL 0x00000040 ++#define CC_CAP_EXTBUS_PROG 0x00000080 ++#define CC_CAP_FLASH_MASK 0x00000700 ++#define CC_CAP_PLL_MASK 0x00038000 ++#define CC_CAP_PWR_CTL 0x00040000 ++#define CC_CAP_OTPSIZE 0x00380000 ++#define CC_CAP_OTPSIZE_SHIFT 19 ++#define CC_CAP_OTPSIZE_BASE 5 ++#define CC_CAP_JTAGP 0x00400000 ++#define CC_CAP_ROM 0x00800000 ++#define CC_CAP_BKPLN64 0x08000000 ++#define CC_CAP_PMU 0x10000000 ++#define CC_CAP_ECI 0x20000000 ++#define CC_CAP_SROM 0x40000000 ++#define CC_CAP_NFLASH 0x80000000 ++ ++#define CC_CAP2_SECI 0x00000001 ++#define CC_CAP2_GSIO 0x00000002 ++ ++ ++#define CC_CAP_EXT_SECI_PRESENT 0x00000001 ++ ++ ++#define PLL_NONE 0x00000000 ++#define PLL_TYPE1 0x00010000 ++#define PLL_TYPE2 0x00020000 ++#define PLL_TYPE3 0x00030000 ++#define PLL_TYPE4 0x00008000 ++#define PLL_TYPE5 0x00018000 ++#define PLL_TYPE6 0x00028000 ++#define PLL_TYPE7 0x00038000 ++ ++ ++#define ILP_CLOCK 32000 ++ ++ ++#define ALP_CLOCK 20000000 ++ ++ ++#define HT_CLOCK 80000000 ++ ++ ++#define CC_UARTCLKO 0x00000001 ++#define CC_SE 0x00000002 ++#define CC_ASYNCGPIO 0x00000004 ++#define CC_UARTCLKEN 0x00000008 ++ ++ ++#define CHIPCTRL_4321A0_DEFAULT 0x3a4 ++#define CHIPCTRL_4321A1_DEFAULT 0x0a4 ++#define CHIPCTRL_4321_PLL_DOWN 0x800000 ++ ++ ++#define OTPS_OL_MASK 0x000000ff ++#define OTPS_OL_MFG 0x00000001 ++#define OTPS_OL_OR1 0x00000002 ++#define OTPS_OL_OR2 0x00000004 ++#define OTPS_OL_GU 0x00000008 ++#define OTPS_GUP_MASK 0x00000f00 ++#define OTPS_GUP_SHIFT 8 ++#define OTPS_GUP_HW 0x00000100 ++#define OTPS_GUP_SW 0x00000200 ++#define OTPS_GUP_CI 0x00000400 ++#define OTPS_GUP_FUSE 0x00000800 ++#define OTPS_READY 0x00001000 ++#define OTPS_RV(x) (1 << (16 + (x))) ++#define OTPS_RV_MASK 0x0fff0000 ++#define OTPS_PROGOK 0x40000000 ++ ++ ++#define OTPC_PROGSEL 0x00000001 ++#define OTPC_PCOUNT_MASK 0x0000000e ++#define OTPC_PCOUNT_SHIFT 1 ++#define OTPC_VSEL_MASK 0x000000f0 ++#define OTPC_VSEL_SHIFT 4 ++#define OTPC_TMM_MASK 0x00000700 ++#define OTPC_TMM_SHIFT 8 ++#define OTPC_ODM 0x00000800 ++#define OTPC_PROGEN 0x80000000 ++ ++ ++#define OTPC_40NM_PROGSEL_SHIFT 0 ++#define OTPC_40NM_PCOUNT_SHIFT 1 ++#define OTPC_40NM_PCOUNT_WR 0xA ++#define OTPC_40NM_PCOUNT_V1X 0xB ++#define OTPC_40NM_REGCSEL_SHIFT 5 ++#define OTPC_40NM_REGCSEL_DEF 0x4 ++#define OTPC_40NM_PROGIN_SHIFT 8 ++#define OTPC_40NM_R2X_SHIFT 10 ++#define OTPC_40NM_ODM_SHIFT 11 ++#define OTPC_40NM_DF_SHIFT 15 ++#define OTPC_40NM_VSEL_SHIFT 16 ++#define OTPC_40NM_VSEL_WR 0xA ++#define OTPC_40NM_VSEL_V1X 0xA ++#define OTPC_40NM_VSEL_R1X 0x5 ++#define OTPC_40NM_COFAIL_SHIFT 30 ++ ++#define OTPC1_CPCSEL_SHIFT 0 ++#define OTPC1_CPCSEL_DEF 6 ++#define OTPC1_TM_SHIFT 8 ++#define OTPC1_TM_WR 0x84 ++#define OTPC1_TM_V1X 0x84 ++#define OTPC1_TM_R1X 0x4 ++ ++ ++#define OTPP_COL_MASK 0x000000ff ++#define OTPP_COL_SHIFT 0 ++#define OTPP_ROW_MASK 0x0000ff00 ++#define OTPP_ROW_SHIFT 8 ++#define OTPP_OC_MASK 0x0f000000 ++#define OTPP_OC_SHIFT 24 ++#define OTPP_READERR 0x10000000 ++#define OTPP_VALUE_MASK 0x20000000 ++#define OTPP_VALUE_SHIFT 29 ++#define OTPP_START_BUSY 0x80000000 ++#define OTPP_READ 0x40000000 ++ ++ ++#define OTPL_HWRGN_OFF_MASK 0x00000FFF ++#define OTPL_HWRGN_OFF_SHIFT 0 ++#define OTPL_WRAP_REVID_MASK 0x00F80000 ++#define OTPL_WRAP_REVID_SHIFT 19 ++#define OTPL_WRAP_TYPE_MASK 0x00070000 ++#define OTPL_WRAP_TYPE_SHIFT 16 ++#define OTPL_WRAP_TYPE_65NM 0 ++#define OTPL_WRAP_TYPE_40NM 1 ++ ++ ++#define OTP_CISFORMAT_NEW 0x80000000 ++ ++ ++#define OTPPOC_READ 0 ++#define OTPPOC_BIT_PROG 1 ++#define OTPPOC_VERIFY 3 ++#define OTPPOC_INIT 4 ++#define OTPPOC_SET 5 ++#define OTPPOC_RESET 6 ++#define OTPPOC_OCST 7 ++#define OTPPOC_ROW_LOCK 8 ++#define OTPPOC_PRESCN_TEST 9 ++ ++ ++#define OTPPOC_READ_40NM 0 ++#define OTPPOC_PROG_ENABLE_40NM 1 ++#define OTPPOC_PROG_DISABLE_40NM 2 ++#define OTPPOC_VERIFY_40NM 3 ++#define OTPPOC_WORD_VERIFY_1_40NM 4 ++#define OTPPOC_ROW_LOCK_40NM 5 ++#define OTPPOC_STBY_40NM 6 ++#define OTPPOC_WAKEUP_40NM 7 ++#define OTPPOC_WORD_VERIFY_0_40NM 8 ++#define OTPPOC_PRESCN_TEST_40NM 9 ++#define OTPPOC_BIT_PROG_40NM 10 ++#define OTPPOC_WORDPROG_40NM 11 ++#define OTPPOC_BURNIN_40NM 12 ++#define OTPPOC_AUTORELOAD_40NM 13 ++#define OTPPOC_OVST_READ_40NM 14 ++#define OTPPOC_OVST_PROG_40NM 15 ++ ++ ++#define OTPLAYOUTEXT_FUSE_MASK 0x3FF ++ ++ ++ ++#define JTAGM_CREV_OLD 10 ++#define JTAGM_CREV_IRP 22 ++#define JTAGM_CREV_RTI 28 ++ ++ ++#define JCMD_START 0x80000000 ++#define JCMD_BUSY 0x80000000 ++#define JCMD_STATE_MASK 0x60000000 ++#define JCMD_STATE_TLR 0x00000000 ++#define JCMD_STATE_PIR 0x20000000 ++#define JCMD_STATE_PDR 0x40000000 ++#define JCMD_STATE_RTI 0x60000000 ++#define JCMD0_ACC_MASK 0x0000f000 ++#define JCMD0_ACC_IRDR 0x00000000 ++#define JCMD0_ACC_DR 0x00001000 ++#define JCMD0_ACC_IR 0x00002000 ++#define JCMD0_ACC_RESET 0x00003000 ++#define JCMD0_ACC_IRPDR 0x00004000 ++#define JCMD0_ACC_PDR 0x00005000 ++#define JCMD0_IRW_MASK 0x00000f00 ++#define JCMD_ACC_MASK 0x000f0000 ++#define JCMD_ACC_IRDR 0x00000000 ++#define JCMD_ACC_DR 0x00010000 ++#define JCMD_ACC_IR 0x00020000 ++#define JCMD_ACC_RESET 0x00030000 ++#define JCMD_ACC_IRPDR 0x00040000 ++#define JCMD_ACC_PDR 0x00050000 ++#define JCMD_ACC_PIR 0x00060000 ++#define JCMD_ACC_IRDR_I 0x00070000 ++#define JCMD_ACC_DR_I 0x00080000 ++#define JCMD_IRW_MASK 0x00001f00 ++#define JCMD_IRW_SHIFT 8 ++#define JCMD_DRW_MASK 0x0000003f ++ ++ ++#define JCTRL_FORCE_CLK 4 ++#define JCTRL_EXT_EN 2 ++#define JCTRL_EN 1 ++ ++ ++#define CLKD_SFLASH 0x0f000000 ++#define CLKD_SFLASH_SHIFT 24 ++#define CLKD_OTP 0x000f0000 ++#define CLKD_OTP_SHIFT 16 ++#define CLKD_JTAG 0x00000f00 ++#define CLKD_JTAG_SHIFT 8 ++#define CLKD_UART 0x000000ff ++ ++#define CLKD2_SROM 0x00000003 ++ ++ ++#define CI_GPIO 0x00000001 ++#define CI_EI 0x00000002 ++#define CI_TEMP 0x00000004 ++#define CI_SIRQ 0x00000008 ++#define CI_ECI 0x00000010 ++#define CI_PMU 0x00000020 ++#define CI_UART 0x00000040 ++#define CI_WDRESET 0x80000000 ++ ++ ++#define SCC_SS_MASK 0x00000007 ++#define SCC_SS_LPO 0x00000000 ++#define SCC_SS_XTAL 0x00000001 ++#define SCC_SS_PCI 0x00000002 ++#define SCC_LF 0x00000200 ++#define SCC_LP 0x00000400 ++#define SCC_FS 0x00000800 ++#define SCC_IP 0x00001000 ++#define SCC_XC 0x00002000 ++#define SCC_XP 0x00004000 ++#define SCC_CD_MASK 0xffff0000 ++#define SCC_CD_SHIFT 16 ++ ++ ++#define SYCC_IE 0x00000001 ++#define SYCC_AE 0x00000002 ++#define SYCC_FP 0x00000004 ++#define SYCC_AR 0x00000008 ++#define SYCC_HR 0x00000010 ++#define SYCC_CD_MASK 0xffff0000 ++#define SYCC_CD_SHIFT 16 ++ ++ ++#define BPIA_BYTEEN 0x0000000f ++#define BPIA_SZ1 0x00000001 ++#define BPIA_SZ2 0x00000003 ++#define BPIA_SZ4 0x00000007 ++#define BPIA_SZ8 0x0000000f ++#define BPIA_WRITE 0x00000100 ++#define BPIA_START 0x00000200 ++#define BPIA_BUSY 0x00000200 ++#define BPIA_ERROR 0x00000400 ++ ++ ++#define CF_EN 0x00000001 ++#define CF_EM_MASK 0x0000000e ++#define CF_EM_SHIFT 1 ++#define CF_EM_FLASH 0 ++#define CF_EM_SYNC 2 ++#define CF_EM_PCMCIA 4 ++#define CF_DS 0x00000010 ++#define CF_BS 0x00000020 ++#define CF_CD_MASK 0x000000c0 ++#define CF_CD_SHIFT 6 ++#define CF_CD_DIV2 0x00000000 ++#define CF_CD_DIV3 0x00000040 ++#define CF_CD_DIV4 0x00000080 ++#define CF_CE 0x00000100 ++#define CF_SB 0x00000200 ++ ++ ++#define PM_W0_MASK 0x0000003f ++#define PM_W1_MASK 0x00001f00 ++#define PM_W1_SHIFT 8 ++#define PM_W2_MASK 0x001f0000 ++#define PM_W2_SHIFT 16 ++#define PM_W3_MASK 0x1f000000 ++#define PM_W3_SHIFT 24 ++ ++ ++#define PA_W0_MASK 0x0000003f ++#define PA_W1_MASK 0x00001f00 ++#define PA_W1_SHIFT 8 ++#define PA_W2_MASK 0x001f0000 ++#define PA_W2_SHIFT 16 ++#define PA_W3_MASK 0x1f000000 ++#define PA_W3_SHIFT 24 ++ ++ ++#define PI_W0_MASK 0x0000003f ++#define PI_W1_MASK 0x00001f00 ++#define PI_W1_SHIFT 8 ++#define PI_W2_MASK 0x001f0000 ++#define PI_W2_SHIFT 16 ++#define PI_W3_MASK 0x1f000000 ++#define PI_W3_SHIFT 24 ++ ++ ++#define PW_W0_MASK 0x0000001f ++#define PW_W1_MASK 0x00001f00 ++#define PW_W1_SHIFT 8 ++#define PW_W2_MASK 0x001f0000 ++#define PW_W2_SHIFT 16 ++#define PW_W3_MASK 0x1f000000 ++#define PW_W3_SHIFT 24 ++ ++#define PW_W0 0x0000000c ++#define PW_W1 0x00000a00 ++#define PW_W2 0x00020000 ++#define PW_W3 0x01000000 ++ ++ ++#define FW_W0_MASK 0x0000003f ++#define FW_W1_MASK 0x00001f00 ++#define FW_W1_SHIFT 8 ++#define FW_W2_MASK 0x001f0000 ++#define FW_W2_SHIFT 16 ++#define FW_W3_MASK 0x1f000000 ++#define FW_W3_SHIFT 24 ++ ++ ++#define SRC_START 0x80000000 ++#define SRC_BUSY 0x80000000 ++#define SRC_OPCODE 0x60000000 ++#define SRC_OP_READ 0x00000000 ++#define SRC_OP_WRITE 0x20000000 ++#define SRC_OP_WRDIS 0x40000000 ++#define SRC_OP_WREN 0x60000000 ++#define SRC_OTPSEL 0x00000010 ++#define SRC_LOCK 0x00000008 ++#define SRC_SIZE_MASK 0x00000006 ++#define SRC_SIZE_1K 0x00000000 ++#define SRC_SIZE_4K 0x00000002 ++#define SRC_SIZE_16K 0x00000004 ++#define SRC_SIZE_SHIFT 1 ++#define SRC_PRESENT 0x00000001 ++ ++ ++#define PCTL_ILP_DIV_MASK 0xffff0000 ++#define PCTL_ILP_DIV_SHIFT 16 ++#define PCTL_PLL_PLLCTL_UPD 0x00000400 ++#define PCTL_NOILP_ON_WAIT 0x00000200 ++#define PCTL_HT_REQ_EN 0x00000100 ++#define PCTL_ALP_REQ_EN 0x00000080 ++#define PCTL_XTALFREQ_MASK 0x0000007c ++#define PCTL_XTALFREQ_SHIFT 2 ++#define PCTL_ILP_DIV_EN 0x00000002 ++#define PCTL_LPO_SEL 0x00000001 ++ ++ ++#define CSTRETCH_HT 0xffff0000 ++#define CSTRETCH_ALP 0x0000ffff ++ ++ ++#define GPIO_ONTIME_SHIFT 16 ++ ++ ++#define CN_N1_MASK 0x3f ++#define CN_N2_MASK 0x3f00 ++#define CN_N2_SHIFT 8 ++#define CN_PLLC_MASK 0xf0000 ++#define CN_PLLC_SHIFT 16 ++ ++ ++#define CC_M1_MASK 0x3f ++#define CC_M2_MASK 0x3f00 ++#define CC_M2_SHIFT 8 ++#define CC_M3_MASK 0x3f0000 ++#define CC_M3_SHIFT 16 ++#define CC_MC_MASK 0x1f000000 ++#define CC_MC_SHIFT 24 ++ ++ ++#define CC_F6_2 0x02 ++#define CC_F6_3 0x03 ++#define CC_F6_4 0x05 ++#define CC_F6_5 0x09 ++#define CC_F6_6 0x11 ++#define CC_F6_7 0x21 ++ ++#define CC_F5_BIAS 5 ++ ++#define CC_MC_BYPASS 0x08 ++#define CC_MC_M1 0x04 ++#define CC_MC_M1M2 0x02 ++#define CC_MC_M1M2M3 0x01 ++#define CC_MC_M1M3 0x11 ++ ++ ++#define CC_T2_BIAS 2 ++#define CC_T2M2_BIAS 3 ++ ++#define CC_T2MC_M1BYP 1 ++#define CC_T2MC_M2BYP 2 ++#define CC_T2MC_M3BYP 4 ++ ++ ++#define CC_T6_MMASK 1 ++#define CC_T6_M0 120000000 ++#define CC_T6_M1 100000000 ++#define SB2MIPS_T6(sb) (2 * (sb)) ++ ++ ++#define CC_CLOCK_BASE1 24000000 ++#define CC_CLOCK_BASE2 12500000 ++ ++ ++#define CLKC_5350_N 0x0311 ++#define CLKC_5350_M 0x04020009 ++ ++ ++#define FLASH_NONE 0x000 ++#define SFLASH_ST 0x100 ++#define SFLASH_AT 0x200 ++#define NFLASH 0x300 ++#define PFLASH 0x700 ++ ++ ++#define CC_CFG_EN 0x0001 ++#define CC_CFG_EM_MASK 0x000e ++#define CC_CFG_EM_ASYNC 0x0000 ++#define CC_CFG_EM_SYNC 0x0002 ++#define CC_CFG_EM_PCMCIA 0x0004 ++#define CC_CFG_EM_IDE 0x0006 ++#define CC_CFG_DS 0x0010 ++#define CC_CFG_CD_MASK 0x00e0 ++#define CC_CFG_CE 0x0100 ++#define CC_CFG_SB 0x0200 ++#define CC_CFG_IS 0x0400 ++ ++ ++#define CC_EB_BASE 0x1a000000 ++#define CC_EB_PCMCIA_MEM 0x1a000000 ++#define CC_EB_PCMCIA_IO 0x1a200000 ++#define CC_EB_PCMCIA_CFG 0x1a400000 ++#define CC_EB_IDE 0x1a800000 ++#define CC_EB_PCMCIA1_MEM 0x1a800000 ++#define CC_EB_PCMCIA1_IO 0x1aa00000 ++#define CC_EB_PCMCIA1_CFG 0x1ac00000 ++#define CC_EB_PROGIF 0x1b000000 ++ ++ ++ ++#define SFLASH_OPCODE 0x000000ff ++#define SFLASH_ACTION 0x00000700 ++#define SFLASH_CS_ACTIVE 0x00001000 ++#define SFLASH_START 0x80000000 ++#define SFLASH_BUSY SFLASH_START ++ ++ ++#define SFLASH_ACT_OPONLY 0x0000 ++#define SFLASH_ACT_OP1D 0x0100 ++#define SFLASH_ACT_OP3A 0x0200 ++#define SFLASH_ACT_OP3A1D 0x0300 ++#define SFLASH_ACT_OP3A4D 0x0400 ++#define SFLASH_ACT_OP3A4X4D 0x0500 ++#define SFLASH_ACT_OP3A1X4D 0x0700 ++ ++ ++#define SFLASH_ST_WREN 0x0006 ++#define SFLASH_ST_WRDIS 0x0004 ++#define SFLASH_ST_RDSR 0x0105 ++#define SFLASH_ST_WRSR 0x0101 ++#define SFLASH_ST_READ 0x0303 ++#define SFLASH_ST_PP 0x0302 ++#define SFLASH_ST_SE 0x02d8 ++#define SFLASH_ST_BE 0x00c7 ++#define SFLASH_ST_DP 0x00b9 ++#define SFLASH_ST_RES 0x03ab ++#define SFLASH_ST_CSA 0x1000 ++#define SFLASH_ST_SSE 0x0220 ++ ++#define SFLASH_MXIC_RDID 0x0390 ++#define SFLASH_MXIC_MFID 0xc2 ++ ++ ++#define SFLASH_ST_WIP 0x01 ++#define SFLASH_ST_WEL 0x02 ++#define SFLASH_ST_BP_MASK 0x1c ++#define SFLASH_ST_BP_SHIFT 2 ++#define SFLASH_ST_SRWD 0x80 ++ ++ ++#define SFLASH_AT_READ 0x07e8 ++#define SFLASH_AT_PAGE_READ 0x07d2 ++#define SFLASH_AT_BUF1_READ ++#define SFLASH_AT_BUF2_READ ++#define SFLASH_AT_STATUS 0x01d7 ++#define SFLASH_AT_BUF1_WRITE 0x0384 ++#define SFLASH_AT_BUF2_WRITE 0x0387 ++#define SFLASH_AT_BUF1_ERASE_PROGRAM 0x0283 ++#define SFLASH_AT_BUF2_ERASE_PROGRAM 0x0286 ++#define SFLASH_AT_BUF1_PROGRAM 0x0288 ++#define SFLASH_AT_BUF2_PROGRAM 0x0289 ++#define SFLASH_AT_PAGE_ERASE 0x0281 ++#define SFLASH_AT_BLOCK_ERASE 0x0250 ++#define SFLASH_AT_BUF1_WRITE_ERASE_PROGRAM 0x0382 ++#define SFLASH_AT_BUF2_WRITE_ERASE_PROGRAM 0x0385 ++#define SFLASH_AT_BUF1_LOAD 0x0253 ++#define SFLASH_AT_BUF2_LOAD 0x0255 ++#define SFLASH_AT_BUF1_COMPARE 0x0260 ++#define SFLASH_AT_BUF2_COMPARE 0x0261 ++#define SFLASH_AT_BUF1_REPROGRAM 0x0258 ++#define SFLASH_AT_BUF2_REPROGRAM 0x0259 ++ ++ ++#define SFLASH_AT_READY 0x80 ++#define SFLASH_AT_MISMATCH 0x40 ++#define SFLASH_AT_ID_MASK 0x38 ++#define SFLASH_AT_ID_SHIFT 3 ++ ++ ++#define GSIO_START 0x80000000 ++#define GSIO_BUSY GSIO_START ++ ++ ++ ++#define UART_RX 0 ++#define UART_TX 0 ++#define UART_DLL 0 ++#define UART_IER 1 ++#define UART_DLM 1 ++#define UART_IIR 2 ++#define UART_FCR 2 ++#define UART_LCR 3 ++#define UART_MCR 4 ++#define UART_LSR 5 ++#define UART_MSR 6 ++#define UART_SCR 7 ++#define UART_LCR_DLAB 0x80 ++#define UART_LCR_WLEN8 0x03 ++#define UART_MCR_OUT2 0x08 ++#define UART_MCR_LOOP 0x10 ++#define UART_LSR_RX_FIFO 0x80 ++#define UART_LSR_TDHR 0x40 ++#define UART_LSR_THRE 0x20 ++#define UART_LSR_BREAK 0x10 ++#define UART_LSR_FRAMING 0x08 ++#define UART_LSR_PARITY 0x04 ++#define UART_LSR_OVERRUN 0x02 ++#define UART_LSR_RXRDY 0x01 ++#define UART_FCR_FIFO_ENABLE 1 ++ ++ ++#define UART_IIR_FIFO_MASK 0xc0 ++#define UART_IIR_INT_MASK 0xf ++#define UART_IIR_MDM_CHG 0x0 ++#define UART_IIR_NOINT 0x1 ++#define UART_IIR_THRE 0x2 ++#define UART_IIR_RCVD_DATA 0x4 ++#define UART_IIR_RCVR_STATUS 0x6 ++#define UART_IIR_CHAR_TIME 0xc ++ ++ ++#define UART_IER_EDSSI 8 ++#define UART_IER_ELSI 4 ++#define UART_IER_ETBEI 2 ++#define UART_IER_ERBFI 1 ++ ++ ++#define PST_EXTLPOAVAIL 0x0100 ++#define PST_WDRESET 0x0080 ++#define PST_INTPEND 0x0040 ++#define PST_SBCLKST 0x0030 ++#define PST_SBCLKST_ILP 0x0010 ++#define PST_SBCLKST_ALP 0x0020 ++#define PST_SBCLKST_HT 0x0030 ++#define PST_ALPAVAIL 0x0008 ++#define PST_HTAVAIL 0x0004 ++#define PST_RESINIT 0x0003 ++ ++ ++#define PCAP_REV_MASK 0x000000ff ++#define PCAP_RC_MASK 0x00001f00 ++#define PCAP_RC_SHIFT 8 ++#define PCAP_TC_MASK 0x0001e000 ++#define PCAP_TC_SHIFT 13 ++#define PCAP_PC_MASK 0x001e0000 ++#define PCAP_PC_SHIFT 17 ++#define PCAP_VC_MASK 0x01e00000 ++#define PCAP_VC_SHIFT 21 ++#define PCAP_CC_MASK 0x1e000000 ++#define PCAP_CC_SHIFT 25 ++#define PCAP5_PC_MASK 0x003e0000 ++#define PCAP5_PC_SHIFT 17 ++#define PCAP5_VC_MASK 0x07c00000 ++#define PCAP5_VC_SHIFT 22 ++#define PCAP5_CC_MASK 0xf8000000 ++#define PCAP5_CC_SHIFT 27 ++ ++ ++ ++#define PRRT_TIME_MASK 0x03ff ++#define PRRT_INTEN 0x0400 ++#define PRRT_REQ_ACTIVE 0x0800 ++#define PRRT_ALP_REQ 0x1000 ++#define PRRT_HT_REQ 0x2000 ++#define PRRT_HQ_REQ 0x4000 ++ ++ ++#define PMURES_BIT(bit) (1 << (bit)) ++ ++ ++#define PMURES_MAX_RESNUM 30 ++ ++ ++#define PMU_CHIPCTL0 0 ++ ++ ++#define PMU_CC1_CLKREQ_TYPE_SHIFT 19 ++#define PMU_CC1_CLKREQ_TYPE_MASK (1 << PMU_CC1_CLKREQ_TYPE_SHIFT) ++ ++#define CLKREQ_TYPE_CONFIG_OPENDRAIN 0 ++#define CLKREQ_TYPE_CONFIG_PUSHPULL 1 ++ ++ ++#define PMU_CHIPCTL1 1 ++#define PMU_CC1_RXC_DLL_BYPASS 0x00010000 ++ ++#define PMU_CC1_IF_TYPE_MASK 0x00000030 ++#define PMU_CC1_IF_TYPE_RMII 0x00000000 ++#define PMU_CC1_IF_TYPE_MII 0x00000010 ++#define PMU_CC1_IF_TYPE_RGMII 0x00000020 ++ ++#define PMU_CC1_SW_TYPE_MASK 0x000000c0 ++#define PMU_CC1_SW_TYPE_EPHY 0x00000000 ++#define PMU_CC1_SW_TYPE_EPHYMII 0x00000040 ++#define PMU_CC1_SW_TYPE_EPHYRMII 0x00000080 ++#define PMU_CC1_SW_TYPE_RGMII 0x000000c0 ++ ++ ++#define PMU_CHIPCTL2 2 ++ ++ ++#define PMU_CHIPCTL3 3 ++ ++#define PMU_CC3_ENABLE_SDIO_WAKEUP_SHIFT 19 ++#define PMU_CC3_ENABLE_RF_SHIFT 22 ++#define PMU_CC3_RF_DISABLE_IVALUE_SHIFT 23 ++ ++ ++ ++ ++ ++#define PMU0_PLL0_PLLCTL0 0 ++#define PMU0_PLL0_PC0_PDIV_MASK 1 ++#define PMU0_PLL0_PC0_PDIV_FREQ 25000 ++#define PMU0_PLL0_PC0_DIV_ARM_MASK 0x00000038 ++#define PMU0_PLL0_PC0_DIV_ARM_SHIFT 3 ++#define PMU0_PLL0_PC0_DIV_ARM_BASE 8 ++ ++ ++#define PMU0_PLL0_PC0_DIV_ARM_110MHZ 0 ++#define PMU0_PLL0_PC0_DIV_ARM_97_7MHZ 1 ++#define PMU0_PLL0_PC0_DIV_ARM_88MHZ 2 ++#define PMU0_PLL0_PC0_DIV_ARM_80MHZ 3 ++#define PMU0_PLL0_PC0_DIV_ARM_73_3MHZ 4 ++#define PMU0_PLL0_PC0_DIV_ARM_67_7MHZ 5 ++#define PMU0_PLL0_PC0_DIV_ARM_62_9MHZ 6 ++#define PMU0_PLL0_PC0_DIV_ARM_58_6MHZ 7 ++ ++ ++#define PMU0_PLL0_PLLCTL1 1 ++#define PMU0_PLL0_PC1_WILD_INT_MASK 0xf0000000 ++#define PMU0_PLL0_PC1_WILD_INT_SHIFT 28 ++#define PMU0_PLL0_PC1_WILD_FRAC_MASK 0x0fffff00 ++#define PMU0_PLL0_PC1_WILD_FRAC_SHIFT 8 ++#define PMU0_PLL0_PC1_STOP_MOD 0x00000040 ++ ++ ++#define PMU0_PLL0_PLLCTL2 2 ++#define PMU0_PLL0_PC2_WILD_INT_MASK 0xf ++#define PMU0_PLL0_PC2_WILD_INT_SHIFT 4 ++ ++ ++ ++#define PMU1_PLL0_PLLCTL0 0 ++#define PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000 ++#define PMU1_PLL0_PC0_P1DIV_SHIFT 20 ++#define PMU1_PLL0_PC0_P2DIV_MASK 0x0f000000 ++#define PMU1_PLL0_PC0_P2DIV_SHIFT 24 ++ ++ ++#define PMU1_PLL0_PLLCTL1 1 ++#define PMU1_PLL0_PC1_M1DIV_MASK 0x000000ff ++#define PMU1_PLL0_PC1_M1DIV_SHIFT 0 ++#define PMU1_PLL0_PC1_M2DIV_MASK 0x0000ff00 ++#define PMU1_PLL0_PC1_M2DIV_SHIFT 8 ++#define PMU1_PLL0_PC1_M3DIV_MASK 0x00ff0000 ++#define PMU1_PLL0_PC1_M3DIV_SHIFT 16 ++#define PMU1_PLL0_PC1_M4DIV_MASK 0xff000000 ++#define PMU1_PLL0_PC1_M4DIV_SHIFT 24 ++#define PMU1_PLL0_PC1_M4DIV_BY_9 9 ++#define PMU1_PLL0_PC1_M4DIV_BY_18 0x12 ++#define PMU1_PLL0_PC1_M4DIV_BY_36 0x24 ++ ++#define DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT 8 ++#define DOT11MAC_880MHZ_CLK_DIVISOR_MASK (0xFF << DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT) ++#define DOT11MAC_880MHZ_CLK_DIVISOR_VAL (0xE << DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT) ++ ++ ++#define PMU1_PLL0_PLLCTL2 2 ++#define PMU1_PLL0_PC2_M5DIV_MASK 0x000000ff ++#define PMU1_PLL0_PC2_M5DIV_SHIFT 0 ++#define PMU1_PLL0_PC2_M5DIV_BY_12 0xc ++#define PMU1_PLL0_PC2_M5DIV_BY_18 0x12 ++#define PMU1_PLL0_PC2_M5DIV_BY_36 0x24 ++#define PMU1_PLL0_PC2_M6DIV_MASK 0x0000ff00 ++#define PMU1_PLL0_PC2_M6DIV_SHIFT 8 ++#define PMU1_PLL0_PC2_M6DIV_BY_18 0x12 ++#define PMU1_PLL0_PC2_M6DIV_BY_36 0x24 ++#define PMU1_PLL0_PC2_NDIV_MODE_MASK 0x000e0000 ++#define PMU1_PLL0_PC2_NDIV_MODE_SHIFT 17 ++#define PMU1_PLL0_PC2_NDIV_MODE_MASH 1 ++#define PMU1_PLL0_PC2_NDIV_MODE_MFB 2 ++#define PMU1_PLL0_PC2_NDIV_INT_MASK 0x1ff00000 ++#define PMU1_PLL0_PC2_NDIV_INT_SHIFT 20 ++ ++ ++#define PMU1_PLL0_PLLCTL3 3 ++#define PMU1_PLL0_PC3_NDIV_FRAC_MASK 0x00ffffff ++#define PMU1_PLL0_PC3_NDIV_FRAC_SHIFT 0 ++ ++ ++#define PMU1_PLL0_PLLCTL4 4 ++ ++ ++#define PMU1_PLL0_PLLCTL5 5 ++#define PMU1_PLL0_PC5_CLK_DRV_MASK 0xffffff00 ++#define PMU1_PLL0_PC5_CLK_DRV_SHIFT 8 ++ ++ ++#define PMU2_PHY_PLL_PLLCTL 4 ++#define PMU2_SI_PLL_PLLCTL 10 ++ ++ ++ ++ ++#define PMU2_PLL_PLLCTL0 0 ++#define PMU2_PLL_PC0_P1DIV_MASK 0x00f00000 ++#define PMU2_PLL_PC0_P1DIV_SHIFT 20 ++#define PMU2_PLL_PC0_P2DIV_MASK 0x0f000000 ++#define PMU2_PLL_PC0_P2DIV_SHIFT 24 ++ ++ ++#define PMU2_PLL_PLLCTL1 1 ++#define PMU2_PLL_PC1_M1DIV_MASK 0x000000ff ++#define PMU2_PLL_PC1_M1DIV_SHIFT 0 ++#define PMU2_PLL_PC1_M2DIV_MASK 0x0000ff00 ++#define PMU2_PLL_PC1_M2DIV_SHIFT 8 ++#define PMU2_PLL_PC1_M3DIV_MASK 0x00ff0000 ++#define PMU2_PLL_PC1_M3DIV_SHIFT 16 ++#define PMU2_PLL_PC1_M4DIV_MASK 0xff000000 ++#define PMU2_PLL_PC1_M4DIV_SHIFT 24 ++ ++ ++#define PMU2_PLL_PLLCTL2 2 ++#define PMU2_PLL_PC2_M5DIV_MASK 0x000000ff ++#define PMU2_PLL_PC2_M5DIV_SHIFT 0 ++#define PMU2_PLL_PC2_M6DIV_MASK 0x0000ff00 ++#define PMU2_PLL_PC2_M6DIV_SHIFT 8 ++#define PMU2_PLL_PC2_NDIV_MODE_MASK 0x000e0000 ++#define PMU2_PLL_PC2_NDIV_MODE_SHIFT 17 ++#define PMU2_PLL_PC2_NDIV_INT_MASK 0x1ff00000 ++#define PMU2_PLL_PC2_NDIV_INT_SHIFT 20 ++ ++ ++#define PMU2_PLL_PLLCTL3 3 ++#define PMU2_PLL_PC3_NDIV_FRAC_MASK 0x00ffffff ++#define PMU2_PLL_PC3_NDIV_FRAC_SHIFT 0 ++ ++ ++#define PMU2_PLL_PLLCTL4 4 ++ ++ ++#define PMU2_PLL_PLLCTL5 5 ++#define PMU2_PLL_PC5_CLKDRIVE_CH1_MASK 0x00000f00 ++#define PMU2_PLL_PC5_CLKDRIVE_CH1_SHIFT 8 ++#define PMU2_PLL_PC5_CLKDRIVE_CH2_MASK 0x0000f000 ++#define PMU2_PLL_PC5_CLKDRIVE_CH2_SHIFT 12 ++#define PMU2_PLL_PC5_CLKDRIVE_CH3_MASK 0x000f0000 ++#define PMU2_PLL_PC5_CLKDRIVE_CH3_SHIFT 16 ++#define PMU2_PLL_PC5_CLKDRIVE_CH4_MASK 0x00f00000 ++#define PMU2_PLL_PC5_CLKDRIVE_CH4_SHIFT 20 ++#define PMU2_PLL_PC5_CLKDRIVE_CH5_MASK 0x0f000000 ++#define PMU2_PLL_PC5_CLKDRIVE_CH5_SHIFT 24 ++#define PMU2_PLL_PC5_CLKDRIVE_CH6_MASK 0xf0000000 ++#define PMU2_PLL_PC5_CLKDRIVE_CH6_SHIFT 28 ++ ++ ++#define PMU5_PLL_P1P2_OFF 0 ++#define PMU5_PLL_P1_MASK 0x0f000000 ++#define PMU5_PLL_P1_SHIFT 24 ++#define PMU5_PLL_P2_MASK 0x00f00000 ++#define PMU5_PLL_P2_SHIFT 20 ++#define PMU5_PLL_M14_OFF 1 ++#define PMU5_PLL_MDIV_MASK 0x000000ff ++#define PMU5_PLL_MDIV_WIDTH 8 ++#define PMU5_PLL_NM5_OFF 2 ++#define PMU5_PLL_NDIV_MASK 0xfff00000 ++#define PMU5_PLL_NDIV_SHIFT 20 ++#define PMU5_PLL_NDIV_MODE_MASK 0x000e0000 ++#define PMU5_PLL_NDIV_MODE_SHIFT 17 ++#define PMU5_PLL_FMAB_OFF 3 ++#define PMU5_PLL_MRAT_MASK 0xf0000000 ++#define PMU5_PLL_MRAT_SHIFT 28 ++#define PMU5_PLL_ABRAT_MASK 0x08000000 ++#define PMU5_PLL_ABRAT_SHIFT 27 ++#define PMU5_PLL_FDIV_MASK 0x07ffffff ++#define PMU5_PLL_PLLCTL_OFF 4 ++#define PMU5_PLL_PCHI_OFF 5 ++#define PMU5_PLL_PCHI_MASK 0x0000003f ++ ++ ++#define PMU_XTALFREQ_REG_ILPCTR_MASK 0x00001FFF ++#define PMU_XTALFREQ_REG_MEASURE_MASK 0x80000000 ++#define PMU_XTALFREQ_REG_MEASURE_SHIFT 31 ++ ++ ++#define PMU5_MAINPLL_CPU 1 ++#define PMU5_MAINPLL_MEM 2 ++#define PMU5_MAINPLL_SI 3 ++ ++ ++#define PMU4706_MAINPLL_PLL0 0 ++#define PMU6_4706_PROCPLL_OFF 4 ++#define PMU6_4706_PROC_P2DIV_MASK 0x000f0000 ++#define PMU6_4706_PROC_P2DIV_SHIFT 16 ++#define PMU6_4706_PROC_P1DIV_MASK 0x0000f000 ++#define PMU6_4706_PROC_P1DIV_SHIFT 12 ++#define PMU6_4706_PROC_NDIV_INT_MASK 0x00000ff8 ++#define PMU6_4706_PROC_NDIV_INT_SHIFT 3 ++#define PMU6_4706_PROC_NDIV_MODE_MASK 0x00000007 ++#define PMU6_4706_PROC_NDIV_MODE_SHIFT 0 ++ ++#define PMU7_PLL_PLLCTL7 7 ++#define PMU7_PLL_CTL7_M4DIV_MASK 0xff000000 ++#define PMU7_PLL_CTL7_M4DIV_SHIFT 24 ++#define PMU7_PLL_CTL7_M4DIV_BY_6 6 ++#define PMU7_PLL_CTL7_M4DIV_BY_12 0xc ++#define PMU7_PLL_CTL7_M4DIV_BY_24 0x18 ++#define PMU7_PLL_PLLCTL8 8 ++#define PMU7_PLL_CTL8_M5DIV_MASK 0x000000ff ++#define PMU7_PLL_CTL8_M5DIV_SHIFT 0 ++#define PMU7_PLL_CTL8_M5DIV_BY_8 8 ++#define PMU7_PLL_CTL8_M5DIV_BY_12 0xc ++#define PMU7_PLL_CTL8_M5DIV_BY_24 0x18 ++#define PMU7_PLL_CTL8_M6DIV_MASK 0x0000ff00 ++#define PMU7_PLL_CTL8_M6DIV_SHIFT 8 ++#define PMU7_PLL_CTL8_M6DIV_BY_12 0xc ++#define PMU7_PLL_CTL8_M6DIV_BY_24 0x18 ++#define PMU7_PLL_PLLCTL11 11 ++#define PMU7_PLL_PLLCTL11_MASK 0xffffff00 ++#define PMU7_PLL_PLLCTL11_VAL 0x22222200 ++ ++ ++#define PMU15_PLL_PLLCTL0 0 ++#define PMU15_PLL_PC0_CLKSEL_MASK 0x00000003 ++#define PMU15_PLL_PC0_CLKSEL_SHIFT 0 ++#define PMU15_PLL_PC0_FREQTGT_MASK 0x003FFFFC ++#define PMU15_PLL_PC0_FREQTGT_SHIFT 2 ++#define PMU15_PLL_PC0_PRESCALE_MASK 0x00C00000 ++#define PMU15_PLL_PC0_PRESCALE_SHIFT 22 ++#define PMU15_PLL_PC0_KPCTRL_MASK 0x07000000 ++#define PMU15_PLL_PC0_KPCTRL_SHIFT 24 ++#define PMU15_PLL_PC0_FCNTCTRL_MASK 0x38000000 ++#define PMU15_PLL_PC0_FCNTCTRL_SHIFT 27 ++#define PMU15_PLL_PC0_FDCMODE_MASK 0x40000000 ++#define PMU15_PLL_PC0_FDCMODE_SHIFT 30 ++#define PMU15_PLL_PC0_CTRLBIAS_MASK 0x80000000 ++#define PMU15_PLL_PC0_CTRLBIAS_SHIFT 31 ++ ++#define PMU15_PLL_PLLCTL1 1 ++#define PMU15_PLL_PC1_BIAS_CTLM_MASK 0x00000060 ++#define PMU15_PLL_PC1_BIAS_CTLM_SHIFT 5 ++#define PMU15_PLL_PC1_BIAS_CTLM_RST_MASK 0x00000040 ++#define PMU15_PLL_PC1_BIAS_CTLM_RST_SHIFT 6 ++#define PMU15_PLL_PC1_BIAS_SS_DIVR_MASK 0x0001FF80 ++#define PMU15_PLL_PC1_BIAS_SS_DIVR_SHIFT 7 ++#define PMU15_PLL_PC1_BIAS_SS_RSTVAL_MASK 0x03FE0000 ++#define PMU15_PLL_PC1_BIAS_SS_RSTVAL_SHIFT 17 ++#define PMU15_PLL_PC1_BIAS_INTG_BW_MASK 0x0C000000 ++#define PMU15_PLL_PC1_BIAS_INTG_BW_SHIFT 26 ++#define PMU15_PLL_PC1_BIAS_INTG_BYP_MASK 0x10000000 ++#define PMU15_PLL_PC1_BIAS_INTG_BYP_SHIFT 28 ++#define PMU15_PLL_PC1_OPENLP_EN_MASK 0x40000000 ++#define PMU15_PLL_PC1_OPENLP_EN_SHIFT 30 ++ ++#define PMU15_PLL_PLLCTL2 2 ++#define PMU15_PLL_PC2_CTEN_MASK 0x00000001 ++#define PMU15_PLL_PC2_CTEN_SHIFT 0 ++ ++#define PMU15_PLL_PLLCTL3 3 ++#define PMU15_PLL_PC3_DITHER_EN_MASK 0x00000001 ++#define PMU15_PLL_PC3_DITHER_EN_SHIFT 0 ++#define PMU15_PLL_PC3_DCOCTLSP_MASK 0xFE000000 ++#define PMU15_PLL_PC3_DCOCTLSP_SHIFT 25 ++#define PMU15_PLL_PC3_DCOCTLSP_DIV2EN_MASK 0x01 ++#define PMU15_PLL_PC3_DCOCTLSP_DIV2EN_SHIFT 0 ++#define PMU15_PLL_PC3_DCOCTLSP_CH0EN_MASK 0x02 ++#define PMU15_PLL_PC3_DCOCTLSP_CH0EN_SHIFT 1 ++#define PMU15_PLL_PC3_DCOCTLSP_CH1EN_MASK 0x04 ++#define PMU15_PLL_PC3_DCOCTLSP_CH1EN_SHIFT 2 ++#define PMU15_PLL_PC3_DCOCTLSP_CH0SEL_MASK 0x18 ++#define PMU15_PLL_PC3_DCOCTLSP_CH0SEL_SHIFT 3 ++#define PMU15_PLL_PC3_DCOCTLSP_CH1SEL_MASK 0x60 ++#define PMU15_PLL_PC3_DCOCTLSP_CH1SEL_SHIFT 5 ++#define PMU15_PLL_PC3_DCOCTLSP_CHSEL_OUTP_DIV1 0 ++#define PMU15_PLL_PC3_DCOCTLSP_CHSEL_OUTP_DIV2 1 ++#define PMU15_PLL_PC3_DCOCTLSP_CHSEL_OUTP_DIV3 2 ++#define PMU15_PLL_PC3_DCOCTLSP_CHSEL_OUTP_DIV5 3 ++ ++#define PMU15_PLL_PLLCTL4 4 ++#define PMU15_PLL_PC4_FLLCLK1_DIV_MASK 0x00000007 ++#define PMU15_PLL_PC4_FLLCLK1_DIV_SHIFT 0 ++#define PMU15_PLL_PC4_FLLCLK2_DIV_MASK 0x00000038 ++#define PMU15_PLL_PC4_FLLCLK2_DIV_SHIFT 3 ++#define PMU15_PLL_PC4_FLLCLK3_DIV_MASK 0x000001C0 ++#define PMU15_PLL_PC4_FLLCLK3_DIV_SHIFT 6 ++#define PMU15_PLL_PC4_DBGMODE_MASK 0x00000E00 ++#define PMU15_PLL_PC4_DBGMODE_SHIFT 9 ++#define PMU15_PLL_PC4_FLL480_CTLSP_LK_MASK 0x00001000 ++#define PMU15_PLL_PC4_FLL480_CTLSP_LK_SHIFT 12 ++#define PMU15_PLL_PC4_FLL480_CTLSP_MASK 0x000FE000 ++#define PMU15_PLL_PC4_FLL480_CTLSP_SHIFT 13 ++#define PMU15_PLL_PC4_DINPOL_MASK 0x00100000 ++#define PMU15_PLL_PC4_DINPOL_SHIFT 20 ++#define PMU15_PLL_PC4_CLKOUT_PD_MASK 0x00200000 ++#define PMU15_PLL_PC4_CLKOUT_PD_SHIFT 21 ++#define PMU15_PLL_PC4_CLKDIV2_PD_MASK 0x00400000 ++#define PMU15_PLL_PC4_CLKDIV2_PD_SHIFT 22 ++#define PMU15_PLL_PC4_CLKDIV4_PD_MASK 0x00800000 ++#define PMU15_PLL_PC4_CLKDIV4_PD_SHIFT 23 ++#define PMU15_PLL_PC4_CLKDIV8_PD_MASK 0x01000000 ++#define PMU15_PLL_PC4_CLKDIV8_PD_SHIFT 24 ++#define PMU15_PLL_PC4_CLKDIV16_PD_MASK 0x02000000 ++#define PMU15_PLL_PC4_CLKDIV16_PD_SHIFT 25 ++#define PMU15_PLL_PC4_TEST_EN_MASK 0x04000000 ++#define PMU15_PLL_PC4_TEST_EN_SHIFT 26 ++ ++#define PMU15_PLL_PLLCTL5 5 ++#define PMU15_PLL_PC5_FREQTGT_MASK 0x000FFFFF ++#define PMU15_PLL_PC5_FREQTGT_SHIFT 0 ++#define PMU15_PLL_PC5_DCOCTLSP_MASK 0x07F00000 ++#define PMU15_PLL_PC5_DCOCTLSP_SHIFT 20 ++#define PMU15_PLL_PC5_PRESCALE_MASK 0x18000000 ++#define PMU15_PLL_PC5_PRESCALE_SHIFT 27 ++ ++#define PMU15_PLL_PLLCTL6 6 ++#define PMU15_PLL_PC6_FREQTGT_MASK 0x000FFFFF ++#define PMU15_PLL_PC6_FREQTGT_SHIFT 0 ++#define PMU15_PLL_PC6_DCOCTLSP_MASK 0x07F00000 ++#define PMU15_PLL_PC6_DCOCTLSP_SHIFT 20 ++#define PMU15_PLL_PC6_PRESCALE_MASK 0x18000000 ++#define PMU15_PLL_PC6_PRESCALE_SHIFT 27 ++ ++#define PMU15_FREQTGT_480_DEFAULT 0x19AB1 ++#define PMU15_FREQTGT_492_DEFAULT 0x1A4F5 ++#define PMU15_ARM_96MHZ 96000000 ++#define PMU15_ARM_98MHZ 98400000 ++#define PMU15_ARM_97MHZ 97000000 ++ ++ ++#define PMU17_PLLCTL2_NDIVTYPE_MASK 0x00000070 ++#define PMU17_PLLCTL2_NDIVTYPE_SHIFT 4 ++ ++#define PMU17_PLLCTL2_NDIV_MODE_INT 0 ++#define PMU17_PLLCTL2_NDIV_MODE_INT1B8 1 ++#define PMU17_PLLCTL2_NDIV_MODE_MASH111 2 ++#define PMU17_PLLCTL2_NDIV_MODE_MASH111B8 3 ++ ++#define PMU17_PLLCTL0_BBPLL_PWRDWN 0 ++#define PMU17_PLLCTL0_BBPLL_DRST 3 ++#define PMU17_PLLCTL0_BBPLL_DISBL_CLK 8 ++ ++ ++#define PMU4716_MAINPLL_PLL0 12 ++ ++ ++#define PMU5356_MAINPLL_PLL0 0 ++#define PMU5357_MAINPLL_PLL0 0 ++ ++ ++#define RES4716_PROC_PLL_ON 0x00000040 ++#define RES4716_PROC_HT_AVAIL 0x00000080 ++ ++ ++#define CCTRL_471X_I2S_PINS_ENABLE 0x0080 ++ ++ ++ ++#define CCTRL_5357_I2S_PINS_ENABLE 0x00040000 ++#define CCTRL_5357_I2CSPI_PINS_ENABLE 0x00080000 ++ ++ ++#define RES5354_EXT_SWITCHER_PWM 0 ++#define RES5354_BB_SWITCHER_PWM 1 ++#define RES5354_BB_SWITCHER_BURST 2 ++#define RES5354_BB_EXT_SWITCHER_BURST 3 ++#define RES5354_ILP_REQUEST 4 ++#define RES5354_RADIO_SWITCHER_PWM 5 ++#define RES5354_RADIO_SWITCHER_BURST 6 ++#define RES5354_ROM_SWITCH 7 ++#define RES5354_PA_REF_LDO 8 ++#define RES5354_RADIO_LDO 9 ++#define RES5354_AFE_LDO 10 ++#define RES5354_PLL_LDO 11 ++#define RES5354_BG_FILTBYP 12 ++#define RES5354_TX_FILTBYP 13 ++#define RES5354_RX_FILTBYP 14 ++#define RES5354_XTAL_PU 15 ++#define RES5354_XTAL_EN 16 ++#define RES5354_BB_PLL_FILTBYP 17 ++#define RES5354_RF_PLL_FILTBYP 18 ++#define RES5354_BB_PLL_PU 19 ++ ++ ++#define CCTRL5357_EXTPA (1<<14) ++#define CCTRL5357_ANT_MUX_2o3 (1<<15) ++#define CCTRL5357_NFLASH (1<<16) ++ ++ ++#define CCTRL43217_EXTPA_C0 (1<<13) ++#define CCTRL43217_EXTPA_C1 (1<<8) ++ ++ ++#define RES4328_EXT_SWITCHER_PWM 0 ++#define RES4328_BB_SWITCHER_PWM 1 ++#define RES4328_BB_SWITCHER_BURST 2 ++#define RES4328_BB_EXT_SWITCHER_BURST 3 ++#define RES4328_ILP_REQUEST 4 ++#define RES4328_RADIO_SWITCHER_PWM 5 ++#define RES4328_RADIO_SWITCHER_BURST 6 ++#define RES4328_ROM_SWITCH 7 ++#define RES4328_PA_REF_LDO 8 ++#define RES4328_RADIO_LDO 9 ++#define RES4328_AFE_LDO 10 ++#define RES4328_PLL_LDO 11 ++#define RES4328_BG_FILTBYP 12 ++#define RES4328_TX_FILTBYP 13 ++#define RES4328_RX_FILTBYP 14 ++#define RES4328_XTAL_PU 15 ++#define RES4328_XTAL_EN 16 ++#define RES4328_BB_PLL_FILTBYP 17 ++#define RES4328_RF_PLL_FILTBYP 18 ++#define RES4328_BB_PLL_PU 19 ++ ++ ++#define RES4325_BUCK_BOOST_BURST 0 ++#define RES4325_CBUCK_BURST 1 ++#define RES4325_CBUCK_PWM 2 ++#define RES4325_CLDO_CBUCK_BURST 3 ++#define RES4325_CLDO_CBUCK_PWM 4 ++#define RES4325_BUCK_BOOST_PWM 5 ++#define RES4325_ILP_REQUEST 6 ++#define RES4325_ABUCK_BURST 7 ++#define RES4325_ABUCK_PWM 8 ++#define RES4325_LNLDO1_PU 9 ++#define RES4325_OTP_PU 10 ++#define RES4325_LNLDO3_PU 11 ++#define RES4325_LNLDO4_PU 12 ++#define RES4325_XTAL_PU 13 ++#define RES4325_ALP_AVAIL 14 ++#define RES4325_RX_PWRSW_PU 15 ++#define RES4325_TX_PWRSW_PU 16 ++#define RES4325_RFPLL_PWRSW_PU 17 ++#define RES4325_LOGEN_PWRSW_PU 18 ++#define RES4325_AFE_PWRSW_PU 19 ++#define RES4325_BBPLL_PWRSW_PU 20 ++#define RES4325_HT_AVAIL 21 ++ ++ ++#define RES4325B0_CBUCK_LPOM 1 ++#define RES4325B0_CBUCK_BURST 2 ++#define RES4325B0_CBUCK_PWM 3 ++#define RES4325B0_CLDO_PU 4 ++ ++ ++#define RES4325C1_LNLDO2_PU 12 ++ ++ ++#define CST4325_SPROM_OTP_SEL_MASK 0x00000003 ++#define CST4325_DEFCIS_SEL 0 ++#define CST4325_SPROM_SEL 1 ++#define CST4325_OTP_SEL 2 ++#define CST4325_OTP_PWRDN 3 ++#define CST4325_SDIO_USB_MODE_MASK 0x00000004 ++#define CST4325_SDIO_USB_MODE_SHIFT 2 ++#define CST4325_RCAL_VALID_MASK 0x00000008 ++#define CST4325_RCAL_VALID_SHIFT 3 ++#define CST4325_RCAL_VALUE_MASK 0x000001f0 ++#define CST4325_RCAL_VALUE_SHIFT 4 ++#define CST4325_PMUTOP_2B_MASK 0x00000200 ++#define CST4325_PMUTOP_2B_SHIFT 9 ++ ++#define RES4329_RESERVED0 0 ++#define RES4329_CBUCK_LPOM 1 ++#define RES4329_CBUCK_BURST 2 ++#define RES4329_CBUCK_PWM 3 ++#define RES4329_CLDO_PU 4 ++#define RES4329_PALDO_PU 5 ++#define RES4329_ILP_REQUEST 6 ++#define RES4329_RESERVED7 7 ++#define RES4329_RESERVED8 8 ++#define RES4329_LNLDO1_PU 9 ++#define RES4329_OTP_PU 10 ++#define RES4329_RESERVED11 11 ++#define RES4329_LNLDO2_PU 12 ++#define RES4329_XTAL_PU 13 ++#define RES4329_ALP_AVAIL 14 ++#define RES4329_RX_PWRSW_PU 15 ++#define RES4329_TX_PWRSW_PU 16 ++#define RES4329_RFPLL_PWRSW_PU 17 ++#define RES4329_LOGEN_PWRSW_PU 18 ++#define RES4329_AFE_PWRSW_PU 19 ++#define RES4329_BBPLL_PWRSW_PU 20 ++#define RES4329_HT_AVAIL 21 ++ ++#define CST4329_SPROM_OTP_SEL_MASK 0x00000003 ++#define CST4329_DEFCIS_SEL 0 ++#define CST4329_SPROM_SEL 1 ++#define CST4329_OTP_SEL 2 ++#define CST4329_OTP_PWRDN 3 ++#define CST4329_SPI_SDIO_MODE_MASK 0x00000004 ++#define CST4329_SPI_SDIO_MODE_SHIFT 2 ++ ++ ++#define CST4312_SPROM_OTP_SEL_MASK 0x00000003 ++#define CST4312_DEFCIS_SEL 0 ++#define CST4312_SPROM_SEL 1 ++#define CST4312_OTP_SEL 2 ++#define CST4312_OTP_BAD 3 ++ ++ ++#define RES4312_SWITCHER_BURST 0 ++#define RES4312_SWITCHER_PWM 1 ++#define RES4312_PA_REF_LDO 2 ++#define RES4312_CORE_LDO_BURST 3 ++#define RES4312_CORE_LDO_PWM 4 ++#define RES4312_RADIO_LDO 5 ++#define RES4312_ILP_REQUEST 6 ++#define RES4312_BG_FILTBYP 7 ++#define RES4312_TX_FILTBYP 8 ++#define RES4312_RX_FILTBYP 9 ++#define RES4312_XTAL_PU 10 ++#define RES4312_ALP_AVAIL 11 ++#define RES4312_BB_PLL_FILTBYP 12 ++#define RES4312_RF_PLL_FILTBYP 13 ++#define RES4312_HT_AVAIL 14 ++ ++ ++#define RES4322_RF_LDO 0 ++#define RES4322_ILP_REQUEST 1 ++#define RES4322_XTAL_PU 2 ++#define RES4322_ALP_AVAIL 3 ++#define RES4322_SI_PLL_ON 4 ++#define RES4322_HT_SI_AVAIL 5 ++#define RES4322_PHY_PLL_ON 6 ++#define RES4322_HT_PHY_AVAIL 7 ++#define RES4322_OTP_PU 8 ++ ++ ++#define CST4322_XTAL_FREQ_20_40MHZ 0x00000020 ++#define CST4322_SPROM_OTP_SEL_MASK 0x000000c0 ++#define CST4322_SPROM_OTP_SEL_SHIFT 6 ++#define CST4322_NO_SPROM_OTP 0 ++#define CST4322_SPROM_PRESENT 1 ++#define CST4322_OTP_PRESENT 2 ++#define CST4322_PCI_OR_USB 0x00000100 ++#define CST4322_BOOT_MASK 0x00000600 ++#define CST4322_BOOT_SHIFT 9 ++#define CST4322_BOOT_FROM_SRAM 0 ++#define CST4322_BOOT_FROM_ROM 1 ++#define CST4322_BOOT_FROM_FLASH 2 ++#define CST4322_BOOT_FROM_INVALID 3 ++#define CST4322_ILP_DIV_EN 0x00000800 ++#define CST4322_FLASH_TYPE_MASK 0x00001000 ++#define CST4322_FLASH_TYPE_SHIFT 12 ++#define CST4322_FLASH_TYPE_SHIFT_ST 0 ++#define CST4322_FLASH_TYPE_SHIFT_ATMEL 1 ++#define CST4322_ARM_TAP_SEL 0x00002000 ++#define CST4322_RES_INIT_MODE_MASK 0x0000c000 ++#define CST4322_RES_INIT_MODE_SHIFT 14 ++#define CST4322_RES_INIT_MODE_ILPAVAIL 0 ++#define CST4322_RES_INIT_MODE_ILPREQ 1 ++#define CST4322_RES_INIT_MODE_ALPAVAIL 2 ++#define CST4322_RES_INIT_MODE_HTAVAIL 3 ++#define CST4322_PCIPLLCLK_GATING 0x00010000 ++#define CST4322_CLK_SWITCH_PCI_TO_ALP 0x00020000 ++#define CST4322_PCI_CARDBUS_MODE 0x00040000 ++ ++ ++#define CCTRL43224_GPIO_TOGGLE 0x8000 ++#define CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 ++#define CCTRL_43224B0_12MA_LED_DRIVE 0xF0 ++ ++ ++#define RES43236_REGULATOR 0 ++#define RES43236_ILP_REQUEST 1 ++#define RES43236_XTAL_PU 2 ++#define RES43236_ALP_AVAIL 3 ++#define RES43236_SI_PLL_ON 4 ++#define RES43236_HT_SI_AVAIL 5 ++ ++ ++#define CCTRL43236_BT_COEXIST (1<<0) ++#define CCTRL43236_SECI (1<<1) ++#define CCTRL43236_EXT_LNA (1<<2) ++#define CCTRL43236_ANT_MUX_2o3 (1<<3) ++#define CCTRL43236_GSIO (1<<4) ++ ++ ++#define CST43236_SFLASH_MASK 0x00000040 ++#define CST43236_OTP_SEL_MASK 0x00000080 ++#define CST43236_OTP_SEL_SHIFT 7 ++#define CST43236_HSIC_MASK 0x00000100 ++#define CST43236_BP_CLK 0x00000200 ++#define CST43236_BOOT_MASK 0x00001800 ++#define CST43236_BOOT_SHIFT 11 ++#define CST43236_BOOT_FROM_SRAM 0 ++#define CST43236_BOOT_FROM_ROM 1 ++#define CST43236_BOOT_FROM_FLASH 2 ++#define CST43236_BOOT_FROM_INVALID 3 ++ ++ ++#define RES43237_REGULATOR 0 ++#define RES43237_ILP_REQUEST 1 ++#define RES43237_XTAL_PU 2 ++#define RES43237_ALP_AVAIL 3 ++#define RES43237_SI_PLL_ON 4 ++#define RES43237_HT_SI_AVAIL 5 ++ ++ ++#define CCTRL43237_BT_COEXIST (1<<0) ++#define CCTRL43237_SECI (1<<1) ++#define CCTRL43237_EXT_LNA (1<<2) ++#define CCTRL43237_ANT_MUX_2o3 (1<<3) ++#define CCTRL43237_GSIO (1<<4) ++ ++ ++#define CST43237_SFLASH_MASK 0x00000040 ++#define CST43237_OTP_SEL_MASK 0x00000080 ++#define CST43237_OTP_SEL_SHIFT 7 ++#define CST43237_HSIC_MASK 0x00000100 ++#define CST43237_BP_CLK 0x00000200 ++#define CST43237_BOOT_MASK 0x00001800 ++#define CST43237_BOOT_SHIFT 11 ++#define CST43237_BOOT_FROM_SRAM 0 ++#define CST43237_BOOT_FROM_ROM 1 ++#define CST43237_BOOT_FROM_FLASH 2 ++#define CST43237_BOOT_FROM_INVALID 3 ++ ++ ++#define RES43239_OTP_PU 9 ++#define RES43239_MACPHY_CLKAVAIL 23 ++#define RES43239_HT_AVAIL 24 ++ ++ ++#define CST43239_SPROM_MASK 0x00000002 ++#define CST43239_SFLASH_MASK 0x00000004 ++#define CST43239_RES_INIT_MODE_SHIFT 7 ++#define CST43239_RES_INIT_MODE_MASK 0x000001f0 ++#define CST43239_CHIPMODE_SDIOD(cs) ((cs) & (1 << 15)) ++#define CST43239_CHIPMODE_USB20D(cs) (~(cs) & (1 << 15)) ++#define CST43239_CHIPMODE_SDIO(cs) (((cs) & (1 << 0)) == 0) ++#define CST43239_CHIPMODE_GSPI(cs) (((cs) & (1 << 0)) == (1 << 0)) ++ ++ ++#define RES4324_OTP_PU 10 ++#define RES4324_HT_AVAIL 29 ++#define RES4324_MACPHY_CLKAVAIL 30 ++ ++ ++#define CST4324_SPROM_MASK 0x00000080 ++#define CST4324_SFLASH_MASK 0x00400000 ++#define CST4324_RES_INIT_MODE_SHIFT 10 ++#define CST4324_RES_INIT_MODE_MASK 0x00000c00 ++#define CST4324_CHIPMODE_MASK 0x7 ++#define CST4324_CHIPMODE_SDIOD(cs) ((~(cs)) & (1 << 2)) ++#define CST4324_CHIPMODE_USB20D(cs) (((cs) & CST4324_CHIPMODE_MASK) == 0x6) ++ ++ ++#define RES4331_REGULATOR 0 ++#define RES4331_ILP_REQUEST 1 ++#define RES4331_XTAL_PU 2 ++#define RES4331_ALP_AVAIL 3 ++#define RES4331_SI_PLL_ON 4 ++#define RES4331_HT_SI_AVAIL 5 ++ ++ ++#define CCTRL4331_BT_COEXIST (1<<0) ++#define CCTRL4331_SECI (1<<1) ++#define CCTRL4331_EXT_LNA_G (1<<2) ++#define CCTRL4331_SPROM_GPIO13_15 (1<<3) ++#define CCTRL4331_EXTPA_EN (1<<4) ++#define CCTRL4331_GPIOCLK_ON_SPROMCS (1<<5) ++#define CCTRL4331_PCIE_MDIO_ON_SPROMCS (1<<6) ++#define CCTRL4331_EXTPA_ON_GPIO2_5 (1<<7) ++#define CCTRL4331_OVR_PIPEAUXCLKEN (1<<8) ++#define CCTRL4331_OVR_PIPEAUXPWRDOWN (1<<9) ++#define CCTRL4331_PCIE_AUXCLKEN (1<<10) ++#define CCTRL4331_PCIE_PIPE_PLLDOWN (1<<11) ++#define CCTRL4331_EXTPA_EN2 (1<<12) ++#define CCTRL4331_EXT_LNA_A (1<<13) ++#define CCTRL4331_BT_SHD0_ON_GPIO4 (1<<16) ++#define CCTRL4331_BT_SHD1_ON_GPIO5 (1<<17) ++#define CCTRL4331_EXTPA_ANA_EN (1<<24) ++ ++ ++#define CST4331_XTAL_FREQ 0x00000001 ++#define CST4331_SPROM_OTP_SEL_MASK 0x00000006 ++#define CST4331_SPROM_OTP_SEL_SHIFT 1 ++#define CST4331_SPROM_PRESENT 0x00000002 ++#define CST4331_OTP_PRESENT 0x00000004 ++#define CST4331_LDO_RF 0x00000008 ++#define CST4331_LDO_PAR 0x00000010 ++ ++ ++#define RES4315_CBUCK_LPOM 1 ++#define RES4315_CBUCK_BURST 2 ++#define RES4315_CBUCK_PWM 3 ++#define RES4315_CLDO_PU 4 ++#define RES4315_PALDO_PU 5 ++#define RES4315_ILP_REQUEST 6 ++#define RES4315_LNLDO1_PU 9 ++#define RES4315_OTP_PU 10 ++#define RES4315_LNLDO2_PU 12 ++#define RES4315_XTAL_PU 13 ++#define RES4315_ALP_AVAIL 14 ++#define RES4315_RX_PWRSW_PU 15 ++#define RES4315_TX_PWRSW_PU 16 ++#define RES4315_RFPLL_PWRSW_PU 17 ++#define RES4315_LOGEN_PWRSW_PU 18 ++#define RES4315_AFE_PWRSW_PU 19 ++#define RES4315_BBPLL_PWRSW_PU 20 ++#define RES4315_HT_AVAIL 21 ++ ++ ++#define CST4315_SPROM_OTP_SEL_MASK 0x00000003 ++#define CST4315_DEFCIS_SEL 0x00000000 ++#define CST4315_SPROM_SEL 0x00000001 ++#define CST4315_OTP_SEL 0x00000002 ++#define CST4315_OTP_PWRDN 0x00000003 ++#define CST4315_SDIO_MODE 0x00000004 ++#define CST4315_RCAL_VALID 0x00000008 ++#define CST4315_RCAL_VALUE_MASK 0x000001f0 ++#define CST4315_RCAL_VALUE_SHIFT 4 ++#define CST4315_PALDO_EXTPNP 0x00000200 ++#define CST4315_CBUCK_MODE_MASK 0x00000c00 ++#define CST4315_CBUCK_MODE_BURST 0x00000400 ++#define CST4315_CBUCK_MODE_LPBURST 0x00000c00 ++ ++ ++#define RES4319_CBUCK_LPOM 1 ++#define RES4319_CBUCK_BURST 2 ++#define RES4319_CBUCK_PWM 3 ++#define RES4319_CLDO_PU 4 ++#define RES4319_PALDO_PU 5 ++#define RES4319_ILP_REQUEST 6 ++#define RES4319_LNLDO1_PU 9 ++#define RES4319_OTP_PU 10 ++#define RES4319_LNLDO2_PU 12 ++#define RES4319_XTAL_PU 13 ++#define RES4319_ALP_AVAIL 14 ++#define RES4319_RX_PWRSW_PU 15 ++#define RES4319_TX_PWRSW_PU 16 ++#define RES4319_RFPLL_PWRSW_PU 17 ++#define RES4319_LOGEN_PWRSW_PU 18 ++#define RES4319_AFE_PWRSW_PU 19 ++#define RES4319_BBPLL_PWRSW_PU 20 ++#define RES4319_HT_AVAIL 21 ++ ++ ++#define CST4319_SPI_CPULESSUSB 0x00000001 ++#define CST4319_SPI_CLK_POL 0x00000002 ++#define CST4319_SPI_CLK_PH 0x00000008 ++#define CST4319_SPROM_OTP_SEL_MASK 0x000000c0 ++#define CST4319_SPROM_OTP_SEL_SHIFT 6 ++#define CST4319_DEFCIS_SEL 0x00000000 ++#define CST4319_SPROM_SEL 0x00000040 ++#define CST4319_OTP_SEL 0x00000080 ++#define CST4319_OTP_PWRDN 0x000000c0 ++#define CST4319_SDIO_USB_MODE 0x00000100 ++#define CST4319_REMAP_SEL_MASK 0x00000600 ++#define CST4319_ILPDIV_EN 0x00000800 ++#define CST4319_XTAL_PD_POL 0x00001000 ++#define CST4319_LPO_SEL 0x00002000 ++#define CST4319_RES_INIT_MODE 0x0000c000 ++#define CST4319_PALDO_EXTPNP 0x00010000 ++#define CST4319_CBUCK_MODE_MASK 0x00060000 ++#define CST4319_CBUCK_MODE_BURST 0x00020000 ++#define CST4319_CBUCK_MODE_LPBURST 0x00060000 ++#define CST4319_RCAL_VALID 0x01000000 ++#define CST4319_RCAL_VALUE_MASK 0x3e000000 ++#define CST4319_RCAL_VALUE_SHIFT 25 ++ ++#define PMU1_PLL0_CHIPCTL0 0 ++#define PMU1_PLL0_CHIPCTL1 1 ++#define PMU1_PLL0_CHIPCTL2 2 ++#define CCTL_4319USB_XTAL_SEL_MASK 0x00180000 ++#define CCTL_4319USB_XTAL_SEL_SHIFT 19 ++#define CCTL_4319USB_48MHZ_PLL_SEL 1 ++#define CCTL_4319USB_24MHZ_PLL_SEL 2 ++ ++ ++#define RES4336_CBUCK_LPOM 0 ++#define RES4336_CBUCK_BURST 1 ++#define RES4336_CBUCK_LP_PWM 2 ++#define RES4336_CBUCK_PWM 3 ++#define RES4336_CLDO_PU 4 ++#define RES4336_DIS_INT_RESET_PD 5 ++#define RES4336_ILP_REQUEST 6 ++#define RES4336_LNLDO_PU 7 ++#define RES4336_LDO3P3_PU 8 ++#define RES4336_OTP_PU 9 ++#define RES4336_XTAL_PU 10 ++#define RES4336_ALP_AVAIL 11 ++#define RES4336_RADIO_PU 12 ++#define RES4336_BG_PU 13 ++#define RES4336_VREG1p4_PU_PU 14 ++#define RES4336_AFE_PWRSW_PU 15 ++#define RES4336_RX_PWRSW_PU 16 ++#define RES4336_TX_PWRSW_PU 17 ++#define RES4336_BB_PWRSW_PU 18 ++#define RES4336_SYNTH_PWRSW_PU 19 ++#define RES4336_MISC_PWRSW_PU 20 ++#define RES4336_LOGEN_PWRSW_PU 21 ++#define RES4336_BBPLL_PWRSW_PU 22 ++#define RES4336_MACPHY_CLKAVAIL 23 ++#define RES4336_HT_AVAIL 24 ++#define RES4336_RSVD 25 ++ ++ ++#define CST4336_SPI_MODE_MASK 0x00000001 ++#define CST4336_SPROM_PRESENT 0x00000002 ++#define CST4336_OTP_PRESENT 0x00000004 ++#define CST4336_ARMREMAP_0 0x00000008 ++#define CST4336_ILPDIV_EN_MASK 0x00000010 ++#define CST4336_ILPDIV_EN_SHIFT 4 ++#define CST4336_XTAL_PD_POL_MASK 0x00000020 ++#define CST4336_XTAL_PD_POL_SHIFT 5 ++#define CST4336_LPO_SEL_MASK 0x00000040 ++#define CST4336_LPO_SEL_SHIFT 6 ++#define CST4336_RES_INIT_MODE_MASK 0x00000180 ++#define CST4336_RES_INIT_MODE_SHIFT 7 ++#define CST4336_CBUCK_MODE_MASK 0x00000600 ++#define CST4336_CBUCK_MODE_SHIFT 9 ++ ++ ++#define PCTL_4336_SERIAL_ENAB (1 << 24) ++ ++ ++#define RES4330_CBUCK_LPOM 0 ++#define RES4330_CBUCK_BURST 1 ++#define RES4330_CBUCK_LP_PWM 2 ++#define RES4330_CBUCK_PWM 3 ++#define RES4330_CLDO_PU 4 ++#define RES4330_DIS_INT_RESET_PD 5 ++#define RES4330_ILP_REQUEST 6 ++#define RES4330_LNLDO_PU 7 ++#define RES4330_LDO3P3_PU 8 ++#define RES4330_OTP_PU 9 ++#define RES4330_XTAL_PU 10 ++#define RES4330_ALP_AVAIL 11 ++#define RES4330_RADIO_PU 12 ++#define RES4330_BG_PU 13 ++#define RES4330_VREG1p4_PU_PU 14 ++#define RES4330_AFE_PWRSW_PU 15 ++#define RES4330_RX_PWRSW_PU 16 ++#define RES4330_TX_PWRSW_PU 17 ++#define RES4330_BB_PWRSW_PU 18 ++#define RES4330_SYNTH_PWRSW_PU 19 ++#define RES4330_MISC_PWRSW_PU 20 ++#define RES4330_LOGEN_PWRSW_PU 21 ++#define RES4330_BBPLL_PWRSW_PU 22 ++#define RES4330_MACPHY_CLKAVAIL 23 ++#define RES4330_HT_AVAIL 24 ++#define RES4330_5gRX_PWRSW_PU 25 ++#define RES4330_5gTX_PWRSW_PU 26 ++#define RES4330_5g_LOGEN_PWRSW_PU 27 ++ ++ ++#define CST4330_CHIPMODE_SDIOD(cs) (((cs) & 0x7) < 6) ++#define CST4330_CHIPMODE_USB20D(cs) (((cs) & 0x7) >= 6) ++#define CST4330_CHIPMODE_SDIO(cs) (((cs) & 0x4) == 0) ++#define CST4330_CHIPMODE_GSPI(cs) (((cs) & 0x6) == 4) ++#define CST4330_CHIPMODE_USB(cs) (((cs) & 0x7) == 6) ++#define CST4330_CHIPMODE_USBDA(cs) (((cs) & 0x7) == 7) ++#define CST4330_OTP_PRESENT 0x00000010 ++#define CST4330_LPO_AUTODET_EN 0x00000020 ++#define CST4330_ARMREMAP_0 0x00000040 ++#define CST4330_SPROM_PRESENT 0x00000080 ++#define CST4330_ILPDIV_EN 0x00000100 ++#define CST4330_LPO_SEL 0x00000200 ++#define CST4330_RES_INIT_MODE_SHIFT 10 ++#define CST4330_RES_INIT_MODE_MASK 0x00000c00 ++#define CST4330_CBUCK_MODE_SHIFT 12 ++#define CST4330_CBUCK_MODE_MASK 0x00003000 ++#define CST4330_CBUCK_POWER_OK 0x00004000 ++#define CST4330_BB_PLL_LOCKED 0x00008000 ++#define SOCDEVRAM_BP_ADDR 0x1E000000 ++#define SOCDEVRAM_ARM_ADDR 0x00800000 ++ ++ ++#define PCTL_4330_SERIAL_ENAB (1 << 24) ++ ++ ++#define CCTRL_4330_GPIO_SEL 0x00000001 ++#define CCTRL_4330_ERCX_SEL 0x00000002 ++#define CCTRL_4330_SDIO_HOST_WAKE 0x00000004 ++#define CCTRL_4330_JTAG_DISABLE 0x00000008 ++ ++#define PMU_VREG0_ADDR 0 ++#define PMU_VREG0_DISABLE_PULLD_BT_SHIFT 2 ++#define PMU_VREG0_DISABLE_PULLD_WL_SHIFT 3 ++ ++ ++#define RES4334_LPLDO_PU 0 ++#define RES4334_RESET_PULLDN_DIS 1 ++#define RES4334_PMU_BG_PU 2 ++#define RES4334_HSIC_LDO_PU 3 ++#define RES4334_CBUCK_LPOM_PU 4 ++#define RES4334_CBUCK_PFM_PU 5 ++#define RES4334_CLDO_PU 6 ++#define RES4334_LPLDO2_LVM 7 ++#define RES4334_LNLDO_PU 8 ++#define RES4334_LDO3P3_PU 9 ++#define RES4334_OTP_PU 10 ++#define RES4334_XTAL_PU 11 ++#define RES4334_WL_PWRSW_PU 12 ++#define RES4334_LQ_AVAIL 13 ++#define RES4334_LOGIC_RET 14 ++#define RES4334_MEM_SLEEP 15 ++#define RES4334_MACPHY_RET 16 ++#define RES4334_WL_CORE_READY 17 ++#define RES4334_ILP_REQ 18 ++#define RES4334_ALP_AVAIL 19 ++#define RES4334_MISC_PWRSW_PU 20 ++#define RES4334_SYNTH_PWRSW_PU 21 ++#define RES4334_RX_PWRSW_PU 22 ++#define RES4334_RADIO_PU 23 ++#define RES4334_WL_PMU_PU 24 ++#define RES4334_VCO_LDO_PU 25 ++#define RES4334_AFE_LDO_PU 26 ++#define RES4334_RX_LDO_PU 27 ++#define RES4334_TX_LDO_PU 28 ++#define RES4334_HT_AVAIL 29 ++#define RES4334_MACPHY_CLK_AVAIL 30 ++ ++ ++#define CST4334_CHIPMODE_MASK 7 ++#define CST4334_SDIO_MODE 0x00000000 ++#define CST4334_SPI_MODE 0x00000004 ++#define CST4334_HSIC_MODE 0x00000006 ++#define CST4334_BLUSB_MODE 0x00000007 ++#define CST4334_CHIPMODE_HSIC(cs) (((cs) & CST4334_CHIPMODE_MASK) == CST4334_HSIC_MODE) ++#define CST4334_OTP_PRESENT 0x00000010 ++#define CST4334_LPO_AUTODET_EN 0x00000020 ++#define CST4334_ARMREMAP_0 0x00000040 ++#define CST4334_SPROM_PRESENT 0x00000080 ++#define CST4334_ILPDIV_EN_MASK 0x00000100 ++#define CST4334_ILPDIV_EN_SHIFT 8 ++#define CST4334_LPO_SEL_MASK 0x00000200 ++#define CST4334_LPO_SEL_SHIFT 9 ++#define CST4334_RES_INIT_MODE_MASK 0x00000C00 ++#define CST4334_RES_INIT_MODE_SHIFT 10 ++ ++ ++#define PCTL_4334_GPIO3_ENAB (1 << 3) ++ ++ ++#define CCTRL4334_HSIC_LDO_PU (1 << 23) ++ ++ ++#define CCTRL1_4324_GPIO_SEL (1 << 0) ++#define CCTRL1_4324_SDIO_HOST_WAKE (1 << 2) ++ ++ ++ ++#define RES4313_BB_PU_RSRC 0 ++#define RES4313_ILP_REQ_RSRC 1 ++#define RES4313_XTAL_PU_RSRC 2 ++#define RES4313_ALP_AVAIL_RSRC 3 ++#define RES4313_RADIO_PU_RSRC 4 ++#define RES4313_BG_PU_RSRC 5 ++#define RES4313_VREG1P4_PU_RSRC 6 ++#define RES4313_AFE_PWRSW_RSRC 7 ++#define RES4313_RX_PWRSW_RSRC 8 ++#define RES4313_TX_PWRSW_RSRC 9 ++#define RES4313_BB_PWRSW_RSRC 10 ++#define RES4313_SYNTH_PWRSW_RSRC 11 ++#define RES4313_MISC_PWRSW_RSRC 12 ++#define RES4313_BB_PLL_PWRSW_RSRC 13 ++#define RES4313_HT_AVAIL_RSRC 14 ++#define RES4313_MACPHY_CLK_AVAIL_RSRC 15 ++ ++ ++#define CST4313_SPROM_PRESENT 1 ++#define CST4313_OTP_PRESENT 2 ++#define CST4313_SPROM_OTP_SEL_MASK 0x00000002 ++#define CST4313_SPROM_OTP_SEL_SHIFT 0 ++ ++ ++#define CCTRL_4313_12MA_LED_DRIVE 0x00000007 ++ ++ ++#define RES4314_LPLDO_PU 0 ++#define RES4314_PMU_SLEEP_DIS 1 ++#define RES4314_PMU_BG_PU 2 ++#define RES4314_CBUCK_LPOM_PU 3 ++#define RES4314_CBUCK_PFM_PU 4 ++#define RES4314_CLDO_PU 5 ++#define RES4314_LPLDO2_LVM 6 ++#define RES4314_WL_PMU_PU 7 ++#define RES4314_LNLDO_PU 8 ++#define RES4314_LDO3P3_PU 9 ++#define RES4314_OTP_PU 10 ++#define RES4314_XTAL_PU 11 ++#define RES4314_WL_PWRSW_PU 12 ++#define RES4314_LQ_AVAIL 13 ++#define RES4314_LOGIC_RET 14 ++#define RES4314_MEM_SLEEP 15 ++#define RES4314_MACPHY_RET 16 ++#define RES4314_WL_CORE_READY 17 ++#define RES4314_ILP_REQ 18 ++#define RES4314_ALP_AVAIL 19 ++#define RES4314_MISC_PWRSW_PU 20 ++#define RES4314_SYNTH_PWRSW_PU 21 ++#define RES4314_RX_PWRSW_PU 22 ++#define RES4314_RADIO_PU 23 ++#define RES4314_VCO_LDO_PU 24 ++#define RES4314_AFE_LDO_PU 25 ++#define RES4314_RX_LDO_PU 26 ++#define RES4314_TX_LDO_PU 27 ++#define RES4314_HT_AVAIL 28 ++#define RES4314_MACPHY_CLK_AVAIL 29 ++ ++ ++#define CST4314_OTP_ENABLED 0x00200000 ++ ++ ++#define RES43228_NOT_USED 0 ++#define RES43228_ILP_REQUEST 1 ++#define RES43228_XTAL_PU 2 ++#define RES43228_ALP_AVAIL 3 ++#define RES43228_PLL_EN 4 ++#define RES43228_HT_PHY_AVAIL 5 ++ ++ ++#define CST43228_ILP_DIV_EN 0x1 ++#define CST43228_OTP_PRESENT 0x2 ++#define CST43228_SERDES_REFCLK_PADSEL 0x4 ++#define CST43228_SDIO_MODE 0x8 ++#define CST43228_SDIO_OTP_PRESENT 0x10 ++#define CST43228_SDIO_RESET 0x20 ++ ++ ++#define CST4706_PKG_OPTION (1<<0) ++#define CST4706_SFLASH_PRESENT (1<<1) ++#define CST4706_SFLASH_TYPE (1<<2) ++#define CST4706_MIPS_BENDIAN (1<<3) ++#define CST4706_PCIE1_DISABLE (1<<5) ++ ++ ++#define FLSTRCF4706_MASK 0x000000ff ++#define FLSTRCF4706_SF1 0x00000001 ++#define FLSTRCF4706_PF1 0x00000002 ++#define FLSTRCF4706_SF1_TYPE 0x00000004 ++#define FLSTRCF4706_NF1 0x00000008 ++#define FLSTRCF4706_1ST_MADDR_SEG_MASK 0x000000f0 ++#define FLSTRCF4706_1ST_MADDR_SEG_4MB 0x00000010 ++#define FLSTRCF4706_1ST_MADDR_SEG_8MB 0x00000020 ++#define FLSTRCF4706_1ST_MADDR_SEG_16MB 0x00000030 ++#define FLSTRCF4706_1ST_MADDR_SEG_32MB 0x00000040 ++#define FLSTRCF4706_1ST_MADDR_SEG_64MB 0x00000050 ++#define FLSTRCF4706_1ST_MADDR_SEG_128MB 0x00000060 ++#define FLSTRCF4706_1ST_MADDR_SEG_256MB 0x00000070 ++ ++ ++#define CCTRL4360_SECI_MODE (1 << 2) ++#define CCTRL4360_BTSWCTRL_MODE (1 << 3) ++#define CCTRL4360_EXTRA_FEMCTRL_MODE (1 << 8) ++#define CCTRL4360_BT_LGCY_MODE (1 << 9) ++#define CCTRL4360_CORE2FEMCTRL4_ON (1 << 21) ++ ++ ++#define RES4360_REGULATOR 0 ++#define RES4360_ILP_AVAIL 1 ++#define RES4360_ILP_REQ 2 ++#define RES4360_XTAL_LDO_PU 3 ++#define RES4360_XTAL_PU 4 ++#define RES4360_ALP_AVAIL 5 ++#define RES4360_BBPLLPWRSW_PU 6 ++#define RES4360_HT_AVAIL 7 ++#define RES4360_OTP_PU 8 ++ ++#define CST4360_XTAL_40MZ 0x00000001 ++#define CST4360_SFLASH 0x00000002 ++#define CST4360_SPROM_PRESENT 0x00000004 ++#define CST4360_SFLASH_TYPE 0x00000004 ++#define CST4360_OTP_ENABLED 0x00000008 ++#define CST4360_REMAP_ROM 0x00000010 ++#define CST4360_RSRC_INIT_MODE_MASK 0x00000060 ++#define CST4360_RSRC_INIT_MODE_SHIFT 5 ++#define CST4360_ILP_DIVEN 0x00000080 ++#define CST4360_MODE_USB 0x00000100 ++#define CST4360_SPROM_SIZE_MASK 0x00000600 ++#define CST4360_SPROM_SIZE_SHIFT 9 ++#define CST4360_BBPLL_LOCK 0x00000800 ++#define CST4360_AVBBPLL_LOCK 0x00001000 ++#define CST4360_USBBBPLL_LOCK 0x00002000 ++ ++#define CCTRL_4360_UART_SEL 0x2 ++ ++ ++#define RES4335_LPLDO_PO 0 ++#define RES4335_PMU_BG_PU 1 ++#define RES4335_PMU_SLEEP 2 ++#define RES4335_RSVD_3 3 ++#define RES4335_CBUCK_LPOM_PU 4 ++#define RES4335_CBUCK_PFM_PU 5 ++#define RES4335_RSVD_6 6 ++#define RES4335_RSVD_7 7 ++#define RES4335_LNLDO_PU 8 ++#define RES4335_XTALLDO_PU 9 ++#define RES4335_LDO3P3_PU 10 ++#define RES4335_OTP_PU 11 ++#define RES4335_XTAL_PU 12 ++#define RES4335_SR_CLK_START 13 ++#define RES4335_LQ_AVAIL 14 ++#define RES4335_LQ_START 15 ++#define RES4335_RSVD_16 16 ++#define RES4335_WL_CORE_RDY 17 ++#define RES4335_ILP_REQ 18 ++#define RES4335_ALP_AVAIL 19 ++#define RES4335_MINI_PMU 20 ++#define RES4335_RADIO_PU 21 ++#define RES4335_SR_CLK_STABLE 22 ++#define RES4335_SR_SAVE_RESTORE 23 ++#define RES4335_SR_PHY_PWRSW 24 ++#define RES4335_SR_VDDM_PWRSW 25 ++#define RES4335_SR_SUBCORE_PWRSW 26 ++#define RES4335_SR_SLEEP 27 ++#define RES4335_HT_START 28 ++#define RES4335_HT_AVAIL 29 ++#define RES4335_MACPHY_CLKAVAIL 30 ++ ++ ++#define CST4335_SPROM_MASK 0x00000020 ++#define CST4335_SFLASH_MASK 0x00000040 ++#define CST4335_RES_INIT_MODE_SHIFT 7 ++#define CST4335_RES_INIT_MODE_MASK 0x00000180 ++#define CST4335_CHIPMODE_MASK 0xF ++#define CST4335_CHIPMODE_SDIOD(cs) (((cs) & (1 << 0)) != 0) ++#define CST4335_CHIPMODE_GSPI(cs) (((cs) & (1 << 1)) != 0) ++#define CST4335_CHIPMODE_USB20D(cs) (((cs) & (1 << 2)) != 0) ++#define CST4335_CHIPMODE_PCIE(cs) (((cs) & (1 << 3)) != 0) ++ ++ ++#define CCTRL1_4335_GPIO_SEL (1 << 0) ++#define CCTRL1_4335_SDIO_HOST_WAKE (1 << 2) ++ ++ ++#define CR4_RAM_BASE (0x180000) ++ ++ ++ ++ ++#define CC_GCI_CHIPCTRL_00 (0) ++#define CC_GCI_CHIPCTRL_01 (1) ++#define CC_GCI_CHIPCTRL_02 (2) ++#define CC_GCI_CHIPCTRL_03 (3) ++#define CC_GCI_CHIPCTRL_04 (4) ++#define CC_GCI_CHIPCTRL_05 (5) ++#define CC_GCI_CHIPCTRL_06 (6) ++#define CC_GCI_CHIPCTRL_07 (7) ++#define CC_GCI_CHIPCTRL_08 (8) ++ ++#define CC_GCI_NUMCHIPCTRLREGS(cap1) ((cap1 & 0xF00) >> 8) ++ ++ ++#define CC4335_PIN_GPIO_00 (0) ++#define CC4335_PIN_GPIO_01 (1) ++#define CC4335_PIN_GPIO_02 (2) ++#define CC4335_PIN_GPIO_03 (3) ++#define CC4335_PIN_GPIO_04 (4) ++#define CC4335_PIN_GPIO_05 (5) ++#define CC4335_PIN_GPIO_06 (6) ++#define CC4335_PIN_GPIO_07 (7) ++#define CC4335_PIN_GPIO_08 (8) ++#define CC4335_PIN_GPIO_09 (9) ++#define CC4335_PIN_GPIO_10 (10) ++#define CC4335_PIN_GPIO_11 (11) ++#define CC4335_PIN_GPIO_12 (12) ++#define CC4335_PIN_GPIO_13 (13) ++#define CC4335_PIN_GPIO_14 (14) ++#define CC4335_PIN_GPIO_15 (15) ++#define CC4335_PIN_SDIO_CLK (16) ++#define CC4335_PIN_SDIO_CMD (17) ++#define CC4335_PIN_SDIO_DATA0 (18) ++#define CC4335_PIN_SDIO_DATA1 (19) ++#define CC4335_PIN_SDIO_DATA2 (20) ++#define CC4335_PIN_SDIO_DATA3 (21) ++#define CC4335_PIN_RF_SW_CTRL_0 (22) ++#define CC4335_PIN_RF_SW_CTRL_1 (23) ++#define CC4335_PIN_RF_SW_CTRL_2 (24) ++#define CC4335_PIN_RF_SW_CTRL_3 (25) ++#define CC4335_PIN_RF_SW_CTRL_4 (26) ++#define CC4335_PIN_RF_SW_CTRL_5 (27) ++#define CC4335_PIN_RF_SW_CTRL_6 (28) ++#define CC4335_PIN_RF_SW_CTRL_7 (29) ++#define CC4335_PIN_RF_SW_CTRL_8 (30) ++#define CC4335_PIN_RF_SW_CTRL_9 (31) ++ ++ ++#define CC4335_FNSEL_HWDEF (0) ++#define CC4335_FNSEL_SAMEASPIN (1) ++#define CC4335_FNSEL_GPIO0 (2) ++#define CC4335_FNSEL_GPIO1 (3) ++#define CC4335_FNSEL_GCI0 (4) ++#define CC4335_FNSEL_GCI1 (5) ++#define CC4335_FNSEL_UART (6) ++#define CC4335_FNSEL_SFLASH (7) ++#define CC4335_FNSEL_SPROM (8) ++#define CC4335_FNSEL_MISC0 (9) ++#define CC4335_FNSEL_MISC1 (10) ++#define CC4335_FNSEL_MISC2 (11) ++#define CC4335_FNSEL_IND (12) ++#define CC4335_FNSEL_PDN (13) ++#define CC4335_FNSEL_PUP (14) ++#define CC4335_FNSEL_TRI (15) ++ ++ ++#define GCIMASK(pos) (((uint32)0xF) << pos) ++ ++ ++#define GCIPOSVAL(val, pos) ((((uint32)val) << pos) & GCIMASK(pos)) ++ ++ ++#define MUXENAB4335_UART_MASK (0x0000000f) ++ ++ ++ ++#define CHIP_HOSTIF_USB(sih) (si_chip_hostif(sih) & CST4360_MODE_USB) ++ ++ ++#define PMU_MAX_TRANSITION_DLY 15000 ++ ++ ++#define PMURES_UP_TRANSITION 2 ++ ++ ++ ++#define SECI_MODE_UART 0x0 ++#define SECI_MODE_SECI 0x1 ++#define SECI_MODE_LEGACY_3WIRE_BT 0x2 ++#define SECI_MODE_LEGACY_3WIRE_WLAN 0x3 ++#define SECI_MODE_HALF_SECI 0x4 ++ ++#define SECI_RESET (1 << 0) ++#define SECI_RESET_BAR_UART (1 << 1) ++#define SECI_ENAB_SECI_ECI (1 << 2) ++#define SECI_ENAB_SECIOUT_DIS (1 << 3) ++#define SECI_MODE_MASK 0x7 ++#define SECI_MODE_SHIFT 4 ++#define SECI_UPD_SECI (1 << 7) ++ ++#define SECI_SIGNOFF_0 0xDB ++#define SECI_SIGNOFF_1 0 ++ ++ ++#define CLKCTL_STS_SECI_CLK_REQ (1 << 8) ++#define CLKCTL_STS_SECI_CLK_AVAIL (1 << 24) ++ ++#define SECI_UART_MSR_CTS_STATE (1 << 0) ++#define SECI_UART_MSR_RTS_STATE (1 << 1) ++#define SECI_UART_SECI_IN_STATE (1 << 2) ++#define SECI_UART_SECI_IN2_STATE (1 << 3) ++ ++ ++#define SECI_UART_LCR_STOP_BITS (1 << 0) ++#define SECI_UART_LCR_PARITY_EN (1 << 1) ++#define SECI_UART_LCR_PARITY (1 << 2) ++#define SECI_UART_LCR_RX_EN (1 << 3) ++#define SECI_UART_LCR_LBRK_CTRL (1 << 4) ++#define SECI_UART_LCR_TXO_EN (1 << 5) ++#define SECI_UART_LCR_RTSO_EN (1 << 6) ++#define SECI_UART_LCR_SLIPMODE_EN (1 << 7) ++#define SECI_UART_LCR_RXCRC_CHK (1 << 8) ++#define SECI_UART_LCR_TXCRC_INV (1 << 9) ++#define SECI_UART_LCR_TXCRC_LSBF (1 << 10) ++#define SECI_UART_LCR_TXCRC_EN (1 << 11) ++ ++#define SECI_UART_MCR_TX_EN (1 << 0) ++#define SECI_UART_MCR_PRTS (1 << 1) ++#define SECI_UART_MCR_SWFLCTRL_EN (1 << 2) ++#define SECI_UART_MCR_HIGHRATE_EN (1 << 3) ++#define SECI_UART_MCR_LOOPBK_EN (1 << 4) ++#define SECI_UART_MCR_AUTO_RTS (1 << 5) ++#define SECI_UART_MCR_AUTO_TX_DIS (1 << 6) ++#define SECI_UART_MCR_BAUD_ADJ_EN (1 << 7) ++#define SECI_UART_MCR_XONOFF_RPT (1 << 9) ++ ++ ++ ++ ++#define ECI_BW_20 0x0 ++#define ECI_BW_25 0x1 ++#define ECI_BW_30 0x2 ++#define ECI_BW_35 0x3 ++#define ECI_BW_40 0x4 ++#define ECI_BW_45 0x5 ++#define ECI_BW_50 0x6 ++#define ECI_BW_ALL 0x7 ++ ++ ++#define WLAN_NUM_ANT1 TXANT_0 ++#define WLAN_NUM_ANT2 TXANT_1 ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/sbconfig.h b/drivers/net/wireless/bcmdhd/include/sbconfig.h +new file mode 100644 +index 00000000..44d68329 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/sbconfig.h +@@ -0,0 +1,275 @@ ++/* ++ * Broadcom SiliconBackplane hardware register definitions. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: sbconfig.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++#ifndef _SBCONFIG_H ++#define _SBCONFIG_H ++ ++ ++#ifndef PAD ++#define _PADLINE(line) pad ## line ++#define _XSTR(line) _PADLINE(line) ++#define PAD _XSTR(__LINE__) ++#endif ++ ++ ++#define SB_BUS_SIZE 0x10000 ++#define SB_BUS_BASE(b) (SI_ENUM_BASE + (b) * SB_BUS_SIZE) ++#define SB_BUS_MAXCORES (SB_BUS_SIZE / SI_CORE_SIZE) ++ ++ ++#define SBCONFIGOFF 0xf00 ++#define SBCONFIGSIZE 256 ++ ++#define SBIPSFLAG 0x08 ++#define SBTPSFLAG 0x18 ++#define SBTMERRLOGA 0x48 ++#define SBTMERRLOG 0x50 ++#define SBADMATCH3 0x60 ++#define SBADMATCH2 0x68 ++#define SBADMATCH1 0x70 ++#define SBIMSTATE 0x90 ++#define SBINTVEC 0x94 ++#define SBTMSTATELOW 0x98 ++#define SBTMSTATEHIGH 0x9c ++#define SBBWA0 0xa0 ++#define SBIMCONFIGLOW 0xa8 ++#define SBIMCONFIGHIGH 0xac ++#define SBADMATCH0 0xb0 ++#define SBTMCONFIGLOW 0xb8 ++#define SBTMCONFIGHIGH 0xbc ++#define SBBCONFIG 0xc0 ++#define SBBSTATE 0xc8 ++#define SBACTCNFG 0xd8 ++#define SBFLAGST 0xe8 ++#define SBIDLOW 0xf8 ++#define SBIDHIGH 0xfc ++ ++ ++ ++#define SBIMERRLOGA 0xea8 ++#define SBIMERRLOG 0xeb0 ++#define SBTMPORTCONNID0 0xed8 ++#define SBTMPORTLOCK0 0xef8 ++ ++#ifndef _LANGUAGE_ASSEMBLY ++ ++typedef volatile struct _sbconfig { ++ uint32 PAD[2]; ++ uint32 sbipsflag; ++ uint32 PAD[3]; ++ uint32 sbtpsflag; ++ uint32 PAD[11]; ++ uint32 sbtmerrloga; ++ uint32 PAD; ++ uint32 sbtmerrlog; ++ uint32 PAD[3]; ++ uint32 sbadmatch3; ++ uint32 PAD; ++ uint32 sbadmatch2; ++ uint32 PAD; ++ uint32 sbadmatch1; ++ uint32 PAD[7]; ++ uint32 sbimstate; ++ uint32 sbintvec; ++ uint32 sbtmstatelow; ++ uint32 sbtmstatehigh; ++ uint32 sbbwa0; ++ uint32 PAD; ++ uint32 sbimconfiglow; ++ uint32 sbimconfighigh; ++ uint32 sbadmatch0; ++ uint32 PAD; ++ uint32 sbtmconfiglow; ++ uint32 sbtmconfighigh; ++ uint32 sbbconfig; ++ uint32 PAD; ++ uint32 sbbstate; ++ uint32 PAD[3]; ++ uint32 sbactcnfg; ++ uint32 PAD[3]; ++ uint32 sbflagst; ++ uint32 PAD[3]; ++ uint32 sbidlow; ++ uint32 sbidhigh; ++} sbconfig_t; ++ ++#endif ++ ++ ++#define SBIPS_INT1_MASK 0x3f ++#define SBIPS_INT1_SHIFT 0 ++#define SBIPS_INT2_MASK 0x3f00 ++#define SBIPS_INT2_SHIFT 8 ++#define SBIPS_INT3_MASK 0x3f0000 ++#define SBIPS_INT3_SHIFT 16 ++#define SBIPS_INT4_MASK 0x3f000000 ++#define SBIPS_INT4_SHIFT 24 ++ ++ ++#define SBTPS_NUM0_MASK 0x3f ++#define SBTPS_F0EN0 0x40 ++ ++ ++#define SBTMEL_CM 0x00000007 ++#define SBTMEL_CI 0x0000ff00 ++#define SBTMEL_EC 0x0f000000 ++#define SBTMEL_ME 0x80000000 ++ ++ ++#define SBIM_PC 0xf ++#define SBIM_AP_MASK 0x30 ++#define SBIM_AP_BOTH 0x00 ++#define SBIM_AP_TS 0x10 ++#define SBIM_AP_TK 0x20 ++#define SBIM_AP_RSV 0x30 ++#define SBIM_IBE 0x20000 ++#define SBIM_TO 0x40000 ++#define SBIM_BY 0x01800000 ++#define SBIM_RJ 0x02000000 ++ ++ ++#define SBTML_RESET 0x0001 ++#define SBTML_REJ_MASK 0x0006 ++#define SBTML_REJ 0x0002 ++#define SBTML_TMPREJ 0x0004 ++ ++#define SBTML_SICF_SHIFT 16 ++ ++ ++#define SBTMH_SERR 0x0001 ++#define SBTMH_INT 0x0002 ++#define SBTMH_BUSY 0x0004 ++#define SBTMH_TO 0x0020 ++ ++#define SBTMH_SISF_SHIFT 16 ++ ++ ++#define SBBWA_TAB0_MASK 0xffff ++#define SBBWA_TAB1_MASK 0xffff ++#define SBBWA_TAB1_SHIFT 16 ++ ++ ++#define SBIMCL_STO_MASK 0x7 ++#define SBIMCL_RTO_MASK 0x70 ++#define SBIMCL_RTO_SHIFT 4 ++#define SBIMCL_CID_MASK 0xff0000 ++#define SBIMCL_CID_SHIFT 16 ++ ++ ++#define SBIMCH_IEM_MASK 0xc ++#define SBIMCH_TEM_MASK 0x30 ++#define SBIMCH_TEM_SHIFT 4 ++#define SBIMCH_BEM_MASK 0xc0 ++#define SBIMCH_BEM_SHIFT 6 ++ ++ ++#define SBAM_TYPE_MASK 0x3 ++#define SBAM_AD64 0x4 ++#define SBAM_ADINT0_MASK 0xf8 ++#define SBAM_ADINT0_SHIFT 3 ++#define SBAM_ADINT1_MASK 0x1f8 ++#define SBAM_ADINT1_SHIFT 3 ++#define SBAM_ADINT2_MASK 0x1f8 ++#define SBAM_ADINT2_SHIFT 3 ++#define SBAM_ADEN 0x400 ++#define SBAM_ADNEG 0x800 ++#define SBAM_BASE0_MASK 0xffffff00 ++#define SBAM_BASE0_SHIFT 8 ++#define SBAM_BASE1_MASK 0xfffff000 ++#define SBAM_BASE1_SHIFT 12 ++#define SBAM_BASE2_MASK 0xffff0000 ++#define SBAM_BASE2_SHIFT 16 ++ ++ ++#define SBTMCL_CD_MASK 0xff ++#define SBTMCL_CO_MASK 0xf800 ++#define SBTMCL_CO_SHIFT 11 ++#define SBTMCL_IF_MASK 0xfc0000 ++#define SBTMCL_IF_SHIFT 18 ++#define SBTMCL_IM_MASK 0x3000000 ++#define SBTMCL_IM_SHIFT 24 ++ ++ ++#define SBTMCH_BM_MASK 0x3 ++#define SBTMCH_RM_MASK 0x3 ++#define SBTMCH_RM_SHIFT 2 ++#define SBTMCH_SM_MASK 0x30 ++#define SBTMCH_SM_SHIFT 4 ++#define SBTMCH_EM_MASK 0x300 ++#define SBTMCH_EM_SHIFT 8 ++#define SBTMCH_IM_MASK 0xc00 ++#define SBTMCH_IM_SHIFT 10 ++ ++ ++#define SBBC_LAT_MASK 0x3 ++#define SBBC_MAX0_MASK 0xf0000 ++#define SBBC_MAX0_SHIFT 16 ++#define SBBC_MAX1_MASK 0xf00000 ++#define SBBC_MAX1_SHIFT 20 ++ ++ ++#define SBBS_SRD 0x1 ++#define SBBS_HRD 0x2 ++ ++ ++#define SBIDL_CS_MASK 0x3 ++#define SBIDL_AR_MASK 0x38 ++#define SBIDL_AR_SHIFT 3 ++#define SBIDL_SYNCH 0x40 ++#define SBIDL_INIT 0x80 ++#define SBIDL_MINLAT_MASK 0xf00 ++#define SBIDL_MINLAT_SHIFT 8 ++#define SBIDL_MAXLAT 0xf000 ++#define SBIDL_MAXLAT_SHIFT 12 ++#define SBIDL_FIRST 0x10000 ++#define SBIDL_CW_MASK 0xc0000 ++#define SBIDL_CW_SHIFT 18 ++#define SBIDL_TP_MASK 0xf00000 ++#define SBIDL_TP_SHIFT 20 ++#define SBIDL_IP_MASK 0xf000000 ++#define SBIDL_IP_SHIFT 24 ++#define SBIDL_RV_MASK 0xf0000000 ++#define SBIDL_RV_SHIFT 28 ++#define SBIDL_RV_2_2 0x00000000 ++#define SBIDL_RV_2_3 0x10000000 ++ ++ ++#define SBIDH_RC_MASK 0x000f ++#define SBIDH_RCE_MASK 0x7000 ++#define SBIDH_RCE_SHIFT 8 ++#define SBCOREREV(sbidh) \ ++ ((((sbidh) & SBIDH_RCE_MASK) >> SBIDH_RCE_SHIFT) | ((sbidh) & SBIDH_RC_MASK)) ++#define SBIDH_CC_MASK 0x8ff0 ++#define SBIDH_CC_SHIFT 4 ++#define SBIDH_VC_MASK 0xffff0000 ++#define SBIDH_VC_SHIFT 16 ++ ++#define SB_COMMIT 0xfd8 ++ ++ ++#define SB_VEND_BCM 0x4243 ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/sbhnddma.h b/drivers/net/wireless/bcmdhd/include/sbhnddma.h +new file mode 100644 +index 00000000..da1f1a10 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/sbhnddma.h +@@ -0,0 +1,370 @@ ++/* ++ * Generic Broadcom Home Networking Division (HND) DMA engine HW interface ++ * This supports the following chips: BCM42xx, 44xx, 47xx . ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: sbhnddma.h 309193 2012-01-19 00:03:57Z $ ++ */ ++ ++#ifndef _sbhnddma_h_ ++#define _sbhnddma_h_ ++ ++ ++ ++ ++ ++ ++ ++typedef volatile struct { ++ uint32 control; ++ uint32 addr; ++ uint32 ptr; ++ uint32 status; ++} dma32regs_t; ++ ++typedef volatile struct { ++ dma32regs_t xmt; ++ dma32regs_t rcv; ++} dma32regp_t; ++ ++typedef volatile struct { ++ uint32 fifoaddr; ++ uint32 fifodatalow; ++ uint32 fifodatahigh; ++ uint32 pad; ++} dma32diag_t; ++ ++ ++typedef volatile struct { ++ uint32 ctrl; ++ uint32 addr; ++} dma32dd_t; ++ ++ ++#define D32RINGALIGN_BITS 12 ++#define D32MAXRINGSZ (1 << D32RINGALIGN_BITS) ++#define D32RINGALIGN (1 << D32RINGALIGN_BITS) ++ ++#define D32MAXDD (D32MAXRINGSZ / sizeof (dma32dd_t)) ++ ++ ++#define XC_XE ((uint32)1 << 0) ++#define XC_SE ((uint32)1 << 1) ++#define XC_LE ((uint32)1 << 2) ++#define XC_FL ((uint32)1 << 4) ++#define XC_MR_MASK 0x000000C0 ++#define XC_MR_SHIFT 6 ++#define XC_PD ((uint32)1 << 11) ++#define XC_AE ((uint32)3 << 16) ++#define XC_AE_SHIFT 16 ++#define XC_BL_MASK 0x001C0000 ++#define XC_BL_SHIFT 18 ++#define XC_PC_MASK 0x00E00000 ++#define XC_PC_SHIFT 21 ++#define XC_PT_MASK 0x03000000 ++#define XC_PT_SHIFT 24 ++ ++ ++#define DMA_MR_1 0 ++#define DMA_MR_2 1 ++ ++ ++ ++#define DMA_BL_16 0 ++#define DMA_BL_32 1 ++#define DMA_BL_64 2 ++#define DMA_BL_128 3 ++#define DMA_BL_256 4 ++#define DMA_BL_512 5 ++#define DMA_BL_1024 6 ++ ++ ++#define DMA_PC_0 0 ++#define DMA_PC_4 1 ++#define DMA_PC_8 2 ++#define DMA_PC_16 3 ++ ++ ++ ++#define DMA_PT_1 0 ++#define DMA_PT_2 1 ++#define DMA_PT_4 2 ++#define DMA_PT_8 3 ++ ++ ++#define XP_LD_MASK 0xfff ++ ++ ++#define XS_CD_MASK 0x0fff ++#define XS_XS_MASK 0xf000 ++#define XS_XS_SHIFT 12 ++#define XS_XS_DISABLED 0x0000 ++#define XS_XS_ACTIVE 0x1000 ++#define XS_XS_IDLE 0x2000 ++#define XS_XS_STOPPED 0x3000 ++#define XS_XS_SUSP 0x4000 ++#define XS_XE_MASK 0xf0000 ++#define XS_XE_SHIFT 16 ++#define XS_XE_NOERR 0x00000 ++#define XS_XE_DPE 0x10000 ++#define XS_XE_DFU 0x20000 ++#define XS_XE_BEBR 0x30000 ++#define XS_XE_BEDA 0x40000 ++#define XS_AD_MASK 0xfff00000 ++#define XS_AD_SHIFT 20 ++ ++ ++#define RC_RE ((uint32)1 << 0) ++#define RC_RO_MASK 0xfe ++#define RC_RO_SHIFT 1 ++#define RC_FM ((uint32)1 << 8) ++#define RC_SH ((uint32)1 << 9) ++#define RC_OC ((uint32)1 << 10) ++#define RC_PD ((uint32)1 << 11) ++#define RC_AE ((uint32)3 << 16) ++#define RC_AE_SHIFT 16 ++#define RC_BL_MASK 0x001C0000 ++#define RC_BL_SHIFT 18 ++#define RC_PC_MASK 0x00E00000 ++#define RC_PC_SHIFT 21 ++#define RC_PT_MASK 0x03000000 ++#define RC_PT_SHIFT 24 ++ ++ ++#define RP_LD_MASK 0xfff ++ ++ ++#define RS_CD_MASK 0x0fff ++#define RS_RS_MASK 0xf000 ++#define RS_RS_SHIFT 12 ++#define RS_RS_DISABLED 0x0000 ++#define RS_RS_ACTIVE 0x1000 ++#define RS_RS_IDLE 0x2000 ++#define RS_RS_STOPPED 0x3000 ++#define RS_RE_MASK 0xf0000 ++#define RS_RE_SHIFT 16 ++#define RS_RE_NOERR 0x00000 ++#define RS_RE_DPE 0x10000 ++#define RS_RE_DFO 0x20000 ++#define RS_RE_BEBW 0x30000 ++#define RS_RE_BEDA 0x40000 ++#define RS_AD_MASK 0xfff00000 ++#define RS_AD_SHIFT 20 ++ ++ ++#define FA_OFF_MASK 0xffff ++#define FA_SEL_MASK 0xf0000 ++#define FA_SEL_SHIFT 16 ++#define FA_SEL_XDD 0x00000 ++#define FA_SEL_XDP 0x10000 ++#define FA_SEL_RDD 0x40000 ++#define FA_SEL_RDP 0x50000 ++#define FA_SEL_XFD 0x80000 ++#define FA_SEL_XFP 0x90000 ++#define FA_SEL_RFD 0xc0000 ++#define FA_SEL_RFP 0xd0000 ++#define FA_SEL_RSD 0xe0000 ++#define FA_SEL_RSP 0xf0000 ++ ++ ++#define CTRL_BC_MASK 0x00001fff ++#define CTRL_AE ((uint32)3 << 16) ++#define CTRL_AE_SHIFT 16 ++#define CTRL_PARITY ((uint32)3 << 18) ++#define CTRL_EOT ((uint32)1 << 28) ++#define CTRL_IOC ((uint32)1 << 29) ++#define CTRL_EOF ((uint32)1 << 30) ++#define CTRL_SOF ((uint32)1 << 31) ++ ++ ++#define CTRL_CORE_MASK 0x0ff00000 ++ ++ ++ ++ ++typedef volatile struct { ++ uint32 control; ++ uint32 ptr; ++ uint32 addrlow; ++ uint32 addrhigh; ++ uint32 status0; ++ uint32 status1; ++} dma64regs_t; ++ ++typedef volatile struct { ++ dma64regs_t tx; ++ dma64regs_t rx; ++} dma64regp_t; ++ ++typedef volatile struct { ++ uint32 fifoaddr; ++ uint32 fifodatalow; ++ uint32 fifodatahigh; ++ uint32 pad; ++} dma64diag_t; ++ ++ ++typedef volatile struct { ++ uint32 ctrl1; ++ uint32 ctrl2; ++ uint32 addrlow; ++ uint32 addrhigh; ++} dma64dd_t; ++ ++ ++#define D64RINGALIGN_BITS 13 ++#define D64MAXRINGSZ (1 << D64RINGALIGN_BITS) ++#define D64RINGALIGN (1 << D64RINGALIGN_BITS) ++ ++#define D64MAXDD (D64MAXRINGSZ / sizeof (dma64dd_t)) ++ ++ ++#define D64_XC_XE 0x00000001 ++#define D64_XC_SE 0x00000002 ++#define D64_XC_LE 0x00000004 ++#define D64_XC_FL 0x00000010 ++#define D64_XC_MR_MASK 0x000000C0 ++#define D64_XC_MR_SHIFT 6 ++#define D64_XC_PD 0x00000800 ++#define D64_XC_AE 0x00030000 ++#define D64_XC_AE_SHIFT 16 ++#define D64_XC_BL_MASK 0x001C0000 ++#define D64_XC_BL_SHIFT 18 ++#define D64_XC_PC_MASK 0x00E00000 ++#define D64_XC_PC_SHIFT 21 ++#define D64_XC_PT_MASK 0x03000000 ++#define D64_XC_PT_SHIFT 24 ++ ++ ++#define D64_XP_LD_MASK 0x00001fff ++ ++ ++#define D64_XS0_CD_MASK 0x00001fff ++#define D64_XS0_XS_MASK 0xf0000000 ++#define D64_XS0_XS_SHIFT 28 ++#define D64_XS0_XS_DISABLED 0x00000000 ++#define D64_XS0_XS_ACTIVE 0x10000000 ++#define D64_XS0_XS_IDLE 0x20000000 ++#define D64_XS0_XS_STOPPED 0x30000000 ++#define D64_XS0_XS_SUSP 0x40000000 ++ ++#define D64_XS1_AD_MASK 0x00001fff ++#define D64_XS1_XE_MASK 0xf0000000 ++#define D64_XS1_XE_SHIFT 28 ++#define D64_XS1_XE_NOERR 0x00000000 ++#define D64_XS1_XE_DPE 0x10000000 ++#define D64_XS1_XE_DFU 0x20000000 ++#define D64_XS1_XE_DTE 0x30000000 ++#define D64_XS1_XE_DESRE 0x40000000 ++#define D64_XS1_XE_COREE 0x50000000 ++ ++ ++#define D64_RC_RE 0x00000001 ++#define D64_RC_RO_MASK 0x000000fe ++#define D64_RC_RO_SHIFT 1 ++#define D64_RC_FM 0x00000100 ++#define D64_RC_SH 0x00000200 ++#define D64_RC_OC 0x00000400 ++#define D64_RC_PD 0x00000800 ++#define D64_RC_AE 0x00030000 ++#define D64_RC_AE_SHIFT 16 ++#define D64_RC_BL_MASK 0x001C0000 ++#define D64_RC_BL_SHIFT 18 ++#define D64_RC_PC_MASK 0x00E00000 ++#define D64_RC_PC_SHIFT 21 ++#define D64_RC_PT_MASK 0x03000000 ++#define D64_RC_PT_SHIFT 24 ++ ++ ++#define DMA_CTRL_PEN (1 << 0) ++#define DMA_CTRL_ROC (1 << 1) ++#define DMA_CTRL_RXMULTI (1 << 2) ++#define DMA_CTRL_UNFRAMED (1 << 3) ++#define DMA_CTRL_USB_BOUNDRY4KB_WAR (1 << 4) ++#define DMA_CTRL_DMA_AVOIDANCE_WAR (1 << 5) ++ ++ ++#define D64_RP_LD_MASK 0x00001fff ++ ++ ++#define D64_RS0_CD_MASK 0x00001fff ++#define D64_RS0_RS_MASK 0xf0000000 ++#define D64_RS0_RS_SHIFT 28 ++#define D64_RS0_RS_DISABLED 0x00000000 ++#define D64_RS0_RS_ACTIVE 0x10000000 ++#define D64_RS0_RS_IDLE 0x20000000 ++#define D64_RS0_RS_STOPPED 0x30000000 ++#define D64_RS0_RS_SUSP 0x40000000 ++ ++#define D64_RS1_AD_MASK 0x0001ffff ++#define D64_RS1_RE_MASK 0xf0000000 ++#define D64_RS1_RE_SHIFT 28 ++#define D64_RS1_RE_NOERR 0x00000000 ++#define D64_RS1_RE_DPO 0x10000000 ++#define D64_RS1_RE_DFU 0x20000000 ++#define D64_RS1_RE_DTE 0x30000000 ++#define D64_RS1_RE_DESRE 0x40000000 ++#define D64_RS1_RE_COREE 0x50000000 ++ ++ ++#define D64_FA_OFF_MASK 0xffff ++#define D64_FA_SEL_MASK 0xf0000 ++#define D64_FA_SEL_SHIFT 16 ++#define D64_FA_SEL_XDD 0x00000 ++#define D64_FA_SEL_XDP 0x10000 ++#define D64_FA_SEL_RDD 0x40000 ++#define D64_FA_SEL_RDP 0x50000 ++#define D64_FA_SEL_XFD 0x80000 ++#define D64_FA_SEL_XFP 0x90000 ++#define D64_FA_SEL_RFD 0xc0000 ++#define D64_FA_SEL_RFP 0xd0000 ++#define D64_FA_SEL_RSD 0xe0000 ++#define D64_FA_SEL_RSP 0xf0000 ++ ++ ++#define D64_CTRL_COREFLAGS 0x0ff00000 ++#define D64_CTRL1_EOT ((uint32)1 << 28) ++#define D64_CTRL1_IOC ((uint32)1 << 29) ++#define D64_CTRL1_EOF ((uint32)1 << 30) ++#define D64_CTRL1_SOF ((uint32)1 << 31) ++ ++ ++#define D64_CTRL2_BC_MASK 0x00007fff ++#define D64_CTRL2_AE 0x00030000 ++#define D64_CTRL2_AE_SHIFT 16 ++#define D64_CTRL2_PARITY 0x00040000 ++ ++ ++#define D64_CTRL_CORE_MASK 0x0ff00000 ++ ++#define D64_RX_FRM_STS_LEN 0x0000ffff ++#define D64_RX_FRM_STS_OVFL 0x00800000 ++#define D64_RX_FRM_STS_DSCRCNT 0x0f000000 ++#define D64_RX_FRM_STS_DATATYPE 0xf0000000 ++ ++ ++typedef volatile struct { ++ uint16 len; ++ uint16 flags; ++} dma_rxh_t; ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/sbpcmcia.h b/drivers/net/wireless/bcmdhd/include/sbpcmcia.h +new file mode 100644 +index 00000000..6ad98b52 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/sbpcmcia.h +@@ -0,0 +1,108 @@ ++/* ++ * BCM43XX Sonics SiliconBackplane PCMCIA core hardware definitions. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: sbpcmcia.h 326494 2012-04-09 13:29:57Z $ ++ */ ++ ++#ifndef _SBPCMCIA_H ++#define _SBPCMCIA_H ++ ++ ++ ++ ++#define PCMCIA_FCR (0x700 / 2) ++ ++#define FCR0_OFF 0 ++#define FCR1_OFF (0x40 / 2) ++#define FCR2_OFF (0x80 / 2) ++#define FCR3_OFF (0xc0 / 2) ++ ++#define PCMCIA_FCR0 (0x700 / 2) ++#define PCMCIA_FCR1 (0x740 / 2) ++#define PCMCIA_FCR2 (0x780 / 2) ++#define PCMCIA_FCR3 (0x7c0 / 2) ++ ++ ++ ++#define PCMCIA_COR 0 ++ ++#define COR_RST 0x80 ++#define COR_LEV 0x40 ++#define COR_IRQEN 0x04 ++#define COR_BLREN 0x01 ++#define COR_FUNEN 0x01 ++ ++ ++#define PCICIA_FCSR (2 / 2) ++#define PCICIA_PRR (4 / 2) ++#define PCICIA_SCR (6 / 2) ++#define PCICIA_ESR (8 / 2) ++ ++ ++#define PCM_MEMOFF 0x0000 ++#define F0_MEMOFF 0x1000 ++#define F1_MEMOFF 0x2000 ++#define F2_MEMOFF 0x3000 ++#define F3_MEMOFF 0x4000 ++ ++ ++#define MEM_ADDR0 (0x728 / 2) ++#define MEM_ADDR1 (0x72a / 2) ++#define MEM_ADDR2 (0x72c / 2) ++ ++ ++#define PCMCIA_ADDR0 (0x072e / 2) ++#define PCMCIA_ADDR1 (0x0730 / 2) ++#define PCMCIA_ADDR2 (0x0732 / 2) ++ ++#define MEM_SEG (0x0734 / 2) ++#define SROM_CS (0x0736 / 2) ++#define SROM_DATAL (0x0738 / 2) ++#define SROM_DATAH (0x073a / 2) ++#define SROM_ADDRL (0x073c / 2) ++#define SROM_ADDRH (0x073e / 2) ++#define SROM_INFO2 (0x0772 / 2) ++#define SROM_INFO (0x07be / 2) ++ ++ ++#define SROM_IDLE 0 ++#define SROM_WRITE 1 ++#define SROM_READ 2 ++#define SROM_WEN 4 ++#define SROM_WDS 7 ++#define SROM_DONE 8 ++ ++ ++#define SRI_SZ_MASK 0x03 ++#define SRI_BLANK 0x04 ++#define SRI_OTP 0x80 ++ ++ ++ ++#define SBTML_INT_ACK 0x40000 ++#define SBTML_INT_EN 0x20000 ++ ++ ++#define SBTMH_INT_STATUS 0x40000 ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/sbsdio.h b/drivers/net/wireless/bcmdhd/include/sbsdio.h +new file mode 100644 +index 00000000..211c4218 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/sbsdio.h +@@ -0,0 +1,188 @@ ++/* ++ * SDIO device core hardware definitions. ++ * sdio is a portion of the pcmcia core in core rev 3 - rev 8 ++ * ++ * SDIO core support 1bit, 4 bit SDIO mode as well as SPI mode. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: sbsdio.h 308945 2012-01-18 02:15:27Z $ ++ */ ++ ++#ifndef _SBSDIO_H ++#define _SBSDIO_H ++ ++#define SBSDIO_NUM_FUNCTION 3 /* as of sdiod rev 0, supports 3 functions */ ++ ++/* function 1 miscellaneous registers */ ++#define SBSDIO_SPROM_CS 0x10000 /* sprom command and status */ ++#define SBSDIO_SPROM_INFO 0x10001 /* sprom info register */ ++#define SBSDIO_SPROM_DATA_LOW 0x10002 /* sprom indirect access data byte 0 */ ++#define SBSDIO_SPROM_DATA_HIGH 0x10003 /* sprom indirect access data byte 1 */ ++#define SBSDIO_SPROM_ADDR_LOW 0x10004 /* sprom indirect access addr byte 0 */ ++#define SBSDIO_SPROM_ADDR_HIGH 0x10005 /* sprom indirect access addr byte 0 */ ++#define SBSDIO_CHIP_CTRL_DATA 0x10006 /* xtal_pu (gpio) output */ ++#define SBSDIO_CHIP_CTRL_EN 0x10007 /* xtal_pu (gpio) enable */ ++#define SBSDIO_WATERMARK 0x10008 /* rev < 7, watermark for sdio device */ ++#define SBSDIO_DEVICE_CTL 0x10009 /* control busy signal generation */ ++ ++/* registers introduced in rev 8, some content (mask/bits) defs in sbsdpcmdev.h */ ++#define SBSDIO_FUNC1_SBADDRLOW 0x1000A /* SB Address Window Low (b15) */ ++#define SBSDIO_FUNC1_SBADDRMID 0x1000B /* SB Address Window Mid (b23:b16) */ ++#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C /* SB Address Window High (b31:b24) */ ++#define SBSDIO_FUNC1_FRAMECTRL 0x1000D /* Frame Control (frame term/abort) */ ++#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E /* ChipClockCSR (ALP/HT ctl/status) */ ++#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F /* SdioPullUp (on cmd, d0-d2) */ ++#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019 /* Write Frame Byte Count Low */ ++#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A /* Write Frame Byte Count High */ ++#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B /* Read Frame Byte Count Low */ ++#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C /* Read Frame Byte Count High */ ++#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D /* MesBusyCtl at 0x1001D (rev 11) */ ++ ++#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */ ++#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001C /* f1 misc register end */ ++ ++/* Sdio Core Rev 12 */ ++#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E ++#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1 ++#define SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT 0 ++#define SBSDIO_FUNC1_WCTRL_HTWAIT_MASK 0x2 ++#define SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT 1 ++#define SBSDIO_FUNC1_SLEEPCSR 0x1001F ++#define SBSDIO_FUNC1_SLEEPCSR_KSO_MASK 0x1 ++#define SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT 0 ++#define SBSDIO_FUNC1_SLEEPCSR_KSO_EN 1 ++#define SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK 0x2 ++#define SBSDIO_FUNC1_SLEEPCSR_DEVON_SHIFT 1 ++ ++/* SBSDIO_SPROM_CS */ ++#define SBSDIO_SPROM_IDLE 0 ++#define SBSDIO_SPROM_WRITE 1 ++#define SBSDIO_SPROM_READ 2 ++#define SBSDIO_SPROM_WEN 4 ++#define SBSDIO_SPROM_WDS 7 ++#define SBSDIO_SPROM_DONE 8 ++ ++/* SBSDIO_SPROM_INFO */ ++#define SROM_SZ_MASK 0x03 /* SROM size, 1: 4k, 2: 16k */ ++#define SROM_BLANK 0x04 /* depreciated in corerev 6 */ ++#define SROM_OTP 0x80 /* OTP present */ ++ ++/* SBSDIO_CHIP_CTRL */ ++#define SBSDIO_CHIP_CTRL_XTAL 0x01 /* or'd with onchip xtal_pu, ++ * 1: power on oscillator ++ * (for 4318 only) ++ */ ++/* SBSDIO_WATERMARK */ ++#define SBSDIO_WATERMARK_MASK 0x7f /* number of words - 1 for sd device ++ * to wait before sending data to host ++ */ ++ ++/* SBSDIO_MESBUSYCTRL */ ++/* When RX FIFO has less entries than this & MBE is set ++ * => busy signal is asserted between data blocks. ++*/ ++#define SBSDIO_MESBUSYCTRL_MASK 0x7f ++ ++/* SBSDIO_DEVICE_CTL */ ++#define SBSDIO_DEVCTL_SETBUSY 0x01 /* 1: device will assert busy signal when ++ * receiving CMD53 ++ */ ++#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02 /* 1: assertion of sdio interrupt is ++ * synchronous to the sdio clock ++ */ ++#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04 /* 1: mask all interrupts to host ++ * except the chipActive (rev 8) ++ */ ++#define SBSDIO_DEVCTL_PADS_ISO 0x08 /* 1: isolate internal sdio signals, put ++ * external pads in tri-state; requires ++ * sdio bus power cycle to clear (rev 9) ++ */ ++#define SBSDIO_DEVCTL_SB_RST_CTL 0x30 /* Force SD->SB reset mapping (rev 11) */ ++#define SBSDIO_DEVCTL_RST_CORECTL 0x00 /* Determined by CoreControl bit */ ++#define SBSDIO_DEVCTL_RST_BPRESET 0x10 /* Force backplane reset */ ++#define SBSDIO_DEVCTL_RST_NOBPRESET 0x20 /* Force no backplane reset */ ++#define SBSDIO_DEVCTL_EN_F2_BLK_WATERMARK 0x10 /* Enable function 2 tx for each block */ ++ ++ ++/* SBSDIO_FUNC1_CHIPCLKCSR */ ++#define SBSDIO_FORCE_ALP 0x01 /* Force ALP request to backplane */ ++#define SBSDIO_FORCE_HT 0x02 /* Force HT request to backplane */ ++#define SBSDIO_FORCE_ILP 0x04 /* Force ILP request to backplane */ ++#define SBSDIO_ALP_AVAIL_REQ 0x08 /* Make ALP ready (power up xtal) */ ++#define SBSDIO_HT_AVAIL_REQ 0x10 /* Make HT ready (power up PLL) */ ++#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 /* Squelch clock requests from HW */ ++#define SBSDIO_ALP_AVAIL 0x40 /* Status: ALP is ready */ ++#define SBSDIO_HT_AVAIL 0x80 /* Status: HT is ready */ ++/* In rev8, actual avail bits followed original docs */ ++#define SBSDIO_Rev8_HT_AVAIL 0x40 ++#define SBSDIO_Rev8_ALP_AVAIL 0x80 ++#define SBSDIO_CSR_MASK 0x1F ++ ++#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) ++#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) ++#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) ++#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) ++#define SBSDIO_CLKAV(regval, alponly) (SBSDIO_ALPAV(regval) && \ ++ (alponly ? 1 : SBSDIO_HTAV(regval))) ++ ++/* SBSDIO_FUNC1_SDIOPULLUP */ ++#define SBSDIO_PULLUP_D0 0x01 /* Enable D0/MISO pullup */ ++#define SBSDIO_PULLUP_D1 0x02 /* Enable D1/INT# pullup */ ++#define SBSDIO_PULLUP_D2 0x04 /* Enable D2 pullup */ ++#define SBSDIO_PULLUP_CMD 0x08 /* Enable CMD/MOSI pullup */ ++#define SBSDIO_PULLUP_ALL 0x0f /* All valid bits */ ++ ++/* function 1 OCP space */ ++#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF /* sb offset addr is <= 15 bits, 32k */ ++#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000 ++#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000 /* with b15, maps to 32-bit SB access */ ++ ++/* some duplication with sbsdpcmdev.h here */ ++/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */ ++#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */ ++#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */ ++#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */ ++#define SBSDIO_SBWINDOW_MASK 0xffff8000 /* Address bits from SBADDR regs */ ++ ++/* direct(mapped) cis space */ ++#define SBSDIO_CIS_BASE_COMMON 0x1000 /* MAPPED common CIS address */ ++#define SBSDIO_CIS_SIZE_LIMIT 0x200 /* maximum bytes in one CIS */ ++#define SBSDIO_OTP_CIS_SIZE_LIMIT 0x078 /* maximum bytes OTP CIS */ ++ ++#define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF /* cis offset addr is < 17 bits */ ++ ++#define SBSDIO_CIS_MANFID_TUPLE_LEN 6 /* manfid tuple length, include tuple, ++ * link bytes ++ */ ++ ++/* indirect cis access (in sprom) */ ++#define SBSDIO_SPROM_CIS_OFFSET 0x8 /* 8 control bytes first, CIS starts from ++ * 8th byte ++ */ ++ ++#define SBSDIO_BYTEMODE_DATALEN_MAX 64 /* sdio byte mode: maximum length of one ++ * data comamnd ++ */ ++ ++#define SBSDIO_CORE_ADDR_MASK 0x1FFFF /* sdio core function one address mask */ ++ ++#endif /* _SBSDIO_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h b/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h +new file mode 100644 +index 00000000..10c7401a +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h +@@ -0,0 +1,295 @@ ++/* ++ * Broadcom SiliconBackplane SDIO/PCMCIA hardware-specific ++ * device core support ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: sbsdpcmdev.h 347614 2012-07-27 10:24:51Z $ ++ */ ++ ++#ifndef _sbsdpcmdev_h_ ++#define _sbsdpcmdev_h_ ++ ++/* cpp contortions to concatenate w/arg prescan */ ++#ifndef PAD ++#define _PADLINE(line) pad ## line ++#define _XSTR(line) _PADLINE(line) ++#define PAD _XSTR(__LINE__) ++#endif /* PAD */ ++ ++ ++typedef volatile struct { ++ dma64regs_t xmt; /* dma tx */ ++ uint32 PAD[2]; ++ dma64regs_t rcv; /* dma rx */ ++ uint32 PAD[2]; ++} dma64p_t; ++ ++/* dma64 sdiod corerev >= 1 */ ++typedef volatile struct { ++ dma64p_t dma64regs[2]; ++ dma64diag_t dmafifo; /* DMA Diagnostic Regs, 0x280-0x28c */ ++ uint32 PAD[92]; ++} sdiodma64_t; ++ ++/* dma32 sdiod corerev == 0 */ ++typedef volatile struct { ++ dma32regp_t dma32regs[2]; /* dma tx & rx, 0x200-0x23c */ ++ dma32diag_t dmafifo; /* DMA Diagnostic Regs, 0x240-0x24c */ ++ uint32 PAD[108]; ++} sdiodma32_t; ++ ++/* dma32 regs for pcmcia core */ ++typedef volatile struct { ++ dma32regp_t dmaregs; /* DMA Regs, 0x200-0x21c, rev8 */ ++ dma32diag_t dmafifo; /* DMA Diagnostic Regs, 0x220-0x22c */ ++ uint32 PAD[116]; ++} pcmdma32_t; ++ ++/* core registers */ ++typedef volatile struct { ++ uint32 corecontrol; /* CoreControl, 0x000, rev8 */ ++ uint32 corestatus; /* CoreStatus, 0x004, rev8 */ ++ uint32 PAD[1]; ++ uint32 biststatus; /* BistStatus, 0x00c, rev8 */ ++ ++ /* PCMCIA access */ ++ uint16 pcmciamesportaladdr; /* PcmciaMesPortalAddr, 0x010, rev8 */ ++ uint16 PAD[1]; ++ uint16 pcmciamesportalmask; /* PcmciaMesPortalMask, 0x014, rev8 */ ++ uint16 PAD[1]; ++ uint16 pcmciawrframebc; /* PcmciaWrFrameBC, 0x018, rev8 */ ++ uint16 PAD[1]; ++ uint16 pcmciaunderflowtimer; /* PcmciaUnderflowTimer, 0x01c, rev8 */ ++ uint16 PAD[1]; ++ ++ /* interrupt */ ++ uint32 intstatus; /* IntStatus, 0x020, rev8 */ ++ uint32 hostintmask; /* IntHostMask, 0x024, rev8 */ ++ uint32 intmask; /* IntSbMask, 0x028, rev8 */ ++ uint32 sbintstatus; /* SBIntStatus, 0x02c, rev8 */ ++ uint32 sbintmask; /* SBIntMask, 0x030, rev8 */ ++ uint32 funcintmask; /* SDIO Function Interrupt Mask, SDIO rev4 */ ++ uint32 PAD[2]; ++ uint32 tosbmailbox; /* ToSBMailbox, 0x040, rev8 */ ++ uint32 tohostmailbox; /* ToHostMailbox, 0x044, rev8 */ ++ uint32 tosbmailboxdata; /* ToSbMailboxData, 0x048, rev8 */ ++ uint32 tohostmailboxdata; /* ToHostMailboxData, 0x04c, rev8 */ ++ ++ /* synchronized access to registers in SDIO clock domain */ ++ uint32 sdioaccess; /* SdioAccess, 0x050, rev8 */ ++ uint32 PAD[3]; ++ ++ /* PCMCIA frame control */ ++ uint8 pcmciaframectrl; /* pcmciaFrameCtrl, 0x060, rev8 */ ++ uint8 PAD[3]; ++ uint8 pcmciawatermark; /* pcmciaWaterMark, 0x064, rev8 */ ++ uint8 PAD[155]; ++ ++ /* interrupt batching control */ ++ uint32 intrcvlazy; /* IntRcvLazy, 0x100, rev8 */ ++ uint32 PAD[3]; ++ ++ /* counters */ ++ uint32 cmd52rd; /* Cmd52RdCount, 0x110, rev8, SDIO: cmd52 reads */ ++ uint32 cmd52wr; /* Cmd52WrCount, 0x114, rev8, SDIO: cmd52 writes */ ++ uint32 cmd53rd; /* Cmd53RdCount, 0x118, rev8, SDIO: cmd53 reads */ ++ uint32 cmd53wr; /* Cmd53WrCount, 0x11c, rev8, SDIO: cmd53 writes */ ++ uint32 abort; /* AbortCount, 0x120, rev8, SDIO: aborts */ ++ uint32 datacrcerror; /* DataCrcErrorCount, 0x124, rev8, SDIO: frames w/bad CRC */ ++ uint32 rdoutofsync; /* RdOutOfSyncCount, 0x128, rev8, SDIO/PCMCIA: Rd Frm OOS */ ++ uint32 wroutofsync; /* RdOutOfSyncCount, 0x12c, rev8, SDIO/PCMCIA: Wr Frm OOS */ ++ uint32 writebusy; /* WriteBusyCount, 0x130, rev8, SDIO: dev asserted "busy" */ ++ uint32 readwait; /* ReadWaitCount, 0x134, rev8, SDIO: read: no data avail */ ++ uint32 readterm; /* ReadTermCount, 0x138, rev8, SDIO: rd frm terminates */ ++ uint32 writeterm; /* WriteTermCount, 0x13c, rev8, SDIO: wr frm terminates */ ++ uint32 PAD[40]; ++ uint32 clockctlstatus; /* ClockCtlStatus, 0x1e0, rev8 */ ++ uint32 PAD[7]; ++ ++ /* DMA engines */ ++ volatile union { ++ pcmdma32_t pcm32; ++ sdiodma32_t sdiod32; ++ sdiodma64_t sdiod64; ++ } dma; ++ ++ /* SDIO/PCMCIA CIS region */ ++ char cis[512]; /* 512 byte CIS, 0x400-0x5ff, rev6 */ ++ ++ /* PCMCIA function control registers */ ++ char pcmciafcr[256]; /* PCMCIA FCR, 0x600-6ff, rev6 */ ++ uint16 PAD[55]; ++ ++ /* PCMCIA backplane access */ ++ uint16 backplanecsr; /* BackplaneCSR, 0x76E, rev6 */ ++ uint16 backplaneaddr0; /* BackplaneAddr0, 0x770, rev6 */ ++ uint16 backplaneaddr1; /* BackplaneAddr1, 0x772, rev6 */ ++ uint16 backplaneaddr2; /* BackplaneAddr2, 0x774, rev6 */ ++ uint16 backplaneaddr3; /* BackplaneAddr3, 0x776, rev6 */ ++ uint16 backplanedata0; /* BackplaneData0, 0x778, rev6 */ ++ uint16 backplanedata1; /* BackplaneData1, 0x77a, rev6 */ ++ uint16 backplanedata2; /* BackplaneData2, 0x77c, rev6 */ ++ uint16 backplanedata3; /* BackplaneData3, 0x77e, rev6 */ ++ uint16 PAD[31]; ++ ++ /* sprom "size" & "blank" info */ ++ uint16 spromstatus; /* SPROMStatus, 0x7BE, rev2 */ ++ uint32 PAD[464]; ++ ++ /* Sonics SiliconBackplane registers */ ++ sbconfig_t sbconfig; /* SbConfig Regs, 0xf00-0xfff, rev8 */ ++} sdpcmd_regs_t; ++ ++/* corecontrol */ ++#define CC_CISRDY (1 << 0) /* CIS Ready */ ++#define CC_BPRESEN (1 << 1) /* CCCR RES signal causes backplane reset */ ++#define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */ ++#define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation bit (rev 11) */ ++#define CC_XMTDATAAVAIL_MODE (1 << 4) /* data avail generates an interrupt */ ++#define CC_XMTDATAAVAIL_CTRL (1 << 5) /* data avail interrupt ctrl */ ++ ++/* corestatus */ ++#define CS_PCMCIAMODE (1 << 0) /* Device Mode; 0=SDIO, 1=PCMCIA */ ++#define CS_SMARTDEV (1 << 1) /* 1=smartDev enabled */ ++#define CS_F2ENABLED (1 << 2) /* 1=host has enabled the device */ ++ ++#define PCMCIA_MES_PA_MASK 0x7fff /* PCMCIA Message Portal Address Mask */ ++#define PCMCIA_MES_PM_MASK 0x7fff /* PCMCIA Message Portal Mask Mask */ ++#define PCMCIA_WFBC_MASK 0xffff /* PCMCIA Write Frame Byte Count Mask */ ++#define PCMCIA_UT_MASK 0x07ff /* PCMCIA Underflow Timer Mask */ ++ ++/* intstatus */ ++#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ ++#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ ++#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */ ++#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */ ++#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */ ++#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */ ++#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */ ++#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */ ++#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */ ++#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */ ++#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */ ++#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */ ++#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */ ++#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */ ++#define I_PC (1 << 10) /* descriptor error */ ++#define I_PD (1 << 11) /* data error */ ++#define I_DE (1 << 12) /* Descriptor protocol Error */ ++#define I_RU (1 << 13) /* Receive descriptor Underflow */ ++#define I_RO (1 << 14) /* Receive fifo Overflow */ ++#define I_XU (1 << 15) /* Transmit fifo Underflow */ ++#define I_RI (1 << 16) /* Receive Interrupt */ ++#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */ ++#define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */ ++#define I_XI (1 << 24) /* Transmit Interrupt */ ++#define I_RF_TERM (1 << 25) /* Read Frame Terminate */ ++#define I_WF_TERM (1 << 26) /* Write Frame Terminate */ ++#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */ ++#define I_SBINT (1 << 28) /* sbintstatus Interrupt */ ++#define I_CHIPACTIVE (1 << 29) /* chip transitioned from doze to active state */ ++#define I_SRESET (1 << 30) /* CCCR RES interrupt */ ++#define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */ ++#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU) /* DMA Errors */ ++#define I_DMA (I_RI | I_XI | I_ERRORS) ++ ++/* sbintstatus */ ++#define I_SB_SERR (1 << 8) /* Backplane SError (write) */ ++#define I_SB_RESPERR (1 << 9) /* Backplane Response Error (read) */ ++#define I_SB_SPROMERR (1 << 10) /* Error accessing the sprom */ ++ ++/* sdioaccess */ ++#define SDA_DATA_MASK 0x000000ff /* Read/Write Data Mask */ ++#define SDA_ADDR_MASK 0x000fff00 /* Read/Write Address Mask */ ++#define SDA_ADDR_SHIFT 8 /* Read/Write Address Shift */ ++#define SDA_WRITE 0x01000000 /* Write bit */ ++#define SDA_READ 0x00000000 /* Write bit cleared for Read */ ++#define SDA_BUSY 0x80000000 /* Busy bit */ ++ ++/* sdioaccess-accessible register address spaces */ ++#define SDA_CCCR_SPACE 0x000 /* sdioAccess CCCR register space */ ++#define SDA_F1_FBR_SPACE 0x100 /* sdioAccess F1 FBR register space */ ++#define SDA_F2_FBR_SPACE 0x200 /* sdioAccess F2 FBR register space */ ++#define SDA_F1_REG_SPACE 0x300 /* sdioAccess F1 core-specific register space */ ++ ++/* SDA_F1_REG_SPACE sdioaccess-accessible F1 reg space register offsets */ ++#define SDA_CHIPCONTROLDATA 0x006 /* ChipControlData */ ++#define SDA_CHIPCONTROLENAB 0x007 /* ChipControlEnable */ ++#define SDA_F2WATERMARK 0x008 /* Function 2 Watermark */ ++#define SDA_DEVICECONTROL 0x009 /* DeviceControl */ ++#define SDA_SBADDRLOW 0x00a /* SbAddrLow */ ++#define SDA_SBADDRMID 0x00b /* SbAddrMid */ ++#define SDA_SBADDRHIGH 0x00c /* SbAddrHigh */ ++#define SDA_FRAMECTRL 0x00d /* FrameCtrl */ ++#define SDA_CHIPCLOCKCSR 0x00e /* ChipClockCSR */ ++#define SDA_SDIOPULLUP 0x00f /* SdioPullUp */ ++#define SDA_SDIOWRFRAMEBCLOW 0x019 /* SdioWrFrameBCLow */ ++#define SDA_SDIOWRFRAMEBCHIGH 0x01a /* SdioWrFrameBCHigh */ ++#define SDA_SDIORDFRAMEBCLOW 0x01b /* SdioRdFrameBCLow */ ++#define SDA_SDIORDFRAMEBCHIGH 0x01c /* SdioRdFrameBCHigh */ ++ ++/* SDA_F2WATERMARK */ ++#define SDA_F2WATERMARK_MASK 0x7f /* F2Watermark Mask */ ++ ++/* SDA_SBADDRLOW */ ++#define SDA_SBADDRLOW_MASK 0x80 /* SbAddrLow Mask */ ++ ++/* SDA_SBADDRMID */ ++#define SDA_SBADDRMID_MASK 0xff /* SbAddrMid Mask */ ++ ++/* SDA_SBADDRHIGH */ ++#define SDA_SBADDRHIGH_MASK 0xff /* SbAddrHigh Mask */ ++ ++/* SDA_FRAMECTRL */ ++#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */ ++#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */ ++#define SFC_CRC4WOOS (1 << 2) /* HW reports CRC error for write out of sync */ ++#define SFC_ABORTALL (1 << 3) /* Abort cancels all in-progress frames */ ++ ++/* pcmciaframectrl */ ++#define PFC_RF_TERM (1 << 0) /* Read Frame Terminate */ ++#define PFC_WF_TERM (1 << 1) /* Write Frame Terminate */ ++ ++/* intrcvlazy */ ++#define IRL_TO_MASK 0x00ffffff /* timeout */ ++#define IRL_FC_MASK 0xff000000 /* frame count */ ++#define IRL_FC_SHIFT 24 /* frame count */ ++ ++/* rx header */ ++typedef volatile struct { ++ uint16 len; ++ uint16 flags; ++} sdpcmd_rxh_t; ++ ++/* rx header flags */ ++#define RXF_CRC 0x0001 /* CRC error detected */ ++#define RXF_WOOS 0x0002 /* write frame out of sync */ ++#define RXF_WF_TERM 0x0004 /* write frame terminated */ ++#define RXF_ABORT 0x0008 /* write frame aborted */ ++#define RXF_DISCARD (RXF_CRC | RXF_WOOS | RXF_WF_TERM | RXF_ABORT) /* bad frame */ ++ ++/* HW frame tag */ ++#define SDPCM_FRAMETAG_LEN 4 /* HW frametag: 2 bytes len, 2 bytes check val */ ++ ++#define SDPCM_HWEXT_LEN 8 ++ ++#endif /* _sbsdpcmdev_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/sbsocram.h b/drivers/net/wireless/bcmdhd/include/sbsocram.h +new file mode 100644 +index 00000000..852d1151 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/sbsocram.h +@@ -0,0 +1,193 @@ ++/* ++ * BCM47XX Sonics SiliconBackplane embedded ram core ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: sbsocram.h 271781 2011-07-13 20:00:06Z $ ++ */ ++ ++#ifndef _SBSOCRAM_H ++#define _SBSOCRAM_H ++ ++#ifndef _LANGUAGE_ASSEMBLY ++ ++ ++#ifndef PAD ++#define _PADLINE(line) pad ## line ++#define _XSTR(line) _PADLINE(line) ++#define PAD _XSTR(__LINE__) ++#endif ++ ++ ++typedef volatile struct sbsocramregs { ++ uint32 coreinfo; ++ uint32 bwalloc; ++ uint32 extracoreinfo; ++ uint32 biststat; ++ uint32 bankidx; ++ uint32 standbyctrl; ++ ++ uint32 errlogstatus; ++ uint32 errlogaddr; ++ ++ uint32 cambankidx; ++ uint32 cambankstandbyctrl; ++ uint32 cambankpatchctrl; ++ uint32 cambankpatchtblbaseaddr; ++ uint32 cambankcmdreg; ++ uint32 cambankdatareg; ++ uint32 cambankmaskreg; ++ uint32 PAD[1]; ++ uint32 bankinfo; ++ uint32 PAD[15]; ++ uint32 extmemconfig; ++ uint32 extmemparitycsr; ++ uint32 extmemparityerrdata; ++ uint32 extmemparityerrcnt; ++ uint32 extmemwrctrlandsize; ++ uint32 PAD[84]; ++ uint32 workaround; ++ uint32 pwrctl; ++ uint32 PAD[133]; ++ uint32 sr_control; ++ uint32 sr_status; ++ uint32 sr_address; ++ uint32 sr_data; ++} sbsocramregs_t; ++ ++#endif ++ ++ ++#define SR_COREINFO 0x00 ++#define SR_BWALLOC 0x04 ++#define SR_BISTSTAT 0x0c ++#define SR_BANKINDEX 0x10 ++#define SR_BANKSTBYCTL 0x14 ++#define SR_PWRCTL 0x1e8 ++ ++ ++#define SRCI_PT_MASK 0x00070000 ++#define SRCI_PT_SHIFT 16 ++ ++#define SRCI_PT_OCP_OCP 0 ++#define SRCI_PT_AXI_OCP 1 ++#define SRCI_PT_ARM7AHB_OCP 2 ++#define SRCI_PT_CM3AHB_OCP 3 ++#define SRCI_PT_AXI_AXI 4 ++#define SRCI_PT_AHB_AXI 5 ++ ++#define SRCI_LSS_MASK 0x00f00000 ++#define SRCI_LSS_SHIFT 20 ++#define SRCI_LRS_MASK 0x0f000000 ++#define SRCI_LRS_SHIFT 24 ++ ++ ++#define SRCI_MS0_MASK 0xf ++#define SR_MS0_BASE 16 ++ ++ ++#define SRCI_ROMNB_MASK 0xf000 ++#define SRCI_ROMNB_SHIFT 12 ++#define SRCI_ROMBSZ_MASK 0xf00 ++#define SRCI_ROMBSZ_SHIFT 8 ++#define SRCI_SRNB_MASK 0xf0 ++#define SRCI_SRNB_SHIFT 4 ++#define SRCI_SRBSZ_MASK 0xf ++#define SRCI_SRBSZ_SHIFT 0 ++ ++#define SR_BSZ_BASE 14 ++ ++ ++#define SRSC_SBYOVR_MASK 0x80000000 ++#define SRSC_SBYOVR_SHIFT 31 ++#define SRSC_SBYOVRVAL_MASK 0x60000000 ++#define SRSC_SBYOVRVAL_SHIFT 29 ++#define SRSC_SBYEN_MASK 0x01000000 ++#define SRSC_SBYEN_SHIFT 24 ++ ++ ++#define SRPC_PMU_STBYDIS_MASK 0x00000010 ++#define SRPC_PMU_STBYDIS_SHIFT 4 ++#define SRPC_STBYOVRVAL_MASK 0x00000008 ++#define SRPC_STBYOVRVAL_SHIFT 3 ++#define SRPC_STBYOVR_MASK 0x00000007 ++#define SRPC_STBYOVR_SHIFT 0 ++ ++ ++#define SRECC_NUM_BANKS_MASK 0x000000F0 ++#define SRECC_NUM_BANKS_SHIFT 4 ++#define SRECC_BANKSIZE_MASK 0x0000000F ++#define SRECC_BANKSIZE_SHIFT 0 ++ ++#define SRECC_BANKSIZE(value) (1 << (value)) ++ ++ ++#define SRCBPC_PATCHENABLE 0x80000000 ++ ++#define SRP_ADDRESS 0x0001FFFC ++#define SRP_VALID 0x8000 ++ ++ ++#define SRCMD_WRITE 0x00020000 ++#define SRCMD_READ 0x00010000 ++#define SRCMD_DONE 0x80000000 ++ ++#define SRCMD_DONE_DLY 1000 ++ ++ ++#define SOCRAM_BANKINFO_SZMASK 0x7f ++#define SOCRAM_BANKIDX_ROM_MASK 0x100 ++ ++#define SOCRAM_BANKIDX_MEMTYPE_SHIFT 8 ++ ++#define SOCRAM_MEMTYPE_RAM 0 ++#define SOCRAM_MEMTYPE_R0M 1 ++#define SOCRAM_MEMTYPE_DEVRAM 2 ++ ++#define SOCRAM_BANKINFO_REG 0x40 ++#define SOCRAM_BANKIDX_REG 0x10 ++#define SOCRAM_BANKINFO_STDBY_MASK 0x400 ++#define SOCRAM_BANKINFO_STDBY_TIMER 0x800 ++ ++ ++#define SOCRAM_BANKINFO_DEVRAMSEL_SHIFT 13 ++#define SOCRAM_BANKINFO_DEVRAMSEL_MASK 0x2000 ++#define SOCRAM_BANKINFO_DEVRAMPRO_SHIFT 14 ++#define SOCRAM_BANKINFO_DEVRAMPRO_MASK 0x4000 ++#define SOCRAM_BANKINFO_SLPSUPP_SHIFT 15 ++#define SOCRAM_BANKINFO_SLPSUPP_MASK 0x8000 ++#define SOCRAM_BANKINFO_RETNTRAM_SHIFT 16 ++#define SOCRAM_BANKINFO_RETNTRAM_MASK 0x00010000 ++#define SOCRAM_BANKINFO_PDASZ_SHIFT 17 ++#define SOCRAM_BANKINFO_PDASZ_MASK 0x003E0000 ++#define SOCRAM_BANKINFO_DEVRAMREMAP_SHIFT 24 ++#define SOCRAM_BANKINFO_DEVRAMREMAP_MASK 0x01000000 ++ ++ ++#define SOCRAM_DEVRAMBANK_MASK 0xF000 ++#define SOCRAM_DEVRAMBANK_SHIFT 12 ++ ++ ++#define SOCRAM_BANKINFO_SZBASE 8192 ++#define SOCRAM_BANKSIZE_SHIFT 13 ++ ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/sdio.h b/drivers/net/wireless/bcmdhd/include/sdio.h +new file mode 100644 +index 00000000..b8eee1ff +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/sdio.h +@@ -0,0 +1,617 @@ ++/* ++ * SDIO spec header file ++ * Protocol and standard (common) device definitions ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: sdio.h 308973 2012-01-18 04:19:34Z $ ++ */ ++ ++#ifndef _SDIO_H ++#define _SDIO_H ++ ++ ++/* CCCR structure for function 0 */ ++typedef volatile struct { ++ uint8 cccr_sdio_rev; /* RO, cccr and sdio revision */ ++ uint8 sd_rev; /* RO, sd spec revision */ ++ uint8 io_en; /* I/O enable */ ++ uint8 io_rdy; /* I/O ready reg */ ++ uint8 intr_ctl; /* Master and per function interrupt enable control */ ++ uint8 intr_status; /* RO, interrupt pending status */ ++ uint8 io_abort; /* read/write abort or reset all functions */ ++ uint8 bus_inter; /* bus interface control */ ++ uint8 capability; /* RO, card capability */ ++ ++ uint8 cis_base_low; /* 0x9 RO, common CIS base address, LSB */ ++ uint8 cis_base_mid; ++ uint8 cis_base_high; /* 0xB RO, common CIS base address, MSB */ ++ ++ /* suspend/resume registers */ ++ uint8 bus_suspend; /* 0xC */ ++ uint8 func_select; /* 0xD */ ++ uint8 exec_flag; /* 0xE */ ++ uint8 ready_flag; /* 0xF */ ++ ++ uint8 fn0_blk_size[2]; /* 0x10(LSB), 0x11(MSB) */ ++ ++ uint8 power_control; /* 0x12 (SDIO version 1.10) */ ++ ++ uint8 speed_control; /* 0x13 */ ++} sdio_regs_t; ++ ++/* SDIO Device CCCR offsets */ ++#define SDIOD_CCCR_REV 0x00 ++#define SDIOD_CCCR_SDREV 0x01 ++#define SDIOD_CCCR_IOEN 0x02 ++#define SDIOD_CCCR_IORDY 0x03 ++#define SDIOD_CCCR_INTEN 0x04 ++#define SDIOD_CCCR_INTPEND 0x05 ++#define SDIOD_CCCR_IOABORT 0x06 ++#define SDIOD_CCCR_BICTRL 0x07 ++#define SDIOD_CCCR_CAPABLITIES 0x08 ++#define SDIOD_CCCR_CISPTR_0 0x09 ++#define SDIOD_CCCR_CISPTR_1 0x0A ++#define SDIOD_CCCR_CISPTR_2 0x0B ++#define SDIOD_CCCR_BUSSUSP 0x0C ++#define SDIOD_CCCR_FUNCSEL 0x0D ++#define SDIOD_CCCR_EXECFLAGS 0x0E ++#define SDIOD_CCCR_RDYFLAGS 0x0F ++#define SDIOD_CCCR_BLKSIZE_0 0x10 ++#define SDIOD_CCCR_BLKSIZE_1 0x11 ++#define SDIOD_CCCR_POWER_CONTROL 0x12 ++#define SDIOD_CCCR_SPEED_CONTROL 0x13 ++#define SDIOD_CCCR_UHSI_SUPPORT 0x14 ++#define SDIOD_CCCR_DRIVER_STRENGTH 0x15 ++#define SDIOD_CCCR_INTR_EXTN 0x16 ++ ++/* Broadcom extensions (corerev >= 1) */ ++#define SDIOD_CCCR_BRCM_CARDCAP 0xf0 ++#define SDIOD_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02 ++#define SDIOD_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04 ++#define SDIOD_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08 ++#define SDIOD_CCCR_BRCM_CARDCTL 0xf1 ++#define SDIOD_CCCR_BRCM_SEPINT 0xf2 ++ ++/* cccr_sdio_rev */ ++#define SDIO_REV_SDIOID_MASK 0xf0 /* SDIO spec revision number */ ++#define SDIO_REV_CCCRID_MASK 0x0f /* CCCR format version number */ ++ ++/* sd_rev */ ++#define SD_REV_PHY_MASK 0x0f /* SD format version number */ ++ ++/* io_en */ ++#define SDIO_FUNC_ENABLE_1 0x02 /* function 1 I/O enable */ ++#define SDIO_FUNC_ENABLE_2 0x04 /* function 2 I/O enable */ ++ ++/* io_rdys */ ++#define SDIO_FUNC_READY_1 0x02 /* function 1 I/O ready */ ++#define SDIO_FUNC_READY_2 0x04 /* function 2 I/O ready */ ++ ++/* intr_ctl */ ++#define INTR_CTL_MASTER_EN 0x1 /* interrupt enable master */ ++#define INTR_CTL_FUNC1_EN 0x2 /* interrupt enable for function 1 */ ++#define INTR_CTL_FUNC2_EN 0x4 /* interrupt enable for function 2 */ ++ ++/* intr_status */ ++#define INTR_STATUS_FUNC1 0x2 /* interrupt pending for function 1 */ ++#define INTR_STATUS_FUNC2 0x4 /* interrupt pending for function 2 */ ++ ++/* io_abort */ ++#define IO_ABORT_RESET_ALL 0x08 /* I/O card reset */ ++#define IO_ABORT_FUNC_MASK 0x07 /* abort selction: function x */ ++ ++/* bus_inter */ ++#define BUS_CARD_DETECT_DIS 0x80 /* Card Detect disable */ ++#define BUS_SPI_CONT_INTR_CAP 0x40 /* support continuous SPI interrupt */ ++#define BUS_SPI_CONT_INTR_EN 0x20 /* continuous SPI interrupt enable */ ++#define BUS_SD_DATA_WIDTH_MASK 0x03 /* bus width mask */ ++#define BUS_SD_DATA_WIDTH_4BIT 0x02 /* bus width 4-bit mode */ ++#define BUS_SD_DATA_WIDTH_1BIT 0x00 /* bus width 1-bit mode */ ++ ++/* capability */ ++#define SDIO_CAP_4BLS 0x80 /* 4-bit support for low speed card */ ++#define SDIO_CAP_LSC 0x40 /* low speed card */ ++#define SDIO_CAP_E4MI 0x20 /* enable interrupt between block of data in 4-bit mode */ ++#define SDIO_CAP_S4MI 0x10 /* support interrupt between block of data in 4-bit mode */ ++#define SDIO_CAP_SBS 0x08 /* support suspend/resume */ ++#define SDIO_CAP_SRW 0x04 /* support read wait */ ++#define SDIO_CAP_SMB 0x02 /* support multi-block transfer */ ++#define SDIO_CAP_SDC 0x01 /* Support Direct commands during multi-byte transfer */ ++ ++/* power_control */ ++#define SDIO_POWER_SMPC 0x01 /* supports master power control (RO) */ ++#define SDIO_POWER_EMPC 0x02 /* enable master power control (allow > 200mA) (RW) */ ++ ++/* speed_control (control device entry into high-speed clocking mode) */ ++#define SDIO_SPEED_SHS 0x01 /* supports high-speed [clocking] mode (RO) */ ++#define SDIO_SPEED_EHS 0x02 /* enable high-speed [clocking] mode (RW) */ ++ ++/* for setting bus speed in card: 0x13h */ ++#define SDIO_BUS_SPEED_UHSISEL_M BITFIELD_MASK(3) ++#define SDIO_BUS_SPEED_UHSISEL_S 1 ++ ++/* for getting bus speed cap in card: 0x14h */ ++#define SDIO_BUS_SPEED_UHSICAP_M BITFIELD_MASK(3) ++#define SDIO_BUS_SPEED_UHSICAP_S 0 ++ ++/* for getting driver type CAP in card: 0x15h */ ++#define SDIO_BUS_DRVR_TYPE_CAP_M BITFIELD_MASK(3) ++#define SDIO_BUS_DRVR_TYPE_CAP_S 0 ++ ++/* for setting driver type selection in card: 0x15h */ ++#define SDIO_BUS_DRVR_TYPE_SEL_M BITFIELD_MASK(2) ++#define SDIO_BUS_DRVR_TYPE_SEL_S 4 ++ ++/* for getting async int support in card: 0x16h */ ++#define SDIO_BUS_ASYNCINT_CAP_M BITFIELD_MASK(1) ++#define SDIO_BUS_ASYNCINT_CAP_S 0 ++ ++/* for setting async int selection in card: 0x16h */ ++#define SDIO_BUS_ASYNCINT_SEL_M BITFIELD_MASK(1) ++#define SDIO_BUS_ASYNCINT_SEL_S 1 ++ ++/* brcm sepint */ ++#define SDIO_SEPINT_MASK 0x01 /* route sdpcmdev intr onto separate pad (chip-specific) */ ++#define SDIO_SEPINT_OE 0x02 /* 1 asserts output enable for above pad */ ++#define SDIO_SEPINT_ACT_HI 0x04 /* use active high interrupt level instead of active low */ ++ ++/* FBR structure for function 1-7, FBR addresses and register offsets */ ++typedef volatile struct { ++ uint8 devctr; /* device interface, CSA control */ ++ uint8 ext_dev; /* extended standard I/O device type code */ ++ uint8 pwr_sel; /* power selection support */ ++ uint8 PAD[6]; /* reserved */ ++ ++ uint8 cis_low; /* CIS LSB */ ++ uint8 cis_mid; ++ uint8 cis_high; /* CIS MSB */ ++ uint8 csa_low; /* code storage area, LSB */ ++ uint8 csa_mid; ++ uint8 csa_high; /* code storage area, MSB */ ++ uint8 csa_dat_win; /* data access window to function */ ++ ++ uint8 fnx_blk_size[2]; /* block size, little endian */ ++} sdio_fbr_t; ++ ++/* Maximum number of I/O funcs */ ++#define SDIOD_MAX_FUNCS 8 ++#define SDIOD_MAX_IOFUNCS 7 ++ ++/* SDIO Device FBR Start Address */ ++#define SDIOD_FBR_STARTADDR 0x100 ++ ++/* SDIO Device FBR Size */ ++#define SDIOD_FBR_SIZE 0x100 ++ ++/* Macro to calculate FBR register base */ ++#define SDIOD_FBR_BASE(n) ((n) * 0x100) ++ ++/* Function register offsets */ ++#define SDIOD_FBR_DEVCTR 0x00 /* basic info for function */ ++#define SDIOD_FBR_EXT_DEV 0x01 /* extended I/O device code */ ++#define SDIOD_FBR_PWR_SEL 0x02 /* power selection bits */ ++ ++/* SDIO Function CIS ptr offset */ ++#define SDIOD_FBR_CISPTR_0 0x09 ++#define SDIOD_FBR_CISPTR_1 0x0A ++#define SDIOD_FBR_CISPTR_2 0x0B ++ ++/* Code Storage Area pointer */ ++#define SDIOD_FBR_CSA_ADDR_0 0x0C ++#define SDIOD_FBR_CSA_ADDR_1 0x0D ++#define SDIOD_FBR_CSA_ADDR_2 0x0E ++#define SDIOD_FBR_CSA_DATA 0x0F ++ ++/* SDIO Function I/O Block Size */ ++#define SDIOD_FBR_BLKSIZE_0 0x10 ++#define SDIOD_FBR_BLKSIZE_1 0x11 ++ ++/* devctr */ ++#define SDIOD_FBR_DEVCTR_DIC 0x0f /* device interface code */ ++#define SDIOD_FBR_DECVTR_CSA 0x40 /* CSA support flag */ ++#define SDIOD_FBR_DEVCTR_CSA_EN 0x80 /* CSA enabled */ ++/* interface codes */ ++#define SDIOD_DIC_NONE 0 /* SDIO standard interface is not supported */ ++#define SDIOD_DIC_UART 1 ++#define SDIOD_DIC_BLUETOOTH_A 2 ++#define SDIOD_DIC_BLUETOOTH_B 3 ++#define SDIOD_DIC_GPS 4 ++#define SDIOD_DIC_CAMERA 5 ++#define SDIOD_DIC_PHS 6 ++#define SDIOD_DIC_WLAN 7 ++#define SDIOD_DIC_EXT 0xf /* extended device interface, read ext_dev register */ ++ ++/* pwr_sel */ ++#define SDIOD_PWR_SEL_SPS 0x01 /* supports power selection */ ++#define SDIOD_PWR_SEL_EPS 0x02 /* enable power selection (low-current mode) */ ++ ++/* misc defines */ ++#define SDIO_FUNC_0 0 ++#define SDIO_FUNC_1 1 ++#define SDIO_FUNC_2 2 ++#define SDIO_FUNC_3 3 ++#define SDIO_FUNC_4 4 ++#define SDIO_FUNC_5 5 ++#define SDIO_FUNC_6 6 ++#define SDIO_FUNC_7 7 ++ ++#define SD_CARD_TYPE_UNKNOWN 0 /* bad type or unrecognized */ ++#define SD_CARD_TYPE_IO 1 /* IO only card */ ++#define SD_CARD_TYPE_MEMORY 2 /* memory only card */ ++#define SD_CARD_TYPE_COMBO 3 /* IO and memory combo card */ ++ ++#define SDIO_MAX_BLOCK_SIZE 2048 /* maximum block size for block mode operation */ ++#define SDIO_MIN_BLOCK_SIZE 1 /* minimum block size for block mode operation */ ++ ++/* Card registers: status bit position */ ++#define CARDREG_STATUS_BIT_OUTOFRANGE 31 ++#define CARDREG_STATUS_BIT_COMCRCERROR 23 ++#define CARDREG_STATUS_BIT_ILLEGALCOMMAND 22 ++#define CARDREG_STATUS_BIT_ERROR 19 ++#define CARDREG_STATUS_BIT_IOCURRENTSTATE3 12 ++#define CARDREG_STATUS_BIT_IOCURRENTSTATE2 11 ++#define CARDREG_STATUS_BIT_IOCURRENTSTATE1 10 ++#define CARDREG_STATUS_BIT_IOCURRENTSTATE0 9 ++#define CARDREG_STATUS_BIT_FUN_NUM_ERROR 4 ++ ++ ++ ++#define SD_CMD_GO_IDLE_STATE 0 /* mandatory for SDIO */ ++#define SD_CMD_SEND_OPCOND 1 ++#define SD_CMD_MMC_SET_RCA 3 ++#define SD_CMD_IO_SEND_OP_COND 5 /* mandatory for SDIO */ ++#define SD_CMD_SELECT_DESELECT_CARD 7 ++#define SD_CMD_SEND_CSD 9 ++#define SD_CMD_SEND_CID 10 ++#define SD_CMD_STOP_TRANSMISSION 12 ++#define SD_CMD_SEND_STATUS 13 ++#define SD_CMD_GO_INACTIVE_STATE 15 ++#define SD_CMD_SET_BLOCKLEN 16 ++#define SD_CMD_READ_SINGLE_BLOCK 17 ++#define SD_CMD_READ_MULTIPLE_BLOCK 18 ++#define SD_CMD_WRITE_BLOCK 24 ++#define SD_CMD_WRITE_MULTIPLE_BLOCK 25 ++#define SD_CMD_PROGRAM_CSD 27 ++#define SD_CMD_SET_WRITE_PROT 28 ++#define SD_CMD_CLR_WRITE_PROT 29 ++#define SD_CMD_SEND_WRITE_PROT 30 ++#define SD_CMD_ERASE_WR_BLK_START 32 ++#define SD_CMD_ERASE_WR_BLK_END 33 ++#define SD_CMD_ERASE 38 ++#define SD_CMD_LOCK_UNLOCK 42 ++#define SD_CMD_IO_RW_DIRECT 52 /* mandatory for SDIO */ ++#define SD_CMD_IO_RW_EXTENDED 53 /* mandatory for SDIO */ ++#define SD_CMD_APP_CMD 55 ++#define SD_CMD_GEN_CMD 56 ++#define SD_CMD_READ_OCR 58 ++#define SD_CMD_CRC_ON_OFF 59 /* mandatory for SDIO */ ++#define SD_ACMD_SD_STATUS 13 ++#define SD_ACMD_SEND_NUM_WR_BLOCKS 22 ++#define SD_ACMD_SET_WR_BLOCK_ERASE_CNT 23 ++#define SD_ACMD_SD_SEND_OP_COND 41 ++#define SD_ACMD_SET_CLR_CARD_DETECT 42 ++#define SD_ACMD_SEND_SCR 51 ++ ++/* argument for SD_CMD_IO_RW_DIRECT and SD_CMD_IO_RW_EXTENDED */ ++#define SD_IO_OP_READ 0 /* Read_Write: Read */ ++#define SD_IO_OP_WRITE 1 /* Read_Write: Write */ ++#define SD_IO_RW_NORMAL 0 /* no RAW */ ++#define SD_IO_RW_RAW 1 /* RAW */ ++#define SD_IO_BYTE_MODE 0 /* Byte Mode */ ++#define SD_IO_BLOCK_MODE 1 /* BlockMode */ ++#define SD_IO_FIXED_ADDRESS 0 /* fix Address */ ++#define SD_IO_INCREMENT_ADDRESS 1 /* IncrementAddress */ ++ ++/* build SD_CMD_IO_RW_DIRECT Argument */ ++#define SDIO_IO_RW_DIRECT_ARG(rw, raw, func, addr, data) \ ++ ((((rw) & 1) << 31) | (((func) & 0x7) << 28) | (((raw) & 1) << 27) | \ ++ (((addr) & 0x1FFFF) << 9) | ((data) & 0xFF)) ++ ++/* build SD_CMD_IO_RW_EXTENDED Argument */ ++#define SDIO_IO_RW_EXTENDED_ARG(rw, blk, func, addr, inc_addr, count) \ ++ ((((rw) & 1) << 31) | (((func) & 0x7) << 28) | (((blk) & 1) << 27) | \ ++ (((inc_addr) & 1) << 26) | (((addr) & 0x1FFFF) << 9) | ((count) & 0x1FF)) ++ ++/* SDIO response parameters */ ++#define SD_RSP_NO_NONE 0 ++#define SD_RSP_NO_1 1 ++#define SD_RSP_NO_2 2 ++#define SD_RSP_NO_3 3 ++#define SD_RSP_NO_4 4 ++#define SD_RSP_NO_5 5 ++#define SD_RSP_NO_6 6 ++ ++ /* Modified R6 response (to CMD3) */ ++#define SD_RSP_MR6_COM_CRC_ERROR 0x8000 ++#define SD_RSP_MR6_ILLEGAL_COMMAND 0x4000 ++#define SD_RSP_MR6_ERROR 0x2000 ++ ++ /* Modified R1 in R4 Response (to CMD5) */ ++#define SD_RSP_MR1_SBIT 0x80 ++#define SD_RSP_MR1_PARAMETER_ERROR 0x40 ++#define SD_RSP_MR1_RFU5 0x20 ++#define SD_RSP_MR1_FUNC_NUM_ERROR 0x10 ++#define SD_RSP_MR1_COM_CRC_ERROR 0x08 ++#define SD_RSP_MR1_ILLEGAL_COMMAND 0x04 ++#define SD_RSP_MR1_RFU1 0x02 ++#define SD_RSP_MR1_IDLE_STATE 0x01 ++ ++ /* R5 response (to CMD52 and CMD53) */ ++#define SD_RSP_R5_COM_CRC_ERROR 0x80 ++#define SD_RSP_R5_ILLEGAL_COMMAND 0x40 ++#define SD_RSP_R5_IO_CURRENTSTATE1 0x20 ++#define SD_RSP_R5_IO_CURRENTSTATE0 0x10 ++#define SD_RSP_R5_ERROR 0x08 ++#define SD_RSP_R5_RFU 0x04 ++#define SD_RSP_R5_FUNC_NUM_ERROR 0x02 ++#define SD_RSP_R5_OUT_OF_RANGE 0x01 ++ ++#define SD_RSP_R5_ERRBITS 0xCB ++ ++ ++/* ------------------------------------------------ ++ * SDIO Commands and responses ++ * ++ * I/O only commands are: ++ * CMD0, CMD3, CMD5, CMD7, CMD14, CMD15, CMD52, CMD53 ++ * ------------------------------------------------ ++ */ ++ ++/* SDIO Commands */ ++#define SDIOH_CMD_0 0 ++#define SDIOH_CMD_3 3 ++#define SDIOH_CMD_5 5 ++#define SDIOH_CMD_7 7 ++#define SDIOH_CMD_11 11 ++#define SDIOH_CMD_14 14 ++#define SDIOH_CMD_15 15 ++#define SDIOH_CMD_19 19 ++#define SDIOH_CMD_52 52 ++#define SDIOH_CMD_53 53 ++#define SDIOH_CMD_59 59 ++ ++/* SDIO Command Responses */ ++#define SDIOH_RSP_NONE 0 ++#define SDIOH_RSP_R1 1 ++#define SDIOH_RSP_R2 2 ++#define SDIOH_RSP_R3 3 ++#define SDIOH_RSP_R4 4 ++#define SDIOH_RSP_R5 5 ++#define SDIOH_RSP_R6 6 ++ ++/* ++ * SDIO Response Error flags ++ */ ++#define SDIOH_RSP5_ERROR_FLAGS 0xCB ++ ++/* ------------------------------------------------ ++ * SDIO Command structures. I/O only commands are: ++ * ++ * CMD0, CMD3, CMD5, CMD7, CMD15, CMD52, CMD53 ++ * ------------------------------------------------ ++ */ ++ ++#define CMD5_OCR_M BITFIELD_MASK(24) ++#define CMD5_OCR_S 0 ++ ++#define CMD5_S18R_M BITFIELD_MASK(1) ++#define CMD5_S18R_S 24 ++ ++#define CMD7_RCA_M BITFIELD_MASK(16) ++#define CMD7_RCA_S 16 ++ ++#define CMD14_RCA_M BITFIELD_MASK(16) ++#define CMD14_RCA_S 16 ++#define CMD14_SLEEP_M BITFIELD_MASK(1) ++#define CMD14_SLEEP_S 15 ++ ++#define CMD_15_RCA_M BITFIELD_MASK(16) ++#define CMD_15_RCA_S 16 ++ ++#define CMD52_DATA_M BITFIELD_MASK(8) /* Bits [7:0] - Write Data/Stuff bits of CMD52 ++ */ ++#define CMD52_DATA_S 0 ++#define CMD52_REG_ADDR_M BITFIELD_MASK(17) /* Bits [25:9] - register address */ ++#define CMD52_REG_ADDR_S 9 ++#define CMD52_RAW_M BITFIELD_MASK(1) /* Bit 27 - Read after Write flag */ ++#define CMD52_RAW_S 27 ++#define CMD52_FUNCTION_M BITFIELD_MASK(3) /* Bits [30:28] - Function number */ ++#define CMD52_FUNCTION_S 28 ++#define CMD52_RW_FLAG_M BITFIELD_MASK(1) /* Bit 31 - R/W flag */ ++#define CMD52_RW_FLAG_S 31 ++ ++ ++#define CMD53_BYTE_BLK_CNT_M BITFIELD_MASK(9) /* Bits [8:0] - Byte/Block Count of CMD53 */ ++#define CMD53_BYTE_BLK_CNT_S 0 ++#define CMD53_REG_ADDR_M BITFIELD_MASK(17) /* Bits [25:9] - register address */ ++#define CMD53_REG_ADDR_S 9 ++#define CMD53_OP_CODE_M BITFIELD_MASK(1) /* Bit 26 - R/W Operation Code */ ++#define CMD53_OP_CODE_S 26 ++#define CMD53_BLK_MODE_M BITFIELD_MASK(1) /* Bit 27 - Block Mode */ ++#define CMD53_BLK_MODE_S 27 ++#define CMD53_FUNCTION_M BITFIELD_MASK(3) /* Bits [30:28] - Function number */ ++#define CMD53_FUNCTION_S 28 ++#define CMD53_RW_FLAG_M BITFIELD_MASK(1) /* Bit 31 - R/W flag */ ++#define CMD53_RW_FLAG_S 31 ++ ++/* ------------------------------------------------------ ++ * SDIO Command Response structures for SD1 and SD4 modes ++ * ----------------------------------------------------- ++ */ ++#define RSP4_IO_OCR_M BITFIELD_MASK(24) /* Bits [23:0] - Card's OCR Bits [23:0] */ ++#define RSP4_IO_OCR_S 0 ++ ++#define RSP4_S18A_M BITFIELD_MASK(1) /* Bits [23:0] - Card's OCR Bits [23:0] */ ++#define RSP4_S18A_S 24 ++ ++#define RSP4_STUFF_M BITFIELD_MASK(3) /* Bits [26:24] - Stuff bits */ ++#define RSP4_STUFF_S 24 ++#define RSP4_MEM_PRESENT_M BITFIELD_MASK(1) /* Bit 27 - Memory present */ ++#define RSP4_MEM_PRESENT_S 27 ++#define RSP4_NUM_FUNCS_M BITFIELD_MASK(3) /* Bits [30:28] - Number of I/O funcs */ ++#define RSP4_NUM_FUNCS_S 28 ++#define RSP4_CARD_READY_M BITFIELD_MASK(1) /* Bit 31 - SDIO card ready */ ++#define RSP4_CARD_READY_S 31 ++ ++#define RSP6_STATUS_M BITFIELD_MASK(16) /* Bits [15:0] - Card status bits [19,22,23,12:0] ++ */ ++#define RSP6_STATUS_S 0 ++#define RSP6_IO_RCA_M BITFIELD_MASK(16) /* Bits [31:16] - RCA bits[31-16] */ ++#define RSP6_IO_RCA_S 16 ++ ++#define RSP1_AKE_SEQ_ERROR_M BITFIELD_MASK(1) /* Bit 3 - Authentication seq error */ ++#define RSP1_AKE_SEQ_ERROR_S 3 ++#define RSP1_APP_CMD_M BITFIELD_MASK(1) /* Bit 5 - Card expects ACMD */ ++#define RSP1_APP_CMD_S 5 ++#define RSP1_READY_FOR_DATA_M BITFIELD_MASK(1) /* Bit 8 - Ready for data (buff empty) */ ++#define RSP1_READY_FOR_DATA_S 8 ++#define RSP1_CURR_STATE_M BITFIELD_MASK(4) /* Bits [12:9] - State of card ++ * when Cmd was received ++ */ ++#define RSP1_CURR_STATE_S 9 ++#define RSP1_EARSE_RESET_M BITFIELD_MASK(1) /* Bit 13 - Erase seq cleared */ ++#define RSP1_EARSE_RESET_S 13 ++#define RSP1_CARD_ECC_DISABLE_M BITFIELD_MASK(1) /* Bit 14 - Card ECC disabled */ ++#define RSP1_CARD_ECC_DISABLE_S 14 ++#define RSP1_WP_ERASE_SKIP_M BITFIELD_MASK(1) /* Bit 15 - Partial blocks erased due to W/P */ ++#define RSP1_WP_ERASE_SKIP_S 15 ++#define RSP1_CID_CSD_OVERW_M BITFIELD_MASK(1) /* Bit 16 - Illegal write to CID or R/O bits ++ * of CSD ++ */ ++#define RSP1_CID_CSD_OVERW_S 16 ++#define RSP1_ERROR_M BITFIELD_MASK(1) /* Bit 19 - General/Unknown error */ ++#define RSP1_ERROR_S 19 ++#define RSP1_CC_ERROR_M BITFIELD_MASK(1) /* Bit 20 - Internal Card Control error */ ++#define RSP1_CC_ERROR_S 20 ++#define RSP1_CARD_ECC_FAILED_M BITFIELD_MASK(1) /* Bit 21 - Card internal ECC failed ++ * to correct data ++ */ ++#define RSP1_CARD_ECC_FAILED_S 21 ++#define RSP1_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 22 - Cmd not legal for the card state */ ++#define RSP1_ILLEGAL_CMD_S 22 ++#define RSP1_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 23 - CRC check of previous command failed ++ */ ++#define RSP1_COM_CRC_ERROR_S 23 ++#define RSP1_LOCK_UNLOCK_FAIL_M BITFIELD_MASK(1) /* Bit 24 - Card lock-unlock Cmd Seq error */ ++#define RSP1_LOCK_UNLOCK_FAIL_S 24 ++#define RSP1_CARD_LOCKED_M BITFIELD_MASK(1) /* Bit 25 - Card locked by the host */ ++#define RSP1_CARD_LOCKED_S 25 ++#define RSP1_WP_VIOLATION_M BITFIELD_MASK(1) /* Bit 26 - Attempt to program ++ * write-protected blocks ++ */ ++#define RSP1_WP_VIOLATION_S 26 ++#define RSP1_ERASE_PARAM_M BITFIELD_MASK(1) /* Bit 27 - Invalid erase blocks */ ++#define RSP1_ERASE_PARAM_S 27 ++#define RSP1_ERASE_SEQ_ERR_M BITFIELD_MASK(1) /* Bit 28 - Erase Cmd seq error */ ++#define RSP1_ERASE_SEQ_ERR_S 28 ++#define RSP1_BLK_LEN_ERR_M BITFIELD_MASK(1) /* Bit 29 - Block length error */ ++#define RSP1_BLK_LEN_ERR_S 29 ++#define RSP1_ADDR_ERR_M BITFIELD_MASK(1) /* Bit 30 - Misaligned address */ ++#define RSP1_ADDR_ERR_S 30 ++#define RSP1_OUT_OF_RANGE_M BITFIELD_MASK(1) /* Bit 31 - Cmd arg was out of range */ ++#define RSP1_OUT_OF_RANGE_S 31 ++ ++ ++#define RSP5_DATA_M BITFIELD_MASK(8) /* Bits [0:7] - data */ ++#define RSP5_DATA_S 0 ++#define RSP5_FLAGS_M BITFIELD_MASK(8) /* Bit [15:8] - Rsp flags */ ++#define RSP5_FLAGS_S 8 ++#define RSP5_STUFF_M BITFIELD_MASK(16) /* Bits [31:16] - Stuff bits */ ++#define RSP5_STUFF_S 16 ++ ++/* ---------------------------------------------- ++ * SDIO Command Response structures for SPI mode ++ * ---------------------------------------------- ++ */ ++#define SPIRSP4_IO_OCR_M BITFIELD_MASK(16) /* Bits [15:0] - Card's OCR Bits [23:8] */ ++#define SPIRSP4_IO_OCR_S 0 ++#define SPIRSP4_STUFF_M BITFIELD_MASK(3) /* Bits [18:16] - Stuff bits */ ++#define SPIRSP4_STUFF_S 16 ++#define SPIRSP4_MEM_PRESENT_M BITFIELD_MASK(1) /* Bit 19 - Memory present */ ++#define SPIRSP4_MEM_PRESENT_S 19 ++#define SPIRSP4_NUM_FUNCS_M BITFIELD_MASK(3) /* Bits [22:20] - Number of I/O funcs */ ++#define SPIRSP4_NUM_FUNCS_S 20 ++#define SPIRSP4_CARD_READY_M BITFIELD_MASK(1) /* Bit 23 - SDIO card ready */ ++#define SPIRSP4_CARD_READY_S 23 ++#define SPIRSP4_IDLE_STATE_M BITFIELD_MASK(1) /* Bit 24 - idle state */ ++#define SPIRSP4_IDLE_STATE_S 24 ++#define SPIRSP4_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 26 - Illegal Cmd error */ ++#define SPIRSP4_ILLEGAL_CMD_S 26 ++#define SPIRSP4_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 27 - COM CRC error */ ++#define SPIRSP4_COM_CRC_ERROR_S 27 ++#define SPIRSP4_FUNC_NUM_ERROR_M BITFIELD_MASK(1) /* Bit 28 - Function number error ++ */ ++#define SPIRSP4_FUNC_NUM_ERROR_S 28 ++#define SPIRSP4_PARAM_ERROR_M BITFIELD_MASK(1) /* Bit 30 - Parameter Error Bit */ ++#define SPIRSP4_PARAM_ERROR_S 30 ++#define SPIRSP4_START_BIT_M BITFIELD_MASK(1) /* Bit 31 - Start Bit */ ++#define SPIRSP4_START_BIT_S 31 ++ ++#define SPIRSP5_DATA_M BITFIELD_MASK(8) /* Bits [23:16] - R/W Data */ ++#define SPIRSP5_DATA_S 16 ++#define SPIRSP5_IDLE_STATE_M BITFIELD_MASK(1) /* Bit 24 - Idle state */ ++#define SPIRSP5_IDLE_STATE_S 24 ++#define SPIRSP5_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 26 - Illegal Cmd error */ ++#define SPIRSP5_ILLEGAL_CMD_S 26 ++#define SPIRSP5_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 27 - COM CRC error */ ++#define SPIRSP5_COM_CRC_ERROR_S 27 ++#define SPIRSP5_FUNC_NUM_ERROR_M BITFIELD_MASK(1) /* Bit 28 - Function number error ++ */ ++#define SPIRSP5_FUNC_NUM_ERROR_S 28 ++#define SPIRSP5_PARAM_ERROR_M BITFIELD_MASK(1) /* Bit 30 - Parameter Error Bit */ ++#define SPIRSP5_PARAM_ERROR_S 30 ++#define SPIRSP5_START_BIT_M BITFIELD_MASK(1) /* Bit 31 - Start Bit */ ++#define SPIRSP5_START_BIT_S 31 ++ ++/* RSP6 card status format; Pg 68 Physical Layer spec v 1.10 */ ++#define RSP6STAT_AKE_SEQ_ERROR_M BITFIELD_MASK(1) /* Bit 3 - Authentication seq error ++ */ ++#define RSP6STAT_AKE_SEQ_ERROR_S 3 ++#define RSP6STAT_APP_CMD_M BITFIELD_MASK(1) /* Bit 5 - Card expects ACMD */ ++#define RSP6STAT_APP_CMD_S 5 ++#define RSP6STAT_READY_FOR_DATA_M BITFIELD_MASK(1) /* Bit 8 - Ready for data ++ * (buff empty) ++ */ ++#define RSP6STAT_READY_FOR_DATA_S 8 ++#define RSP6STAT_CURR_STATE_M BITFIELD_MASK(4) /* Bits [12:9] - Card state at ++ * Cmd reception ++ */ ++#define RSP6STAT_CURR_STATE_S 9 ++#define RSP6STAT_ERROR_M BITFIELD_MASK(1) /* Bit 13 - General/Unknown error Bit 19 ++ */ ++#define RSP6STAT_ERROR_S 13 ++#define RSP6STAT_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 14 - Illegal cmd for ++ * card state Bit 22 ++ */ ++#define RSP6STAT_ILLEGAL_CMD_S 14 ++#define RSP6STAT_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 15 - CRC previous command ++ * failed Bit 23 ++ */ ++#define RSP6STAT_COM_CRC_ERROR_S 15 ++ ++#define SDIOH_XFER_TYPE_READ SD_IO_OP_READ ++#define SDIOH_XFER_TYPE_WRITE SD_IO_OP_WRITE ++ ++/* command issue options */ ++#define CMD_OPTION_DEFAULT 0 ++#define CMD_OPTION_TUNING 1 ++#endif /* _SDIO_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/sdioh.h b/drivers/net/wireless/bcmdhd/include/sdioh.h +new file mode 100644 +index 00000000..5517a718 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/sdioh.h +@@ -0,0 +1,445 @@ ++/* ++ * SDIO Host Controller Spec header file ++ * Register map and definitions for the Standard Host Controller ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: sdioh.h 347633 2012-07-27 11:02:02Z $ ++ */ ++ ++#ifndef _SDIOH_H ++#define _SDIOH_H ++ ++#define SD_SysAddr 0x000 ++#define SD_BlockSize 0x004 ++#define SD_BlockCount 0x006 ++#define SD_Arg0 0x008 ++#define SD_Arg1 0x00A ++#define SD_TransferMode 0x00C ++#define SD_Command 0x00E ++#define SD_Response0 0x010 ++#define SD_Response1 0x012 ++#define SD_Response2 0x014 ++#define SD_Response3 0x016 ++#define SD_Response4 0x018 ++#define SD_Response5 0x01A ++#define SD_Response6 0x01C ++#define SD_Response7 0x01E ++#define SD_BufferDataPort0 0x020 ++#define SD_BufferDataPort1 0x022 ++#define SD_PresentState 0x024 ++#define SD_HostCntrl 0x028 ++#define SD_PwrCntrl 0x029 ++#define SD_BlockGapCntrl 0x02A ++#define SD_WakeupCntrl 0x02B ++#define SD_ClockCntrl 0x02C ++#define SD_TimeoutCntrl 0x02E ++#define SD_SoftwareReset 0x02F ++#define SD_IntrStatus 0x030 ++#define SD_ErrorIntrStatus 0x032 ++#define SD_IntrStatusEnable 0x034 ++#define SD_ErrorIntrStatusEnable 0x036 ++#define SD_IntrSignalEnable 0x038 ++#define SD_ErrorIntrSignalEnable 0x03A ++#define SD_CMD12ErrorStatus 0x03C ++#define SD_Capabilities 0x040 ++#define SD_Capabilities3 0x044 ++#define SD_MaxCurCap 0x048 ++#define SD_MaxCurCap_Reserved 0x04C ++#define SD_ADMA_ErrStatus 0x054 ++#define SD_ADMA_SysAddr 0x58 ++#define SD_SlotInterruptStatus 0x0FC ++#define SD_HostControllerVersion 0x0FE ++#define SD_GPIO_Reg 0x100 ++#define SD_GPIO_OE 0x104 ++#define SD_GPIO_Enable 0x108 ++ ++/* SD specific registers in PCI config space */ ++#define SD_SlotInfo 0x40 ++ ++/* HC 3.0 specific registers and offsets */ ++#define SD3_HostCntrl2 0x03E ++/* preset regsstart and count */ ++#define SD3_PresetValStart 0x060 ++#define SD3_PresetValCount 8 ++/* preset-indiv regs */ ++#define SD3_PresetVal_init 0x060 ++#define SD3_PresetVal_default 0x062 ++#define SD3_PresetVal_HS 0x064 ++#define SD3_PresetVal_SDR12 0x066 ++#define SD3_PresetVal_SDR25 0x068 ++#define SD3_PresetVal_SDR50 0x06a ++#define SD3_PresetVal_SDR104 0x06c ++#define SD3_PresetVal_DDR50 0x06e ++/* SDIO3.0 Revx specific Registers */ ++#define SD3_Tuning_Info_Register 0x0EC ++#define SD3_WL_BT_reset_register 0x0F0 ++ ++ ++/* preset value indices */ ++#define SD3_PRESETVAL_INITIAL_IX 0 ++#define SD3_PRESETVAL_DESPEED_IX 1 ++#define SD3_PRESETVAL_HISPEED_IX 2 ++#define SD3_PRESETVAL_SDR12_IX 3 ++#define SD3_PRESETVAL_SDR25_IX 4 ++#define SD3_PRESETVAL_SDR50_IX 5 ++#define SD3_PRESETVAL_SDR104_IX 6 ++#define SD3_PRESETVAL_DDR50_IX 7 ++ ++/* SD_Capabilities reg (0x040) */ ++#define CAP_TO_CLKFREQ_M BITFIELD_MASK(6) ++#define CAP_TO_CLKFREQ_S 0 ++#define CAP_TO_CLKUNIT_M BITFIELD_MASK(1) ++#define CAP_TO_CLKUNIT_S 7 ++/* Note: for sdio-2.0 case, this mask has to be 6 bits, but msb 2 ++ bits are reserved. going ahead with 8 bits, as it is req for 3.0 ++*/ ++#define CAP_BASECLK_M BITFIELD_MASK(8) ++#define CAP_BASECLK_S 8 ++#define CAP_MAXBLOCK_M BITFIELD_MASK(2) ++#define CAP_MAXBLOCK_S 16 ++#define CAP_ADMA2_M BITFIELD_MASK(1) ++#define CAP_ADMA2_S 19 ++#define CAP_ADMA1_M BITFIELD_MASK(1) ++#define CAP_ADMA1_S 20 ++#define CAP_HIGHSPEED_M BITFIELD_MASK(1) ++#define CAP_HIGHSPEED_S 21 ++#define CAP_DMA_M BITFIELD_MASK(1) ++#define CAP_DMA_S 22 ++#define CAP_SUSPEND_M BITFIELD_MASK(1) ++#define CAP_SUSPEND_S 23 ++#define CAP_VOLT_3_3_M BITFIELD_MASK(1) ++#define CAP_VOLT_3_3_S 24 ++#define CAP_VOLT_3_0_M BITFIELD_MASK(1) ++#define CAP_VOLT_3_0_S 25 ++#define CAP_VOLT_1_8_M BITFIELD_MASK(1) ++#define CAP_VOLT_1_8_S 26 ++#define CAP_64BIT_HOST_M BITFIELD_MASK(1) ++#define CAP_64BIT_HOST_S 28 ++ ++#define SDIO_OCR_READ_FAIL (2) ++ ++ ++#define CAP_ASYNCINT_SUP_M BITFIELD_MASK(1) ++#define CAP_ASYNCINT_SUP_S 29 ++ ++#define CAP_SLOTTYPE_M BITFIELD_MASK(2) ++#define CAP_SLOTTYPE_S 30 ++ ++#define CAP3_MSBits_OFFSET (32) ++/* note: following are caps MSB32 bits. ++ So the bits start from 0, instead of 32. that is why ++ CAP3_MSBits_OFFSET is subtracted. ++*/ ++#define CAP3_SDR50_SUP_M BITFIELD_MASK(1) ++#define CAP3_SDR50_SUP_S (32 - CAP3_MSBits_OFFSET) ++ ++#define CAP3_SDR104_SUP_M BITFIELD_MASK(1) ++#define CAP3_SDR104_SUP_S (33 - CAP3_MSBits_OFFSET) ++ ++#define CAP3_DDR50_SUP_M BITFIELD_MASK(1) ++#define CAP3_DDR50_SUP_S (34 - CAP3_MSBits_OFFSET) ++ ++/* for knowing the clk caps in a single read */ ++#define CAP3_30CLKCAP_M BITFIELD_MASK(3) ++#define CAP3_30CLKCAP_S (32 - CAP3_MSBits_OFFSET) ++ ++#define CAP3_DRIVTYPE_A_M BITFIELD_MASK(1) ++#define CAP3_DRIVTYPE_A_S (36 - CAP3_MSBits_OFFSET) ++ ++#define CAP3_DRIVTYPE_C_M BITFIELD_MASK(1) ++#define CAP3_DRIVTYPE_C_S (37 - CAP3_MSBits_OFFSET) ++ ++#define CAP3_DRIVTYPE_D_M BITFIELD_MASK(1) ++#define CAP3_DRIVTYPE_D_S (38 - CAP3_MSBits_OFFSET) ++ ++#define CAP3_RETUNING_TC_M BITFIELD_MASK(4) ++#define CAP3_RETUNING_TC_S (40 - CAP3_MSBits_OFFSET) ++ ++#define CAP3_TUNING_SDR50_M BITFIELD_MASK(1) ++#define CAP3_TUNING_SDR50_S (45 - CAP3_MSBits_OFFSET) ++ ++#define CAP3_RETUNING_MODES_M BITFIELD_MASK(2) ++#define CAP3_RETUNING_MODES_S (46 - CAP3_MSBits_OFFSET) ++ ++#define CAP3_CLK_MULT_M BITFIELD_MASK(8) ++#define CAP3_CLK_MULT_S (48 - CAP3_MSBits_OFFSET) ++ ++#define PRESET_DRIVR_SELECT_M BITFIELD_MASK(2) ++#define PRESET_DRIVR_SELECT_S 14 ++ ++#define PRESET_CLK_DIV_M BITFIELD_MASK(10) ++#define PRESET_CLK_DIV_S 0 ++ ++/* SD_MaxCurCap reg (0x048) */ ++#define CAP_CURR_3_3_M BITFIELD_MASK(8) ++#define CAP_CURR_3_3_S 0 ++#define CAP_CURR_3_0_M BITFIELD_MASK(8) ++#define CAP_CURR_3_0_S 8 ++#define CAP_CURR_1_8_M BITFIELD_MASK(8) ++#define CAP_CURR_1_8_S 16 ++ ++/* SD_SysAddr: Offset 0x0000, Size 4 bytes */ ++ ++/* SD_BlockSize: Offset 0x004, Size 2 bytes */ ++#define BLKSZ_BLKSZ_M BITFIELD_MASK(12) ++#define BLKSZ_BLKSZ_S 0 ++#define BLKSZ_BNDRY_M BITFIELD_MASK(3) ++#define BLKSZ_BNDRY_S 12 ++ ++/* SD_BlockCount: Offset 0x006, size 2 bytes */ ++ ++/* SD_Arg0: Offset 0x008, size = 4 bytes */ ++/* SD_TransferMode Offset 0x00C, size = 2 bytes */ ++#define XFER_DMA_ENABLE_M BITFIELD_MASK(1) ++#define XFER_DMA_ENABLE_S 0 ++#define XFER_BLK_COUNT_EN_M BITFIELD_MASK(1) ++#define XFER_BLK_COUNT_EN_S 1 ++#define XFER_CMD_12_EN_M BITFIELD_MASK(1) ++#define XFER_CMD_12_EN_S 2 ++#define XFER_DATA_DIRECTION_M BITFIELD_MASK(1) ++#define XFER_DATA_DIRECTION_S 4 ++#define XFER_MULTI_BLOCK_M BITFIELD_MASK(1) ++#define XFER_MULTI_BLOCK_S 5 ++ ++/* SD_Command: Offset 0x00E, size = 2 bytes */ ++/* resp_type field */ ++#define RESP_TYPE_NONE 0 ++#define RESP_TYPE_136 1 ++#define RESP_TYPE_48 2 ++#define RESP_TYPE_48_BUSY 3 ++/* type field */ ++#define CMD_TYPE_NORMAL 0 ++#define CMD_TYPE_SUSPEND 1 ++#define CMD_TYPE_RESUME 2 ++#define CMD_TYPE_ABORT 3 ++ ++#define CMD_RESP_TYPE_M BITFIELD_MASK(2) /* Bits [0-1] - Response type */ ++#define CMD_RESP_TYPE_S 0 ++#define CMD_CRC_EN_M BITFIELD_MASK(1) /* Bit 3 - CRC enable */ ++#define CMD_CRC_EN_S 3 ++#define CMD_INDEX_EN_M BITFIELD_MASK(1) /* Bit 4 - Enable index checking */ ++#define CMD_INDEX_EN_S 4 ++#define CMD_DATA_EN_M BITFIELD_MASK(1) /* Bit 5 - Using DAT line */ ++#define CMD_DATA_EN_S 5 ++#define CMD_TYPE_M BITFIELD_MASK(2) /* Bit [6-7] - Normal, abort, resume, etc ++ */ ++#define CMD_TYPE_S 6 ++#define CMD_INDEX_M BITFIELD_MASK(6) /* Bits [8-13] - Command number */ ++#define CMD_INDEX_S 8 ++ ++/* SD_BufferDataPort0 : Offset 0x020, size = 2 or 4 bytes */ ++/* SD_BufferDataPort1 : Offset 0x022, size = 2 bytes */ ++/* SD_PresentState : Offset 0x024, size = 4 bytes */ ++#define PRES_CMD_INHIBIT_M BITFIELD_MASK(1) /* Bit 0 May use CMD */ ++#define PRES_CMD_INHIBIT_S 0 ++#define PRES_DAT_INHIBIT_M BITFIELD_MASK(1) /* Bit 1 May use DAT */ ++#define PRES_DAT_INHIBIT_S 1 ++#define PRES_DAT_BUSY_M BITFIELD_MASK(1) /* Bit 2 DAT is busy */ ++#define PRES_DAT_BUSY_S 2 ++#define PRES_PRESENT_RSVD_M BITFIELD_MASK(5) /* Bit [3-7] rsvd */ ++#define PRES_PRESENT_RSVD_S 3 ++#define PRES_WRITE_ACTIVE_M BITFIELD_MASK(1) /* Bit 8 Write is active */ ++#define PRES_WRITE_ACTIVE_S 8 ++#define PRES_READ_ACTIVE_M BITFIELD_MASK(1) /* Bit 9 Read is active */ ++#define PRES_READ_ACTIVE_S 9 ++#define PRES_WRITE_DATA_RDY_M BITFIELD_MASK(1) /* Bit 10 Write buf is avail */ ++#define PRES_WRITE_DATA_RDY_S 10 ++#define PRES_READ_DATA_RDY_M BITFIELD_MASK(1) /* Bit 11 Read buf data avail */ ++#define PRES_READ_DATA_RDY_S 11 ++#define PRES_CARD_PRESENT_M BITFIELD_MASK(1) /* Bit 16 Card present - debounced */ ++#define PRES_CARD_PRESENT_S 16 ++#define PRES_CARD_STABLE_M BITFIELD_MASK(1) /* Bit 17 Debugging */ ++#define PRES_CARD_STABLE_S 17 ++#define PRES_CARD_PRESENT_RAW_M BITFIELD_MASK(1) /* Bit 18 Not debounced */ ++#define PRES_CARD_PRESENT_RAW_S 18 ++#define PRES_WRITE_ENABLED_M BITFIELD_MASK(1) /* Bit 19 Write protected? */ ++#define PRES_WRITE_ENABLED_S 19 ++#define PRES_DAT_SIGNAL_M BITFIELD_MASK(4) /* Bit [20-23] Debugging */ ++#define PRES_DAT_SIGNAL_S 20 ++#define PRES_CMD_SIGNAL_M BITFIELD_MASK(1) /* Bit 24 Debugging */ ++#define PRES_CMD_SIGNAL_S 24 ++ ++/* SD_HostCntrl: Offset 0x028, size = 1 bytes */ ++#define HOST_LED_M BITFIELD_MASK(1) /* Bit 0 LED On/Off */ ++#define HOST_LED_S 0 ++#define HOST_DATA_WIDTH_M BITFIELD_MASK(1) /* Bit 1 4 bit enable */ ++#define HOST_DATA_WIDTH_S 1 ++#define HOST_HI_SPEED_EN_M BITFIELD_MASK(1) /* Bit 2 High speed vs low speed */ ++#define HOST_DMA_SEL_S 3 ++#define HOST_DMA_SEL_M BITFIELD_MASK(2) /* Bit 4:3 DMA Select */ ++#define HOST_HI_SPEED_EN_S 2 ++ ++/* Host Control2: */ ++#define HOSTCtrl2_PRESVAL_EN_M BITFIELD_MASK(1) /* 1 bit */ ++#define HOSTCtrl2_PRESVAL_EN_S 15 /* bit# */ ++ ++#define HOSTCtrl2_ASYINT_EN_M BITFIELD_MASK(1) /* 1 bit */ ++#define HOSTCtrl2_ASYINT_EN_S 14 /* bit# */ ++ ++#define HOSTCtrl2_SAMPCLK_SEL_M BITFIELD_MASK(1) /* 1 bit */ ++#define HOSTCtrl2_SAMPCLK_SEL_S 7 /* bit# */ ++ ++#define HOSTCtrl2_EXEC_TUNING_M BITFIELD_MASK(1) /* 1 bit */ ++#define HOSTCtrl2_EXEC_TUNING_S 6 /* bit# */ ++ ++#define HOSTCtrl2_DRIVSTRENGTH_SEL_M BITFIELD_MASK(2) /* 2 bit */ ++#define HOSTCtrl2_DRIVSTRENGTH_SEL_S 4 /* bit# */ ++ ++#define HOSTCtrl2_1_8SIG_EN_M BITFIELD_MASK(1) /* 1 bit */ ++#define HOSTCtrl2_1_8SIG_EN_S 3 /* bit# */ ++ ++#define HOSTCtrl2_UHSMODE_SEL_M BITFIELD_MASK(3) /* 3 bit */ ++#define HOSTCtrl2_UHSMODE_SEL_S 0 /* bit# */ ++ ++#define HOST_CONTR_VER_2 (1) ++#define HOST_CONTR_VER_3 (2) ++ ++/* misc defines */ ++#define SD1_MODE 0x1 /* SD Host Cntrlr Spec */ ++#define SD4_MODE 0x2 /* SD Host Cntrlr Spec */ ++ ++/* SD_PwrCntrl: Offset 0x029, size = 1 bytes */ ++#define PWR_BUS_EN_M BITFIELD_MASK(1) /* Bit 0 Power the bus */ ++#define PWR_BUS_EN_S 0 ++#define PWR_VOLTS_M BITFIELD_MASK(3) /* Bit [1-3] Voltage Select */ ++#define PWR_VOLTS_S 1 ++ ++/* SD_SoftwareReset: Offset 0x02F, size = 1 byte */ ++#define SW_RESET_ALL_M BITFIELD_MASK(1) /* Bit 0 Reset All */ ++#define SW_RESET_ALL_S 0 ++#define SW_RESET_CMD_M BITFIELD_MASK(1) /* Bit 1 CMD Line Reset */ ++#define SW_RESET_CMD_S 1 ++#define SW_RESET_DAT_M BITFIELD_MASK(1) /* Bit 2 DAT Line Reset */ ++#define SW_RESET_DAT_S 2 ++ ++/* SD_IntrStatus: Offset 0x030, size = 2 bytes */ ++/* Defs also serve SD_IntrStatusEnable and SD_IntrSignalEnable */ ++#define INTSTAT_CMD_COMPLETE_M BITFIELD_MASK(1) /* Bit 0 */ ++#define INTSTAT_CMD_COMPLETE_S 0 ++#define INTSTAT_XFER_COMPLETE_M BITFIELD_MASK(1) ++#define INTSTAT_XFER_COMPLETE_S 1 ++#define INTSTAT_BLOCK_GAP_EVENT_M BITFIELD_MASK(1) ++#define INTSTAT_BLOCK_GAP_EVENT_S 2 ++#define INTSTAT_DMA_INT_M BITFIELD_MASK(1) ++#define INTSTAT_DMA_INT_S 3 ++#define INTSTAT_BUF_WRITE_READY_M BITFIELD_MASK(1) ++#define INTSTAT_BUF_WRITE_READY_S 4 ++#define INTSTAT_BUF_READ_READY_M BITFIELD_MASK(1) ++#define INTSTAT_BUF_READ_READY_S 5 ++#define INTSTAT_CARD_INSERTION_M BITFIELD_MASK(1) ++#define INTSTAT_CARD_INSERTION_S 6 ++#define INTSTAT_CARD_REMOVAL_M BITFIELD_MASK(1) ++#define INTSTAT_CARD_REMOVAL_S 7 ++#define INTSTAT_CARD_INT_M BITFIELD_MASK(1) ++#define INTSTAT_CARD_INT_S 8 ++#define INTSTAT_RETUNING_INT_M BITFIELD_MASK(1) /* Bit 12 */ ++#define INTSTAT_RETUNING_INT_S 12 ++#define INTSTAT_ERROR_INT_M BITFIELD_MASK(1) /* Bit 15 */ ++#define INTSTAT_ERROR_INT_S 15 ++ ++/* SD_ErrorIntrStatus: Offset 0x032, size = 2 bytes */ ++/* Defs also serve SD_ErrorIntrStatusEnable and SD_ErrorIntrSignalEnable */ ++#define ERRINT_CMD_TIMEOUT_M BITFIELD_MASK(1) ++#define ERRINT_CMD_TIMEOUT_S 0 ++#define ERRINT_CMD_CRC_M BITFIELD_MASK(1) ++#define ERRINT_CMD_CRC_S 1 ++#define ERRINT_CMD_ENDBIT_M BITFIELD_MASK(1) ++#define ERRINT_CMD_ENDBIT_S 2 ++#define ERRINT_CMD_INDEX_M BITFIELD_MASK(1) ++#define ERRINT_CMD_INDEX_S 3 ++#define ERRINT_DATA_TIMEOUT_M BITFIELD_MASK(1) ++#define ERRINT_DATA_TIMEOUT_S 4 ++#define ERRINT_DATA_CRC_M BITFIELD_MASK(1) ++#define ERRINT_DATA_CRC_S 5 ++#define ERRINT_DATA_ENDBIT_M BITFIELD_MASK(1) ++#define ERRINT_DATA_ENDBIT_S 6 ++#define ERRINT_CURRENT_LIMIT_M BITFIELD_MASK(1) ++#define ERRINT_CURRENT_LIMIT_S 7 ++#define ERRINT_AUTO_CMD12_M BITFIELD_MASK(1) ++#define ERRINT_AUTO_CMD12_S 8 ++#define ERRINT_VENDOR_M BITFIELD_MASK(4) ++#define ERRINT_VENDOR_S 12 ++#define ERRINT_ADMA_M BITFIELD_MASK(1) ++#define ERRINT_ADMA_S 9 ++ ++/* Also provide definitions in "normal" form to allow combined masks */ ++#define ERRINT_CMD_TIMEOUT_BIT 0x0001 ++#define ERRINT_CMD_CRC_BIT 0x0002 ++#define ERRINT_CMD_ENDBIT_BIT 0x0004 ++#define ERRINT_CMD_INDEX_BIT 0x0008 ++#define ERRINT_DATA_TIMEOUT_BIT 0x0010 ++#define ERRINT_DATA_CRC_BIT 0x0020 ++#define ERRINT_DATA_ENDBIT_BIT 0x0040 ++#define ERRINT_CURRENT_LIMIT_BIT 0x0080 ++#define ERRINT_AUTO_CMD12_BIT 0x0100 ++#define ERRINT_ADMA_BIT 0x0200 ++ ++/* Masks to select CMD vs. DATA errors */ ++#define ERRINT_CMD_ERRS (ERRINT_CMD_TIMEOUT_BIT | ERRINT_CMD_CRC_BIT |\ ++ ERRINT_CMD_ENDBIT_BIT | ERRINT_CMD_INDEX_BIT) ++#define ERRINT_DATA_ERRS (ERRINT_DATA_TIMEOUT_BIT | ERRINT_DATA_CRC_BIT |\ ++ ERRINT_DATA_ENDBIT_BIT | ERRINT_ADMA_BIT) ++#define ERRINT_TRANSFER_ERRS (ERRINT_CMD_ERRS | ERRINT_DATA_ERRS) ++ ++/* SD_WakeupCntr_BlockGapCntrl : Offset 0x02A , size = bytes */ ++/* SD_ClockCntrl : Offset 0x02C , size = bytes */ ++/* SD_SoftwareReset_TimeoutCntrl : Offset 0x02E , size = bytes */ ++/* SD_IntrStatus : Offset 0x030 , size = bytes */ ++/* SD_ErrorIntrStatus : Offset 0x032 , size = bytes */ ++/* SD_IntrStatusEnable : Offset 0x034 , size = bytes */ ++/* SD_ErrorIntrStatusEnable : Offset 0x036 , size = bytes */ ++/* SD_IntrSignalEnable : Offset 0x038 , size = bytes */ ++/* SD_ErrorIntrSignalEnable : Offset 0x03A , size = bytes */ ++/* SD_CMD12ErrorStatus : Offset 0x03C , size = bytes */ ++/* SD_Capabilities : Offset 0x040 , size = bytes */ ++/* SD_MaxCurCap : Offset 0x048 , size = bytes */ ++/* SD_MaxCurCap_Reserved: Offset 0x04C , size = bytes */ ++/* SD_SlotInterruptStatus: Offset 0x0FC , size = bytes */ ++/* SD_HostControllerVersion : Offset 0x0FE , size = bytes */ ++ ++/* SDIO Host Control Register DMA Mode Definitions */ ++#define SDIOH_SDMA_MODE 0 ++#define SDIOH_ADMA1_MODE 1 ++#define SDIOH_ADMA2_MODE 2 ++#define SDIOH_ADMA2_64_MODE 3 ++ ++#define ADMA2_ATTRIBUTE_VALID (1 << 0) /* ADMA Descriptor line valid */ ++#define ADMA2_ATTRIBUTE_END (1 << 1) /* End of Descriptor */ ++#define ADMA2_ATTRIBUTE_INT (1 << 2) /* Interrupt when line is done */ ++#define ADMA2_ATTRIBUTE_ACT_NOP (0 << 4) /* Skip current line, go to next. */ ++#define ADMA2_ATTRIBUTE_ACT_RSV (1 << 4) /* Same as NOP */ ++#define ADMA1_ATTRIBUTE_ACT_SET (1 << 4) /* ADMA1 Only - set transfer length */ ++#define ADMA2_ATTRIBUTE_ACT_TRAN (2 << 4) /* Transfer Data of one descriptor line. */ ++#define ADMA2_ATTRIBUTE_ACT_LINK (3 << 4) /* Link Descriptor */ ++ ++/* ADMA2 Descriptor Table Entry for 32-bit Address */ ++typedef struct adma2_dscr_32b { ++ uint32 len_attr; ++ uint32 phys_addr; ++} adma2_dscr_32b_t; ++ ++/* ADMA1 Descriptor Table Entry */ ++typedef struct adma1_dscr { ++ uint32 phys_addr_attr; ++} adma1_dscr_t; ++ ++#endif /* _SDIOH_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/sdiovar.h b/drivers/net/wireless/bcmdhd/include/sdiovar.h +new file mode 100644 +index 00000000..83f82de2 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/sdiovar.h +@@ -0,0 +1,58 @@ ++/* ++ * Structure used by apps whose drivers access SDIO drivers. ++ * Pulled out separately so dhdu and wlu can both use it. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: sdiovar.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++#ifndef _sdiovar_h_ ++#define _sdiovar_h_ ++ ++#include ++ ++/* require default structure packing */ ++#define BWL_DEFAULT_PACKING ++#include ++ ++typedef struct sdreg { ++ int func; ++ int offset; ++ int value; ++} sdreg_t; ++ ++/* Common msglevel constants */ ++#define SDH_ERROR_VAL 0x0001 /* Error */ ++#define SDH_TRACE_VAL 0x0002 /* Trace */ ++#define SDH_INFO_VAL 0x0004 /* Info */ ++#define SDH_DEBUG_VAL 0x0008 /* Debug */ ++#define SDH_DATA_VAL 0x0010 /* Data */ ++#define SDH_CTRL_VAL 0x0020 /* Control Regs */ ++#define SDH_LOG_VAL 0x0040 /* Enable bcmlog */ ++#define SDH_DMA_VAL 0x0080 /* DMA */ ++ ++#define NUM_PREV_TRANSACTIONS 16 ++ ++ ++#include ++ ++#endif /* _sdiovar_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/include/siutils.h b/drivers/net/wireless/bcmdhd/include/siutils.h +new file mode 100644 +index 00000000..a797b3d1 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/siutils.h +@@ -0,0 +1,332 @@ ++/* ++ * Misc utility routines for accessing the SOC Interconnects ++ * of Broadcom HNBU chips. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: siutils.h 347614 2012-07-27 10:24:51Z $ ++ */ ++ ++#ifndef _siutils_h_ ++#define _siutils_h_ ++ ++ ++struct si_pub { ++ uint socitype; ++ ++ uint bustype; ++ uint buscoretype; ++ uint buscorerev; ++ uint buscoreidx; ++ int ccrev; ++ uint32 cccaps; ++ uint32 cccaps_ext; ++ int pmurev; ++ uint32 pmucaps; ++ uint boardtype; ++ uint boardrev; ++ uint boardvendor; ++ uint boardflags; ++ uint boardflags2; ++ uint chip; ++ uint chiprev; ++ uint chippkg; ++ uint32 chipst; ++ bool issim; ++ uint socirev; ++ bool pci_pr32414; ++ ++}; ++ ++ ++typedef const struct si_pub si_t; ++ ++ ++ ++#define SI_OSH NULL ++ ++#define BADIDX (SI_MAXCORES + 1) ++ ++ ++#define XTAL 0x1 ++#define PLL 0x2 ++ ++ ++#define CLK_FAST 0 ++#define CLK_DYNAMIC 2 ++ ++ ++#define GPIO_DRV_PRIORITY 0 ++#define GPIO_APP_PRIORITY 1 ++#define GPIO_HI_PRIORITY 2 ++ ++ ++#define GPIO_PULLUP 0 ++#define GPIO_PULLDN 1 ++ ++ ++#define GPIO_REGEVT 0 ++#define GPIO_REGEVT_INTMSK 1 ++#define GPIO_REGEVT_INTPOL 2 ++ ++ ++#define SI_DEVPATH_BUFSZ 16 ++ ++ ++#define SI_DOATTACH 1 ++#define SI_PCIDOWN 2 ++#define SI_PCIUP 3 ++ ++#define ISSIM_ENAB(sih) 0 ++ ++ ++#if defined(BCMPMUCTL) ++#define PMUCTL_ENAB(sih) (BCMPMUCTL) ++#else ++#define PMUCTL_ENAB(sih) ((sih)->cccaps & CC_CAP_PMU) ++#endif ++ ++ ++#if defined(BCMPMUCTL) && BCMPMUCTL ++#define CCCTL_ENAB(sih) (0) ++#define CCPLL_ENAB(sih) (0) ++#else ++#define CCCTL_ENAB(sih) ((sih)->cccaps & CC_CAP_PWR_CTL) ++#define CCPLL_ENAB(sih) ((sih)->cccaps & CC_CAP_PLL_MASK) ++#endif ++ ++typedef void (*gpio_handler_t)(uint32 stat, void *arg); ++ ++#define CC_BTCOEX_EN_MASK 0x01 ++ ++#define GPIO_CTRL_EPA_EN_MASK 0x40 ++ ++#define GPIO_CTRL_5_6_EN_MASK 0x60 ++#define GPIO_CTRL_7_6_EN_MASK 0xC0 ++#define GPIO_OUT_7_EN_MASK 0x80 ++ ++ ++ ++#define SI_CR4_CAP (0x04) ++#define SI_CR4_BANKIDX (0x40) ++#define SI_CR4_BANKINFO (0x44) ++ ++#define ARMCR4_TCBBNB_MASK 0xf0 ++#define ARMCR4_TCBBNB_SHIFT 4 ++#define ARMCR4_TCBANB_MASK 0xf ++#define ARMCR4_TCBANB_SHIFT 0 ++ ++#define SICF_CPUHALT (0x0020) ++#define ARMCR4_BSZ_MASK 0x3f ++#define ARMCR4_BSZ_MULT 8192 ++ ++ ++ ++extern si_t *si_attach(uint pcidev, osl_t *osh, void *regs, uint bustype, ++ void *sdh, char **vars, uint *varsz); ++extern si_t *si_kattach(osl_t *osh); ++extern void si_detach(si_t *sih); ++extern bool si_pci_war16165(si_t *sih); ++ ++extern uint si_corelist(si_t *sih, uint coreid[]); ++extern uint si_coreid(si_t *sih); ++extern uint si_flag(si_t *sih); ++extern uint si_intflag(si_t *sih); ++extern uint si_coreidx(si_t *sih); ++extern uint si_coreunit(si_t *sih); ++extern uint si_corevendor(si_t *sih); ++extern uint si_corerev(si_t *sih); ++extern void *si_osh(si_t *sih); ++extern void si_setosh(si_t *sih, osl_t *osh); ++extern uint si_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val); ++extern void *si_coreregs(si_t *sih); ++extern uint si_wrapperreg(si_t *sih, uint32 offset, uint32 mask, uint32 val); ++extern uint32 si_core_cflags(si_t *sih, uint32 mask, uint32 val); ++extern void si_core_cflags_wo(si_t *sih, uint32 mask, uint32 val); ++extern uint32 si_core_sflags(si_t *sih, uint32 mask, uint32 val); ++extern bool si_iscoreup(si_t *sih); ++extern uint si_findcoreidx(si_t *sih, uint coreid, uint coreunit); ++extern void *si_setcoreidx(si_t *sih, uint coreidx); ++extern void *si_setcore(si_t *sih, uint coreid, uint coreunit); ++extern void *si_switch_core(si_t *sih, uint coreid, uint *origidx, uint *intr_val); ++extern void si_restore_core(si_t *sih, uint coreid, uint intr_val); ++extern int si_numaddrspaces(si_t *sih); ++extern uint32 si_addrspace(si_t *sih, uint asidx); ++extern uint32 si_addrspacesize(si_t *sih, uint asidx); ++extern void si_coreaddrspaceX(si_t *sih, uint asidx, uint32 *addr, uint32 *size); ++extern int si_corebist(si_t *sih); ++extern void si_core_reset(si_t *sih, uint32 bits, uint32 resetbits); ++extern void si_core_disable(si_t *sih, uint32 bits); ++extern uint32 si_clock_rate(uint32 pll_type, uint32 n, uint32 m); ++extern bool si_read_pmu_autopll(si_t *sih); ++extern uint32 si_clock(si_t *sih); ++extern uint32 si_alp_clock(si_t *sih); ++extern uint32 si_ilp_clock(si_t *sih); ++extern void si_pci_setup(si_t *sih, uint coremask); ++extern void si_pcmcia_init(si_t *sih); ++extern void si_setint(si_t *sih, int siflag); ++extern bool si_backplane64(si_t *sih); ++extern void si_register_intr_callback(si_t *sih, void *intrsoff_fn, void *intrsrestore_fn, ++ void *intrsenabled_fn, void *intr_arg); ++extern void si_deregister_intr_callback(si_t *sih); ++extern void si_clkctl_init(si_t *sih); ++extern uint16 si_clkctl_fast_pwrup_delay(si_t *sih); ++extern bool si_clkctl_cc(si_t *sih, uint mode); ++extern int si_clkctl_xtal(si_t *sih, uint what, bool on); ++extern uint32 si_gpiotimerval(si_t *sih, uint32 mask, uint32 val); ++extern void si_btcgpiowar(si_t *sih); ++extern bool si_deviceremoved(si_t *sih); ++extern uint32 si_socram_size(si_t *sih); ++extern uint32 si_socdevram_size(si_t *sih); ++extern uint32 si_socram_srmem_size(si_t *sih); ++extern void si_socdevram(si_t *sih, bool set, uint8 *ennable, uint8 *protect, uint8 *remap); ++extern bool si_socdevram_pkg(si_t *sih); ++extern bool si_socdevram_remap_isenb(si_t *sih); ++extern uint32 si_socdevram_remap_size(si_t *sih); ++ ++extern void si_watchdog(si_t *sih, uint ticks); ++extern void si_watchdog_ms(si_t *sih, uint32 ms); ++extern uint32 si_watchdog_msticks(void); ++extern void *si_gpiosetcore(si_t *sih); ++extern uint32 si_gpiocontrol(si_t *sih, uint32 mask, uint32 val, uint8 priority); ++extern uint32 si_gpioouten(si_t *sih, uint32 mask, uint32 val, uint8 priority); ++extern uint32 si_gpioout(si_t *sih, uint32 mask, uint32 val, uint8 priority); ++extern uint32 si_gpioin(si_t *sih); ++extern uint32 si_gpiointpolarity(si_t *sih, uint32 mask, uint32 val, uint8 priority); ++extern uint32 si_gpiointmask(si_t *sih, uint32 mask, uint32 val, uint8 priority); ++extern uint32 si_gpioled(si_t *sih, uint32 mask, uint32 val); ++extern uint32 si_gpioreserve(si_t *sih, uint32 gpio_num, uint8 priority); ++extern uint32 si_gpiorelease(si_t *sih, uint32 gpio_num, uint8 priority); ++extern uint32 si_gpiopull(si_t *sih, bool updown, uint32 mask, uint32 val); ++extern uint32 si_gpioevent(si_t *sih, uint regtype, uint32 mask, uint32 val); ++extern uint32 si_gpio_int_enable(si_t *sih, bool enable); ++ ++ ++extern void *si_gpio_handler_register(si_t *sih, uint32 e, bool lev, gpio_handler_t cb, void *arg); ++extern void si_gpio_handler_unregister(si_t *sih, void* gpioh); ++extern void si_gpio_handler_process(si_t *sih); ++ ++ ++extern bool si_pci_pmecap(si_t *sih); ++struct osl_info; ++extern bool si_pci_fastpmecap(struct osl_info *osh); ++extern bool si_pci_pmestat(si_t *sih); ++extern void si_pci_pmeclr(si_t *sih); ++extern void si_pci_pmeen(si_t *sih); ++extern void si_pci_pmestatclr(si_t *sih); ++extern uint si_pcie_readreg(void *sih, uint addrtype, uint offset); ++ ++extern void si_sdio_init(si_t *sih); ++ ++extern uint16 si_d11_devid(si_t *sih); ++extern int si_corepciid(si_t *sih, uint func, uint16 *pcivendor, uint16 *pcidevice, ++ uint8 *pciclass, uint8 *pcisubclass, uint8 *pciprogif, uint8 *pciheader); ++ ++#define si_eci(sih) 0 ++static INLINE void * si_eci_init(si_t *sih) {return NULL;} ++#define si_eci_notify_bt(sih, type, val) (0) ++#define si_seci(sih) 0 ++#define si_seci_upd(sih, a) do {} while (0) ++static INLINE void * si_seci_init(si_t *sih, uint8 use_seci) {return NULL;} ++#define si_seci_down(sih) do {} while (0) ++ ++ ++extern bool si_is_otp_disabled(si_t *sih); ++extern bool si_is_otp_powered(si_t *sih); ++extern void si_otp_power(si_t *sih, bool on); ++ ++ ++extern bool si_is_sprom_available(si_t *sih); ++extern bool si_is_sprom_enabled(si_t *sih); ++extern void si_sprom_enable(si_t *sih, bool enable); ++ ++ ++extern int si_cis_source(si_t *sih); ++#define CIS_DEFAULT 0 ++#define CIS_SROM 1 ++#define CIS_OTP 2 ++ ++ ++#define DEFAULT_FAB 0x0 ++#define CSM_FAB7 0x1 ++#define TSMC_FAB12 0x2 ++#define SMIC_FAB4 0x3 ++extern int si_otp_fabid(si_t *sih, uint16 *fabid, bool rw); ++extern uint16 si_fabid(si_t *sih); ++ ++ ++extern int si_devpath(si_t *sih, char *path, int size); ++ ++extern char *si_getdevpathvar(si_t *sih, const char *name); ++extern int si_getdevpathintvar(si_t *sih, const char *name); ++extern char *si_coded_devpathvar(si_t *sih, char *varname, int var_len, const char *name); ++ ++ ++extern uint8 si_pcieclkreq(si_t *sih, uint32 mask, uint32 val); ++extern uint32 si_pcielcreg(si_t *sih, uint32 mask, uint32 val); ++extern void si_war42780_clkreq(si_t *sih, bool clkreq); ++extern void si_pci_down(si_t *sih); ++extern void si_pci_up(si_t *sih); ++extern void si_pci_sleep(si_t *sih); ++extern void si_pcie_war_ovr_update(si_t *sih, uint8 aspm); ++extern void si_pcie_power_save_enable(si_t *sih, bool enable); ++extern void si_pcie_extendL1timer(si_t *sih, bool extend); ++extern int si_pci_fixcfg(si_t *sih); ++extern void si_chippkg_set(si_t *sih, uint); ++ ++extern void si_chipcontrl_btshd0_4331(si_t *sih, bool on); ++extern void si_chipcontrl_restore(si_t *sih, uint32 val); ++extern uint32 si_chipcontrl_read(si_t *sih); ++extern void si_chipcontrl_epa4331(si_t *sih, bool on); ++extern void si_chipcontrl_epa4331_wowl(si_t *sih, bool enter_wowl); ++extern void si_chipcontrl_srom4360(si_t *sih, bool on); ++ ++extern void si_epa_4313war(si_t *sih); ++extern void si_btc_enable_chipcontrol(si_t *sih); ++ ++extern void si_btcombo_p250_4313_war(si_t *sih); ++extern void si_btcombo_43228_war(si_t *sih); ++extern void si_clk_pmu_htavail_set(si_t *sih, bool set_clear); ++extern uint si_pll_reset(si_t *sih); ++ ++ ++extern bool si_taclear(si_t *sih, bool details); ++ ++ ++ ++extern uint32 si_pciereg(si_t *sih, uint32 offset, uint32 mask, uint32 val, uint type); ++extern uint32 si_pcieserdesreg(si_t *sih, uint32 mdioslave, uint32 offset, uint32 mask, uint32 val); ++extern void si_pcie_set_request_size(si_t *sih, uint16 size); ++extern uint16 si_pcie_get_request_size(si_t *sih); ++extern uint16 si_pcie_get_ssid(si_t *sih); ++extern uint32 si_pcie_get_bar0(si_t *sih); ++extern int si_pcie_configspace_cache(si_t *sih); ++extern int si_pcie_configspace_restore(si_t *sih); ++extern int si_pcie_configspace_get(si_t *sih, uint8 *buf, uint size); ++ ++char *si_getnvramflvar(si_t *sih, const char *name); ++ ++ ++extern uint32 si_tcm_size(si_t *sih); ++ ++extern int si_set_sromctl(si_t *sih, uint32 value); ++extern uint32 si_get_sromctl(si_t *sih); ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/trxhdr.h b/drivers/net/wireless/bcmdhd/include/trxhdr.h +new file mode 100644 +index 00000000..bf92a565 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/trxhdr.h +@@ -0,0 +1,53 @@ ++/* ++ * TRX image file header format. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: trxhdr.h 260898 2011-05-20 23:11:12Z $ ++ */ ++ ++#ifndef _TRX_HDR_H ++#define _TRX_HDR_H ++ ++#include ++ ++#define TRX_MAGIC 0x30524448 /* "HDR0" */ ++#define TRX_VERSION 1 /* Version 1 */ ++#define TRX_MAX_LEN 0x3B0000 /* Max length */ ++#define TRX_NO_HEADER 1 /* Do not write TRX header */ ++#define TRX_GZ_FILES 0x2 /* Contains up to TRX_MAX_OFFSET individual gzip files */ ++#define TRX_EMBED_UCODE 0x8 /* Trx contains embedded ucode image */ ++#define TRX_ROMSIM_IMAGE 0x10 /* Trx contains ROM simulation image */ ++#define TRX_UNCOMP_IMAGE 0x20 /* Trx contains uncompressed rtecdc.bin image */ ++#define TRX_MAX_OFFSET 3 /* Max number of individual files */ ++ ++struct trx_header { ++ uint32 magic; /* "HDR0" */ ++ uint32 len; /* Length of file including header */ ++ uint32 crc32; /* 32-bit CRC from flag_version to end of file */ ++ uint32 flag_version; /* 0:15 flags, 16:31 version */ ++ uint32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of header */ ++}; ++ ++/* Compatibility */ ++typedef struct trx_header TRXHDR, *PTRXHDR; ++ ++#endif /* _TRX_HDR_H */ +diff --git a/drivers/net/wireless/bcmdhd/include/typedefs.h b/drivers/net/wireless/bcmdhd/include/typedefs.h +new file mode 100644 +index 00000000..4eee5bab +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/typedefs.h +@@ -0,0 +1,310 @@ ++/* ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * $Id: typedefs.h 286783 2011-09-29 06:18:57Z $ ++ */ ++ ++#ifndef _TYPEDEFS_H_ ++#define _TYPEDEFS_H_ ++ ++#ifdef SITE_TYPEDEFS ++ ++ ++ ++#include "site_typedefs.h" ++ ++#else ++ ++ ++ ++#ifdef __cplusplus ++ ++#define TYPEDEF_BOOL ++#ifndef FALSE ++#define FALSE false ++#endif ++#ifndef TRUE ++#define TRUE true ++#endif ++ ++#else ++ ++ ++#endif ++ ++#if defined(__x86_64__) ++#define TYPEDEF_UINTPTR ++typedef unsigned long long int uintptr; ++#endif ++ ++ ++ ++ ++ ++#if defined(_NEED_SIZE_T_) ++typedef long unsigned int size_t; ++#endif ++ ++ ++ ++ ++#if defined(__sparc__) ++#define TYPEDEF_ULONG ++#endif ++ ++ ++ ++#if !defined(LINUX_HYBRID) || defined(LINUX_PORT) ++#define TYPEDEF_UINT ++#ifndef TARGETENV_android ++#define TYPEDEF_USHORT ++#define TYPEDEF_ULONG ++#endif ++#ifdef __KERNEL__ ++#include ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)) ++#define TYPEDEF_BOOL ++#endif ++ ++#if (LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 18)) ++#include ++#ifdef noinline_for_stack ++#define TYPEDEF_BOOL ++#endif ++#endif ++#endif ++#endif ++ ++ ++ ++ ++ ++#if defined(__GNUC__) && defined(__STRICT_ANSI__) ++#define TYPEDEF_INT64 ++#define TYPEDEF_UINT64 ++#endif ++ ++ ++#if defined(__ICL) ++ ++#define TYPEDEF_INT64 ++ ++#if defined(__STDC__) ++#define TYPEDEF_UINT64 ++#endif ++ ++#endif ++ ++#if !defined(__DJGPP__) ++ ++ ++#if defined(__KERNEL__) ++ ++ ++#if !defined(LINUX_HYBRID) || defined(LINUX_PORT) ++#include ++#endif ++ ++#else ++ ++ ++#include ++ ++#endif ++ ++#endif ++ ++ ++ ++ ++#define USE_TYPEDEF_DEFAULTS ++ ++#endif ++ ++ ++ ++ ++#ifdef USE_TYPEDEF_DEFAULTS ++#undef USE_TYPEDEF_DEFAULTS ++ ++#ifndef TYPEDEF_BOOL ++typedef unsigned char bool; ++#endif ++ ++ ++ ++#ifndef TYPEDEF_UCHAR ++typedef unsigned char uchar; ++#endif ++ ++#ifndef TYPEDEF_USHORT ++typedef unsigned short ushort; ++#endif ++ ++#ifndef TYPEDEF_UINT ++typedef unsigned int uint; ++#endif ++ ++#ifndef TYPEDEF_ULONG ++typedef unsigned long ulong; ++#endif ++ ++ ++ ++#ifndef TYPEDEF_UINT8 ++typedef unsigned char uint8; ++#endif ++ ++#ifndef TYPEDEF_UINT16 ++typedef unsigned short uint16; ++#endif ++ ++#ifndef TYPEDEF_UINT32 ++typedef unsigned int uint32; ++#endif ++ ++#ifndef TYPEDEF_UINT64 ++typedef unsigned long long uint64; ++#endif ++ ++#ifndef TYPEDEF_UINTPTR ++typedef unsigned int uintptr; ++#endif ++ ++#ifndef TYPEDEF_INT8 ++typedef signed char int8; ++#endif ++ ++#ifndef TYPEDEF_INT16 ++typedef signed short int16; ++#endif ++ ++#ifndef TYPEDEF_INT32 ++typedef signed int int32; ++#endif ++ ++#ifndef TYPEDEF_INT64 ++typedef signed long long int64; ++#endif ++ ++ ++ ++#ifndef TYPEDEF_FLOAT32 ++typedef float float32; ++#endif ++ ++#ifndef TYPEDEF_FLOAT64 ++typedef double float64; ++#endif ++ ++ ++ ++#ifndef TYPEDEF_FLOAT_T ++ ++#if defined(FLOAT32) ++typedef float32 float_t; ++#else ++typedef float64 float_t; ++#endif ++ ++#endif ++ ++ ++ ++#ifndef FALSE ++#define FALSE 0 ++#endif ++ ++#ifndef TRUE ++#define TRUE 1 ++#endif ++ ++#ifndef NULL ++#define NULL 0 ++#endif ++ ++#ifndef OFF ++#define OFF 0 ++#endif ++ ++#ifndef ON ++#define ON 1 ++#endif ++ ++#define AUTO (-1) ++ ++ ++ ++#ifndef PTRSZ ++#define PTRSZ sizeof(char*) ++#endif ++ ++ ++ ++#if defined(__GNUC__) || defined(__lint) ++ #define BWL_COMPILER_GNU ++#elif defined(__CC_ARM) && __CC_ARM ++ #define BWL_COMPILER_ARMCC ++#else ++ #error "Unknown compiler!" ++#endif ++ ++ ++#ifndef INLINE ++ #if defined(BWL_COMPILER_MICROSOFT) ++ #define INLINE __inline ++ #elif defined(BWL_COMPILER_GNU) ++ #define INLINE __inline__ ++ #elif defined(BWL_COMPILER_ARMCC) ++ #define INLINE __inline ++ #else ++ #define INLINE ++ #endif ++#endif ++ ++#undef TYPEDEF_BOOL ++#undef TYPEDEF_UCHAR ++#undef TYPEDEF_USHORT ++#undef TYPEDEF_UINT ++#undef TYPEDEF_ULONG ++#undef TYPEDEF_UINT8 ++#undef TYPEDEF_UINT16 ++#undef TYPEDEF_UINT32 ++#undef TYPEDEF_UINT64 ++#undef TYPEDEF_UINTPTR ++#undef TYPEDEF_INT8 ++#undef TYPEDEF_INT16 ++#undef TYPEDEF_INT32 ++#undef TYPEDEF_INT64 ++#undef TYPEDEF_FLOAT32 ++#undef TYPEDEF_FLOAT64 ++#undef TYPEDEF_FLOAT_T ++ ++#endif ++ ++ ++#define UNUSED_PARAMETER(x) (void)(x) ++ ++ ++#define DISCARD_QUAL(ptr, type) ((type *)(uintptr)(ptr)) ++ ++ ++#include ++#endif +diff --git a/drivers/net/wireless/bcmdhd/include/wlfc_proto.h b/drivers/net/wireless/bcmdhd/include/wlfc_proto.h +new file mode 100644 +index 00000000..6b421b5d +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/wlfc_proto.h +@@ -0,0 +1,233 @@ ++/* ++* Copyright (C) 1999-2012, Broadcom Corporation ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2 (the "GPL"), ++* available at http://www.broadcom.com/licenses/GPLv2.php, with the ++* following added to such license: ++* ++* As a special exception, the copyright holders of this software give you ++* permission to link this software with independent modules, and to copy and ++* distribute the resulting executable under terms of your choice, provided that ++* you also meet, for each linked independent module, the terms and conditions of ++* the license of that module. An independent module is a module which is not ++* derived from this software. The special exception does not apply to any ++* modifications of the software. ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a license ++* other than the GPL, without Broadcom's express prior written consent. ++* $Id: wlfc_proto.h 361006 2012-10-05 07:45:51Z $ ++* ++*/ ++#ifndef __wlfc_proto_definitions_h__ ++#define __wlfc_proto_definitions_h__ ++ ++ /* Use TLV to convey WLFC information. ++ --------------------------------------------------------------------------- ++ | Type | Len | value | Description ++ --------------------------------------------------------------------------- ++ | 1 | 1 | (handle) | MAC OPEN ++ --------------------------------------------------------------------------- ++ | 2 | 1 | (handle) | MAC CLOSE ++ --------------------------------------------------------------------------- ++ | 3 | 2 | (count, handle, prec_bmp)| Set the credit depth for a MAC dstn ++ --------------------------------------------------------------------------- ++ | 4 | 4 | see pkttag comments | TXSTATUS ++ --------------------------------------------------------------------------- ++ | 5 | 4 | see pkttag comments | PKKTTAG [host->firmware] ++ --------------------------------------------------------------------------- ++ | 6 | 8 | (handle, ifid, MAC) | MAC ADD ++ --------------------------------------------------------------------------- ++ | 7 | 8 | (handle, ifid, MAC) | MAC DEL ++ --------------------------------------------------------------------------- ++ | 8 | 1 | (rssi) | RSSI - RSSI value for the packet. ++ --------------------------------------------------------------------------- ++ | 9 | 1 | (interface ID) | Interface OPEN ++ --------------------------------------------------------------------------- ++ | 10 | 1 | (interface ID) | Interface CLOSE ++ --------------------------------------------------------------------------- ++ | 11 | 8 | fifo credit returns map | FIFO credits back to the host ++ | | | | ++ | | | | -------------------------------------- ++ | | | | | ac0 | ac1 | ac2 | ac3 | bcmc | atim | ++ | | | | -------------------------------------- ++ | | | | ++ --------------------------------------------------------------------------- ++ | 12 | 2 | MAC handle, | Host provides a bitmap of pending ++ | | | AC[0-3] traffic bitmap | unicast traffic for MAC-handle dstn. ++ | | | | [host->firmware] ++ --------------------------------------------------------------------------- ++ | 13 | 3 | (count, handle, prec_bmp)| One time request for packet to a specific ++ | | | | MAC destination. ++ --------------------------------------------------------------------------- ++ | 15 | 1 | interface ID | NIC period start ++ --------------------------------------------------------------------------- ++ | 16 | 1 | interface ID | NIC period end ++ --------------------------------------------------------------------------- ++ | 17 | 3 | (ifid, txs) | Action frame tx status ++ --------------------------------------------------------------------------- ++ | 255 | N/A | N/A | FILLER - This is a special type ++ | | | | that has no length or value. ++ | | | | Typically used for padding. ++ --------------------------------------------------------------------------- ++ */ ++ ++#define WLFC_CTL_TYPE_MAC_OPEN 1 ++#define WLFC_CTL_TYPE_MAC_CLOSE 2 ++#define WLFC_CTL_TYPE_MAC_REQUEST_CREDIT 3 ++#define WLFC_CTL_TYPE_TXSTATUS 4 ++#define WLFC_CTL_TYPE_PKTTAG 5 ++ ++#define WLFC_CTL_TYPE_MACDESC_ADD 6 ++#define WLFC_CTL_TYPE_MACDESC_DEL 7 ++#define WLFC_CTL_TYPE_RSSI 8 ++ ++#define WLFC_CTL_TYPE_INTERFACE_OPEN 9 ++#define WLFC_CTL_TYPE_INTERFACE_CLOSE 10 ++ ++#define WLFC_CTL_TYPE_FIFO_CREDITBACK 11 ++ ++#define WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP 12 ++#define WLFC_CTL_TYPE_MAC_REQUEST_PACKET 13 ++#define WLFC_CTL_TYPE_HOST_REORDER_RXPKTS 14 ++ ++#define WLFC_CTL_TYPE_NIC_PRD_START 15 ++#define WLFC_CTL_TYPE_NIC_PRD_END 16 ++#define WLFC_CTL_TYPE_AF_TXS 17 ++#define WLFC_CTL_TYPE_TRANS_ID 18 ++#define WLFC_CTL_TYPE_COMP_TXSTATUS 19 ++ ++#define WLFC_CTL_TYPE_FILLER 255 ++ ++#define WLFC_CTL_VALUE_LEN_MACDESC 8 /* handle, interface, MAC */ ++ ++#define WLFC_CTL_VALUE_LEN_MAC 1 /* MAC-handle */ ++#define WLFC_CTL_VALUE_LEN_RSSI 1 ++ ++#define WLFC_CTL_VALUE_LEN_INTERFACE 1 ++#define WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP 2 ++ ++#define WLFC_CTL_VALUE_LEN_TXSTATUS 4 ++#define WLFC_CTL_VALUE_LEN_PKTTAG 4 ++ ++/* enough space to host all 4 ACs, bc/mc and atim fifo credit */ ++#define WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK 6 ++ ++#define WLFC_CTL_VALUE_LEN_REQUEST_CREDIT 3 /* credit, MAC-handle, prec_bitmap */ ++#define WLFC_CTL_VALUE_LEN_REQUEST_PACKET 3 /* credit, MAC-handle, prec_bitmap */ ++ ++#define WLFC_CTL_VALUE_LEN_NIC_PRD_START 1 ++#define WLFC_CTL_VALUE_LEN_NIC_PRD_END 1 ++#define WLFC_CTL_VALUE_LEN_AF_TXS 3 ++ ++ ++#define WLFC_PKTID_GEN_MASK 0x80000000 ++#define WLFC_PKTID_GEN_SHIFT 31 ++ ++#define WLFC_PKTID_GEN(x) (((x) & WLFC_PKTID_GEN_MASK) >> WLFC_PKTID_GEN_SHIFT) ++#define WLFC_PKTID_SETGEN(x, gen) (x) = ((x) & ~WLFC_PKTID_GEN_MASK) | \ ++ (((gen) << WLFC_PKTID_GEN_SHIFT) & WLFC_PKTID_GEN_MASK) ++ ++#define WLFC_PKTFLAG_PKTFROMHOST 0x01 ++#define WLFC_PKTFLAG_PKT_REQUESTED 0x02 ++ ++#define WL_TXSTATUS_FLAGS_MASK 0xf /* allow 4 bits only */ ++#define WL_TXSTATUS_FLAGS_SHIFT 27 ++ ++#define WL_TXSTATUS_SET_FLAGS(x, flags) ((x) = \ ++ ((x) & ~(WL_TXSTATUS_FLAGS_MASK << WL_TXSTATUS_FLAGS_SHIFT)) | \ ++ (((flags) & WL_TXSTATUS_FLAGS_MASK) << WL_TXSTATUS_FLAGS_SHIFT)) ++#define WL_TXSTATUS_GET_FLAGS(x) (((x) >> WL_TXSTATUS_FLAGS_SHIFT) & \ ++ WL_TXSTATUS_FLAGS_MASK) ++ ++#define WL_TXSTATUS_FIFO_MASK 0x7 /* allow 3 bits for FIFO ID */ ++#define WL_TXSTATUS_FIFO_SHIFT 24 ++ ++#define WL_TXSTATUS_SET_FIFO(x, flags) ((x) = \ ++ ((x) & ~(WL_TXSTATUS_FIFO_MASK << WL_TXSTATUS_FIFO_SHIFT)) | \ ++ (((flags) & WL_TXSTATUS_FIFO_MASK) << WL_TXSTATUS_FIFO_SHIFT)) ++#define WL_TXSTATUS_GET_FIFO(x) (((x) >> WL_TXSTATUS_FIFO_SHIFT) & WL_TXSTATUS_FIFO_MASK) ++ ++#define WL_TXSTATUS_PKTID_MASK 0xffffff /* allow 24 bits */ ++#define WL_TXSTATUS_SET_PKTID(x, num) ((x) = \ ++ ((x) & ~WL_TXSTATUS_PKTID_MASK) | (num)) ++#define WL_TXSTATUS_GET_PKTID(x) ((x) & WL_TXSTATUS_PKTID_MASK) ++ ++/* 32 STA should be enough??, 6 bits; Must be power of 2 */ ++#define WLFC_MAC_DESC_TABLE_SIZE 32 ++#define WLFC_MAX_IFNUM 16 ++#define WLFC_MAC_DESC_ID_INVALID 0xff ++ ++/* b[7:5] -reuse guard, b[4:0] -value */ ++#define WLFC_MAC_DESC_GET_LOOKUP_INDEX(x) ((x) & 0x1f) ++ ++#define WLFC_PKTFLAG_SET_PKTREQUESTED(x) (x) |= \ ++ (WLFC_PKTFLAG_PKT_REQUESTED << WL_TXSTATUS_FLAGS_SHIFT) ++ ++#define WLFC_PKTFLAG_CLR_PKTREQUESTED(x) (x) &= \ ++ ~(WLFC_PKTFLAG_PKT_REQUESTED << WL_TXSTATUS_FLAGS_SHIFT) ++ ++#define WL_TXSTATUS_GENERATION_MASK 1 ++#define WL_TXSTATUS_GENERATION_SHIFT 31 ++ ++#define WLFC_PKTFLAG_SET_GENERATION(x, gen) ((x) = \ ++ ((x) & ~(WL_TXSTATUS_GENERATION_MASK << WL_TXSTATUS_GENERATION_SHIFT)) | \ ++ (((gen) & WL_TXSTATUS_GENERATION_MASK) << WL_TXSTATUS_GENERATION_SHIFT)) ++ ++#define WLFC_PKTFLAG_GENERATION(x) (((x) >> WL_TXSTATUS_GENERATION_SHIFT) & \ ++ WL_TXSTATUS_GENERATION_MASK) ++ ++#define WLFC_MAX_PENDING_DATALEN 120 ++ ++/* host is free to discard the packet */ ++#define WLFC_CTL_PKTFLAG_DISCARD 0 ++/* D11 suppressed a packet */ ++#define WLFC_CTL_PKTFLAG_D11SUPPRESS 1 ++/* WL firmware suppressed a packet because MAC is ++ already in PSMode (short time window) ++*/ ++#define WLFC_CTL_PKTFLAG_WLSUPPRESS 2 ++/* Firmware tossed this packet */ ++#define WLFC_CTL_PKTFLAG_TOSSED_BYWLC 3 ++ ++#define WLFC_D11_STATUS_INTERPRET(txs) \ ++ (((txs)->status.suppr_ind != 0) ? WLFC_CTL_PKTFLAG_D11SUPPRESS : WLFC_CTL_PKTFLAG_DISCARD) ++ ++#ifdef PROP_TXSTATUS_DEBUG ++#define WLFC_DBGMESG(x) printf x ++/* wlfc-breadcrumb */ ++#define WLFC_BREADCRUMB(x) do {if ((x) == NULL) \ ++ {printf("WLFC: %s():%d:caller:%p\n", \ ++ __FUNCTION__, __LINE__, __builtin_return_address(0));}} while (0) ++#define WLFC_PRINTMAC(banner, ea) do {printf("%s MAC: [%02x:%02x:%02x:%02x:%02x:%02x]\n", \ ++ banner, ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]); } while (0) ++#define WLFC_WHEREIS(s) printf("WLFC: at %s():%d, %s\n", __FUNCTION__, __LINE__, (s)) ++#else ++#define WLFC_DBGMESG(x) ++#define WLFC_BREADCRUMB(x) ++#define WLFC_PRINTMAC(banner, ea) ++#define WLFC_WHEREIS(s) ++#endif ++ ++/* AMPDU host reorder packet flags */ ++#define WLHOST_REORDERDATA_MAXFLOWS 256 ++#define WLHOST_REORDERDATA_LEN 10 ++#define WLHOST_REORDERDATA_TOTLEN (WLHOST_REORDERDATA_LEN + 1 + 1) /* +tag +len */ ++ ++#define WLHOST_REORDERDATA_FLOWID_OFFSET 0 ++#define WLHOST_REORDERDATA_MAXIDX_OFFSET 2 ++#define WLHOST_REORDERDATA_FLAGS_OFFSET 4 ++#define WLHOST_REORDERDATA_CURIDX_OFFSET 6 ++#define WLHOST_REORDERDATA_EXPIDX_OFFSET 8 ++ ++#define WLHOST_REORDERDATA_DEL_FLOW 0x01 ++#define WLHOST_REORDERDATA_FLUSH_ALL 0x02 ++#define WLHOST_REORDERDATA_CURIDX_VALID 0x04 ++#define WLHOST_REORDERDATA_EXPIDX_VALID 0x08 ++#define WLHOST_REORDERDATA_NEW_HOLE 0x10 ++/* transaction id data len byte 0: rsvd, byte 1: seqnumber, byte 2-5 will be used for timestampe */ ++#define WLFC_CTL_TRANS_ID_LEN 6 ++ ++#endif /* __wlfc_proto_definitions_h__ */ +diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h +new file mode 100644 +index 00000000..c8c19950 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/include/wlioctl.h +@@ -0,0 +1,5052 @@ ++/* ++ * Custom OID/ioctl definitions for ++ * Broadcom 802.11abg Networking Device Driver ++ * ++ * Definitions subject to change without notice. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wlioctl.h 366141 2012-11-01 01:55:06Z $ ++ */ ++ ++#ifndef _wlioctl_h_ ++#define _wlioctl_h_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++#include ++#include ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++/* LINUX_POSTMOGRIFY_REMOVAL: undefined during compile phase, so its ++ * a no-op for most cases. For hybrid and other open source releases, ++ * its defined during a second pass and mogrified out for distribution. ++ */ ++ ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++ ++#ifndef INTF_NAME_SIZ ++#define INTF_NAME_SIZ 16 ++#endif ++ ++/* Used to send ioctls over the transport pipe */ ++typedef struct remote_ioctl { ++ cdc_ioctl_t msg; ++ uint data_len; ++ char intf_name[INTF_NAME_SIZ]; ++} rem_ioctl_t; ++#define REMOTE_SIZE sizeof(rem_ioctl_t) ++ ++#define ACTION_FRAME_SIZE 1800 ++ ++typedef struct wl_action_frame { ++ struct ether_addr da; ++ uint16 len; ++ uint32 packetId; ++ uint8 data[ACTION_FRAME_SIZE]; ++} wl_action_frame_t; ++ ++#define WL_WIFI_ACTION_FRAME_SIZE sizeof(struct wl_action_frame) ++ ++typedef struct ssid_info ++{ ++ uint8 ssid_len; /* the length of SSID */ ++ uint8 ssid[32]; /* SSID string */ ++} ssid_info_t; ++ ++typedef struct wl_af_params { ++ uint32 channel; ++ int32 dwell_time; ++ struct ether_addr BSSID; ++ wl_action_frame_t action_frame; ++} wl_af_params_t; ++ ++#define WL_WIFI_AF_PARAMS_SIZE sizeof(struct wl_af_params) ++ ++#define MFP_TEST_FLAG_NORMAL 0 ++#define MFP_TEST_FLAG_ANY_KEY 1 ++typedef struct wl_sa_query { ++ uint32 flag; ++ uint8 action; ++ uint16 id; ++ struct ether_addr da; ++} wl_sa_query_t; ++ ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++/* require default structure packing */ ++#define BWL_DEFAULT_PACKING ++#include ++ ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++/* Legacy structure to help keep backward compatible wl tool and tray app */ ++ ++#define LEGACY_WL_BSS_INFO_VERSION 107 /* older version of wl_bss_info struct */ ++ ++typedef struct wl_bss_info_107 { ++ uint32 version; /* version field */ ++ uint32 length; /* byte length of data in this record, ++ * starting at version and including IEs ++ */ ++ struct ether_addr BSSID; ++ uint16 beacon_period; /* units are Kusec */ ++ uint16 capability; /* Capability information */ ++ uint8 SSID_len; ++ uint8 SSID[32]; ++ struct { ++ uint count; /* # rates in this set */ ++ uint8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */ ++ } rateset; /* supported rates */ ++ uint8 channel; /* Channel no. */ ++ uint16 atim_window; /* units are Kusec */ ++ uint8 dtim_period; /* DTIM period */ ++ int16 RSSI; /* receive signal strength (in dBm) */ ++ int8 phy_noise; /* noise (in dBm) */ ++ uint32 ie_length; /* byte length of Information Elements */ ++ /* variable length Information Elements */ ++} wl_bss_info_107_t; ++ ++/* ++ * Per-BSS information structure. ++ */ ++ ++#define LEGACY2_WL_BSS_INFO_VERSION 108 /* old version of wl_bss_info struct */ ++ ++/* BSS info structure ++ * Applications MUST CHECK ie_offset field and length field to access IEs and ++ * next bss_info structure in a vector (in wl_scan_results_t) ++ */ ++typedef struct wl_bss_info_108 { ++ uint32 version; /* version field */ ++ uint32 length; /* byte length of data in this record, ++ * starting at version and including IEs ++ */ ++ struct ether_addr BSSID; ++ uint16 beacon_period; /* units are Kusec */ ++ uint16 capability; /* Capability information */ ++ uint8 SSID_len; ++ uint8 SSID[32]; ++ struct { ++ uint count; /* # rates in this set */ ++ uint8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */ ++ } rateset; /* supported rates */ ++ chanspec_t chanspec; /* chanspec for bss */ ++ uint16 atim_window; /* units are Kusec */ ++ uint8 dtim_period; /* DTIM period */ ++ int16 RSSI; /* receive signal strength (in dBm) */ ++ int8 phy_noise; /* noise (in dBm) */ ++ ++ uint8 n_cap; /* BSS is 802.11N Capable */ ++ uint32 nbss_cap; /* 802.11N BSS Capabilities (based on HT_CAP_*) */ ++ uint8 ctl_ch; /* 802.11N BSS control channel number */ ++ uint32 reserved32[1]; /* Reserved for expansion of BSS properties */ ++ uint8 flags; /* flags */ ++ uint8 reserved[3]; /* Reserved for expansion of BSS properties */ ++ uint8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */ ++ ++ uint16 ie_offset; /* offset at which IEs start, from beginning */ ++ uint32 ie_length; /* byte length of Information Elements */ ++ /* Add new fields here */ ++ /* variable length Information Elements */ ++} wl_bss_info_108_t; ++ ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++#define WL_BSS_INFO_VERSION 109 /* current version of wl_bss_info struct */ ++ ++/* BSS info structure ++ * Applications MUST CHECK ie_offset field and length field to access IEs and ++ * next bss_info structure in a vector (in wl_scan_results_t) ++ */ ++typedef struct wl_bss_info { ++ uint32 version; /* version field */ ++ uint32 length; /* byte length of data in this record, ++ * starting at version and including IEs ++ */ ++ struct ether_addr BSSID; ++ uint16 beacon_period; /* units are Kusec */ ++ uint16 capability; /* Capability information */ ++ uint8 SSID_len; ++ uint8 SSID[32]; ++ struct { ++ uint count; /* # rates in this set */ ++ uint8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */ ++ } rateset; /* supported rates */ ++ chanspec_t chanspec; /* chanspec for bss */ ++ uint16 atim_window; /* units are Kusec */ ++ uint8 dtim_period; /* DTIM period */ ++ int16 RSSI; /* receive signal strength (in dBm) */ ++ int8 phy_noise; /* noise (in dBm) */ ++ ++ uint8 n_cap; /* BSS is 802.11N Capable */ ++ uint32 nbss_cap; /* 802.11N+AC BSS Capabilities */ ++ uint8 ctl_ch; /* 802.11N BSS control channel number */ ++ uint8 padding1[3]; /* explicit struct alignment padding */ ++ uint16 vht_rxmcsmap; /* VHT rx mcs map */ ++ uint16 vht_txmcsmap; /* VHT tx mcs map */ ++ uint8 flags; /* flags */ ++ uint8 vht_cap; /* BSS is vht capable */ ++ uint8 reserved[2]; /* Reserved for expansion of BSS properties */ ++ uint8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */ ++ ++ uint16 ie_offset; /* offset at which IEs start, from beginning */ ++ uint32 ie_length; /* byte length of Information Elements */ ++ int16 SNR; /* average SNR of during frame reception */ ++ /* Add new fields here */ ++ /* variable length Information Elements */ ++} wl_bss_info_t; ++ ++/* bss_info_cap_t flags */ ++#define WL_BSS_FLAGS_FROM_BEACON 0x01 /* bss_info derived from beacon */ ++#define WL_BSS_FLAGS_FROM_CACHE 0x02 /* bss_info collected from cache */ ++#define WL_BSS_FLAGS_RSSI_ONCHANNEL 0x04 /* rssi info was received on channel (vs offchannel) */ ++ ++/* bssinfo flag for nbss_cap */ ++#define VHT_BI_SGI_80MHZ 0x00000100 ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++ ++typedef struct wl_bsscfg { ++ uint32 wsec; ++ uint32 WPA_auth; ++ uint32 wsec_index; ++ uint32 associated; ++ uint32 BSS; ++ uint32 phytest_on; ++ struct ether_addr prev_BSSID; ++ struct ether_addr BSSID; ++ uint32 targetbss_wpa2_flags; ++ uint32 assoc_type; ++ uint32 assoc_state; ++} wl_bsscfg_t; ++ ++typedef struct wl_bss_config { ++ uint32 atim_window; ++ uint32 beacon_period; ++ uint32 chanspec; ++} wl_bss_config_t; ++ ++#define DLOAD_HANDLER_VER 1 /* Downloader version */ ++#define DLOAD_FLAG_VER_MASK 0xf000 /* Downloader version mask */ ++#define DLOAD_FLAG_VER_SHIFT 12 /* Downloader version shift */ ++ ++#define DL_CRC_NOT_INUSE 0x0001 ++ ++/* generic download types & flags */ ++enum { ++ DL_TYPE_UCODE = 1, ++ DL_TYPE_CLM = 2 ++}; ++ ++/* ucode type values */ ++enum { ++ UCODE_FW, ++ INIT_VALS, ++ BS_INIT_VALS ++}; ++ ++struct wl_dload_data { ++ uint16 flag; ++ uint16 dload_type; ++ uint32 len; ++ uint32 crc; ++ uint8 data[1]; ++}; ++typedef struct wl_dload_data wl_dload_data_t; ++ ++struct wl_ucode_info { ++ uint32 ucode_type; ++ uint32 num_chunks; ++ uint32 chunk_len; ++ uint32 chunk_num; ++ uint8 data_chunk[1]; ++}; ++typedef struct wl_ucode_info wl_ucode_info_t; ++ ++struct wl_clm_dload_info { ++ uint32 ds_id; ++ uint32 clm_total_len; ++ uint32 num_chunks; ++ uint32 chunk_len; ++ uint32 chunk_offset; ++ uint8 data_chunk[1]; ++}; ++typedef struct wl_clm_dload_info wl_clm_dload_info_t; ++ ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++typedef struct wlc_ssid { ++ uint32 SSID_len; ++ uchar SSID[32]; ++} wlc_ssid_t; ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++ ++#define MAX_PREFERRED_AP_NUM 5 ++typedef struct wlc_fastssidinfo { ++ uint32 SSID_channel[MAX_PREFERRED_AP_NUM]; ++ wlc_ssid_t SSID_info[MAX_PREFERRED_AP_NUM]; ++} wlc_fastssidinfo_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct wnm_url { ++ uint8 len; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT wnm_url_t; ++ ++typedef struct chan_scandata { ++ uint8 txpower; ++ uint8 pad; ++ chanspec_t channel; /* Channel num, bw, ctrl_sb and band */ ++ uint32 channel_mintime; ++ uint32 channel_maxtime; ++} chan_scandata_t; ++ ++typedef enum wl_scan_type { ++ EXTDSCAN_FOREGROUND_SCAN, ++ EXTDSCAN_BACKGROUND_SCAN, ++ EXTDSCAN_FORCEDBACKGROUND_SCAN ++} wl_scan_type_t; ++ ++#define WLC_EXTDSCAN_MAX_SSID 5 ++ ++typedef struct wl_extdscan_params { ++ int8 nprobes; /* 0, passive, otherwise active */ ++ int8 split_scan; /* split scan */ ++ int8 band; /* band */ ++ int8 pad; ++ wlc_ssid_t ssid[WLC_EXTDSCAN_MAX_SSID]; /* ssid list */ ++ uint32 tx_rate; /* in 500ksec units */ ++ wl_scan_type_t scan_type; /* enum */ ++ int32 channel_num; ++ chan_scandata_t channel_list[1]; /* list of chandata structs */ ++} wl_extdscan_params_t; ++ ++#define WL_EXTDSCAN_PARAMS_FIXED_SIZE (sizeof(wl_extdscan_params_t) - sizeof(chan_scandata_t)) ++ ++#define WL_BSSTYPE_INFRA 1 ++#define WL_BSSTYPE_INDEP 0 ++#define WL_BSSTYPE_ANY 2 ++ ++/* Bitmask for scan_type */ ++#define WL_SCANFLAGS_PASSIVE 0x01 /* force passive scan */ ++#define WL_SCANFLAGS_RESERVED 0x02 /* Reserved */ ++#define WL_SCANFLAGS_PROHIBITED 0x04 /* allow scanning prohibited channels */ ++ ++#define WL_SCAN_PARAMS_SSID_MAX 10 ++ ++typedef struct wl_scan_params { ++ wlc_ssid_t ssid; /* default: {0, ""} */ ++ struct ether_addr bssid; /* default: bcast */ ++ int8 bss_type; /* default: any, ++ * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT ++ */ ++ uint8 scan_type; /* flags, 0 use default */ ++ int32 nprobes; /* -1 use default, number of probes per channel */ ++ int32 active_time; /* -1 use default, dwell time per channel for ++ * active scanning ++ */ ++ int32 passive_time; /* -1 use default, dwell time per channel ++ * for passive scanning ++ */ ++ int32 home_time; /* -1 use default, dwell time for the home channel ++ * between channel scans ++ */ ++ int32 channel_num; /* count of channels and ssids that follow ++ * ++ * low half is count of channels in channel_list, 0 ++ * means default (use all available channels) ++ * ++ * high half is entries in wlc_ssid_t array that ++ * follows channel_list, aligned for int32 (4 bytes) ++ * meaning an odd channel count implies a 2-byte pad ++ * between end of channel_list and first ssid ++ * ++ * if ssid count is zero, single ssid in the fixed ++ * parameter portion is assumed, otherwise ssid in ++ * the fixed portion is ignored ++ */ ++ uint16 channel_list[1]; /* list of chanspecs */ ++} wl_scan_params_t; ++ ++/* size of wl_scan_params not including variable length array */ ++#define WL_SCAN_PARAMS_FIXED_SIZE 64 ++ ++/* masks for channel and ssid count */ ++#define WL_SCAN_PARAMS_COUNT_MASK 0x0000ffff ++#define WL_SCAN_PARAMS_NSSID_SHIFT 16 ++ ++#define WL_SCAN_ACTION_START 1 ++#define WL_SCAN_ACTION_CONTINUE 2 ++#define WL_SCAN_ACTION_ABORT 3 ++ ++#define ISCAN_REQ_VERSION 1 ++ ++/* incremental scan struct */ ++typedef struct wl_iscan_params { ++ uint32 version; ++ uint16 action; ++ uint16 scan_duration; ++ wl_scan_params_t params; ++} wl_iscan_params_t; ++ ++/* 3 fields + size of wl_scan_params, not including variable length array */ ++#define WL_ISCAN_PARAMS_FIXED_SIZE (OFFSETOF(wl_iscan_params_t, params) + sizeof(wlc_ssid_t)) ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++typedef struct wl_scan_results { ++ uint32 buflen; ++ uint32 version; ++ uint32 count; ++ wl_bss_info_t bss_info[1]; ++} wl_scan_results_t; ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++/* size of wl_scan_results not including variable length array */ ++#define WL_SCAN_RESULTS_FIXED_SIZE (sizeof(wl_scan_results_t) - sizeof(wl_bss_info_t)) ++ ++/* wl_iscan_results status values */ ++#define WL_SCAN_RESULTS_SUCCESS 0 ++#define WL_SCAN_RESULTS_PARTIAL 1 ++#define WL_SCAN_RESULTS_PENDING 2 ++#define WL_SCAN_RESULTS_ABORTED 3 ++#define WL_SCAN_RESULTS_NO_MEM 4 ++ ++/* Used in EXT_STA */ ++#define DNGL_RXCTXT_SIZE 45 ++ ++ ++#define ESCAN_REQ_VERSION 1 ++ ++typedef struct wl_escan_params { ++ uint32 version; ++ uint16 action; ++ uint16 sync_id; ++ wl_scan_params_t params; ++} wl_escan_params_t; ++ ++#define WL_ESCAN_PARAMS_FIXED_SIZE (OFFSETOF(wl_escan_params_t, params) + sizeof(wlc_ssid_t)) ++ ++typedef struct wl_escan_result { ++ uint32 buflen; ++ uint32 version; ++ uint16 sync_id; ++ uint16 bss_count; ++ wl_bss_info_t bss_info[1]; ++} wl_escan_result_t; ++ ++#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(wl_escan_result_t) - sizeof(wl_bss_info_t)) ++ ++/* incremental scan results struct */ ++typedef struct wl_iscan_results { ++ uint32 status; ++ wl_scan_results_t results; ++} wl_iscan_results_t; ++ ++/* size of wl_iscan_results not including variable length array */ ++#define WL_ISCAN_RESULTS_FIXED_SIZE \ ++ (WL_SCAN_RESULTS_FIXED_SIZE + OFFSETOF(wl_iscan_results_t, results)) ++ ++typedef struct wl_probe_params { ++ wlc_ssid_t ssid; ++ struct ether_addr bssid; ++ struct ether_addr mac; ++} wl_probe_params_t; ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++#define WL_MAXRATES_IN_SET 16 /* max # of rates in a rateset */ ++typedef struct wl_rateset { ++ uint32 count; /* # rates in this set */ ++ uint8 rates[WL_MAXRATES_IN_SET]; /* rates in 500kbps units w/hi bit set if basic */ ++} wl_rateset_t; ++ ++typedef struct wl_rateset_args { ++ uint32 count; /* # rates in this set */ ++ uint8 rates[WL_MAXRATES_IN_SET]; /* rates in 500kbps units w/hi bit set if basic */ ++ uint8 mcs[MCSSET_LEN]; /* supported mcs index bit map */ ++} wl_rateset_args_t; ++ ++/* uint32 list */ ++typedef struct wl_uint32_list { ++ /* in - # of elements, out - # of entries */ ++ uint32 count; ++ /* variable length uint32 list */ ++ uint32 element[1]; ++} wl_uint32_list_t; ++ ++/* used for association with a specific BSSID and chanspec list */ ++typedef struct wl_assoc_params { ++ struct ether_addr bssid; /* 00:00:00:00:00:00: broadcast scan */ ++ uint16 bssid_cnt; /* 0: use chanspec_num, and the single bssid, ++ * otherwise count of chanspecs in chanspec_list ++ * AND paired bssids following chanspec_list ++ */ ++ int32 chanspec_num; /* 0: all available channels, ++ * otherwise count of chanspecs in chanspec_list ++ */ ++ chanspec_t chanspec_list[1]; /* list of chanspecs */ ++} wl_assoc_params_t; ++#define WL_ASSOC_PARAMS_FIXED_SIZE OFFSETOF(wl_assoc_params_t, chanspec_list) ++ ++/* used for reassociation/roam to a specific BSSID and channel */ ++typedef wl_assoc_params_t wl_reassoc_params_t; ++#define WL_REASSOC_PARAMS_FIXED_SIZE WL_ASSOC_PARAMS_FIXED_SIZE ++ ++/* used for association to a specific BSSID and channel */ ++typedef wl_assoc_params_t wl_join_assoc_params_t; ++#define WL_JOIN_ASSOC_PARAMS_FIXED_SIZE WL_ASSOC_PARAMS_FIXED_SIZE ++ ++/* used for join with or without a specific bssid and channel list */ ++typedef struct wl_join_params { ++ wlc_ssid_t ssid; ++ wl_assoc_params_t params; /* optional field, but it must include the fixed portion ++ * of the wl_assoc_params_t struct when it does present. ++ */ ++} wl_join_params_t; ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++#define WL_JOIN_PARAMS_FIXED_SIZE (OFFSETOF(wl_join_params_t, params) + \ ++ WL_ASSOC_PARAMS_FIXED_SIZE) ++/* scan params for extended join */ ++typedef struct wl_join_scan_params { ++ uint8 scan_type; /* 0 use default, active or passive scan */ ++ int32 nprobes; /* -1 use default, number of probes per channel */ ++ int32 active_time; /* -1 use default, dwell time per channel for ++ * active scanning ++ */ ++ int32 passive_time; /* -1 use default, dwell time per channel ++ * for passive scanning ++ */ ++ int32 home_time; /* -1 use default, dwell time for the home channel ++ * between channel scans ++ */ ++} wl_join_scan_params_t; ++ ++/* extended join params */ ++typedef struct wl_extjoin_params { ++ wlc_ssid_t ssid; /* {0, ""}: wildcard scan */ ++ wl_join_scan_params_t scan; ++ wl_join_assoc_params_t assoc; /* optional field, but it must include the fixed portion ++ * of the wl_join_assoc_params_t struct when it does ++ * present. ++ */ ++} wl_extjoin_params_t; ++#define WL_EXTJOIN_PARAMS_FIXED_SIZE (OFFSETOF(wl_extjoin_params_t, assoc) + \ ++ WL_JOIN_ASSOC_PARAMS_FIXED_SIZE) ++ ++/* All builds use the new 11ac ratespec/chanspec */ ++#undef D11AC_IOTYPES ++#define D11AC_IOTYPES ++ ++#ifndef D11AC_IOTYPES ++ ++/* defines used by the nrate iovar */ ++#define NRATE_MCS_INUSE 0x00000080 /* MSC in use,indicates b0-6 holds an mcs */ ++#define NRATE_RATE_MASK 0x0000007f /* rate/mcs value */ ++#define NRATE_STF_MASK 0x0000ff00 /* stf mode mask: siso, cdd, stbc, sdm */ ++#define NRATE_STF_SHIFT 8 /* stf mode shift */ ++#define NRATE_OVERRIDE 0x80000000 /* bit indicates override both rate & mode */ ++#define NRATE_OVERRIDE_MCS_ONLY 0x40000000 /* bit indicate to override mcs only */ ++#define NRATE_SGI_MASK 0x00800000 /* sgi mode */ ++#define NRATE_SGI_SHIFT 23 /* sgi mode */ ++#define NRATE_LDPC_CODING 0x00400000 /* bit indicates adv coding in use */ ++#define NRATE_LDPC_SHIFT 22 /* ldpc shift */ ++ ++#define NRATE_STF_SISO 0 /* stf mode SISO */ ++#define NRATE_STF_CDD 1 /* stf mode CDD */ ++#define NRATE_STF_STBC 2 /* stf mode STBC */ ++#define NRATE_STF_SDM 3 /* stf mode SDM */ ++ ++#else /* D11AC_IOTYPES */ ++ ++/* WL_RSPEC defines for rate information */ ++#define WL_RSPEC_RATE_MASK 0x000000FF /* rate or HT MCS value */ ++#define WL_RSPEC_VHT_MCS_MASK 0x0000000F /* VHT MCS value */ ++#define WL_RSPEC_VHT_NSS_MASK 0x000000F0 /* VHT Nss value */ ++#define WL_RSPEC_VHT_NSS_SHIFT 4 /* VHT Nss value shift */ ++#define WL_RSPEC_TXEXP_MASK 0x00000300 ++#define WL_RSPEC_TXEXP_SHIFT 8 ++#define WL_RSPEC_BW_MASK 0x00070000 /* bandwidth mask */ ++#define WL_RSPEC_BW_SHIFT 16 /* bandwidth shift */ ++#define WL_RSPEC_STBC 0x00100000 /* STBC encoding, Nsts = 2 x Nss */ ++#define WL_RSPEC_LDPC 0x00400000 /* bit indicates adv coding in use */ ++#define WL_RSPEC_SGI 0x00800000 /* Short GI mode */ ++#define WL_RSPEC_ENCODING_MASK 0x03000000 /* Encoding of Rate/MCS field */ ++#define WL_RSPEC_OVERRIDE_RATE 0x40000000 /* bit indicate to override mcs only */ ++#define WL_RSPEC_OVERRIDE_MODE 0x80000000 /* bit indicates override both rate & mode */ ++ ++/* WL_RSPEC_ENCODING field defs */ ++#define WL_RSPEC_ENCODE_RATE 0x00000000 /* Legacy rate is stored in RSPEC_RATE_MASK */ ++#define WL_RSPEC_ENCODE_HT 0x01000000 /* HT MCS is stored in RSPEC_RATE_MASK */ ++#define WL_RSPEC_ENCODE_VHT 0x02000000 /* VHT MCS and Nss is stored in RSPEC_RATE_MASK */ ++ ++/* WL_RSPEC_BW field defs */ ++#define WL_RSPEC_BW_UNSPECIFIED 0 ++#define WL_RSPEC_BW_20MHZ 0x00010000 ++#define WL_RSPEC_BW_40MHZ 0x00020000 ++#define WL_RSPEC_BW_80MHZ 0x00030000 ++#define WL_RSPEC_BW_160MHZ 0x00040000 ++ ++/* Legacy defines for the nrate iovar */ ++#define OLD_NRATE_MCS_INUSE 0x00000080 /* MSC in use,indicates b0-6 holds an mcs */ ++#define OLD_NRATE_RATE_MASK 0x0000007f /* rate/mcs value */ ++#define OLD_NRATE_STF_MASK 0x0000ff00 /* stf mode mask: siso, cdd, stbc, sdm */ ++#define OLD_NRATE_STF_SHIFT 8 /* stf mode shift */ ++#define OLD_NRATE_OVERRIDE 0x80000000 /* bit indicates override both rate & mode */ ++#define OLD_NRATE_OVERRIDE_MCS_ONLY 0x40000000 /* bit indicate to override mcs only */ ++#define OLD_NRATE_SGI 0x00800000 /* sgi mode */ ++#define OLD_NRATE_LDPC_CODING 0x00400000 /* bit indicates adv coding in use */ ++ ++#define OLD_NRATE_STF_SISO 0 /* stf mode SISO */ ++#define OLD_NRATE_STF_CDD 1 /* stf mode CDD */ ++#define OLD_NRATE_STF_STBC 2 /* stf mode STBC */ ++#define OLD_NRATE_STF_SDM 3 /* stf mode SDM */ ++ ++#endif /* D11AC_IOTYPES */ ++ ++#define ANTENNA_NUM_1 1 /* total number of antennas to be used */ ++#define ANTENNA_NUM_2 2 ++#define ANTENNA_NUM_3 3 ++#define ANTENNA_NUM_4 4 ++ ++#define ANT_SELCFG_AUTO 0x80 /* bit indicates antenna sel AUTO */ ++#define ANT_SELCFG_MASK 0x33 /* antenna configuration mask */ ++#define ANT_SELCFG_MAX 4 /* max number of antenna configurations */ ++#define ANT_SELCFG_TX_UNICAST 0 /* unicast tx antenna configuration */ ++#define ANT_SELCFG_RX_UNICAST 1 /* unicast rx antenna configuration */ ++#define ANT_SELCFG_TX_DEF 2 /* default tx antenna configuration */ ++#define ANT_SELCFG_RX_DEF 3 /* default rx antenna configuration */ ++ ++#define MAX_STREAMS_SUPPORTED 4 /* max number of streams supported */ ++ ++typedef struct { ++ uint8 ant_config[ANT_SELCFG_MAX]; /* antenna configuration */ ++ uint8 num_antcfg; /* number of available antenna configurations */ ++} wlc_antselcfg_t; ++ ++#define HIGHEST_SINGLE_STREAM_MCS 7 /* MCS values greater than this enable multiple streams */ ++ ++#define MAX_CCA_CHANNELS 38 /* Max number of 20 Mhz wide channels */ ++#define MAX_CCA_SECS 60 /* CCA keeps this many seconds history */ ++ ++#define IBSS_MED 15 /* Mediom in-bss congestion percentage */ ++#define IBSS_HI 25 /* Hi in-bss congestion percentage */ ++#define OBSS_MED 12 ++#define OBSS_HI 25 ++#define INTERFER_MED 5 ++#define INTERFER_HI 10 ++ ++#define CCA_FLAG_2G_ONLY 0x01 /* Return a channel from 2.4 Ghz band */ ++#define CCA_FLAG_5G_ONLY 0x02 /* Return a channel from 2.4 Ghz band */ ++#define CCA_FLAG_IGNORE_DURATION 0x04 /* Ignore dwell time for each channel */ ++#define CCA_FLAGS_PREFER_1_6_11 0x10 ++#define CCA_FLAG_IGNORE_INTERFER 0x20 /* do not exlude channel based on interfer level */ ++ ++#define CCA_ERRNO_BAND 1 /* After filtering for band pref, no choices left */ ++#define CCA_ERRNO_DURATION 2 /* After filtering for duration, no choices left */ ++#define CCA_ERRNO_PREF_CHAN 3 /* After filtering for chan pref, no choices left */ ++#define CCA_ERRNO_INTERFER 4 /* After filtering for interference, no choices left */ ++#define CCA_ERRNO_TOO_FEW 5 /* Only 1 channel was input */ ++ ++typedef struct { ++ uint32 duration; /* millisecs spent sampling this channel */ ++ uint32 congest_ibss; /* millisecs in our bss (presumably this traffic will */ ++ /* move if cur bss moves channels) */ ++ uint32 congest_obss; /* traffic not in our bss */ ++ uint32 interference; /* millisecs detecting a non 802.11 interferer. */ ++ uint32 timestamp; /* second timestamp */ ++} cca_congest_t; ++ ++typedef struct { ++ chanspec_t chanspec; /* Which channel? */ ++ uint8 num_secs; /* How many secs worth of data */ ++ cca_congest_t secs[1]; /* Data */ ++} cca_congest_channel_req_t; ++ ++/* interference source detection and identification mode */ ++#define ITFR_MODE_DISABLE 0 /* disable feature */ ++#define ITFR_MODE_MANUAL_ENABLE 1 /* enable manual detection */ ++#define ITFR_MODE_AUTO_ENABLE 2 /* enable auto detection */ ++ ++/* interference sources */ ++enum interference_source { ++ ITFR_NONE = 0, /* interference */ ++ ITFR_PHONE, /* wireless phone */ ++ ITFR_VIDEO_CAMERA, /* wireless video camera */ ++ ITFR_MICROWAVE_OVEN, /* microwave oven */ ++ ITFR_BABY_MONITOR, /* wireless baby monitor */ ++ ITFR_BLUETOOTH, /* bluetooth */ ++ ITFR_VIDEO_CAMERA_OR_BABY_MONITOR, /* wireless camera or baby monitor */ ++ ITFR_BLUETOOTH_OR_BABY_MONITOR, /* bluetooth or baby monitor */ ++ ITFR_VIDEO_CAMERA_OR_PHONE, /* video camera or phone */ ++ ITFR_UNIDENTIFIED /* interference from unidentified source */ ++}; ++ ++/* structure for interference source report */ ++typedef struct { ++ uint32 flags; /* flags. bit definitions below */ ++ uint32 source; /* last detected interference source */ ++ uint32 timestamp; /* second timestamp on interferenced flag change */ ++} interference_source_rep_t; ++ ++/* bit definitions for flags in interference source report */ ++#define ITFR_INTERFERENCED 1 /* interference detected */ ++#define ITFR_HOME_CHANNEL 2 /* home channel has interference */ ++#define ITFR_NOISY_ENVIRONMENT 4 /* noisy environemnt so feature stopped */ ++ ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++#define WLC_CNTRY_BUF_SZ 4 /* Country string is 3 bytes + NUL */ ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++ ++typedef struct wl_country { ++ char country_abbrev[WLC_CNTRY_BUF_SZ]; /* nul-terminated country code used in ++ * the Country IE ++ */ ++ int32 rev; /* revision specifier for ccode ++ * on set, -1 indicates unspecified. ++ * on get, rev >= 0 ++ */ ++ char ccode[WLC_CNTRY_BUF_SZ]; /* nul-terminated built-in country code. ++ * variable length, but fixed size in ++ * struct allows simple allocation for ++ * expected country strings <= 3 chars. ++ */ ++} wl_country_t; ++ ++typedef struct wl_channels_in_country { ++ uint32 buflen; ++ uint32 band; ++ char country_abbrev[WLC_CNTRY_BUF_SZ]; ++ uint32 count; ++ uint32 channel[1]; ++} wl_channels_in_country_t; ++ ++typedef struct wl_country_list { ++ uint32 buflen; ++ uint32 band_set; ++ uint32 band; ++ uint32 count; ++ char country_abbrev[1]; ++} wl_country_list_t; ++ ++#define WL_NUM_RPI_BINS 8 ++#define WL_RM_TYPE_BASIC 1 ++#define WL_RM_TYPE_CCA 2 ++#define WL_RM_TYPE_RPI 3 ++ ++#define WL_RM_FLAG_PARALLEL (1<<0) ++ ++#define WL_RM_FLAG_LATE (1<<1) ++#define WL_RM_FLAG_INCAPABLE (1<<2) ++#define WL_RM_FLAG_REFUSED (1<<3) ++ ++typedef struct wl_rm_req_elt { ++ int8 type; ++ int8 flags; ++ chanspec_t chanspec; ++ uint32 token; /* token for this measurement */ ++ uint32 tsf_h; /* TSF high 32-bits of Measurement start time */ ++ uint32 tsf_l; /* TSF low 32-bits */ ++ uint32 dur; /* TUs */ ++} wl_rm_req_elt_t; ++ ++typedef struct wl_rm_req { ++ uint32 token; /* overall measurement set token */ ++ uint32 count; /* number of measurement requests */ ++ void *cb; /* completion callback function: may be NULL */ ++ void *cb_arg; /* arg to completion callback function */ ++ wl_rm_req_elt_t req[1]; /* variable length block of requests */ ++} wl_rm_req_t; ++#define WL_RM_REQ_FIXED_LEN OFFSETOF(wl_rm_req_t, req) ++ ++typedef struct wl_rm_rep_elt { ++ int8 type; ++ int8 flags; ++ chanspec_t chanspec; ++ uint32 token; /* token for this measurement */ ++ uint32 tsf_h; /* TSF high 32-bits of Measurement start time */ ++ uint32 tsf_l; /* TSF low 32-bits */ ++ uint32 dur; /* TUs */ ++ uint32 len; /* byte length of data block */ ++ uint8 data[1]; /* variable length data block */ ++} wl_rm_rep_elt_t; ++#define WL_RM_REP_ELT_FIXED_LEN 24 /* length excluding data block */ ++ ++#define WL_RPI_REP_BIN_NUM 8 ++typedef struct wl_rm_rpi_rep { ++ uint8 rpi[WL_RPI_REP_BIN_NUM]; ++ int8 rpi_max[WL_RPI_REP_BIN_NUM]; ++} wl_rm_rpi_rep_t; ++ ++typedef struct wl_rm_rep { ++ uint32 token; /* overall measurement set token */ ++ uint32 len; /* length of measurement report block */ ++ wl_rm_rep_elt_t rep[1]; /* variable length block of reports */ ++} wl_rm_rep_t; ++#define WL_RM_REP_FIXED_LEN 8 ++ ++ ++typedef enum sup_auth_status { ++ /* Basic supplicant authentication states */ ++ WLC_SUP_DISCONNECTED = 0, ++ WLC_SUP_CONNECTING, ++ WLC_SUP_IDREQUIRED, ++ WLC_SUP_AUTHENTICATING, ++ WLC_SUP_AUTHENTICATED, ++ WLC_SUP_KEYXCHANGE, ++ WLC_SUP_KEYED, ++ WLC_SUP_TIMEOUT, ++ WLC_SUP_LAST_BASIC_STATE, ++ ++ /* Extended supplicant authentication states */ ++ /* Waiting to receive handshake msg M1 */ ++ WLC_SUP_KEYXCHANGE_WAIT_M1 = WLC_SUP_AUTHENTICATED, ++ /* Preparing to send handshake msg M2 */ ++ WLC_SUP_KEYXCHANGE_PREP_M2 = WLC_SUP_KEYXCHANGE, ++ /* Waiting to receive handshake msg M3 */ ++ WLC_SUP_KEYXCHANGE_WAIT_M3 = WLC_SUP_LAST_BASIC_STATE, ++ WLC_SUP_KEYXCHANGE_PREP_M4, /* Preparing to send handshake msg M4 */ ++ WLC_SUP_KEYXCHANGE_WAIT_G1, /* Waiting to receive handshake msg G1 */ ++ WLC_SUP_KEYXCHANGE_PREP_G2 /* Preparing to send handshake msg G2 */ ++} sup_auth_status_t; ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++/* Enumerate crypto algorithms */ ++#define CRYPTO_ALGO_OFF 0 ++#define CRYPTO_ALGO_WEP1 1 ++#define CRYPTO_ALGO_TKIP 2 ++#define CRYPTO_ALGO_WEP128 3 ++#define CRYPTO_ALGO_AES_CCM 4 ++#define CRYPTO_ALGO_AES_OCB_MSDU 5 ++#define CRYPTO_ALGO_AES_OCB_MPDU 6 ++#if !defined(BCMEXTCCX) ++#define CRYPTO_ALGO_NALG 7 ++#else ++#define CRYPTO_ALGO_CKIP 7 ++#define CRYPTO_ALGO_CKIP_MMH 8 ++#define CRYPTO_ALGO_WEP_MMH 9 ++#define CRYPTO_ALGO_NALG 10 ++#endif ++#define CRYPTO_ALGO_PMK 12 /* for 802.1x supp to set PMK before 4-way */ ++ ++#define WSEC_GEN_MIC_ERROR 0x0001 ++#define WSEC_GEN_REPLAY 0x0002 ++#define WSEC_GEN_ICV_ERROR 0x0004 ++#define WSEC_GEN_MFP_ACT_ERROR 0x0008 ++#define WSEC_GEN_MFP_DISASSOC_ERROR 0x0010 ++#define WSEC_GEN_MFP_DEAUTH_ERROR 0x0020 ++ ++#define WL_SOFT_KEY (1 << 0) /* Indicates this key is using soft encrypt */ ++#define WL_PRIMARY_KEY (1 << 1) /* Indicates this key is the primary (ie tx) key */ ++#if defined(BCMEXTCCX) ++#define WL_CKIP_KP (1 << 4) /* CMIC */ ++#define WL_CKIP_MMH (1 << 5) /* CKIP */ ++#else ++#define WL_KF_RES_4 (1 << 4) /* Reserved for backward compat */ ++#define WL_KF_RES_5 (1 << 5) /* Reserved for backward compat */ ++#endif ++#define WL_IBSS_PEER_GROUP_KEY (1 << 6) /* Indicates a group key for a IBSS PEER */ ++ ++typedef struct wl_wsec_key { ++ uint32 index; /* key index */ ++ uint32 len; /* key length */ ++ uint8 data[DOT11_MAX_KEY_SIZE]; /* key data */ ++ uint32 pad_1[18]; ++ uint32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */ ++ uint32 flags; /* misc flags */ ++ uint32 pad_2[2]; ++ int pad_3; ++ int iv_initialized; /* has IV been initialized already? */ ++ int pad_4; ++ /* Rx IV */ ++ struct { ++ uint32 hi; /* upper 32 bits of IV */ ++ uint16 lo; /* lower 16 bits of IV */ ++ } rxiv; ++ uint32 pad_5[2]; ++ struct ether_addr ea; /* per station */ ++} wl_wsec_key_t; ++ ++#define WSEC_MIN_PSK_LEN 8 ++#define WSEC_MAX_PSK_LEN 64 ++ ++/* Flag for key material needing passhash'ing */ ++#define WSEC_PASSPHRASE (1<<0) ++ ++/* receptacle for WLC_SET_WSEC_PMK parameter */ ++typedef struct { ++ ushort key_len; /* octets in key material */ ++ ushort flags; /* key handling qualification */ ++ uint8 key[WSEC_MAX_PSK_LEN]; /* PMK material */ ++} wsec_pmk_t; ++ ++/* wireless security bitvec */ ++#define WEP_ENABLED 0x0001 ++#define TKIP_ENABLED 0x0002 ++#define AES_ENABLED 0x0004 ++#define WSEC_SWFLAG 0x0008 ++#define SES_OW_ENABLED 0x0040 /* to go into transition mode without setting wep */ ++ ++/* wsec macros for operating on the above definitions */ ++#define WSEC_WEP_ENABLED(wsec) ((wsec) & WEP_ENABLED) ++#define WSEC_TKIP_ENABLED(wsec) ((wsec) & TKIP_ENABLED) ++#define WSEC_AES_ENABLED(wsec) ((wsec) & AES_ENABLED) ++ ++#define WSEC_ENABLED(wsec) ((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED)) ++#define WSEC_SES_OW_ENABLED(wsec) ((wsec) & SES_OW_ENABLED) ++ ++#ifdef MFP ++#define MFP_CAPABLE 0x0200 ++#define MFP_REQUIRED 0x0400 ++#define MFP_SHA256 0x0800 /* a special configuration for STA for WIFI test tool */ ++#endif /* MFP */ ++ ++/* WPA authentication mode bitvec */ ++#define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */ ++#define WPA_AUTH_NONE 0x0001 /* none (IBSS) */ ++#define WPA_AUTH_UNSPECIFIED 0x0002 /* over 802.1x */ ++#define WPA_AUTH_PSK 0x0004 /* Pre-shared key */ ++#if defined(BCMEXTCCX) ++#define WPA_AUTH_CCKM 0x0008 /* CCKM */ ++#define WPA2_AUTH_CCKM 0x0010 /* CCKM2 */ ++#endif ++/* #define WPA_AUTH_8021X 0x0020 */ /* 802.1x, reserved */ ++#define WPA2_AUTH_UNSPECIFIED 0x0040 /* over 802.1x */ ++#define WPA2_AUTH_PSK 0x0080 /* Pre-shared key */ ++#define BRCM_AUTH_PSK 0x0100 /* BRCM specific PSK */ ++#define BRCM_AUTH_DPT 0x0200 /* DPT PSK without group keys */ ++#define WPA2_AUTH_MFP 0x1000 /* MFP (11w) in contrast to CCX */ ++#define WPA2_AUTH_TPK 0x2000 /* TDLS Peer Key */ ++#define WPA2_AUTH_FT 0x4000 /* Fast Transition. */ ++#define WPA_AUTH_PFN_ANY 0xffffffff /* for PFN, match only ssid */ ++ ++/* pmkid */ ++#define MAXPMKID 16 ++ ++typedef struct _pmkid { ++ struct ether_addr BSSID; ++ uint8 PMKID[WPA2_PMKID_LEN]; ++} pmkid_t; ++ ++typedef struct _pmkid_list { ++ uint32 npmkid; ++ pmkid_t pmkid[1]; ++} pmkid_list_t; ++ ++typedef struct _pmkid_cand { ++ struct ether_addr BSSID; ++ uint8 preauth; ++} pmkid_cand_t; ++ ++typedef struct _pmkid_cand_list { ++ uint32 npmkid_cand; ++ pmkid_cand_t pmkid_cand[1]; ++} pmkid_cand_list_t; ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++typedef struct wl_assoc_info { ++ uint32 req_len; ++ uint32 resp_len; ++ uint32 flags; ++ struct dot11_assoc_req req; ++ struct ether_addr reassoc_bssid; /* used in reassoc's */ ++ struct dot11_assoc_resp resp; ++} wl_assoc_info_t; ++ ++/* flags */ ++#define WLC_ASSOC_REQ_IS_REASSOC 0x01 /* assoc req was actually a reassoc */ ++ ++typedef struct wl_led_info { ++ uint32 index; /* led index */ ++ uint32 behavior; ++ uint8 activehi; ++} wl_led_info_t; ++ ++ ++/* srom read/write struct passed through ioctl */ ++typedef struct { ++ uint byteoff; /* byte offset */ ++ uint nbytes; /* number of bytes */ ++ uint16 buf[1]; ++} srom_rw_t; ++ ++/* similar cis (srom or otp) struct [iovar: may not be aligned] */ ++typedef struct { ++ uint32 source; /* cis source */ ++ uint32 byteoff; /* byte offset */ ++ uint32 nbytes; /* number of bytes */ ++ /* data follows here */ ++} cis_rw_t; ++ ++#define WLC_CIS_DEFAULT 0 /* built-in default */ ++#define WLC_CIS_SROM 1 /* source is sprom */ ++#define WLC_CIS_OTP 2 /* source is otp */ ++ ++/* R_REG and W_REG struct passed through ioctl */ ++typedef struct { ++ uint32 byteoff; /* byte offset of the field in d11regs_t */ ++ uint32 val; /* read/write value of the field */ ++ uint32 size; /* sizeof the field */ ++ uint band; /* band (optional) */ ++} rw_reg_t; ++ ++/* Structure used by GET/SET_ATTEN ioctls - it controls power in b/g-band */ ++/* PCL - Power Control Loop */ ++/* current gain setting is replaced by user input */ ++#define WL_ATTEN_APP_INPUT_PCL_OFF 0 /* turn off PCL, apply supplied input */ ++#define WL_ATTEN_PCL_ON 1 /* turn on PCL */ ++/* current gain setting is maintained */ ++#define WL_ATTEN_PCL_OFF 2 /* turn off PCL. */ ++ ++typedef struct { ++ uint16 auto_ctrl; /* WL_ATTEN_XX */ ++ uint16 bb; /* Baseband attenuation */ ++ uint16 radio; /* Radio attenuation */ ++ uint16 txctl1; /* Radio TX_CTL1 value */ ++} atten_t; ++ ++/* Per-AC retry parameters */ ++struct wme_tx_params_s { ++ uint8 short_retry; ++ uint8 short_fallback; ++ uint8 long_retry; ++ uint8 long_fallback; ++ uint16 max_rate; /* In units of 512 Kbps */ ++}; ++ ++typedef struct wme_tx_params_s wme_tx_params_t; ++ ++#define WL_WME_TX_PARAMS_IO_BYTES (sizeof(wme_tx_params_t) * AC_COUNT) ++ ++/* defines used by poweridx iovar - it controls power in a-band */ ++/* current gain setting is maintained */ ++#define WL_PWRIDX_PCL_OFF -2 /* turn off PCL. */ ++#define WL_PWRIDX_PCL_ON -1 /* turn on PCL */ ++#define WL_PWRIDX_LOWER_LIMIT -2 /* lower limit */ ++#define WL_PWRIDX_UPPER_LIMIT 63 /* upper limit */ ++/* value >= 0 causes ++ * - input to be set to that value ++ * - PCL to be off ++ */ ++ ++/* Used to get specific link/ac parameters */ ++typedef struct { ++ int ac; ++ uint8 val; ++ struct ether_addr ea; ++} link_val_t; ++ ++#define BCM_MAC_STATUS_INDICATION (0x40010200L) ++ ++typedef struct { ++ uint16 ver; /* version of this struct */ ++ uint16 len; /* length in bytes of this structure */ ++ uint16 cap; /* sta's advertised capabilities */ ++ uint32 flags; /* flags defined below */ ++ uint32 idle; /* time since data pkt rx'd from sta */ ++ struct ether_addr ea; /* Station address */ ++ wl_rateset_t rateset; /* rateset in use */ ++ uint32 in; /* seconds elapsed since associated */ ++ uint32 listen_interval_inms; /* Min Listen interval in ms for this STA */ ++ uint32 tx_pkts; /* # of packets transmitted */ ++ uint32 tx_failures; /* # of packets failed */ ++ uint32 rx_ucast_pkts; /* # of unicast packets received */ ++ uint32 rx_mcast_pkts; /* # of multicast packets received */ ++ uint32 tx_rate; /* Rate of last successful tx frame */ ++ uint32 rx_rate; /* Rate of last successful rx frame */ ++ uint32 rx_decrypt_succeeds; /* # of packet decrypted successfully */ ++ uint32 rx_decrypt_failures; /* # of packet decrypted unsuccessfully */ ++} sta_info_t; ++ ++#define WL_OLD_STAINFO_SIZE OFFSETOF(sta_info_t, tx_pkts) ++ ++#define WL_STA_VER 3 ++ ++/* Flags for sta_info_t indicating properties of STA */ ++#define WL_STA_BRCM 0x1 /* Running a Broadcom driver */ ++#define WL_STA_WME 0x2 /* WMM association */ ++#define WL_STA_UNUSED 0x4 ++#define WL_STA_AUTHE 0x8 /* Authenticated */ ++#define WL_STA_ASSOC 0x10 /* Associated */ ++#define WL_STA_AUTHO 0x20 /* Authorized */ ++#define WL_STA_WDS 0x40 /* Wireless Distribution System */ ++#define WL_STA_WDS_LINKUP 0x80 /* WDS traffic/probes flowing properly */ ++#define WL_STA_PS 0x100 /* STA is in power save mode from AP's viewpoint */ ++#define WL_STA_APSD_BE 0x200 /* APSD delv/trigger for AC_BE is default enabled */ ++#define WL_STA_APSD_BK 0x400 /* APSD delv/trigger for AC_BK is default enabled */ ++#define WL_STA_APSD_VI 0x800 /* APSD delv/trigger for AC_VI is default enabled */ ++#define WL_STA_APSD_VO 0x1000 /* APSD delv/trigger for AC_VO is default enabled */ ++#define WL_STA_N_CAP 0x2000 /* STA 802.11n capable */ ++#define WL_STA_SCBSTATS 0x4000 /* Per STA debug stats */ ++ ++#define WL_WDS_LINKUP WL_STA_WDS_LINKUP /* deprecated */ ++ ++/* Values for TX Filter override mode */ ++#define WLC_TXFILTER_OVERRIDE_DISABLED 0 ++#define WLC_TXFILTER_OVERRIDE_ENABLED 1 ++ ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++/* Used to get specific STA parameters */ ++typedef struct { ++ uint32 val; ++ struct ether_addr ea; ++} scb_val_t; ++ ++/* Used by iovar versions of some ioctls, i.e. WLC_SCB_AUTHORIZE et al */ ++typedef struct { ++ uint32 code; ++ scb_val_t ioctl_args; ++} authops_t; ++ ++/* channel encoding */ ++typedef struct channel_info { ++ int hw_channel; ++ int target_channel; ++ int scan_channel; ++} channel_info_t; ++ ++/* For ioctls that take a list of MAC addresses */ ++struct maclist { ++ uint count; /* number of MAC addresses */ ++ struct ether_addr ea[1]; /* variable length array of MAC addresses */ ++}; ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++/* get pkt count struct passed through ioctl */ ++typedef struct get_pktcnt { ++ uint rx_good_pkt; ++ uint rx_bad_pkt; ++ uint tx_good_pkt; ++ uint tx_bad_pkt; ++ uint rx_ocast_good_pkt; /* unicast packets destined for others */ ++} get_pktcnt_t; ++ ++/* NINTENDO2 */ ++#define LQ_IDX_MIN 0 ++#define LQ_IDX_MAX 1 ++#define LQ_IDX_AVG 2 ++#define LQ_IDX_SUM 2 ++#define LQ_IDX_LAST 3 ++#define LQ_STOP_MONITOR 0 ++#define LQ_START_MONITOR 1 ++ ++/* Get averages RSSI, Rx PHY rate and SNR values */ ++typedef struct { ++ int rssi[LQ_IDX_LAST]; /* Array to keep min, max, avg rssi */ ++ int snr[LQ_IDX_LAST]; /* Array to keep min, max, avg snr */ ++ int isvalid; /* Flag indicating whether above data is valid */ ++} wl_lq_t; /* Link Quality */ ++ ++typedef enum wl_wakeup_reason_type { ++ LCD_ON = 1, ++ LCD_OFF, ++ DRC1_WAKE, ++ DRC2_WAKE, ++ REASON_LAST ++} wl_wr_type_t; ++ ++typedef struct { ++/* Unique filter id */ ++ uint32 id; ++ ++/* stores the reason for the last wake up */ ++ uint8 reason; ++} wl_wr_t; ++ ++/* Get MAC specific rate histogram command */ ++typedef struct { ++ struct ether_addr ea; /* MAC Address */ ++ uint8 ac_cat; /* Access Category */ ++ uint8 num_pkts; /* Number of packet entries to be averaged */ ++} wl_mac_ratehisto_cmd_t; /* MAC Specific Rate Histogram command */ ++ ++/* Get MAC rate histogram response */ ++typedef struct { ++ uint32 rate[WLC_MAXRATE + 1]; /* Rates */ ++ uint32 mcs[WL_RATESET_SZ_HT_MCS * WL_TX_CHAINS_MAX]; /* MCS counts */ ++ uint32 vht[WL_RATESET_SZ_VHT_MCS][WL_TX_CHAINS_MAX]; /* VHT counts */ ++ uint32 tsf_timer[2][2]; /* Start and End time for 8bytes value */ ++} wl_mac_ratehisto_res_t; /* MAC Specific Rate Histogram Response */ ++ ++/* Values for TX Filter override mode */ ++#define WLC_TXFILTER_OVERRIDE_DISABLED 0 ++#define WLC_TXFILTER_OVERRIDE_ENABLED 1 ++ ++#define WL_IOCTL_ACTION_GET 0x0 ++#define WL_IOCTL_ACTION_SET 0x1 ++#define WL_IOCTL_ACTION_OVL_IDX_MASK 0x1e ++#define WL_IOCTL_ACTION_OVL_RSV 0x20 ++#define WL_IOCTL_ACTION_OVL 0x40 ++#define WL_IOCTL_ACTION_MASK 0x7e ++#define WL_IOCTL_ACTION_OVL_SHIFT 1 ++ ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++/* Linux network driver ioctl encoding */ ++typedef struct wl_ioctl { ++ uint cmd; /* common ioctl definition */ ++ void *buf; /* pointer to user buffer */ ++ uint len; /* length of user buffer */ ++ uint8 set; /* 1=set IOCTL; 0=query IOCTL */ ++ uint used; /* bytes read or written (optional) */ ++ uint needed; /* bytes needed (optional) */ ++} wl_ioctl_t; ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++ ++/* reference to wl_ioctl_t struct used by usermode driver */ ++#define ioctl_subtype set /* subtype param */ ++#define ioctl_pid used /* pid param */ ++#define ioctl_status needed /* status param */ ++ ++/* ++ * Structure for passing hardware and software ++ * revision info up from the driver. ++ */ ++typedef struct wlc_rev_info { ++ uint vendorid; /* PCI vendor id */ ++ uint deviceid; /* device id of chip */ ++ uint radiorev; /* radio revision */ ++ uint chiprev; /* chip revision */ ++ uint corerev; /* core revision */ ++ uint boardid; /* board identifier (usu. PCI sub-device id) */ ++ uint boardvendor; /* board vendor (usu. PCI sub-vendor id) */ ++ uint boardrev; /* board revision */ ++ uint driverrev; /* driver version */ ++ uint ucoderev; /* microcode version */ ++ uint bus; /* bus type */ ++ uint chipnum; /* chip number */ ++ uint phytype; /* phy type */ ++ uint phyrev; /* phy revision */ ++ uint anarev; /* anacore rev */ ++ uint chippkg; /* chip package info */ ++} wlc_rev_info_t; ++ ++#define WL_REV_INFO_LEGACY_LENGTH 48 ++ ++#define WL_BRAND_MAX 10 ++typedef struct wl_instance_info { ++ uint instance; ++ char brand[WL_BRAND_MAX]; ++} wl_instance_info_t; ++ ++/* structure to change size of tx fifo */ ++typedef struct wl_txfifo_sz { ++ uint16 magic; ++ uint16 fifo; ++ uint16 size; ++} wl_txfifo_sz_t; ++/* magic pattern used for mismatch driver and wl */ ++#define WL_TXFIFO_SZ_MAGIC 0xa5a5 ++ ++/* Transfer info about an IOVar from the driver */ ++/* Max supported IOV name size in bytes, + 1 for nul termination */ ++#define WLC_IOV_NAME_LEN 30 ++typedef struct wlc_iov_trx_s { ++ uint8 module; ++ uint8 type; ++ char name[WLC_IOV_NAME_LEN]; ++} wlc_iov_trx_t; ++ ++/* check this magic number */ ++#define WLC_IOCTL_MAGIC 0x14e46c77 ++ ++/* bump this number if you change the ioctl interface */ ++#ifdef D11AC_IOTYPES ++#define WLC_IOCTL_VERSION 2 ++#define WLC_IOCTL_VERSION_LEGACY_IOTYPES 1 ++#else ++#define WLC_IOCTL_VERSION 1 ++#endif /* D11AC_IOTYPES */ ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++#define WLC_IOCTL_MAXLEN 8192 /* max length ioctl buffer required */ ++#define WLC_IOCTL_SMLEN 256 /* "small" length ioctl buffer required */ ++#define WLC_IOCTL_MEDLEN 1536 /* "med" length ioctl buffer required */ ++#if defined(LCNCONF) || defined(LCN40CONF) ++#define WLC_SAMPLECOLLECT_MAXLEN 8192 /* Max Sample Collect buffer */ ++#else ++#define WLC_SAMPLECOLLECT_MAXLEN 10240 /* Max Sample Collect buffer for two cores */ ++#endif ++ ++/* common ioctl definitions */ ++#define WLC_GET_MAGIC 0 ++#define WLC_GET_VERSION 1 ++#define WLC_UP 2 ++#define WLC_DOWN 3 ++#define WLC_GET_LOOP 4 ++#define WLC_SET_LOOP 5 ++#define WLC_DUMP 6 ++#define WLC_GET_MSGLEVEL 7 ++#define WLC_SET_MSGLEVEL 8 ++#define WLC_GET_PROMISC 9 ++#define WLC_SET_PROMISC 10 ++/* #define WLC_OVERLAY_IOCTL 11 */ /* not supported */ ++#define WLC_GET_RATE 12 ++#define WLC_GET_MAX_RATE 13 ++#define WLC_GET_INSTANCE 14 ++/* #define WLC_GET_FRAG 15 */ /* no longer supported */ ++/* #define WLC_SET_FRAG 16 */ /* no longer supported */ ++/* #define WLC_GET_RTS 17 */ /* no longer supported */ ++/* #define WLC_SET_RTS 18 */ /* no longer supported */ ++#define WLC_GET_INFRA 19 ++#define WLC_SET_INFRA 20 ++#define WLC_GET_AUTH 21 ++#define WLC_SET_AUTH 22 ++#define WLC_GET_BSSID 23 ++#define WLC_SET_BSSID 24 ++#define WLC_GET_SSID 25 ++#define WLC_SET_SSID 26 ++#define WLC_RESTART 27 ++#define WLC_TERMINATED 28 ++/* #define WLC_DUMP_SCB 28 */ /* no longer supported */ ++#define WLC_GET_CHANNEL 29 ++#define WLC_SET_CHANNEL 30 ++#define WLC_GET_SRL 31 ++#define WLC_SET_SRL 32 ++#define WLC_GET_LRL 33 ++#define WLC_SET_LRL 34 ++#define WLC_GET_PLCPHDR 35 ++#define WLC_SET_PLCPHDR 36 ++#define WLC_GET_RADIO 37 ++#define WLC_SET_RADIO 38 ++#define WLC_GET_PHYTYPE 39 ++#define WLC_DUMP_RATE 40 ++#define WLC_SET_RATE_PARAMS 41 ++#define WLC_GET_FIXRATE 42 ++#define WLC_SET_FIXRATE 43 ++/* #define WLC_GET_WEP 42 */ /* no longer supported */ ++/* #define WLC_SET_WEP 43 */ /* no longer supported */ ++#define WLC_GET_KEY 44 ++#define WLC_SET_KEY 45 ++#define WLC_GET_REGULATORY 46 ++#define WLC_SET_REGULATORY 47 ++#define WLC_GET_PASSIVE_SCAN 48 ++#define WLC_SET_PASSIVE_SCAN 49 ++#define WLC_SCAN 50 ++#define WLC_SCAN_RESULTS 51 ++#define WLC_DISASSOC 52 ++#define WLC_REASSOC 53 ++#define WLC_GET_ROAM_TRIGGER 54 ++#define WLC_SET_ROAM_TRIGGER 55 ++#define WLC_GET_ROAM_DELTA 56 ++#define WLC_SET_ROAM_DELTA 57 ++#define WLC_GET_ROAM_SCAN_PERIOD 58 ++#define WLC_SET_ROAM_SCAN_PERIOD 59 ++#define WLC_EVM 60 /* diag */ ++#define WLC_GET_TXANT 61 ++#define WLC_SET_TXANT 62 ++#define WLC_GET_ANTDIV 63 ++#define WLC_SET_ANTDIV 64 ++/* #define WLC_GET_TXPWR 65 */ /* no longer supported */ ++/* #define WLC_SET_TXPWR 66 */ /* no longer supported */ ++#define WLC_GET_CLOSED 67 ++#define WLC_SET_CLOSED 68 ++#define WLC_GET_MACLIST 69 ++#define WLC_SET_MACLIST 70 ++#define WLC_GET_RATESET 71 ++#define WLC_SET_RATESET 72 ++/* #define WLC_GET_LOCALE 73 */ /* no longer supported */ ++#define WLC_LONGTRAIN 74 ++#define WLC_GET_BCNPRD 75 ++#define WLC_SET_BCNPRD 76 ++#define WLC_GET_DTIMPRD 77 ++#define WLC_SET_DTIMPRD 78 ++#define WLC_GET_SROM 79 ++#define WLC_SET_SROM 80 ++#define WLC_GET_WEP_RESTRICT 81 ++#define WLC_SET_WEP_RESTRICT 82 ++#define WLC_GET_COUNTRY 83 ++#define WLC_SET_COUNTRY 84 ++#define WLC_GET_PM 85 ++#define WLC_SET_PM 86 ++#define WLC_GET_WAKE 87 ++#define WLC_SET_WAKE 88 ++/* #define WLC_GET_D11CNTS 89 */ /* -> "counters" iovar */ ++#define WLC_GET_FORCELINK 90 /* ndis only */ ++#define WLC_SET_FORCELINK 91 /* ndis only */ ++#define WLC_FREQ_ACCURACY 92 /* diag */ ++#define WLC_CARRIER_SUPPRESS 93 /* diag */ ++#define WLC_GET_PHYREG 94 ++#define WLC_SET_PHYREG 95 ++#define WLC_GET_RADIOREG 96 ++#define WLC_SET_RADIOREG 97 ++#define WLC_GET_REVINFO 98 ++#define WLC_GET_UCANTDIV 99 ++#define WLC_SET_UCANTDIV 100 ++#define WLC_R_REG 101 ++#define WLC_W_REG 102 ++/* #define WLC_DIAG_LOOPBACK 103 old tray diag */ ++/* #define WLC_RESET_D11CNTS 104 */ /* -> "reset_d11cnts" iovar */ ++#define WLC_GET_MACMODE 105 ++#define WLC_SET_MACMODE 106 ++#define WLC_GET_MONITOR 107 ++#define WLC_SET_MONITOR 108 ++#define WLC_GET_GMODE 109 ++#define WLC_SET_GMODE 110 ++#define WLC_GET_LEGACY_ERP 111 ++#define WLC_SET_LEGACY_ERP 112 ++#define WLC_GET_RX_ANT 113 ++#define WLC_GET_CURR_RATESET 114 /* current rateset */ ++#define WLC_GET_SCANSUPPRESS 115 ++#define WLC_SET_SCANSUPPRESS 116 ++#define WLC_GET_AP 117 ++#define WLC_SET_AP 118 ++#define WLC_GET_EAP_RESTRICT 119 ++#define WLC_SET_EAP_RESTRICT 120 ++#define WLC_SCB_AUTHORIZE 121 ++#define WLC_SCB_DEAUTHORIZE 122 ++#define WLC_GET_WDSLIST 123 ++#define WLC_SET_WDSLIST 124 ++#define WLC_GET_ATIM 125 ++#define WLC_SET_ATIM 126 ++#define WLC_GET_RSSI 127 ++#define WLC_GET_PHYANTDIV 128 ++#define WLC_SET_PHYANTDIV 129 ++#define WLC_AP_RX_ONLY 130 ++#define WLC_GET_TX_PATH_PWR 131 ++#define WLC_SET_TX_PATH_PWR 132 ++#define WLC_GET_WSEC 133 ++#define WLC_SET_WSEC 134 ++#define WLC_GET_PHY_NOISE 135 ++#define WLC_GET_BSS_INFO 136 ++#define WLC_GET_PKTCNTS 137 ++#define WLC_GET_LAZYWDS 138 ++#define WLC_SET_LAZYWDS 139 ++#define WLC_GET_BANDLIST 140 ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++#define WLC_GET_BAND 141 ++#define WLC_SET_BAND 142 ++#define WLC_SCB_DEAUTHENTICATE 143 ++#define WLC_GET_SHORTSLOT 144 ++#define WLC_GET_SHORTSLOT_OVERRIDE 145 ++#define WLC_SET_SHORTSLOT_OVERRIDE 146 ++#define WLC_GET_SHORTSLOT_RESTRICT 147 ++#define WLC_SET_SHORTSLOT_RESTRICT 148 ++#define WLC_GET_GMODE_PROTECTION 149 ++#define WLC_GET_GMODE_PROTECTION_OVERRIDE 150 ++#define WLC_SET_GMODE_PROTECTION_OVERRIDE 151 ++#define WLC_UPGRADE 152 ++/* #define WLC_GET_MRATE 153 */ /* no longer supported */ ++/* #define WLC_SET_MRATE 154 */ /* no longer supported */ ++#define WLC_GET_IGNORE_BCNS 155 ++#define WLC_SET_IGNORE_BCNS 156 ++#define WLC_GET_SCB_TIMEOUT 157 ++#define WLC_SET_SCB_TIMEOUT 158 ++#define WLC_GET_ASSOCLIST 159 ++#define WLC_GET_CLK 160 ++#define WLC_SET_CLK 161 ++#define WLC_GET_UP 162 ++#define WLC_OUT 163 ++#define WLC_GET_WPA_AUTH 164 ++#define WLC_SET_WPA_AUTH 165 ++#define WLC_GET_UCFLAGS 166 ++#define WLC_SET_UCFLAGS 167 ++#define WLC_GET_PWRIDX 168 ++#define WLC_SET_PWRIDX 169 ++#define WLC_GET_TSSI 170 ++#define WLC_GET_SUP_RATESET_OVERRIDE 171 ++#define WLC_SET_SUP_RATESET_OVERRIDE 172 ++/* #define WLC_SET_FAST_TIMER 173 */ /* no longer supported */ ++/* #define WLC_GET_FAST_TIMER 174 */ /* no longer supported */ ++/* #define WLC_SET_SLOW_TIMER 175 */ /* no longer supported */ ++/* #define WLC_GET_SLOW_TIMER 176 */ /* no longer supported */ ++/* #define WLC_DUMP_PHYREGS 177 */ /* no longer supported */ ++#define WLC_GET_PROTECTION_CONTROL 178 ++#define WLC_SET_PROTECTION_CONTROL 179 ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++#define WLC_GET_PHYLIST 180 ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++#define WLC_ENCRYPT_STRENGTH 181 /* ndis only */ ++#define WLC_DECRYPT_STATUS 182 /* ndis only */ ++#define WLC_GET_KEY_SEQ 183 ++#define WLC_GET_SCAN_CHANNEL_TIME 184 ++#define WLC_SET_SCAN_CHANNEL_TIME 185 ++#define WLC_GET_SCAN_UNASSOC_TIME 186 ++#define WLC_SET_SCAN_UNASSOC_TIME 187 ++#define WLC_GET_SCAN_HOME_TIME 188 ++#define WLC_SET_SCAN_HOME_TIME 189 ++#define WLC_GET_SCAN_NPROBES 190 ++#define WLC_SET_SCAN_NPROBES 191 ++#define WLC_GET_PRB_RESP_TIMEOUT 192 ++#define WLC_SET_PRB_RESP_TIMEOUT 193 ++#define WLC_GET_ATTEN 194 ++#define WLC_SET_ATTEN 195 ++#define WLC_GET_SHMEM 196 /* diag */ ++#define WLC_SET_SHMEM 197 /* diag */ ++/* #define WLC_GET_GMODE_PROTECTION_CTS 198 */ /* no longer supported */ ++/* #define WLC_SET_GMODE_PROTECTION_CTS 199 */ /* no longer supported */ ++#define WLC_SET_WSEC_TEST 200 ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++#define WLC_SCB_DEAUTHENTICATE_FOR_REASON 201 ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++#define WLC_TKIP_COUNTERMEASURES 202 ++#define WLC_GET_PIOMODE 203 ++#define WLC_SET_PIOMODE 204 ++#define WLC_SET_ASSOC_PREFER 205 ++#define WLC_GET_ASSOC_PREFER 206 ++#define WLC_SET_ROAM_PREFER 207 ++#define WLC_GET_ROAM_PREFER 208 ++#define WLC_SET_LED 209 ++#define WLC_GET_LED 210 ++#define WLC_GET_INTERFERENCE_MODE 211 ++#define WLC_SET_INTERFERENCE_MODE 212 ++#define WLC_GET_CHANNEL_QA 213 ++#define WLC_START_CHANNEL_QA 214 ++#define WLC_GET_CHANNEL_SEL 215 ++#define WLC_START_CHANNEL_SEL 216 ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++#define WLC_GET_VALID_CHANNELS 217 ++#define WLC_GET_FAKEFRAG 218 ++#define WLC_SET_FAKEFRAG 219 ++#define WLC_GET_PWROUT_PERCENTAGE 220 ++#define WLC_SET_PWROUT_PERCENTAGE 221 ++#define WLC_SET_BAD_FRAME_PREEMPT 222 ++#define WLC_GET_BAD_FRAME_PREEMPT 223 ++#define WLC_SET_LEAP_LIST 224 ++#define WLC_GET_LEAP_LIST 225 ++#define WLC_GET_CWMIN 226 ++#define WLC_SET_CWMIN 227 ++#define WLC_GET_CWMAX 228 ++#define WLC_SET_CWMAX 229 ++#define WLC_GET_WET 230 ++#define WLC_SET_WET 231 ++#define WLC_GET_PUB 232 ++/* #define WLC_SET_GLACIAL_TIMER 233 */ /* no longer supported */ ++/* #define WLC_GET_GLACIAL_TIMER 234 */ /* no longer supported */ ++#define WLC_GET_KEY_PRIMARY 235 ++#define WLC_SET_KEY_PRIMARY 236 ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++ ++/* #define WLC_DUMP_RADIOREGS 237 */ /* no longer supported */ ++#define WLC_GET_ACI_ARGS 238 ++#define WLC_SET_ACI_ARGS 239 ++#define WLC_UNSET_CALLBACK 240 ++#define WLC_SET_CALLBACK 241 ++#define WLC_GET_RADAR 242 ++#define WLC_SET_RADAR 243 ++#define WLC_SET_SPECT_MANAGMENT 244 ++#define WLC_GET_SPECT_MANAGMENT 245 ++#define WLC_WDS_GET_REMOTE_HWADDR 246 /* handled in wl_linux.c/wl_vx.c */ ++#define WLC_WDS_GET_WPA_SUP 247 ++#define WLC_SET_CS_SCAN_TIMER 248 ++#define WLC_GET_CS_SCAN_TIMER 249 ++#define WLC_MEASURE_REQUEST 250 ++#define WLC_INIT 251 ++#define WLC_SEND_QUIET 252 ++#define WLC_KEEPALIVE 253 ++#define WLC_SEND_PWR_CONSTRAINT 254 ++#define WLC_UPGRADE_STATUS 255 ++#define WLC_CURRENT_PWR 256 ++#define WLC_GET_SCAN_PASSIVE_TIME 257 ++#define WLC_SET_SCAN_PASSIVE_TIME 258 ++#define WLC_LEGACY_LINK_BEHAVIOR 259 ++#define WLC_GET_CHANNELS_IN_COUNTRY 260 ++#define WLC_GET_COUNTRY_LIST 261 ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++#define WLC_GET_VAR 262 /* get value of named variable */ ++#define WLC_SET_VAR 263 /* set named variable to value */ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++#define WLC_NVRAM_GET 264 /* deprecated */ ++#define WLC_NVRAM_SET 265 ++#define WLC_NVRAM_DUMP 266 ++#define WLC_REBOOT 267 ++#define WLC_SET_WSEC_PMK 268 ++#define WLC_GET_AUTH_MODE 269 ++#define WLC_SET_AUTH_MODE 270 ++#define WLC_GET_WAKEENTRY 271 ++#define WLC_SET_WAKEENTRY 272 ++#define WLC_NDCONFIG_ITEM 273 /* currently handled in wl_oid.c */ ++#define WLC_NVOTPW 274 ++#define WLC_OTPW 275 ++#define WLC_IOV_BLOCK_GET 276 ++#define WLC_IOV_MODULES_GET 277 ++#define WLC_SOFT_RESET 278 ++#define WLC_GET_ALLOW_MODE 279 ++#define WLC_SET_ALLOW_MODE 280 ++#define WLC_GET_DESIRED_BSSID 281 ++#define WLC_SET_DESIRED_BSSID 282 ++#define WLC_DISASSOC_MYAP 283 ++#define WLC_GET_NBANDS 284 /* for Dongle EXT_STA support */ ++#define WLC_GET_BANDSTATES 285 /* for Dongle EXT_STA support */ ++#define WLC_GET_WLC_BSS_INFO 286 /* for Dongle EXT_STA support */ ++#define WLC_GET_ASSOC_INFO 287 /* for Dongle EXT_STA support */ ++#define WLC_GET_OID_PHY 288 /* for Dongle EXT_STA support */ ++#define WLC_SET_OID_PHY 289 /* for Dongle EXT_STA support */ ++#define WLC_SET_ASSOC_TIME 290 /* for Dongle EXT_STA support */ ++#define WLC_GET_DESIRED_SSID 291 /* for Dongle EXT_STA support */ ++#define WLC_GET_CHANSPEC 292 /* for Dongle EXT_STA support */ ++#define WLC_GET_ASSOC_STATE 293 /* for Dongle EXT_STA support */ ++#define WLC_SET_PHY_STATE 294 /* for Dongle EXT_STA support */ ++#define WLC_GET_SCAN_PENDING 295 /* for Dongle EXT_STA support */ ++#define WLC_GET_SCANREQ_PENDING 296 /* for Dongle EXT_STA support */ ++#define WLC_GET_PREV_ROAM_REASON 297 /* for Dongle EXT_STA support */ ++#define WLC_SET_PREV_ROAM_REASON 298 /* for Dongle EXT_STA support */ ++#define WLC_GET_BANDSTATES_PI 299 /* for Dongle EXT_STA support */ ++#define WLC_GET_PHY_STATE 300 /* for Dongle EXT_STA support */ ++#define WLC_GET_BSS_WPA_RSN 301 /* for Dongle EXT_STA support */ ++#define WLC_GET_BSS_WPA2_RSN 302 /* for Dongle EXT_STA support */ ++#define WLC_GET_BSS_BCN_TS 303 /* for Dongle EXT_STA support */ ++#define WLC_GET_INT_DISASSOC 304 /* for Dongle EXT_STA support */ ++#define WLC_SET_NUM_PEERS 305 /* for Dongle EXT_STA support */ ++#define WLC_GET_NUM_BSS 306 /* for Dongle EXT_STA support */ ++#define WLC_PHY_SAMPLE_COLLECT 307 /* phy sample collect mode */ ++/* #define WLC_UM_PRIV 308 */ /* Deprecated: usermode driver */ ++#define WLC_GET_CMD 309 ++/* #define WLC_LAST 310 */ /* Never used - can be reused */ ++#define WLC_SET_INTERFERENCE_OVERRIDE_MODE 311 /* set inter mode override */ ++#define WLC_GET_INTERFERENCE_OVERRIDE_MODE 312 /* get inter mode override */ ++/* #define WLC_GET_WAI_RESTRICT 313 */ /* for WAPI, deprecated use iovar instead */ ++/* #define WLC_SET_WAI_RESTRICT 314 */ /* for WAPI, deprecated use iovar instead */ ++/* #define WLC_SET_WAI_REKEY 315 */ /* for WAPI, deprecated use iovar instead */ ++#define WLC_SET_NAT_CONFIG 316 /* for configuring NAT filter driver */ ++#define WLC_GET_NAT_STATE 317 ++#define WLC_LAST 318 ++ ++#ifndef EPICTRL_COOKIE ++#define EPICTRL_COOKIE 0xABADCEDE ++#endif ++ ++/* vx wlc ioctl's offset */ ++#define CMN_IOCTL_OFF 0x180 ++ ++/* ++ * custom OID support ++ * ++ * 0xFF - implementation specific OID ++ * 0xE4 - first byte of Broadcom PCI vendor ID ++ * 0x14 - second byte of Broadcom PCI vendor ID ++ * 0xXX - the custom OID number ++ */ ++ ++/* begin 0x1f values beyond the start of the ET driver range. */ ++#define WL_OID_BASE 0xFFE41420 ++ ++/* NDIS overrides */ ++#define OID_WL_GETINSTANCE (WL_OID_BASE + WLC_GET_INSTANCE) ++#define OID_WL_GET_FORCELINK (WL_OID_BASE + WLC_GET_FORCELINK) ++#define OID_WL_SET_FORCELINK (WL_OID_BASE + WLC_SET_FORCELINK) ++#define OID_WL_ENCRYPT_STRENGTH (WL_OID_BASE + WLC_ENCRYPT_STRENGTH) ++#define OID_WL_DECRYPT_STATUS (WL_OID_BASE + WLC_DECRYPT_STATUS) ++#define OID_LEGACY_LINK_BEHAVIOR (WL_OID_BASE + WLC_LEGACY_LINK_BEHAVIOR) ++#define OID_WL_NDCONFIG_ITEM (WL_OID_BASE + WLC_NDCONFIG_ITEM) ++ ++/* EXT_STA Dongle suuport */ ++#define OID_STA_CHANSPEC (WL_OID_BASE + WLC_GET_CHANSPEC) ++#define OID_STA_NBANDS (WL_OID_BASE + WLC_GET_NBANDS) ++#define OID_STA_GET_PHY (WL_OID_BASE + WLC_GET_OID_PHY) ++#define OID_STA_SET_PHY (WL_OID_BASE + WLC_SET_OID_PHY) ++#define OID_STA_ASSOC_TIME (WL_OID_BASE + WLC_SET_ASSOC_TIME) ++#define OID_STA_DESIRED_SSID (WL_OID_BASE + WLC_GET_DESIRED_SSID) ++#define OID_STA_SET_PHY_STATE (WL_OID_BASE + WLC_SET_PHY_STATE) ++#define OID_STA_SCAN_PENDING (WL_OID_BASE + WLC_GET_SCAN_PENDING) ++#define OID_STA_SCANREQ_PENDING (WL_OID_BASE + WLC_GET_SCANREQ_PENDING) ++#define OID_STA_GET_ROAM_REASON (WL_OID_BASE + WLC_GET_PREV_ROAM_REASON) ++#define OID_STA_SET_ROAM_REASON (WL_OID_BASE + WLC_SET_PREV_ROAM_REASON) ++#define OID_STA_GET_PHY_STATE (WL_OID_BASE + WLC_GET_PHY_STATE) ++#define OID_STA_INT_DISASSOC (WL_OID_BASE + WLC_GET_INT_DISASSOC) ++#define OID_STA_SET_NUM_PEERS (WL_OID_BASE + WLC_SET_NUM_PEERS) ++#define OID_STA_GET_NUM_BSS (WL_OID_BASE + WLC_GET_NUM_BSS) ++ ++/* NAT filter driver support */ ++#define OID_NAT_SET_CONFIG (WL_OID_BASE + WLC_SET_NAT_CONFIG) ++#define OID_NAT_GET_STATE (WL_OID_BASE + WLC_GET_NAT_STATE) ++ ++#define WL_DECRYPT_STATUS_SUCCESS 1 ++#define WL_DECRYPT_STATUS_FAILURE 2 ++#define WL_DECRYPT_STATUS_UNKNOWN 3 ++ ++/* allows user-mode app to poll the status of USB image upgrade */ ++#define WLC_UPGRADE_SUCCESS 0 ++#define WLC_UPGRADE_PENDING 1 ++ ++#ifdef CONFIG_USBRNDIS_RETAIL ++/* struct passed in for WLC_NDCONFIG_ITEM */ ++typedef struct { ++ char *name; ++ void *param; ++} ndconfig_item_t; ++#endif ++ ++ ++/* WLC_GET_AUTH, WLC_SET_AUTH values */ ++#define WL_AUTH_OPEN_SYSTEM 0 /* d11 open authentication */ ++#define WL_AUTH_SHARED_KEY 1 /* d11 shared authentication */ ++#ifdef BCM4330_CHIP ++#define WL_AUTH_OPEN_SHARED 2 /* try open, then shared if open failed w/rc 13 */ ++#else ++/* BCM4334(Phoenex branch) value changed to 3 */ ++#define WL_AUTH_OPEN_SHARED 3 /* try open, then shared if open failed w/rc 13 */ ++#endif ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++/* Bit masks for radio disabled status - returned by WL_GET_RADIO */ ++#define WL_RADIO_SW_DISABLE (1<<0) ++#define WL_RADIO_HW_DISABLE (1<<1) ++#define WL_RADIO_MPC_DISABLE (1<<2) ++#define WL_RADIO_COUNTRY_DISABLE (1<<3) /* some countries don't support any channel */ ++ ++#define WL_SPURAVOID_OFF 0 ++#define WL_SPURAVOID_ON1 1 ++#define WL_SPURAVOID_ON2 2 ++ ++/* Override bit for WLC_SET_TXPWR. if set, ignore other level limits */ ++#define WL_TXPWR_OVERRIDE (1U<<31) ++#define WL_TXPWR_NEG (1U<<30) ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++#define WL_PHY_PAVARS_LEN 32 /* Phy type, Band range, chain, a1[0], b0[0], b1[0] ... */ ++ ++#define WL_PHY_PAVAR_VER 1 /* pavars version */ ++ ++typedef struct wl_po { ++ uint16 phy_type; /* Phy type */ ++ uint16 band; ++ uint16 cckpo; ++ uint32 ofdmpo; ++ uint16 mcspo[8]; ++} wl_po_t; ++ ++/* a large TX Power as an init value to factor out of MIN() calculations, ++ * keep low enough to fit in an int8, units are .25 dBm ++ */ ++#define WLC_TXPWR_MAX (127) /* ~32 dBm = 1,500 mW */ ++ ++/* "diag" iovar argument and error code */ ++#define WL_DIAG_INTERRUPT 1 /* d11 loopback interrupt test */ ++#define WL_DIAG_LOOPBACK 2 /* d11 loopback data test */ ++#define WL_DIAG_MEMORY 3 /* d11 memory test */ ++#define WL_DIAG_LED 4 /* LED test */ ++#define WL_DIAG_REG 5 /* d11/phy register test */ ++#define WL_DIAG_SROM 6 /* srom read/crc test */ ++#define WL_DIAG_DMA 7 /* DMA test */ ++#define WL_DIAG_LOOPBACK_EXT 8 /* enhenced d11 loopback data test */ ++ ++#define WL_DIAGERR_SUCCESS 0 ++#define WL_DIAGERR_FAIL_TO_RUN 1 /* unable to run requested diag */ ++#define WL_DIAGERR_NOT_SUPPORTED 2 /* diag requested is not supported */ ++#define WL_DIAGERR_INTERRUPT_FAIL 3 /* loopback interrupt test failed */ ++#define WL_DIAGERR_LOOPBACK_FAIL 4 /* loopback data test failed */ ++#define WL_DIAGERR_SROM_FAIL 5 /* srom read failed */ ++#define WL_DIAGERR_SROM_BADCRC 6 /* srom crc failed */ ++#define WL_DIAGERR_REG_FAIL 7 /* d11/phy register test failed */ ++#define WL_DIAGERR_MEMORY_FAIL 8 /* d11 memory test failed */ ++#define WL_DIAGERR_NOMEM 9 /* diag test failed due to no memory */ ++#define WL_DIAGERR_DMA_FAIL 10 /* DMA test failed */ ++ ++#define WL_DIAGERR_MEMORY_TIMEOUT 11 /* d11 memory test didn't finish in time */ ++#define WL_DIAGERR_MEMORY_BADPATTERN 12 /* d11 memory test result in bad pattern */ ++ ++/* band types */ ++#define WLC_BAND_AUTO 0 /* auto-select */ ++#define WLC_BAND_5G 1 /* 5 Ghz */ ++#define WLC_BAND_2G 2 /* 2.4 Ghz */ ++#define WLC_BAND_ALL 3 /* all bands */ ++ ++/* band range returned by band_range iovar */ ++#define WL_CHAN_FREQ_RANGE_2G 0 ++#define WL_CHAN_FREQ_RANGE_5GL 1 ++#define WL_CHAN_FREQ_RANGE_5GM 2 ++#define WL_CHAN_FREQ_RANGE_5GH 3 ++ ++#define WL_CHAN_FREQ_RANGE_5G_BAND0 1 ++#define WL_CHAN_FREQ_RANGE_5G_BAND1 2 ++#define WL_CHAN_FREQ_RANGE_5G_BAND2 3 ++#define WL_CHAN_FREQ_RANGE_5G_BAND3 4 ++ ++#define WL_CHAN_FREQ_RANGE_5G_4BAND 5 ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++/* phy types (returned by WLC_GET_PHYTPE) */ ++#define WLC_PHY_TYPE_A 0 ++#define WLC_PHY_TYPE_B 1 ++#define WLC_PHY_TYPE_G 2 ++#define WLC_PHY_TYPE_N 4 ++#define WLC_PHY_TYPE_LP 5 ++#define WLC_PHY_TYPE_SSN 6 ++#define WLC_PHY_TYPE_HT 7 ++#define WLC_PHY_TYPE_LCN 8 ++#define WLC_PHY_TYPE_LCN40 10 ++#define WLC_PHY_TYPE_AC 11 ++#define WLC_PHY_TYPE_NULL 0xf ++ ++/* Values for PM */ ++#define PM_OFF 0 ++#define PM_MAX 1 ++#define PM_FAST 2 ++#define PM_FORCE_OFF 3 /* use this bit to force PM off even bt is active */ ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++/* MAC list modes */ ++#define WLC_MACMODE_DISABLED 0 /* MAC list disabled */ ++#define WLC_MACMODE_DENY 1 /* Deny specified (i.e. allow unspecified) */ ++#define WLC_MACMODE_ALLOW 2 /* Allow specified (i.e. deny unspecified) */ ++ ++/* ++ * 54g modes (basic bits may still be overridden) ++ * ++ * GMODE_LEGACY_B Rateset: 1b, 2b, 5.5, 11 ++ * Preamble: Long ++ * Shortslot: Off ++ * GMODE_AUTO Rateset: 1b, 2b, 5.5b, 11b, 18, 24, 36, 54 ++ * Extended Rateset: 6, 9, 12, 48 ++ * Preamble: Long ++ * Shortslot: Auto ++ * GMODE_ONLY Rateset: 1b, 2b, 5.5b, 11b, 18, 24b, 36, 54 ++ * Extended Rateset: 6b, 9, 12b, 48 ++ * Preamble: Short required ++ * Shortslot: Auto ++ * GMODE_B_DEFERRED Rateset: 1b, 2b, 5.5b, 11b, 18, 24, 36, 54 ++ * Extended Rateset: 6, 9, 12, 48 ++ * Preamble: Long ++ * Shortslot: On ++ * GMODE_PERFORMANCE Rateset: 1b, 2b, 5.5b, 6b, 9, 11b, 12b, 18, 24b, 36, 48, 54 ++ * Preamble: Short required ++ * Shortslot: On and required ++ * GMODE_LRS Rateset: 1b, 2b, 5.5b, 11b ++ * Extended Rateset: 6, 9, 12, 18, 24, 36, 48, 54 ++ * Preamble: Long ++ * Shortslot: Auto ++ */ ++#define GMODE_LEGACY_B 0 ++#define GMODE_AUTO 1 ++#define GMODE_ONLY 2 ++#define GMODE_B_DEFERRED 3 ++#define GMODE_PERFORMANCE 4 ++#define GMODE_LRS 5 ++#define GMODE_MAX 6 ++ ++/* values for PLCPHdr_override */ ++#define WLC_PLCP_AUTO -1 ++#define WLC_PLCP_SHORT 0 ++#define WLC_PLCP_LONG 1 ++ ++/* values for g_protection_override and n_protection_override */ ++#define WLC_PROTECTION_AUTO -1 ++#define WLC_PROTECTION_OFF 0 ++#define WLC_PROTECTION_ON 1 ++#define WLC_PROTECTION_MMHDR_ONLY 2 ++#define WLC_PROTECTION_CTS_ONLY 3 ++ ++/* values for g_protection_control and n_protection_control */ ++#define WLC_PROTECTION_CTL_OFF 0 ++#define WLC_PROTECTION_CTL_LOCAL 1 ++#define WLC_PROTECTION_CTL_OVERLAP 2 ++ ++/* values for n_protection */ ++#define WLC_N_PROTECTION_OFF 0 ++#define WLC_N_PROTECTION_OPTIONAL 1 ++#define WLC_N_PROTECTION_20IN40 2 ++#define WLC_N_PROTECTION_MIXEDMODE 3 ++ ++/* values for n_preamble_type */ ++#define WLC_N_PREAMBLE_MIXEDMODE 0 ++#define WLC_N_PREAMBLE_GF 1 ++#define WLC_N_PREAMBLE_GF_BRCM 2 ++ ++/* values for band specific 40MHz capabilities (deprecated) */ ++#define WLC_N_BW_20ALL 0 ++#define WLC_N_BW_40ALL 1 ++#define WLC_N_BW_20IN2G_40IN5G 2 ++ ++#define WLC_BW_20MHZ_BIT (1<<0) ++#define WLC_BW_40MHZ_BIT (1<<1) ++#define WLC_BW_80MHZ_BIT (1<<2) ++ ++/* Bandwidth capabilities */ ++#define WLC_BW_CAP_20MHZ (WLC_BW_20MHZ_BIT) ++#define WLC_BW_CAP_40MHZ (WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT) ++#define WLC_BW_CAP_80MHZ (WLC_BW_80MHZ_BIT|WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT) ++#define WLC_BW_CAP_UNRESTRICTED 0xFF ++ ++#define WL_BW_CAP_20MHZ(bw_cap) (((bw_cap) & WLC_BW_20MHZ_BIT) ? TRUE : FALSE) ++#define WL_BW_CAP_40MHZ(bw_cap) (((bw_cap) & WLC_BW_40MHZ_BIT) ? TRUE : FALSE) ++#define WL_BW_CAP_80MHZ(bw_cap) (((bw_cap) & WLC_BW_80MHZ_BIT) ? TRUE : FALSE) ++ ++/* values to force tx/rx chain */ ++#define WLC_N_TXRX_CHAIN0 0 ++#define WLC_N_TXRX_CHAIN1 1 ++ ++/* bitflags for SGI support (sgi_rx iovar) */ ++#define WLC_N_SGI_20 0x01 ++#define WLC_N_SGI_40 0x02 ++#define WLC_VHT_SGI_80 0x04 ++ ++/* when sgi_tx==WLC_SGI_ALL, bypass rate selection, enable sgi for all mcs */ ++#define WLC_SGI_ALL 0x02 ++ ++#define LISTEN_INTERVAL 10 ++/* interference mitigation options */ ++#define INTERFERE_OVRRIDE_OFF -1 /* interference override off */ ++#define INTERFERE_NONE 0 /* off */ ++#define NON_WLAN 1 /* foreign/non 802.11 interference, no auto detect */ ++#define WLAN_MANUAL 2 /* ACI: no auto detection */ ++#define WLAN_AUTO 3 /* ACI: auto detect */ ++#define WLAN_AUTO_W_NOISE 4 /* ACI: auto - detect and non 802.11 interference */ ++#define AUTO_ACTIVE (1 << 7) /* Auto is currently active */ ++ ++/* AP environment */ ++#define AP_ENV_DETECT_NOT_USED 0 /* We aren't using AP environment detection */ ++#define AP_ENV_DENSE 1 /* "Corporate" or other AP dense environment */ ++#define AP_ENV_SPARSE 2 /* "Home" or other sparse environment */ ++#define AP_ENV_INDETERMINATE 3 /* AP environment hasn't been identified */ ++ ++typedef struct wl_aci_args { ++ int enter_aci_thresh; /* Trigger level to start detecting ACI */ ++ int exit_aci_thresh; /* Trigger level to exit ACI mode */ ++ int usec_spin; /* microsecs to delay between rssi samples */ ++ int glitch_delay; /* interval between ACI scans when glitch count is consistently high */ ++ uint16 nphy_adcpwr_enter_thresh; /* ADC power to enter ACI mitigation mode */ ++ uint16 nphy_adcpwr_exit_thresh; /* ADC power to exit ACI mitigation mode */ ++ uint16 nphy_repeat_ctr; /* Number of tries per channel to compute power */ ++ uint16 nphy_num_samples; /* Number of samples to compute power on one channel */ ++ uint16 nphy_undetect_window_sz; /* num of undetects to exit ACI Mitigation mode */ ++ uint16 nphy_b_energy_lo_aci; /* low ACI power energy threshold for bphy */ ++ uint16 nphy_b_energy_md_aci; /* mid ACI power energy threshold for bphy */ ++ uint16 nphy_b_energy_hi_aci; /* high ACI power energy threshold for bphy */ ++ uint16 nphy_noise_noassoc_glitch_th_up; /* wl interference 4 */ ++ uint16 nphy_noise_noassoc_glitch_th_dn; ++ uint16 nphy_noise_assoc_glitch_th_up; ++ uint16 nphy_noise_assoc_glitch_th_dn; ++ uint16 nphy_noise_assoc_aci_glitch_th_up; ++ uint16 nphy_noise_assoc_aci_glitch_th_dn; ++ uint16 nphy_noise_assoc_enter_th; ++ uint16 nphy_noise_noassoc_enter_th; ++ uint16 nphy_noise_assoc_rx_glitch_badplcp_enter_th; ++ uint16 nphy_noise_noassoc_crsidx_incr; ++ uint16 nphy_noise_assoc_crsidx_incr; ++ uint16 nphy_noise_crsidx_decr; ++} wl_aci_args_t; ++ ++#define TRIGGER_NOW 0 ++#define TRIGGER_CRS 0x01 ++#define TRIGGER_CRSDEASSERT 0x02 ++#define TRIGGER_GOODFCS 0x04 ++#define TRIGGER_BADFCS 0x08 ++#define TRIGGER_BADPLCP 0x10 ++#define TRIGGER_CRSGLITCH 0x20 ++#define WL_ACI_ARGS_LEGACY_LENGTH 16 /* bytes of pre NPHY aci args */ ++#define WL_SAMPLECOLLECT_T_VERSION 2 /* version of wl_samplecollect_args_t struct */ ++typedef struct wl_samplecollect_args { ++ /* version 0 fields */ ++ uint8 coll_us; ++ int cores; ++ /* add'l version 1 fields */ ++ uint16 version; /* see definition of WL_SAMPLECOLLECT_T_VERSION */ ++ uint16 length; /* length of entire structure */ ++ int8 trigger; ++ uint16 timeout; ++ uint16 mode; ++ uint32 pre_dur; ++ uint32 post_dur; ++ uint8 gpio_sel; ++ bool downsamp; ++ bool be_deaf; ++ bool agc; /* loop from init gain and going down */ ++ bool filter; /* override high pass corners to lowest */ ++ /* add'l version 2 fields */ ++ uint8 trigger_state; ++ uint8 module_sel1; ++ uint8 module_sel2; ++ uint16 nsamps; ++} wl_samplecollect_args_t; ++ ++#define WL_SAMPLEDATA_HEADER_TYPE 1 ++#define WL_SAMPLEDATA_HEADER_SIZE 80 /* sample collect header size (bytes) */ ++#define WL_SAMPLEDATA_TYPE 2 ++#define WL_SAMPLEDATA_SEQ 0xff /* sequence # */ ++#define WL_SAMPLEDATA_MORE_DATA 0x100 /* more data mask */ ++#define WL_SAMPLEDATA_T_VERSION 1 /* version of wl_samplecollect_args_t struct */ ++/* version for unpacked sample data, int16 {(I,Q),Core(0..N)} */ ++#define WL_SAMPLEDATA_T_VERSION_SPEC_AN 2 ++ ++typedef struct wl_sampledata { ++ uint16 version; /* structure version */ ++ uint16 size; /* size of structure */ ++ uint16 tag; /* Header/Data */ ++ uint16 length; /* data length */ ++ uint32 flag; /* bit def */ ++} wl_sampledata_t; ++ ++/* wl_radar_args_t */ ++typedef struct { ++ int npulses; /* required number of pulses at n * t_int */ ++ int ncontig; /* required number of pulses at t_int */ ++ int min_pw; /* minimum pulse width (20 MHz clocks) */ ++ int max_pw; /* maximum pulse width (20 MHz clocks) */ ++ uint16 thresh0; /* Radar detection, thresh 0 */ ++ uint16 thresh1; /* Radar detection, thresh 1 */ ++ uint16 blank; /* Radar detection, blank control */ ++ uint16 fmdemodcfg; /* Radar detection, fmdemod config */ ++ int npulses_lp; /* Radar detection, minimum long pulses */ ++ int min_pw_lp; /* Minimum pulsewidth for long pulses */ ++ int max_pw_lp; /* Maximum pulsewidth for long pulses */ ++ int min_fm_lp; /* Minimum fm for long pulses */ ++ int max_span_lp; /* Maximum deltat for long pulses */ ++ int min_deltat; /* Minimum spacing between pulses */ ++ int max_deltat; /* Maximum spacing between pulses */ ++ uint16 autocorr; /* Radar detection, autocorr on or off */ ++ uint16 st_level_time; /* Radar detection, start_timing level */ ++ uint16 t2_min; /* minimum clocks needed to remain in state 2 */ ++ uint32 version; /* version */ ++ uint32 fra_pulse_err; /* sample error margin for detecting French radar pulsed */ ++ int npulses_fra; /* Radar detection, minimum French pulses set */ ++ int npulses_stg2; /* Radar detection, minimum staggered-2 pulses set */ ++ int npulses_stg3; /* Radar detection, minimum staggered-3 pulses set */ ++ uint16 percal_mask; /* defines which period cal is masked from radar detection */ ++ int quant; /* quantization resolution to pulse positions */ ++ uint32 min_burst_intv_lp; /* minimum burst to burst interval for bin3 radar */ ++ uint32 max_burst_intv_lp; /* maximum burst to burst interval for bin3 radar */ ++ int nskip_rst_lp; /* number of skipped pulses before resetting lp buffer */ ++ int max_pw_tol; /* maximum tollerance allowed in detected pulse width for radar detection */ ++ uint16 feature_mask; /* 16-bit mask to specify enabled features */ ++} wl_radar_args_t; ++ ++#define WL_RADAR_ARGS_VERSION 2 ++ ++typedef struct { ++ uint32 version; /* version */ ++ uint16 thresh0_20_lo; /* Radar detection, thresh 0 (range 5250-5350MHz) for BW 20MHz */ ++ uint16 thresh1_20_lo; /* Radar detection, thresh 1 (range 5250-5350MHz) for BW 20MHz */ ++ uint16 thresh0_40_lo; /* Radar detection, thresh 0 (range 5250-5350MHz) for BW 40MHz */ ++ uint16 thresh1_40_lo; /* Radar detection, thresh 1 (range 5250-5350MHz) for BW 40MHz */ ++ uint16 thresh0_80_lo; /* Radar detection, thresh 0 (range 5250-5350MHz) for BW 80MHz */ ++ uint16 thresh1_80_lo; /* Radar detection, thresh 1 (range 5250-5350MHz) for BW 80MHz */ ++ uint16 thresh0_160_lo; /* Radar detection, thresh 0 (range 5250-5350MHz) for BW 160MHz */ ++ uint16 thresh1_160_lo; /* Radar detection, thresh 1 (range 5250-5350MHz) for BW 160MHz */ ++ uint16 thresh0_20_hi; /* Radar detection, thresh 0 (range 5470-5725MHz) for BW 20MHz */ ++ uint16 thresh1_20_hi; /* Radar detection, thresh 1 (range 5470-5725MHz) for BW 20MHz */ ++ uint16 thresh0_40_hi; /* Radar detection, thresh 0 (range 5470-5725MHz) for BW 40MHz */ ++ uint16 thresh1_40_hi; /* Radar detection, thresh 1 (range 5470-5725MHz) for BW 40MHz */ ++ uint16 thresh0_80_hi; /* Radar detection, thresh 0 (range 5470-5725MHz) for BW 80MHz */ ++ uint16 thresh1_80_hi; /* Radar detection, thresh 1 (range 5470-5725MHz) for BW 80MHz */ ++ uint16 thresh0_160_hi; /* Radar detection, thresh 0 (range 5470-5725MHz) for BW 160MHz */ ++ uint16 thresh1_160_hi; /* Radar detection, thresh 1 (range 5470-5725MHz) for BW 160MHz */ ++} wl_radar_thr_t; ++ ++#define WL_RADAR_THR_VERSION 2 ++#define WL_THRESHOLD_LO_BAND 70 /* range from 5250MHz - 5350MHz */ ++ ++/* radar iovar SET defines */ ++#define WL_RADAR_DETECTOR_OFF 0 /* radar detector off */ ++#define WL_RADAR_DETECTOR_ON 1 /* radar detector on */ ++#define WL_RADAR_SIMULATED 2 /* force radar detector to declare ++ * detection once ++ */ ++#define WL_RSSI_ANT_VERSION 1 /* current version of wl_rssi_ant_t */ ++#define WL_ANT_RX_MAX 2 /* max 2 receive antennas */ ++#define WL_ANT_HT_RX_MAX 3 /* max 3 receive antennas/cores */ ++#define WL_ANT_IDX_1 0 /* antenna index 1 */ ++#define WL_ANT_IDX_2 1 /* antenna index 2 */ ++ ++#ifndef WL_RSSI_ANT_MAX ++#define WL_RSSI_ANT_MAX 4 /* max possible rx antennas */ ++#elif WL_RSSI_ANT_MAX != 4 ++#error "WL_RSSI_ANT_MAX does not match" ++#endif ++ ++/* RSSI per antenna */ ++typedef struct { ++ uint32 version; /* version field */ ++ uint32 count; /* number of valid antenna rssi */ ++ int8 rssi_ant[WL_RSSI_ANT_MAX]; /* rssi per antenna */ ++} wl_rssi_ant_t; ++ ++/* dfs_status iovar-related defines */ ++ ++/* cac - channel availability check, ++ * ism - in-service monitoring ++ * csa - channel switching announcement ++ */ ++ ++/* cac state values */ ++#define WL_DFS_CACSTATE_IDLE 0 /* state for operating in non-radar channel */ ++#define WL_DFS_CACSTATE_PREISM_CAC 1 /* CAC in progress */ ++#define WL_DFS_CACSTATE_ISM 2 /* ISM in progress */ ++#define WL_DFS_CACSTATE_CSA 3 /* csa */ ++#define WL_DFS_CACSTATE_POSTISM_CAC 4 /* ISM CAC */ ++#define WL_DFS_CACSTATE_PREISM_OOC 5 /* PREISM OOC */ ++#define WL_DFS_CACSTATE_POSTISM_OOC 6 /* POSTISM OOC */ ++#define WL_DFS_CACSTATES 7 /* this many states exist */ ++ ++/* data structure used in 'dfs_status' wl interface, which is used to query dfs status */ ++typedef struct { ++ uint state; /* noted by WL_DFS_CACSTATE_XX. */ ++ uint duration; /* time spent in ms in state. */ ++ /* as dfs enters ISM state, it removes the operational channel from quiet channel ++ * list and notes the channel in channel_cleared. set to 0 if no channel is cleared ++ */ ++ chanspec_t chanspec_cleared; ++ /* chanspec cleared used to be a uint, add another to uint16 to maintain size */ ++ uint16 pad; ++} wl_dfs_status_t; ++ ++#define NUM_PWRCTRL_RATES 12 ++ ++typedef struct { ++ uint8 txpwr_band_max[NUM_PWRCTRL_RATES]; /* User set target */ ++ uint8 txpwr_limit[NUM_PWRCTRL_RATES]; /* reg and local power limit */ ++ uint8 txpwr_local_max; /* local max according to the AP */ ++ uint8 txpwr_local_constraint; /* local constraint according to the AP */ ++ uint8 txpwr_chan_reg_max; /* Regulatory max for this channel */ ++ uint8 txpwr_target[2][NUM_PWRCTRL_RATES]; /* Latest target for 2.4 and 5 Ghz */ ++ uint8 txpwr_est_Pout[2]; /* Latest estimate for 2.4 and 5 Ghz */ ++ uint8 txpwr_opo[NUM_PWRCTRL_RATES]; /* On G phy, OFDM power offset */ ++ uint8 txpwr_bphy_cck_max[NUM_PWRCTRL_RATES]; /* Max CCK power for this band (SROM) */ ++ uint8 txpwr_bphy_ofdm_max; /* Max OFDM power for this band (SROM) */ ++ uint8 txpwr_aphy_max[NUM_PWRCTRL_RATES]; /* Max power for A band (SROM) */ ++ int8 txpwr_antgain[2]; /* Ant gain for each band - from SROM */ ++ uint8 txpwr_est_Pout_gofdm; /* Pwr estimate for 2.4 OFDM */ ++} tx_power_legacy_t; ++ ++#define WL_TX_POWER_RATES_LEGACY 45 ++#define WL_TX_POWER_MCS20_FIRST 12 ++#define WL_TX_POWER_MCS20_NUM 16 ++#define WL_TX_POWER_MCS40_FIRST 28 ++#define WL_TX_POWER_MCS40_NUM 17 ++ ++typedef struct { ++ uint32 flags; ++ chanspec_t chanspec; /* txpwr report for this channel */ ++ chanspec_t local_chanspec; /* channel on which we are associated */ ++ uint8 local_max; /* local max according to the AP */ ++ uint8 local_constraint; /* local constraint according to the AP */ ++ int8 antgain[2]; /* Ant gain for each band - from SROM */ ++ uint8 rf_cores; /* count of RF Cores being reported */ ++ uint8 est_Pout[4]; /* Latest tx power out estimate per RF ++ * chain without adjustment ++ */ ++ uint8 est_Pout_cck; /* Latest CCK tx power out estimate */ ++ uint8 user_limit[WL_TX_POWER_RATES_LEGACY]; /* User limit */ ++ uint8 reg_limit[WL_TX_POWER_RATES_LEGACY]; /* Regulatory power limit */ ++ uint8 board_limit[WL_TX_POWER_RATES_LEGACY]; /* Max power board can support (SROM) */ ++ uint8 target[WL_TX_POWER_RATES_LEGACY]; /* Latest target power */ ++} tx_power_legacy2_t; ++ ++/* TX Power index defines */ ++#define WL_NUM_RATES_CCK 4 /* 1, 2, 5.5, 11 Mbps */ ++#define WL_NUM_RATES_OFDM 8 /* 6, 9, 12, 18, 24, 36, 48, 54 Mbps SISO/CDD */ ++#define WL_NUM_RATES_MCS_1STREAM 8 /* MCS 0-7 1-stream rates - SISO/CDD/STBC/MCS */ ++#define WL_NUM_RATES_EXTRA_VHT 2 /* Additional VHT 11AC rates */ ++#define WL_NUM_RATES_VHT 10 ++#define WL_NUM_RATES_MCS32 1 ++ ++#define WLC_NUM_RATES_CCK WL_NUM_RATES_CCK ++#define WLC_NUM_RATES_OFDM WL_NUM_RATES_OFDM ++#define WLC_NUM_RATES_MCS_1_STREAM WL_NUM_RATES_MCS_1STREAM ++#define WLC_NUM_RATES_MCS_2_STREAM WL_NUM_RATES_MCS_1STREAM ++#define WLC_NUM_RATES_MCS32 WL_NUM_RATES_MCS32 ++#define WL_TX_POWER_CCK_NUM WL_NUM_RATES_CCK ++#define WL_TX_POWER_OFDM_NUM WL_NUM_RATES_OFDM ++#define WL_TX_POWER_MCS_1_STREAM_NUM WL_NUM_RATES_MCS_1STREAM ++#define WL_TX_POWER_MCS_2_STREAM_NUM WL_NUM_RATES_MCS_1STREAM ++#define WL_TX_POWER_MCS_32_NUM WL_NUM_RATES_MCS32 ++ ++#define WL_NUM_2x2_ELEMENTS 4 ++#define WL_NUM_3x3_ELEMENTS 6 ++ ++typedef struct txppr { ++ /* start of 20MHz tx power limits */ ++ uint8 b20_1x1dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b20_1x1ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM transmission */ ++ uint8 b20_1x1mcs0[WL_NUM_RATES_MCS_1STREAM]; /* SISO MCS 0-7 */ ++ ++ uint8 b20_1x2dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b20_1x2cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ ++ uint8 b20_1x2cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* CDD MCS 0-7 */ ++ uint8 b20_2x2stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ ++ uint8 b20_2x2sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* MCS 8-15 */ ++ ++ uint8 b20_1x3dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b20_1x3cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ ++ uint8 b20_1x3cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* 1 Nsts to 3 Tx Chain */ ++ uint8 b20_2x3stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ ++ uint8 b20_2x3sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* 2 Nsts to 3 Tx Chain */ ++ uint8 b20_3x3sdm_mcs16[WL_NUM_RATES_MCS_1STREAM]; /* 3 Nsts to 3 Tx Chain */ ++ ++ uint8 b20_1x1vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1 */ ++ uint8 b20_1x2cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD1 */ ++ uint8 b20_2x2stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC */ ++ uint8 b20_2x2sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2 */ ++ uint8 b20_1x3cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD2 */ ++ uint8 b20_2x3stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC_SPEXP1 */ ++ uint8 b20_2x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2_SPEXP1 */ ++ uint8 b20_3x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS3 */ ++ ++ /* start of 40MHz tx power limits */ ++ uint8 b40_dummy1x1dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b40_1x1ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM transmission */ ++ uint8 b40_1x1mcs0[WL_NUM_RATES_MCS_1STREAM]; /* SISO MCS 0-7 */ ++ ++ uint8 b40_dummy1x2dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b40_1x2cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ ++ uint8 b40_1x2cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* CDD MCS 0-7 */ ++ uint8 b40_2x2stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ ++ uint8 b40_2x2sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* MCS 8-15 */ ++ ++ uint8 b40_dummy1x3dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b40_1x3cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ ++ uint8 b40_1x3cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* 1 Nsts to 3 Tx Chain */ ++ uint8 b40_2x3stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ ++ uint8 b40_2x3sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* 2 Nsts to 3 Tx Chain */ ++ uint8 b40_3x3sdm_mcs16[WL_NUM_RATES_MCS_1STREAM]; /* 3 Nsts to 3 Tx Chain */ ++ ++ uint8 b40_1x1vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1 */ ++ uint8 b40_1x2cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD1 */ ++ uint8 b40_2x2stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC */ ++ uint8 b40_2x2sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2 */ ++ uint8 b40_1x3cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD2 */ ++ uint8 b40_2x3stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC_SPEXP1 */ ++ uint8 b40_2x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2_SPEXP1 */ ++ uint8 b40_3x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS3 */ ++ ++ /* start of 20in40MHz tx power limits */ ++ uint8 b20in40_1x1dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b20in40_1x1ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM transmission */ ++ uint8 b20in40_1x1mcs0[WL_NUM_RATES_MCS_1STREAM]; /* SISO MCS 0-7 */ ++ ++ uint8 b20in40_1x2dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b20in40_1x2cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ ++ uint8 b20in40_1x2cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* CDD MCS 0-7 */ ++ uint8 b20in40_2x2stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ ++ uint8 b20in40_2x2sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* MCS 8-15 */ ++ ++ uint8 b20in40_1x3dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b20in40_1x3cdd_ofdm[WL_NUM_RATES_OFDM]; /* 20 in 40 MHz Legacy OFDM CDD */ ++ uint8 b20in40_1x3cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* 1 Nsts to 3 Tx Chain */ ++ uint8 b20in40_2x3stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ ++ uint8 b20in40_2x3sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* 2 Nsts to 3 Tx Chain */ ++ uint8 b20in40_3x3sdm_mcs16[WL_NUM_RATES_MCS_1STREAM]; /* 3 Nsts to 3 Tx Chain */ ++ ++ uint8 b20in40_1x1vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1 */ ++ uint8 b20in40_1x2cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD1 */ ++ uint8 b20in40_2x2stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC */ ++ uint8 b20in40_2x2sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2 */ ++ uint8 b20in40_1x3cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD2 */ ++ uint8 b20in40_2x3stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC_SPEXP1 */ ++ uint8 b20in40_2x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2_SPEXP1 */ ++ uint8 b20in40_3x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS3 */ ++ ++ /* start of 80MHz tx power limits */ ++ uint8 b80_dummy1x1dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b80_1x1ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM transmission */ ++ uint8 b80_1x1mcs0[WL_NUM_RATES_MCS_1STREAM]; /* SISO MCS 0-7 */ ++ ++ uint8 b80_dummy1x2dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b80_1x2cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ ++ uint8 b80_1x2cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* CDD MCS 0-7 */ ++ uint8 b80_2x2stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ ++ uint8 b80_2x2sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* MCS 8-15 */ ++ ++ uint8 b80_dummy1x3dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b80_1x3cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ ++ uint8 b80_1x3cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* 1 Nsts to 3 Tx Chain */ ++ uint8 b80_2x3stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ ++ uint8 b80_2x3sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* 2 Nsts to 3 Tx Chain */ ++ uint8 b80_3x3sdm_mcs16[WL_NUM_RATES_MCS_1STREAM]; /* 3 Nsts to 3 Tx Chain */ ++ ++ uint8 b80_1x1vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1 */ ++ uint8 b80_1x2cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD1 */ ++ uint8 b80_2x2stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC */ ++ uint8 b80_2x2sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2 */ ++ uint8 b80_1x3cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD2 */ ++ uint8 b80_2x3stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC_SPEXP1 */ ++ uint8 b80_2x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2_SPEXP1 */ ++ uint8 b80_3x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS3 */ ++ ++ /* start of 20in80MHz tx power limits */ ++ uint8 b20in80_1x1dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b20in80_1x1ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM transmission */ ++ uint8 b20in80_1x1mcs0[WL_NUM_RATES_MCS_1STREAM]; /* SISO MCS 0-7 */ ++ ++ uint8 b20in80_1x2dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b20in80_1x2cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ ++ uint8 b20in80_1x2cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* CDD MCS 0-7 */ ++ uint8 b20in80_2x2stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ ++ uint8 b20in80_2x2sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* MCS 8-15 */ ++ ++ uint8 b20in80_1x3dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b20in80_1x3cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ ++ uint8 b20in80_1x3cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* 1 Nsts to 3 Tx Chain */ ++ uint8 b20in80_2x3stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ ++ uint8 b20in80_2x3sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* 2 Nsts to 3 Tx Chain */ ++ uint8 b20in80_3x3sdm_mcs16[WL_NUM_RATES_MCS_1STREAM]; /* 3 Nsts to 3 Tx Chain */ ++ ++ uint8 b20in80_1x1vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1 */ ++ uint8 b20in80_1x2cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD1 */ ++ uint8 b20in80_2x2stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC */ ++ uint8 b20in80_2x2sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2 */ ++ uint8 b20in80_1x3cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD2 */ ++ uint8 b20in80_2x3stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC_SPEXP1 */ ++ uint8 b20in80_2x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2_SPEXP1 */ ++ uint8 b20in80_3x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS3 */ ++ ++ /* start of 40in80MHz tx power limits */ ++ uint8 b40in80_dummy1x1dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b40in80_1x1ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM transmission */ ++ uint8 b40in80_1x1mcs0[WL_NUM_RATES_MCS_1STREAM]; /* SISO MCS 0-7 */ ++ ++ uint8 b40in80_dummy1x2dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b40in80_1x2cdd_ofdm[WL_NUM_RATES_OFDM]; /* Legacy OFDM CDD transmission */ ++ uint8 b40in80_1x2cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* CDD MCS 0-7 */ ++ uint8 b40in80_2x2stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ ++ uint8 b40in80_2x2sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* MCS 8-15 */ ++ ++ uint8 b40in80_dummy1x3dsss[WL_NUM_RATES_CCK]; /* Legacy CCK/DSSS */ ++ uint8 b40in80_1x3cdd_ofdm[WL_NUM_RATES_OFDM]; /* MHz Legacy OFDM CDD */ ++ uint8 b40in80_1x3cdd_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* 1 Nsts to 3 Tx Chain */ ++ uint8 b40in80_2x3stbc_mcs0[WL_NUM_RATES_MCS_1STREAM]; /* STBC MCS 0-7 */ ++ uint8 b40in80_2x3sdm_mcs8[WL_NUM_RATES_MCS_1STREAM]; /* 2 Nsts to 3 Tx Chain */ ++ uint8 b40in80_3x3sdm_mcs16[WL_NUM_RATES_MCS_1STREAM]; /* 3 Nsts to 3 Tx Chain */ ++ ++ uint8 b40in80_1x1vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1 */ ++ uint8 b40in80_1x2cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD1 */ ++ uint8 b40in80_2x2stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC */ ++ uint8 b40in80_2x2sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2 */ ++ uint8 b40in80_1x3cdd_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_CDD2 */ ++ uint8 b40in80_2x3stbc_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS1_STBC_SPEXP1 */ ++ uint8 b40in80_2x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS2_SPEXP1 */ ++ uint8 b40in80_3x3sdm_vht[WL_NUM_RATES_EXTRA_VHT]; /* VHT8_9SS3 */ ++ ++ uint8 mcs32; /* C_CHECK - THIS NEEDS TO BE REMOVED THROUGHOUT THE CODE */ ++} txppr_t; ++ ++/* 20MHz */ ++#define WL_TX_POWER_CCK_FIRST OFFSETOF(txppr_t, b20_1x1dsss) ++#define WL_TX_POWER_OFDM20_FIRST OFFSETOF(txppr_t, b20_1x1ofdm) ++#define WL_TX_POWER_MCS20_SISO_FIRST OFFSETOF(txppr_t, b20_1x1mcs0) ++#define WL_TX_POWER_20_S1x1_FIRST OFFSETOF(txppr_t, b20_1x1mcs0) ++ ++#define WL_TX_POWER_CCK_CDD_S1x2_FIRST OFFSETOF(txppr_t, b20_1x2dsss) ++#define WL_TX_POWER_OFDM20_CDD_FIRST OFFSETOF(txppr_t, b20_1x2cdd_ofdm) ++#define WL_TX_POWER_MCS20_CDD_FIRST OFFSETOF(txppr_t, b20_1x2cdd_mcs0) ++#define WL_TX_POWER_20_S1x2_FIRST OFFSETOF(txppr_t, b20_1x2cdd_mcs0) ++#define WL_TX_POWER_MCS20_STBC_FIRST OFFSETOF(txppr_t, b20_2x2stbc_mcs0) ++#define WL_TX_POWER_MCS20_SDM_FIRST OFFSETOF(txppr_t, b20_2x2sdm_mcs8) ++#define WL_TX_POWER_20_S2x2_FIRST OFFSETOF(txppr_t, b20_2x2sdm_mcs8) ++ ++#define WL_TX_POWER_CCK_CDD_S1x3_FIRST OFFSETOF(txppr_t, b20_1x3dsss) ++#define WL_TX_POWER_OFDM20_CDD_S1x3_FIRST OFFSETOF(txppr_t, b20_1x3cdd_ofdm) ++#define WL_TX_POWER_20_S1x3_FIRST OFFSETOF(txppr_t, b20_1x3cdd_mcs0) ++#define WL_TX_POWER_20_STBC_S2x3_FIRST OFFSETOF(txppr_t, b20_2x3stbc_mcs0) ++#define WL_TX_POWER_20_S2x3_FIRST OFFSETOF(txppr_t, b20_2x3sdm_mcs8) ++#define WL_TX_POWER_20_S3x3_FIRST OFFSETOF(txppr_t, b20_3x3sdm_mcs16) ++ ++#define WL_TX_POWER_20_S1X1_VHT OFFSETOF(txppr_t, b20_1x1vht) ++#define WL_TX_POWER_20_S1X2_CDD_VHT OFFSETOF(txppr_t, b20_1x2cdd_vht) ++#define WL_TX_POWER_20_S2X2_STBC_VHT OFFSETOF(txppr_t, b20_2x2stbc_vht) ++#define WL_TX_POWER_20_S2X2_VHT OFFSETOF(txppr_t, b20_2x2sdm_vht) ++#define WL_TX_POWER_20_S1X3_CDD_VHT OFFSETOF(txppr_t, b20_1x3cdd_vht) ++#define WL_TX_POWER_20_S2X3_STBC_VHT OFFSETOF(txppr_t, b20_2x3stbc_vht) ++#define WL_TX_POWER_20_S2X3_VHT OFFSETOF(txppr_t, b20_2x3sdm_vht) ++#define WL_TX_POWER_20_S3X3_VHT OFFSETOF(txppr_t, b20_3x3sdm_vht) ++ ++/* 40MHz */ ++#define WL_TX_POWER_40_DUMMY_CCK_FIRST OFFSETOF(txppr_t, b40_dummy1x1dsss) ++#define WL_TX_POWER_OFDM40_FIRST OFFSETOF(txppr_t, b40_1x1ofdm) ++#define WL_TX_POWER_MCS40_SISO_FIRST OFFSETOF(txppr_t, b40_1x1mcs0) ++#define WL_TX_POWER_40_S1x1_FIRST OFFSETOF(txppr_t, b40_1x1mcs0) ++ ++#define WL_TX_POWER_40_DUMMY_CCK_CDD_S1x2_FIRST OFFSETOF(txppr_t, b40_dummy1x2dsss) ++#define WL_TX_POWER_OFDM40_CDD_FIRST OFFSETOF(txppr_t, b40_1x2cdd_ofdm) ++#define WL_TX_POWER_MCS40_CDD_FIRST OFFSETOF(txppr_t, b40_1x2cdd_mcs0) ++#define WL_TX_POWER_40_S1x2_FIRST OFFSETOF(txppr_t, b40_1x2cdd_mcs0) ++#define WL_TX_POWER_MCS40_STBC_FIRST OFFSETOF(txppr_t, b40_2x2stbc_mcs0) ++#define WL_TX_POWER_MCS40_SDM_FIRST OFFSETOF(txppr_t, b40_2x2sdm_mcs8) ++#define WL_TX_POWER_40_S2x2_FIRST OFFSETOF(txppr_t, b40_2x2sdm_mcs8) ++ ++#define WL_TX_POWER_40_DUMMY_CCK_CDD_S1x3_FIRST OFFSETOF(txppr_t, b40_dummy1x3dsss) ++#define WL_TX_POWER_OFDM40_CDD_S1x3_FIRST OFFSETOF(txppr_t, b40_1x3cdd_ofdm) ++#define WL_TX_POWER_40_S1x3_FIRST OFFSETOF(txppr_t, b40_1x3cdd_mcs0) ++#define WL_TX_POWER_40_STBC_S2x3_FIRST OFFSETOF(txppr_t, b40_2x3stbc_mcs0) ++#define WL_TX_POWER_40_S2x3_FIRST OFFSETOF(txppr_t, b40_2x3sdm_mcs8) ++#define WL_TX_POWER_40_S3x3_FIRST OFFSETOF(txppr_t, b40_3x3sdm_mcs16) ++ ++#define WL_TX_POWER_40_S1X1_VHT OFFSETOF(txppr_t, b40_1x1vht) ++#define WL_TX_POWER_40_S1X2_CDD_VHT OFFSETOF(txppr_t, b40_1x2cdd_vht) ++#define WL_TX_POWER_40_S2X2_STBC_VHT OFFSETOF(txppr_t, b40_2x2stbc_vht) ++#define WL_TX_POWER_40_S2X2_VHT OFFSETOF(txppr_t, b40_2x2sdm_vht) ++#define WL_TX_POWER_40_S1X3_CDD_VHT OFFSETOF(txppr_t, b40_1x3cdd_vht) ++#define WL_TX_POWER_40_S2X3_STBC_VHT OFFSETOF(txppr_t, b40_2x3stbc_vht) ++#define WL_TX_POWER_40_S2X3_VHT OFFSETOF(txppr_t, b40_2x3sdm_vht) ++#define WL_TX_POWER_40_S3X3_VHT OFFSETOF(txppr_t, b40_3x3sdm_vht) ++ ++/* 20 in 40MHz */ ++#define WL_TX_POWER_20UL_CCK_FIRST OFFSETOF(txppr_t, b20in40_1x1dsss) ++#define WL_TX_POWER_20UL_OFDM_FIRST OFFSETOF(txppr_t, b20in40_1x1ofdm) ++#define WL_TX_POWER_20UL_S1x1_FIRST OFFSETOF(txppr_t, b20in40_1x1mcs0) ++ ++#define WL_TX_POWER_CCK_20U_CDD_S1x2_FIRST OFFSETOF(txppr_t, b20in40_1x2dsss) ++#define WL_TX_POWER_20UL_OFDM_CDD_FIRST OFFSETOF(txppr_t, b20in40_1x2cdd_ofdm) ++#define WL_TX_POWER_20UL_S1x2_FIRST OFFSETOF(txppr_t, b20in40_1x2cdd_mcs0) ++#define WL_TX_POWER_20UL_STBC_S2x2_FIRST OFFSETOF(txppr_t, b20in40_2x2stbc_mcs0) ++#define WL_TX_POWER_20UL_S2x2_FIRST OFFSETOF(txppr_t, b20in40_2x2sdm_mcs8) ++ ++#define WL_TX_POWER_CCK_20U_CDD_S1x3_FIRST OFFSETOF(txppr_t, b20in40_1x3dsss) ++#define WL_TX_POWER_20UL_OFDM_CDD_S1x3_FIRST OFFSETOF(txppr_t, b20in40_1x3cdd_ofdm) ++#define WL_TX_POWER_20UL_S1x3_FIRST OFFSETOF(txppr_t, b20in40_1x3cdd_mcs0) ++#define WL_TX_POWER_20UL_STBC_S2x3_FIRST OFFSETOF(txppr_t, b20in40_2x3stbc_mcs0) ++#define WL_TX_POWER_20UL_S2x3_FIRST OFFSETOF(txppr_t, b20in40_2x3sdm_mcs8) ++#define WL_TX_POWER_20UL_S3x3_FIRST OFFSETOF(txppr_t, b20in40_3x3sdm_mcs16) ++ ++#define WL_TX_POWER_20UL_S1X1_VHT OFFSETOF(txppr_t, b20in40_1x1vht) ++#define WL_TX_POWER_20UL_S1X2_CDD_VHT OFFSETOF(txppr_t, b20in40_1x2cdd_vht) ++#define WL_TX_POWER_20UL_S2X2_STBC_VHT OFFSETOF(txppr_t, b20in40_2x2stbc_vht) ++#define WL_TX_POWER_20UL_S2X2_VHT OFFSETOF(txppr_t, b20in40_2x2sdm_vht) ++#define WL_TX_POWER_20UL_S1X3_CDD_VHT OFFSETOF(txppr_t, b20in40_1x3cdd_vht) ++#define WL_TX_POWER_20UL_S2X3_STBC_VHT OFFSETOF(txppr_t, b20in40_2x3stbc_vht) ++#define WL_TX_POWER_20UL_S2X3_VHT OFFSETOF(txppr_t, b20in40_2x3sdm_vht) ++#define WL_TX_POWER_20UL_S3X3_VHT OFFSETOF(txppr_t, b20in40_3x3sdm_vht) ++ ++/* 80MHz */ ++#define WL_TX_POWER_80_DUMMY_CCK_FIRST OFFSETOF(txppr_t, b80_dummy1x1dsss) ++#define WL_TX_POWER_OFDM80_FIRST OFFSETOF(txppr_t, b80_1x1ofdm) ++#define WL_TX_POWER_MCS80_SISO_FIRST OFFSETOF(txppr_t, b80_1x1mcs0) ++#define WL_TX_POWER_80_S1x1_FIRST OFFSETOF(txppr_t, b80_1x1mcs0) ++ ++#define WL_TX_POWER_80_DUMMY_CCK_CDD_S1x2_FIRST OFFSETOF(txppr_t, b80_dummy1x2dsss) ++#define WL_TX_POWER_OFDM80_CDD_FIRST OFFSETOF(txppr_t, b80_1x2cdd_ofdm) ++#define WL_TX_POWER_MCS80_CDD_FIRST OFFSETOF(txppr_t, b80_1x2cdd_mcs0) ++#define WL_TX_POWER_80_S1x2_FIRST OFFSETOF(txppr_t, b80_1x2cdd_mcs0) ++#define WL_TX_POWER_MCS80_STBC_FIRST OFFSETOF(txppr_t, b80_2x2stbc_mcs0) ++#define WL_TX_POWER_MCS80_SDM_FIRST OFFSETOF(txppr_t, b80_2x2sdm_mcs8) ++#define WL_TX_POWER_80_S2x2_FIRST OFFSETOF(txppr_t, b80_2x2sdm_mcs8) ++ ++#define WL_TX_POWER_80_DUMMY_CCK_CDD_S1x3_FIRST OFFSETOF(txppr_t, b80_dummy1x3dsss) ++#define WL_TX_POWER_OFDM80_CDD_S1x3_FIRST OFFSETOF(txppr_t, b80_1x3cdd_ofdm) ++#define WL_TX_POWER_80_S1x3_FIRST OFFSETOF(txppr_t, b80_1x3cdd_mcs0) ++#define WL_TX_POWER_80_STBC_S2x3_FIRST OFFSETOF(txppr_t, b80_2x3stbc_mcs0) ++#define WL_TX_POWER_80_S2x3_FIRST OFFSETOF(txppr_t, b80_2x3sdm_mcs8) ++#define WL_TX_POWER_80_S3x3_FIRST OFFSETOF(txppr_t, b80_3x3sdm_mcs16) ++ ++#define WL_TX_POWER_80_S1X1_VHT OFFSETOF(txppr_t, b80_1x1vht) ++#define WL_TX_POWER_80_S1X2_CDD_VHT OFFSETOF(txppr_t, b80_1x2cdd_vht) ++#define WL_TX_POWER_80_S2X2_STBC_VHT OFFSETOF(txppr_t, b80_2x2stbc_vht) ++#define WL_TX_POWER_80_S2X2_VHT OFFSETOF(txppr_t, b80_2x2sdm_vht) ++#define WL_TX_POWER_80_S1X3_CDD_VHT OFFSETOF(txppr_t, b80_1x3cdd_vht) ++#define WL_TX_POWER_80_S2X3_STBC_VHT OFFSETOF(txppr_t, b80_2x3stbc_vht) ++#define WL_TX_POWER_80_S2X3_VHT OFFSETOF(txppr_t, b80_2x3sdm_vht) ++#define WL_TX_POWER_80_S3X3_VHT OFFSETOF(txppr_t, b80_3x3sdm_vht) ++ ++/* 20 in 80MHz */ ++#define WL_TX_POWER_20UUL_CCK_FIRST OFFSETOF(txppr_t, b20in80_1x1dsss) ++#define WL_TX_POWER_20UUL_OFDM_FIRST OFFSETOF(txppr_t, b20in80_1x1ofdm) ++#define WL_TX_POWER_20UUL_S1x1_FIRST OFFSETOF(txppr_t, b20in80_1x1mcs0) ++ ++#define WL_TX_POWER_CCK_20UU_CDD_S1x2_FIRST OFFSETOF(txppr_t, b20in80_1x2dsss) ++#define WL_TX_POWER_20UUL_OFDM_CDD_FIRST OFFSETOF(txppr_t, b20in80_1x2cdd_ofdm) ++#define WL_TX_POWER_20UUL_S1x2_FIRST OFFSETOF(txppr_t, b20in80_1x2cdd_mcs0) ++#define WL_TX_POWER_20UUL_STBC_S2x2_FIRST OFFSETOF(txppr_t, b20in80_2x2stbc_mcs0) ++#define WL_TX_POWER_20UUL_S2x2_FIRST OFFSETOF(txppr_t, b20in80_2x2sdm_mcs8) ++ ++#define WL_TX_POWER_CCK_20UU_CDD_S1x3_FIRST OFFSETOF(txppr_t, b20in80_1x3dsss) ++#define WL_TX_POWER_20UUL_OFDM_CDD_S1x3_FIRST OFFSETOF(txppr_t, b20in80_1x3cdd_ofdm) ++#define WL_TX_POWER_20UUL_S1x3_FIRST OFFSETOF(txppr_t, b20in80_1x3cdd_mcs0) ++#define WL_TX_POWER_20UUL_STBC_S2x3_FIRST OFFSETOF(txppr_t, b20in80_2x3stbc_mcs0) ++#define WL_TX_POWER_20UUL_S2x3_FIRST OFFSETOF(txppr_t, b20in80_2x3sdm_mcs8) ++#define WL_TX_POWER_20UUL_S3x3_FIRST OFFSETOF(txppr_t, b20in80_3x3sdm_mcs16) ++ ++#define WL_TX_POWER_20UUL_S1X1_VHT OFFSETOF(txppr_t, b20in80_1x1vht) ++#define WL_TX_POWER_20UUL_S1X2_CDD_VHT OFFSETOF(txppr_t, b20in80_1x2cdd_vht) ++#define WL_TX_POWER_20UUL_S2X2_STBC_VHT OFFSETOF(txppr_t, b20in80_2x2stbc_vht) ++#define WL_TX_POWER_20UUL_S2X2_VHT OFFSETOF(txppr_t, b20in80_2x2sdm_vht) ++#define WL_TX_POWER_20UUL_S1X3_CDD_VHT OFFSETOF(txppr_t, b20in80_1x3cdd_vht) ++#define WL_TX_POWER_20UUL_S2X3_STBC_VHT OFFSETOF(txppr_t, b20in80_2x3stbc_vht) ++#define WL_TX_POWER_20UUL_S2X3_VHT OFFSETOF(txppr_t, b20in80_2x3sdm_vht) ++#define WL_TX_POWER_20UUL_S3X3_VHT OFFSETOF(txppr_t, b20in80_3x3sdm_vht) ++ ++/* 40 in 80MHz */ ++#define WL_TX_POWER_40UUL_DUMMY_CCK_FIRST OFFSETOF(txppr_t, b40in80_dummy1x1dsss) ++#define WL_TX_POWER_40UUL_OFDM_FIRST OFFSETOF(txppr_t, b40in80_1x1ofdm) ++#define WL_TX_POWER_40UUL_S1x1_FIRST OFFSETOF(txppr_t, b40in80_1x1mcs0) ++ ++#define WL_TX_POWER_CCK_40UU_DUMMY_CDD_S1x2_FIRST OFFSETOF(txppr_t, b40in80_dummy1x2dsss) ++#define WL_TX_POWER_40UUL_OFDM_CDD_FIRST OFFSETOF(txppr_t, b40in80_1x2cdd_ofdm) ++#define WL_TX_POWER_40UUL_S1x2_FIRST OFFSETOF(txppr_t, b40in80_1x2cdd_mcs0) ++#define WL_TX_POWER_40UUL_STBC_S2x2_FIRST OFFSETOF(txppr_t, b40in80_2x2stbc_mcs0) ++#define WL_TX_POWER_40UUL_S2x2_FIRST OFFSETOF(txppr_t, b40in80_2x2sdm_mcs8) ++ ++#define WL_TX_POWER_CCK_40UU_DUMMY_CDD_S1x3_FIRST OFFSETOF(txppr_t, b40in80_dummy1x3dsss) ++#define WL_TX_POWER_40UUL_OFDM_CDD_S1x3_FIRST OFFSETOF(txppr_t, b40in80_1x3cdd_ofdm) ++#define WL_TX_POWER_40UUL_S1x3_FIRST OFFSETOF(txppr_t, b40in80_1x3cdd_mcs0) ++#define WL_TX_POWER_40UUL_STBC_S2x3_FIRST OFFSETOF(txppr_t, b40in80_2x3stbc_mcs0) ++#define WL_TX_POWER_40UUL_S2x3_FIRST OFFSETOF(txppr_t, b40in80_2x3sdm_mcs8) ++#define WL_TX_POWER_40UUL_S3x3_FIRST OFFSETOF(txppr_t, b40in80_3x3sdm_mcs16) ++ ++#define WL_TX_POWER_40UUL_S1X1_VHT OFFSETOF(txppr_t, b40in80_1x1vht) ++#define WL_TX_POWER_40UUL_S1X2_CDD_VHT OFFSETOF(txppr_t, b40in80_1x2cdd_vht) ++#define WL_TX_POWER_40UUL_S2X2_STBC_VHT OFFSETOF(txppr_t, b40in80_2x2stbc_vht) ++#define WL_TX_POWER_40UUL_S2X2_VHT OFFSETOF(txppr_t, b40in80_2x2sdm_vht) ++#define WL_TX_POWER_40UUL_S1X3_CDD_VHT OFFSETOF(txppr_t, b40in80_1x3cdd_vht) ++#define WL_TX_POWER_40UUL_S2X3_STBC_VHT OFFSETOF(txppr_t, b40in80_2x3stbc_vht) ++#define WL_TX_POWER_40UUL_S2X3_VHT OFFSETOF(txppr_t, b40in80_2x3sdm_vht) ++#define WL_TX_POWER_40UUL_S3X3_VHT OFFSETOF(txppr_t, b40in80_3x3sdm_vht) ++ ++#define WL_TX_POWER_MCS_32 OFFSETOF(txppr_t, mcs32) /* C_CHECK remove later */ ++ ++#define WL_TX_POWER_RATES sizeof(struct txppr) ++ ++/* sslpnphy specifics */ ++#define WL_TX_POWER_MCS20_SISO_FIRST_SSN WL_TX_POWER_MCS20_SISO_FIRST ++#define WL_TX_POWER_MCS40_SISO_FIRST_SSN WL_TX_POWER_MCS40_SISO_FIRST ++ ++/* tx_power_t.flags bits */ ++#define WL_TX_POWER_F_ENABLED 1 ++#define WL_TX_POWER_F_HW 2 ++#define WL_TX_POWER_F_MIMO 4 ++#define WL_TX_POWER_F_SISO 8 ++#define WL_TX_POWER_F_HT 0x10 ++ ++typedef struct { ++ uint16 ver; /* version of this struct */ ++ uint16 len; /* length in bytes of this structure */ ++ uint32 flags; ++ chanspec_t chanspec; /* txpwr report for this channel */ ++ chanspec_t local_chanspec; /* channel on which we are associated */ ++ uint8 ppr[WL_TX_POWER_RATES]; /* Latest target power */ ++} wl_txppr_t; ++ ++#define WL_TXPPR_VERSION 0 ++#define WL_TXPPR_LENGTH (sizeof(wl_txppr_t)) ++#define TX_POWER_T_VERSION 43 ++ ++/* Defines used with channel_bandwidth for curpower */ ++#define WL_BW_20MHZ 0 ++#define WL_BW_40MHZ 1 ++#define WL_BW_80MHZ 2 ++ ++/* tx_power_t.flags bits */ ++#ifdef PPR_API ++#define WL_TX_POWER2_F_ENABLED 1 ++#define WL_TX_POWER2_F_HW 2 ++#define WL_TX_POWER2_F_MIMO 4 ++#define WL_TX_POWER2_F_SISO 8 ++#define WL_TX_POWER2_F_HT 0x10 ++#else ++#define WL_TX_POWER_F_ENABLED 1 ++#define WL_TX_POWER_F_HW 2 ++#define WL_TX_POWER_F_MIMO 4 ++#define WL_TX_POWER_F_SISO 8 ++#define WL_TX_POWER_F_HT 0x10 ++#endif ++typedef struct { ++ uint32 flags; ++ chanspec_t chanspec; /* txpwr report for this channel */ ++ chanspec_t local_chanspec; /* channel on which we are associated */ ++ uint8 local_max; /* local max according to the AP */ ++ uint8 local_constraint; /* local constraint according to the AP */ ++ int8 antgain[2]; /* Ant gain for each band - from SROM */ ++ uint8 rf_cores; /* count of RF Cores being reported */ ++ uint8 est_Pout[4]; /* Latest tx power out estimate per RF chain */ ++ uint8 est_Pout_act[4]; /* Latest tx power out estimate per RF chain ++ * without adjustment ++ */ ++ uint8 est_Pout_cck; /* Latest CCK tx power out estimate */ ++ uint8 tx_power_max[4]; /* Maximum target power among all rates */ ++ uint tx_power_max_rate_ind[4]; /* Index of the rate with the max target power */ ++ uint8 user_limit[WL_TX_POWER_RATES]; /* User limit */ ++ int8 board_limit[WL_TX_POWER_RATES]; /* Max power board can support (SROM) */ ++ int8 target[WL_TX_POWER_RATES]; /* Latest target power */ ++ int8 clm_limits[WL_NUMRATES]; /* regulatory limits - 20, 40 or 80MHz */ ++ int8 clm_limits_subchan1[WL_NUMRATES]; /* regulatory limits - 20in40 or 40in80 */ ++ int8 clm_limits_subchan2[WL_NUMRATES]; /* regulatory limits - 20in80MHz */ ++ int8 sar; /* SAR limit for display by wl executable */ ++ int8 channel_bandwidth; /* 20, 40 or 80 MHz bandwidth? */ ++ uint8 version; /* Version of the data format wlu <--> driver */ ++ uint8 display_core; /* Displayed curpower core */ ++#ifdef PPR_API ++} tx_power_new_t; ++#else ++} tx_power_t; ++#endif ++ ++typedef struct tx_inst_power { ++ uint8 txpwr_est_Pout[2]; /* Latest estimate for 2.4 and 5 Ghz */ ++ uint8 txpwr_est_Pout_gofdm; /* Pwr estimate for 2.4 OFDM */ ++} tx_inst_power_t; ++ ++ ++typedef struct { ++ uint32 flags; ++ chanspec_t chanspec; /* txpwr report for this channel */ ++ chanspec_t local_chanspec; /* channel on which we are associated */ ++ uint8 local_max; /* local max according to the AP */ ++ uint8 local_constraint; /* local constraint according to the AP */ ++ int8 antgain[2]; /* Ant gain for each band - from SROM */ ++ uint8 rf_cores; /* count of RF Cores being reported */ ++ uint8 est_Pout[4]; /* Latest tx power out estimate per RF chain */ ++ uint8 est_Pout_act[4]; /* Latest tx power out estimate per RF chain ++ * without adjustment ++ */ ++ uint8 est_Pout_cck; /* Latest CCK tx power out estimate */ ++ uint8 tx_power_max[4]; /* Maximum target power among all rates */ ++ uint tx_power_max_rate_ind[4]; /* Index of the rate with the max target power */ ++ txppr_t user_limit; /* User limit */ ++ txppr_t reg_limit; /* Regulatory power limit */ ++ txppr_t board_limit; /* Max power board can support (SROM) */ ++ txppr_t target; /* Latest target power */ ++} wl_txpwr_t; ++ ++#define WL_NUM_TXCHAIN_MAX 4 ++typedef struct wl_txchain_pwr_offsets { ++ int8 offset[WL_NUM_TXCHAIN_MAX]; /* quarter dBm signed offset for each chain */ ++} wl_txchain_pwr_offsets_t; ++ ++/* 802.11h measurement types */ ++#define WLC_MEASURE_TPC 1 ++#define WLC_MEASURE_CHANNEL_BASIC 2 ++#define WLC_MEASURE_CHANNEL_CCA 3 ++#define WLC_MEASURE_CHANNEL_RPI 4 ++ ++/* regulatory enforcement levels */ ++#define SPECT_MNGMT_OFF 0 /* both 11h and 11d disabled */ ++#define SPECT_MNGMT_LOOSE_11H 1 /* allow non-11h APs in scan lists */ ++#define SPECT_MNGMT_STRICT_11H 2 /* prune out non-11h APs from scan list */ ++#define SPECT_MNGMT_STRICT_11D 3 /* switch to 802.11D mode */ ++/* SPECT_MNGMT_LOOSE_11H_D - same as SPECT_MNGMT_LOOSE with the exception that Country IE ++ * adoption is done regardless of capability spectrum_management ++ */ ++#define SPECT_MNGMT_LOOSE_11H_D 4 /* operation defined above */ ++ ++#define WL_CHAN_VALID_HW (1 << 0) /* valid with current HW */ ++#define WL_CHAN_VALID_SW (1 << 1) /* valid with current country setting */ ++#define WL_CHAN_BAND_5G (1 << 2) /* 5GHz-band channel */ ++#define WL_CHAN_RADAR (1 << 3) /* radar sensitive channel */ ++#define WL_CHAN_INACTIVE (1 << 4) /* temporarily inactive due to radar */ ++#define WL_CHAN_PASSIVE (1 << 5) /* channel is in passive mode */ ++#define WL_CHAN_RESTRICTED (1 << 6) /* restricted use channel */ ++ ++/* BTC mode used by "btc_mode" iovar */ ++#define WL_BTC_DISABLE 0 /* disable BT coexistence */ ++#define WL_BTC_FULLTDM 1 /* full TDM COEX */ ++#define WL_BTC_ENABLE 1 /* full TDM COEX to maintain backward compatiblity */ ++#define WL_BTC_PREMPT 2 /* full TDM COEX with preemption */ ++#define WL_BTC_LITE 3 /* light weight coex for large isolation platform */ ++#define WL_BTC_PARALLEL 4 /* BT and WLAN run in parallel with separate antenna */ ++#define WL_BTC_HYBRID 5 /* hybrid coex, only ack is allowed to transmit in BT slot */ ++#define WL_BTC_DEFAULT 8 /* set the default mode for the device */ ++#define WL_INF_BTC_DISABLE 0 ++#define WL_INF_BTC_ENABLE 1 ++#define WL_INF_BTC_AUTO 3 ++ ++/* BTC wire used by "btc_wire" iovar */ ++#define WL_BTC_DEFWIRE 0 /* use default wire setting */ ++#define WL_BTC_2WIRE 2 /* use 2-wire BTC */ ++#define WL_BTC_3WIRE 3 /* use 3-wire BTC */ ++#define WL_BTC_4WIRE 4 /* use 4-wire BTC */ ++ ++/* BTC flags: BTC configuration that can be set by host */ ++#define WL_BTC_FLAG_PREMPT (1 << 0) ++#define WL_BTC_FLAG_BT_DEF (1 << 1) ++#define WL_BTC_FLAG_ACTIVE_PROT (1 << 2) ++#define WL_BTC_FLAG_SIM_RSP (1 << 3) ++#define WL_BTC_FLAG_PS_PROTECT (1 << 4) ++#define WL_BTC_FLAG_SIM_TX_LP (1 << 5) ++#define WL_BTC_FLAG_ECI (1 << 6) ++#define WL_BTC_FLAG_LIGHT (1 << 7) ++#define WL_BTC_FLAG_PARALLEL (1 << 8) ++ ++/* Message levels */ ++#define WL_ERROR_VAL 0x00000001 ++#define WL_TRACE_VAL 0x00000002 ++#define WL_PRHDRS_VAL 0x00000004 ++#define WL_PRPKT_VAL 0x00000008 ++#define WL_INFORM_VAL 0x00000010 ++#define WL_TMP_VAL 0x00000020 ++#define WL_OID_VAL 0x00000040 ++#define WL_RATE_VAL 0x00000080 ++#define WL_ASSOC_VAL 0x00000100 ++#define WL_PRUSR_VAL 0x00000200 ++#define WL_PS_VAL 0x00000400 ++#define WL_TXPWR_VAL 0x00000800 /* retired in TOT on 6/10/2009 */ ++#define WL_PORT_VAL 0x00001000 ++#define WL_DUAL_VAL 0x00002000 ++#define WL_WSEC_VAL 0x00004000 ++#define WL_WSEC_DUMP_VAL 0x00008000 ++#define WL_LOG_VAL 0x00010000 ++#define WL_NRSSI_VAL 0x00020000 /* retired in TOT on 6/10/2009 */ ++#define WL_LOFT_VAL 0x00040000 /* retired in TOT on 6/10/2009 */ ++#define WL_REGULATORY_VAL 0x00080000 ++#define WL_PHYCAL_VAL 0x00100000 /* retired in TOT on 6/10/2009 */ ++#define WL_RADAR_VAL 0x00200000 /* retired in TOT on 6/10/2009 */ ++#define WL_MPC_VAL 0x00400000 ++#define WL_APSTA_VAL 0x00800000 ++#define WL_DFS_VAL 0x01000000 ++#define WL_BA_VAL 0x02000000 /* retired in TOT on 6/14/2010 */ ++#define WL_ACI_VAL 0x04000000 ++#define WL_MBSS_VAL 0x04000000 ++#define WL_CAC_VAL 0x08000000 ++#define WL_AMSDU_VAL 0x10000000 ++#define WL_AMPDU_VAL 0x20000000 ++#define WL_FFPLD_VAL 0x40000000 ++ ++/* wl_msg_level is full. For new bits take the next one and AND with ++ * wl_msg_level2 in wl_dbg.h ++ */ ++#define WL_DPT_VAL 0x00000001 ++#define WL_SCAN_VAL 0x00000002 ++#define WL_WOWL_VAL 0x00000004 ++#define WL_COEX_VAL 0x00000008 ++#define WL_RTDC_VAL 0x00000010 ++#define WL_PROTO_VAL 0x00000020 ++#define WL_BTA_VAL 0x00000040 ++#define WL_CHANINT_VAL 0x00000080 ++#define WL_THERMAL_VAL 0x00000100 /* retired in TOT on 6/10/2009 */ ++#define WL_P2P_VAL 0x00000200 ++#define WL_ITFR_VAL 0x00000400 ++#define WL_MCHAN_VAL 0x00000800 ++#define WL_TDLS_VAL 0x00001000 ++#define WL_MCNX_VAL 0x00002000 ++#define WL_PROT_VAL 0x00004000 ++#define WL_PSTA_VAL 0x00008000 ++#define WL_TBTT_VAL 0x00010000 ++#define WL_NIC_VAL 0x00020000 ++#define WL_PWRSEL_VAL 0x00040000 ++/* use top-bit for WL_TIME_STAMP_VAL because this is a modifier ++ * rather than a message-type of its own ++ */ ++#define WL_TIMESTAMP_VAL 0x80000000 ++ ++/* max # of leds supported by GPIO (gpio pin# == led index#) */ ++#define WL_LED_NUMGPIO 32 /* gpio 0-31 */ ++ ++/* led per-pin behaviors */ ++#define WL_LED_OFF 0 /* always off */ ++#define WL_LED_ON 1 /* always on */ ++#define WL_LED_ACTIVITY 2 /* activity */ ++#define WL_LED_RADIO 3 /* radio enabled */ ++#define WL_LED_ARADIO 4 /* 5 Ghz radio enabled */ ++#define WL_LED_BRADIO 5 /* 2.4Ghz radio enabled */ ++#define WL_LED_BGMODE 6 /* on if gmode, off if bmode */ ++#define WL_LED_WI1 7 ++#define WL_LED_WI2 8 ++#define WL_LED_WI3 9 ++#define WL_LED_ASSOC 10 /* associated state indicator */ ++#define WL_LED_INACTIVE 11 /* null behavior (clears default behavior) */ ++#define WL_LED_ASSOCACT 12 /* on when associated; blink fast for activity */ ++#define WL_LED_WI4 13 ++#define WL_LED_WI5 14 ++#define WL_LED_BLINKSLOW 15 /* blink slow */ ++#define WL_LED_BLINKMED 16 /* blink med */ ++#define WL_LED_BLINKFAST 17 /* blink fast */ ++#define WL_LED_BLINKCUSTOM 18 /* blink custom */ ++#define WL_LED_BLINKPERIODIC 19 /* blink periodic (custom 1000ms / off 400ms) */ ++#define WL_LED_ASSOC_WITH_SEC 20 /* when connected with security */ ++ /* keep on for 300 sec */ ++#define WL_LED_START_OFF 21 /* off upon boot, could be turned on later */ ++#define WL_LED_NUMBEHAVIOR 22 ++ ++/* led behavior numeric value format */ ++#define WL_LED_BEH_MASK 0x7f /* behavior mask */ ++#define WL_LED_AL_MASK 0x80 /* activelow (polarity) bit */ ++ ++/* maximum channels returned by the get valid channels iovar */ ++#define WL_NUMCHANNELS 64 ++ ++/* max number of chanspecs (used by the iovar to calc. buf space) */ ++#define WL_NUMCHANSPECS 110 ++ ++/* WDS link local endpoint WPA role */ ++#define WL_WDS_WPA_ROLE_AUTH 0 /* authenticator */ ++#define WL_WDS_WPA_ROLE_SUP 1 /* supplicant */ ++#define WL_WDS_WPA_ROLE_AUTO 255 /* auto, based on mac addr value */ ++ ++/* number of bytes needed to define a 128-bit mask for MAC event reporting */ ++#define WL_EVENTING_MASK_LEN 16 ++ ++/* ++ * Join preference iovar value is an array of tuples. Each tuple has a one-byte type, ++ * a one-byte length, and a variable length value. RSSI type tuple must be present ++ * in the array. ++ * ++ * Types are defined in "join preference types" section. ++ * ++ * Length is the value size in octets. It is reserved for WL_JOIN_PREF_WPA type tuple ++ * and must be set to zero. ++ * ++ * Values are defined below. ++ * ++ * 1. RSSI - 2 octets ++ * offset 0: reserved ++ * offset 1: reserved ++ * ++ * 2. WPA - 2 + 12 * n octets (n is # tuples defined below) ++ * offset 0: reserved ++ * offset 1: # of tuples ++ * offset 2: tuple 1 ++ * offset 14: tuple 2 ++ * ... ++ * offset 2 + 12 * (n - 1) octets: tuple n ++ * ++ * struct wpa_cfg_tuple { ++ * uint8 akm[DOT11_OUI_LEN+1]; akm suite ++ * uint8 ucipher[DOT11_OUI_LEN+1]; unicast cipher suite ++ * uint8 mcipher[DOT11_OUI_LEN+1]; multicast cipher suite ++ * }; ++ * ++ * multicast cipher suite can be specified as a specific cipher suite or WL_WPA_ACP_MCS_ANY. ++ * ++ * 3. BAND - 2 octets ++ * offset 0: reserved ++ * offset 1: see "band preference" and "band types" ++ * ++ * 4. BAND RSSI - 2 octets ++ * offset 0: band types ++ * offset 1: +ve RSSI boost balue in dB ++ */ ++ ++/* join preference types */ ++#define WL_JOIN_PREF_RSSI 1 /* by RSSI */ ++#define WL_JOIN_PREF_WPA 2 /* by akm and ciphers */ ++#define WL_JOIN_PREF_BAND 3 /* by 802.11 band */ ++#define WL_JOIN_PREF_RSSI_DELTA 4 /* by 802.11 band only if RSSI delta condition matches */ ++#define WL_JOIN_PREF_TRANS_PREF 5 /* defined by requesting AP */ ++ ++/* band preference */ ++#define WLJP_BAND_ASSOC_PREF 255 /* use what WLC_SET_ASSOC_PREFER ioctl specifies */ ++ ++/* any multicast cipher suite */ ++#define WL_WPA_ACP_MCS_ANY "\x00\x00\x00\x00" ++ ++struct tsinfo_arg { ++ uint8 octets[3]; ++}; ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++#define NFIFO 6 /* # tx/rx fifopairs */ ++ ++#define WL_CNT_T_VERSION 8 /* current version of wl_cnt_t struct */ ++ ++typedef struct { ++ uint16 version; /* see definition of WL_CNT_T_VERSION */ ++ uint16 length; /* length of entire structure */ ++ ++ /* transmit stat counters */ ++ uint32 txframe; /* tx data frames */ ++ uint32 txbyte; /* tx data bytes */ ++ uint32 txretrans; /* tx mac retransmits */ ++ uint32 txerror; /* tx data errors (derived: sum of others) */ ++ uint32 txctl; /* tx management frames */ ++ uint32 txprshort; /* tx short preamble frames */ ++ uint32 txserr; /* tx status errors */ ++ uint32 txnobuf; /* tx out of buffers errors */ ++ uint32 txnoassoc; /* tx discard because we're not associated */ ++ uint32 txrunt; /* tx runt frames */ ++ uint32 txchit; /* tx header cache hit (fastpath) */ ++ uint32 txcmiss; /* tx header cache miss (slowpath) */ ++ ++ /* transmit chip error counters */ ++ uint32 txuflo; /* tx fifo underflows */ ++ uint32 txphyerr; /* tx phy errors (indicated in tx status) */ ++ uint32 txphycrs; ++ ++ /* receive stat counters */ ++ uint32 rxframe; /* rx data frames */ ++ uint32 rxbyte; /* rx data bytes */ ++ uint32 rxerror; /* rx data errors (derived: sum of others) */ ++ uint32 rxctl; /* rx management frames */ ++ uint32 rxnobuf; /* rx out of buffers errors */ ++ uint32 rxnondata; /* rx non data frames in the data channel errors */ ++ uint32 rxbadds; /* rx bad DS errors */ ++ uint32 rxbadcm; /* rx bad control or management frames */ ++ uint32 rxfragerr; /* rx fragmentation errors */ ++ uint32 rxrunt; /* rx runt frames */ ++ uint32 rxgiant; /* rx giant frames */ ++ uint32 rxnoscb; /* rx no scb error */ ++ uint32 rxbadproto; /* rx invalid frames */ ++ uint32 rxbadsrcmac; /* rx frames with Invalid Src Mac */ ++ uint32 rxbadda; /* rx frames tossed for invalid da */ ++ uint32 rxfilter; /* rx frames filtered out */ ++ ++ /* receive chip error counters */ ++ uint32 rxoflo; /* rx fifo overflow errors */ ++ uint32 rxuflo[NFIFO]; /* rx dma descriptor underflow errors */ ++ ++ uint32 d11cnt_txrts_off; /* d11cnt txrts value when reset d11cnt */ ++ uint32 d11cnt_rxcrc_off; /* d11cnt rxcrc value when reset d11cnt */ ++ uint32 d11cnt_txnocts_off; /* d11cnt txnocts value when reset d11cnt */ ++ ++ /* misc counters */ ++ uint32 dmade; /* tx/rx dma descriptor errors */ ++ uint32 dmada; /* tx/rx dma data errors */ ++ uint32 dmape; /* tx/rx dma descriptor protocol errors */ ++ uint32 reset; /* reset count */ ++ uint32 tbtt; /* cnts the TBTT int's */ ++ uint32 txdmawar; ++ uint32 pkt_callback_reg_fail; /* callbacks register failure */ ++ ++ /* MAC counters: 32-bit version of d11.h's macstat_t */ ++ uint32 txallfrm; /* total number of frames sent, incl. Data, ACK, RTS, CTS, ++ * Control Management (includes retransmissions) ++ */ ++ uint32 txrtsfrm; /* number of RTS sent out by the MAC */ ++ uint32 txctsfrm; /* number of CTS sent out by the MAC */ ++ uint32 txackfrm; /* number of ACK frames sent out */ ++ uint32 txdnlfrm; /* Not used */ ++ uint32 txbcnfrm; /* beacons transmitted */ ++ uint32 txfunfl[8]; /* per-fifo tx underflows */ ++ uint32 txtplunfl; /* Template underflows (mac was too slow to transmit ACK/CTS ++ * or BCN) ++ */ ++ uint32 txphyerror; /* Transmit phy error, type of error is reported in tx-status for ++ * driver enqueued frames ++ */ ++ uint32 rxfrmtoolong; /* Received frame longer than legal limit (2346 bytes) */ ++ uint32 rxfrmtooshrt; /* Received frame did not contain enough bytes for its frame type */ ++ uint32 rxinvmachdr; /* Either the protocol version != 0 or frame type not ++ * data/control/management ++ */ ++ uint32 rxbadfcs; /* number of frames for which the CRC check failed in the MAC */ ++ uint32 rxbadplcp; /* parity check of the PLCP header failed */ ++ uint32 rxcrsglitch; /* PHY was able to correlate the preamble but not the header */ ++ uint32 rxstrt; /* Number of received frames with a good PLCP ++ * (i.e. passing parity check) ++ */ ++ uint32 rxdfrmucastmbss; /* Number of received DATA frames with good FCS and matching RA */ ++ uint32 rxmfrmucastmbss; /* number of received mgmt frames with good FCS and matching RA */ ++ uint32 rxcfrmucast; /* number of received CNTRL frames with good FCS and matching RA */ ++ uint32 rxrtsucast; /* number of unicast RTS addressed to the MAC (good FCS) */ ++ uint32 rxctsucast; /* number of unicast CTS addressed to the MAC (good FCS) */ ++ uint32 rxackucast; /* number of ucast ACKS received (good FCS) */ ++ uint32 rxdfrmocast; /* number of received DATA frames (good FCS and not matching RA) */ ++ uint32 rxmfrmocast; /* number of received MGMT frames (good FCS and not matching RA) */ ++ uint32 rxcfrmocast; /* number of received CNTRL frame (good FCS and not matching RA) */ ++ uint32 rxrtsocast; /* number of received RTS not addressed to the MAC */ ++ uint32 rxctsocast; /* number of received CTS not addressed to the MAC */ ++ uint32 rxdfrmmcast; /* number of RX Data multicast frames received by the MAC */ ++ uint32 rxmfrmmcast; /* number of RX Management multicast frames received by the MAC */ ++ uint32 rxcfrmmcast; /* number of RX Control multicast frames received by the MAC ++ * (unlikely to see these) ++ */ ++ uint32 rxbeaconmbss; /* beacons received from member of BSS */ ++ uint32 rxdfrmucastobss; /* number of unicast frames addressed to the MAC from ++ * other BSS (WDS FRAME) ++ */ ++ uint32 rxbeaconobss; /* beacons received from other BSS */ ++ uint32 rxrsptmout; /* Number of response timeouts for transmitted frames ++ * expecting a response ++ */ ++ uint32 bcntxcancl; /* transmit beacons canceled due to receipt of beacon (IBSS) */ ++ uint32 rxf0ovfl; /* Number of receive fifo 0 overflows */ ++ uint32 rxf1ovfl; /* Number of receive fifo 1 overflows (obsolete) */ ++ uint32 rxf2ovfl; /* Number of receive fifo 2 overflows (obsolete) */ ++ uint32 txsfovfl; /* Number of transmit status fifo overflows (obsolete) */ ++ uint32 pmqovfl; /* Number of PMQ overflows */ ++ uint32 rxcgprqfrm; /* Number of received Probe requests that made it into ++ * the PRQ fifo ++ */ ++ uint32 rxcgprsqovfl; /* Rx Probe Request Que overflow in the AP */ ++ uint32 txcgprsfail; /* Tx Probe Response Fail. AP sent probe response but did ++ * not get ACK ++ */ ++ uint32 txcgprssuc; /* Tx Probe Response Success (ACK was received) */ ++ uint32 prs_timeout; /* Number of probe requests that were dropped from the PRQ ++ * fifo because a probe response could not be sent out within ++ * the time limit defined in M_PRS_MAXTIME ++ */ ++ uint32 rxnack; /* obsolete */ ++ uint32 frmscons; /* obsolete */ ++ uint32 txnack; /* obsolete */ ++ uint32 txglitch_nack; /* obsolete */ ++ uint32 txburst; /* obsolete */ ++ ++ /* 802.11 MIB counters, pp. 614 of 802.11 reaff doc. */ ++ uint32 txfrag; /* dot11TransmittedFragmentCount */ ++ uint32 txmulti; /* dot11MulticastTransmittedFrameCount */ ++ uint32 txfail; /* dot11FailedCount */ ++ uint32 txretry; /* dot11RetryCount */ ++ uint32 txretrie; /* dot11MultipleRetryCount */ ++ uint32 rxdup; /* dot11FrameduplicateCount */ ++ uint32 txrts; /* dot11RTSSuccessCount */ ++ uint32 txnocts; /* dot11RTSFailureCount */ ++ uint32 txnoack; /* dot11ACKFailureCount */ ++ uint32 rxfrag; /* dot11ReceivedFragmentCount */ ++ uint32 rxmulti; /* dot11MulticastReceivedFrameCount */ ++ uint32 rxcrc; /* dot11FCSErrorCount */ ++ uint32 txfrmsnt; /* dot11TransmittedFrameCount (bogus MIB?) */ ++ uint32 rxundec; /* dot11WEPUndecryptableCount */ ++ ++ /* WPA2 counters (see rxundec for DecryptFailureCount) */ ++ uint32 tkipmicfaill; /* TKIPLocalMICFailures */ ++ uint32 tkipcntrmsr; /* TKIPCounterMeasuresInvoked */ ++ uint32 tkipreplay; /* TKIPReplays */ ++ uint32 ccmpfmterr; /* CCMPFormatErrors */ ++ uint32 ccmpreplay; /* CCMPReplays */ ++ uint32 ccmpundec; /* CCMPDecryptErrors */ ++ uint32 fourwayfail; /* FourWayHandshakeFailures */ ++ uint32 wepundec; /* dot11WEPUndecryptableCount */ ++ uint32 wepicverr; /* dot11WEPICVErrorCount */ ++ uint32 decsuccess; /* DecryptSuccessCount */ ++ uint32 tkipicverr; /* TKIPICVErrorCount */ ++ uint32 wepexcluded; /* dot11WEPExcludedCount */ ++ ++ uint32 txchanrej; /* Tx frames suppressed due to channel rejection */ ++ uint32 psmwds; /* Count PSM watchdogs */ ++ uint32 phywatchdog; /* Count Phy watchdogs (triggered by ucode) */ ++ ++ /* MBSS counters, AP only */ ++ uint32 prq_entries_handled; /* PRQ entries read in */ ++ uint32 prq_undirected_entries; /* which were bcast bss & ssid */ ++ uint32 prq_bad_entries; /* which could not be translated to info */ ++ uint32 atim_suppress_count; /* TX suppressions on ATIM fifo */ ++ uint32 bcn_template_not_ready; /* Template marked in use on send bcn ... */ ++ uint32 bcn_template_not_ready_done; /* ...but "DMA done" interrupt rcvd */ ++ uint32 late_tbtt_dpc; /* TBTT DPC did not happen in time */ ++ ++ /* per-rate receive stat counters */ ++ uint32 rx1mbps; /* packets rx at 1Mbps */ ++ uint32 rx2mbps; /* packets rx at 2Mbps */ ++ uint32 rx5mbps5; /* packets rx at 5.5Mbps */ ++ uint32 rx6mbps; /* packets rx at 6Mbps */ ++ uint32 rx9mbps; /* packets rx at 9Mbps */ ++ uint32 rx11mbps; /* packets rx at 11Mbps */ ++ uint32 rx12mbps; /* packets rx at 12Mbps */ ++ uint32 rx18mbps; /* packets rx at 18Mbps */ ++ uint32 rx24mbps; /* packets rx at 24Mbps */ ++ uint32 rx36mbps; /* packets rx at 36Mbps */ ++ uint32 rx48mbps; /* packets rx at 48Mbps */ ++ uint32 rx54mbps; /* packets rx at 54Mbps */ ++ uint32 rx108mbps; /* packets rx at 108mbps */ ++ uint32 rx162mbps; /* packets rx at 162mbps */ ++ uint32 rx216mbps; /* packets rx at 216 mbps */ ++ uint32 rx270mbps; /* packets rx at 270 mbps */ ++ uint32 rx324mbps; /* packets rx at 324 mbps */ ++ uint32 rx378mbps; /* packets rx at 378 mbps */ ++ uint32 rx432mbps; /* packets rx at 432 mbps */ ++ uint32 rx486mbps; /* packets rx at 486 mbps */ ++ uint32 rx540mbps; /* packets rx at 540 mbps */ ++ ++ /* pkteng rx frame stats */ ++ uint32 pktengrxducast; /* unicast frames rxed by the pkteng code */ ++ uint32 pktengrxdmcast; /* multicast frames rxed by the pkteng code */ ++ ++ uint32 rfdisable; /* count of radio disables */ ++ uint32 bphy_rxcrsglitch; /* PHY count of bphy glitches */ ++ ++ uint32 txexptime; /* Tx frames suppressed due to timer expiration */ ++ ++ uint32 txmpdu_sgi; /* count for sgi transmit */ ++ uint32 rxmpdu_sgi; /* count for sgi received */ ++ uint32 txmpdu_stbc; /* count for stbc transmit */ ++ uint32 rxmpdu_stbc; /* count for stbc received */ ++ ++ uint32 rxundec_mcst; /* dot11WEPUndecryptableCount */ ++ ++ /* WPA2 counters (see rxundec for DecryptFailureCount) */ ++ uint32 tkipmicfaill_mcst; /* TKIPLocalMICFailures */ ++ uint32 tkipcntrmsr_mcst; /* TKIPCounterMeasuresInvoked */ ++ uint32 tkipreplay_mcst; /* TKIPReplays */ ++ uint32 ccmpfmterr_mcst; /* CCMPFormatErrors */ ++ uint32 ccmpreplay_mcst; /* CCMPReplays */ ++ uint32 ccmpundec_mcst; /* CCMPDecryptErrors */ ++ uint32 fourwayfail_mcst; /* FourWayHandshakeFailures */ ++ uint32 wepundec_mcst; /* dot11WEPUndecryptableCount */ ++ uint32 wepicverr_mcst; /* dot11WEPICVErrorCount */ ++ uint32 decsuccess_mcst; /* DecryptSuccessCount */ ++ uint32 tkipicverr_mcst; /* TKIPICVErrorCount */ ++ uint32 wepexcluded_mcst; /* dot11WEPExcludedCount */ ++ ++ uint32 dma_hang; /* count for dma hang */ ++ uint32 reinit; /* count for reinit */ ++ ++ uint32 pstatxucast; /* count of ucast frames xmitted on all psta assoc */ ++ uint32 pstatxnoassoc; /* count of txnoassoc frames xmitted on all psta assoc */ ++ uint32 pstarxucast; /* count of ucast frames received on all psta assoc */ ++ uint32 pstarxbcmc; /* count of bcmc frames received on all psta */ ++ uint32 pstatxbcmc; /* count of bcmc frames transmitted on all psta */ ++ ++ uint32 cso_passthrough; /* hw cso required but passthrough */ ++} wl_cnt_t; ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++typedef struct { ++ uint16 version; /* see definition of WL_CNT_T_VERSION */ ++ uint16 length; /* length of entire structure */ ++ ++ /* transmit stat counters */ ++ uint32 txframe; /* tx data frames */ ++ uint32 txbyte; /* tx data bytes */ ++ uint32 txretrans; /* tx mac retransmits */ ++ uint32 txerror; /* tx data errors (derived: sum of others) */ ++ uint32 txctl; /* tx management frames */ ++ uint32 txprshort; /* tx short preamble frames */ ++ uint32 txserr; /* tx status errors */ ++ uint32 txnobuf; /* tx out of buffers errors */ ++ uint32 txnoassoc; /* tx discard because we're not associated */ ++ uint32 txrunt; /* tx runt frames */ ++ uint32 txchit; /* tx header cache hit (fastpath) */ ++ uint32 txcmiss; /* tx header cache miss (slowpath) */ ++ ++ /* transmit chip error counters */ ++ uint32 txuflo; /* tx fifo underflows */ ++ uint32 txphyerr; /* tx phy errors (indicated in tx status) */ ++ uint32 txphycrs; ++ ++ /* receive stat counters */ ++ uint32 rxframe; /* rx data frames */ ++ uint32 rxbyte; /* rx data bytes */ ++ uint32 rxerror; /* rx data errors (derived: sum of others) */ ++ uint32 rxctl; /* rx management frames */ ++ uint32 rxnobuf; /* rx out of buffers errors */ ++ uint32 rxnondata; /* rx non data frames in the data channel errors */ ++ uint32 rxbadds; /* rx bad DS errors */ ++ uint32 rxbadcm; /* rx bad control or management frames */ ++ uint32 rxfragerr; /* rx fragmentation errors */ ++ uint32 rxrunt; /* rx runt frames */ ++ uint32 rxgiant; /* rx giant frames */ ++ uint32 rxnoscb; /* rx no scb error */ ++ uint32 rxbadproto; /* rx invalid frames */ ++ uint32 rxbadsrcmac; /* rx frames with Invalid Src Mac */ ++ uint32 rxbadda; /* rx frames tossed for invalid da */ ++ uint32 rxfilter; /* rx frames filtered out */ ++ ++ /* receive chip error counters */ ++ uint32 rxoflo; /* rx fifo overflow errors */ ++ uint32 rxuflo[NFIFO]; /* rx dma descriptor underflow errors */ ++ ++ uint32 d11cnt_txrts_off; /* d11cnt txrts value when reset d11cnt */ ++ uint32 d11cnt_rxcrc_off; /* d11cnt rxcrc value when reset d11cnt */ ++ uint32 d11cnt_txnocts_off; /* d11cnt txnocts value when reset d11cnt */ ++ ++ /* misc counters */ ++ uint32 dmade; /* tx/rx dma descriptor errors */ ++ uint32 dmada; /* tx/rx dma data errors */ ++ uint32 dmape; /* tx/rx dma descriptor protocol errors */ ++ uint32 reset; /* reset count */ ++ uint32 tbtt; /* cnts the TBTT int's */ ++ uint32 txdmawar; ++ uint32 pkt_callback_reg_fail; /* callbacks register failure */ ++ ++ /* MAC counters: 32-bit version of d11.h's macstat_t */ ++ uint32 txallfrm; /* total number of frames sent, incl. Data, ACK, RTS, CTS, ++ * Control Management (includes retransmissions) ++ */ ++ uint32 txrtsfrm; /* number of RTS sent out by the MAC */ ++ uint32 txctsfrm; /* number of CTS sent out by the MAC */ ++ uint32 txackfrm; /* number of ACK frames sent out */ ++ uint32 txdnlfrm; /* Not used */ ++ uint32 txbcnfrm; /* beacons transmitted */ ++ uint32 txfunfl[8]; /* per-fifo tx underflows */ ++ uint32 txtplunfl; /* Template underflows (mac was too slow to transmit ACK/CTS ++ * or BCN) ++ */ ++ uint32 txphyerror; /* Transmit phy error, type of error is reported in tx-status for ++ * driver enqueued frames ++ */ ++ uint32 rxfrmtoolong; /* Received frame longer than legal limit (2346 bytes) */ ++ uint32 rxfrmtooshrt; /* Received frame did not contain enough bytes for its frame type */ ++ uint32 rxinvmachdr; /* Either the protocol version != 0 or frame type not ++ * data/control/management ++ */ ++ uint32 rxbadfcs; /* number of frames for which the CRC check failed in the MAC */ ++ uint32 rxbadplcp; /* parity check of the PLCP header failed */ ++ uint32 rxcrsglitch; /* PHY was able to correlate the preamble but not the header */ ++ uint32 rxstrt; /* Number of received frames with a good PLCP ++ * (i.e. passing parity check) ++ */ ++ uint32 rxdfrmucastmbss; /* Number of received DATA frames with good FCS and matching RA */ ++ uint32 rxmfrmucastmbss; /* number of received mgmt frames with good FCS and matching RA */ ++ uint32 rxcfrmucast; /* number of received CNTRL frames with good FCS and matching RA */ ++ uint32 rxrtsucast; /* number of unicast RTS addressed to the MAC (good FCS) */ ++ uint32 rxctsucast; /* number of unicast CTS addressed to the MAC (good FCS) */ ++ uint32 rxackucast; /* number of ucast ACKS received (good FCS) */ ++ uint32 rxdfrmocast; /* number of received DATA frames (good FCS and not matching RA) */ ++ uint32 rxmfrmocast; /* number of received MGMT frames (good FCS and not matching RA) */ ++ uint32 rxcfrmocast; /* number of received CNTRL frame (good FCS and not matching RA) */ ++ uint32 rxrtsocast; /* number of received RTS not addressed to the MAC */ ++ uint32 rxctsocast; /* number of received CTS not addressed to the MAC */ ++ uint32 rxdfrmmcast; /* number of RX Data multicast frames received by the MAC */ ++ uint32 rxmfrmmcast; /* number of RX Management multicast frames received by the MAC */ ++ uint32 rxcfrmmcast; /* number of RX Control multicast frames received by the MAC ++ * (unlikely to see these) ++ */ ++ uint32 rxbeaconmbss; /* beacons received from member of BSS */ ++ uint32 rxdfrmucastobss; /* number of unicast frames addressed to the MAC from ++ * other BSS (WDS FRAME) ++ */ ++ uint32 rxbeaconobss; /* beacons received from other BSS */ ++ uint32 rxrsptmout; /* Number of response timeouts for transmitted frames ++ * expecting a response ++ */ ++ uint32 bcntxcancl; /* transmit beacons canceled due to receipt of beacon (IBSS) */ ++ uint32 rxf0ovfl; /* Number of receive fifo 0 overflows */ ++ uint32 rxf1ovfl; /* Number of receive fifo 1 overflows (obsolete) */ ++ uint32 rxf2ovfl; /* Number of receive fifo 2 overflows (obsolete) */ ++ uint32 txsfovfl; /* Number of transmit status fifo overflows (obsolete) */ ++ uint32 pmqovfl; /* Number of PMQ overflows */ ++ uint32 rxcgprqfrm; /* Number of received Probe requests that made it into ++ * the PRQ fifo ++ */ ++ uint32 rxcgprsqovfl; /* Rx Probe Request Que overflow in the AP */ ++ uint32 txcgprsfail; /* Tx Probe Response Fail. AP sent probe response but did ++ * not get ACK ++ */ ++ uint32 txcgprssuc; /* Tx Probe Response Success (ACK was received) */ ++ uint32 prs_timeout; /* Number of probe requests that were dropped from the PRQ ++ * fifo because a probe response could not be sent out within ++ * the time limit defined in M_PRS_MAXTIME ++ */ ++ uint32 rxnack; ++ uint32 frmscons; ++ uint32 txnack; ++ uint32 txglitch_nack; /* obsolete */ ++ uint32 txburst; /* obsolete */ ++ ++ /* 802.11 MIB counters, pp. 614 of 802.11 reaff doc. */ ++ uint32 txfrag; /* dot11TransmittedFragmentCount */ ++ uint32 txmulti; /* dot11MulticastTransmittedFrameCount */ ++ uint32 txfail; /* dot11FailedCount */ ++ uint32 txretry; /* dot11RetryCount */ ++ uint32 txretrie; /* dot11MultipleRetryCount */ ++ uint32 rxdup; /* dot11FrameduplicateCount */ ++ uint32 txrts; /* dot11RTSSuccessCount */ ++ uint32 txnocts; /* dot11RTSFailureCount */ ++ uint32 txnoack; /* dot11ACKFailureCount */ ++ uint32 rxfrag; /* dot11ReceivedFragmentCount */ ++ uint32 rxmulti; /* dot11MulticastReceivedFrameCount */ ++ uint32 rxcrc; /* dot11FCSErrorCount */ ++ uint32 txfrmsnt; /* dot11TransmittedFrameCount (bogus MIB?) */ ++ uint32 rxundec; /* dot11WEPUndecryptableCount */ ++ ++ /* WPA2 counters (see rxundec for DecryptFailureCount) */ ++ uint32 tkipmicfaill; /* TKIPLocalMICFailures */ ++ uint32 tkipcntrmsr; /* TKIPCounterMeasuresInvoked */ ++ uint32 tkipreplay; /* TKIPReplays */ ++ uint32 ccmpfmterr; /* CCMPFormatErrors */ ++ uint32 ccmpreplay; /* CCMPReplays */ ++ uint32 ccmpundec; /* CCMPDecryptErrors */ ++ uint32 fourwayfail; /* FourWayHandshakeFailures */ ++ uint32 wepundec; /* dot11WEPUndecryptableCount */ ++ uint32 wepicverr; /* dot11WEPICVErrorCount */ ++ uint32 decsuccess; /* DecryptSuccessCount */ ++ uint32 tkipicverr; /* TKIPICVErrorCount */ ++ uint32 wepexcluded; /* dot11WEPExcludedCount */ ++ ++ uint32 rxundec_mcst; /* dot11WEPUndecryptableCount */ ++ ++ /* WPA2 counters (see rxundec for DecryptFailureCount) */ ++ uint32 tkipmicfaill_mcst; /* TKIPLocalMICFailures */ ++ uint32 tkipcntrmsr_mcst; /* TKIPCounterMeasuresInvoked */ ++ uint32 tkipreplay_mcst; /* TKIPReplays */ ++ uint32 ccmpfmterr_mcst; /* CCMPFormatErrors */ ++ uint32 ccmpreplay_mcst; /* CCMPReplays */ ++ uint32 ccmpundec_mcst; /* CCMPDecryptErrors */ ++ uint32 fourwayfail_mcst; /* FourWayHandshakeFailures */ ++ uint32 wepundec_mcst; /* dot11WEPUndecryptableCount */ ++ uint32 wepicverr_mcst; /* dot11WEPICVErrorCount */ ++ uint32 decsuccess_mcst; /* DecryptSuccessCount */ ++ uint32 tkipicverr_mcst; /* TKIPICVErrorCount */ ++ uint32 wepexcluded_mcst; /* dot11WEPExcludedCount */ ++ ++ uint32 txchanrej; /* Tx frames suppressed due to channel rejection */ ++ uint32 txexptime; /* Tx frames suppressed due to timer expiration */ ++ uint32 psmwds; /* Count PSM watchdogs */ ++ uint32 phywatchdog; /* Count Phy watchdogs (triggered by ucode) */ ++ ++ /* MBSS counters, AP only */ ++ uint32 prq_entries_handled; /* PRQ entries read in */ ++ uint32 prq_undirected_entries; /* which were bcast bss & ssid */ ++ uint32 prq_bad_entries; /* which could not be translated to info */ ++ uint32 atim_suppress_count; /* TX suppressions on ATIM fifo */ ++ uint32 bcn_template_not_ready; /* Template marked in use on send bcn ... */ ++ uint32 bcn_template_not_ready_done; /* ...but "DMA done" interrupt rcvd */ ++ uint32 late_tbtt_dpc; /* TBTT DPC did not happen in time */ ++ ++ /* per-rate receive stat counters */ ++ uint32 rx1mbps; /* packets rx at 1Mbps */ ++ uint32 rx2mbps; /* packets rx at 2Mbps */ ++ uint32 rx5mbps5; /* packets rx at 5.5Mbps */ ++ uint32 rx6mbps; /* packets rx at 6Mbps */ ++ uint32 rx9mbps; /* packets rx at 9Mbps */ ++ uint32 rx11mbps; /* packets rx at 11Mbps */ ++ uint32 rx12mbps; /* packets rx at 12Mbps */ ++ uint32 rx18mbps; /* packets rx at 18Mbps */ ++ uint32 rx24mbps; /* packets rx at 24Mbps */ ++ uint32 rx36mbps; /* packets rx at 36Mbps */ ++ uint32 rx48mbps; /* packets rx at 48Mbps */ ++ uint32 rx54mbps; /* packets rx at 54Mbps */ ++ uint32 rx108mbps; /* packets rx at 108mbps */ ++ uint32 rx162mbps; /* packets rx at 162mbps */ ++ uint32 rx216mbps; /* packets rx at 216 mbps */ ++ uint32 rx270mbps; /* packets rx at 270 mbps */ ++ uint32 rx324mbps; /* packets rx at 324 mbps */ ++ uint32 rx378mbps; /* packets rx at 378 mbps */ ++ uint32 rx432mbps; /* packets rx at 432 mbps */ ++ uint32 rx486mbps; /* packets rx at 486 mbps */ ++ uint32 rx540mbps; /* packets rx at 540 mbps */ ++ ++ /* pkteng rx frame stats */ ++ uint32 pktengrxducast; /* unicast frames rxed by the pkteng code */ ++ uint32 pktengrxdmcast; /* multicast frames rxed by the pkteng code */ ++ ++ uint32 rfdisable; /* count of radio disables */ ++ uint32 bphy_rxcrsglitch; /* PHY count of bphy glitches */ ++ ++ uint32 txmpdu_sgi; /* count for sgi transmit */ ++ uint32 rxmpdu_sgi; /* count for sgi received */ ++ uint32 txmpdu_stbc; /* count for stbc transmit */ ++ uint32 rxmpdu_stbc; /* count for stbc received */ ++} wl_cnt_ver_six_t; ++ ++#define WL_DELTA_STATS_T_VERSION 1 /* current version of wl_delta_stats_t struct */ ++ ++typedef struct { ++ uint16 version; /* see definition of WL_DELTA_STATS_T_VERSION */ ++ uint16 length; /* length of entire structure */ ++ ++ /* transmit stat counters */ ++ uint32 txframe; /* tx data frames */ ++ uint32 txbyte; /* tx data bytes */ ++ uint32 txretrans; /* tx mac retransmits */ ++ uint32 txfail; /* tx failures */ ++ ++ /* receive stat counters */ ++ uint32 rxframe; /* rx data frames */ ++ uint32 rxbyte; /* rx data bytes */ ++ ++ /* per-rate receive stat counters */ ++ uint32 rx1mbps; /* packets rx at 1Mbps */ ++ uint32 rx2mbps; /* packets rx at 2Mbps */ ++ uint32 rx5mbps5; /* packets rx at 5.5Mbps */ ++ uint32 rx6mbps; /* packets rx at 6Mbps */ ++ uint32 rx9mbps; /* packets rx at 9Mbps */ ++ uint32 rx11mbps; /* packets rx at 11Mbps */ ++ uint32 rx12mbps; /* packets rx at 12Mbps */ ++ uint32 rx18mbps; /* packets rx at 18Mbps */ ++ uint32 rx24mbps; /* packets rx at 24Mbps */ ++ uint32 rx36mbps; /* packets rx at 36Mbps */ ++ uint32 rx48mbps; /* packets rx at 48Mbps */ ++ uint32 rx54mbps; /* packets rx at 54Mbps */ ++ uint32 rx108mbps; /* packets rx at 108mbps */ ++ uint32 rx162mbps; /* packets rx at 162mbps */ ++ uint32 rx216mbps; /* packets rx at 216 mbps */ ++ uint32 rx270mbps; /* packets rx at 270 mbps */ ++ uint32 rx324mbps; /* packets rx at 324 mbps */ ++ uint32 rx378mbps; /* packets rx at 378 mbps */ ++ uint32 rx432mbps; /* packets rx at 432 mbps */ ++ uint32 rx486mbps; /* packets rx at 486 mbps */ ++ uint32 rx540mbps; /* packets rx at 540 mbps */ ++} wl_delta_stats_t; ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++#define WL_WME_CNT_VERSION 1 /* current version of wl_wme_cnt_t */ ++ ++typedef struct { ++ uint32 packets; ++ uint32 bytes; ++} wl_traffic_stats_t; ++ ++typedef struct { ++ uint16 version; /* see definition of WL_WME_CNT_VERSION */ ++ uint16 length; /* length of entire structure */ ++ ++ wl_traffic_stats_t tx[AC_COUNT]; /* Packets transmitted */ ++ wl_traffic_stats_t tx_failed[AC_COUNT]; /* Packets dropped or failed to transmit */ ++ wl_traffic_stats_t rx[AC_COUNT]; /* Packets received */ ++ wl_traffic_stats_t rx_failed[AC_COUNT]; /* Packets failed to receive */ ++ ++ wl_traffic_stats_t forward[AC_COUNT]; /* Packets forwarded by AP */ ++ ++ wl_traffic_stats_t tx_expired[AC_COUNT]; /* packets dropped due to lifetime expiry */ ++ ++} wl_wme_cnt_t; ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++struct wl_msglevel2 { ++ uint32 low; ++ uint32 high; ++}; ++ ++typedef struct wl_mkeep_alive_pkt { ++ uint16 version; /* Version for mkeep_alive */ ++ uint16 length; /* length of fixed parameters in the structure */ ++ uint32 period_msec; ++ uint16 len_bytes; ++ uint8 keep_alive_id; /* 0 - 3 for N = 4 */ ++ uint8 data[1]; ++} wl_mkeep_alive_pkt_t; ++ ++#define WL_MKEEP_ALIVE_VERSION 1 ++#define WL_MKEEP_ALIVE_FIXED_LEN OFFSETOF(wl_mkeep_alive_pkt_t, data) ++#define WL_MKEEP_ALIVE_PRECISION 500 ++ ++#ifdef WLBA ++ ++#define WLC_BA_CNT_VERSION 1 /* current version of wlc_ba_cnt_t */ ++ ++/* block ack related stats */ ++typedef struct wlc_ba_cnt { ++ uint16 version; /* WLC_BA_CNT_VERSION */ ++ uint16 length; /* length of entire structure */ ++ ++ /* transmit stat counters */ ++ uint32 txpdu; /* pdus sent */ ++ uint32 txsdu; /* sdus sent */ ++ uint32 txfc; /* tx side flow controlled packets */ ++ uint32 txfci; /* tx side flow control initiated */ ++ uint32 txretrans; /* retransmitted pdus */ ++ uint32 txbatimer; /* ba resend due to timer */ ++ uint32 txdrop; /* dropped packets */ ++ uint32 txaddbareq; /* addba req sent */ ++ uint32 txaddbaresp; /* addba resp sent */ ++ uint32 txdelba; /* delba sent */ ++ uint32 txba; /* ba sent */ ++ uint32 txbar; /* bar sent */ ++ uint32 txpad[4]; /* future */ ++ ++ /* receive side counters */ ++ uint32 rxpdu; /* pdus recd */ ++ uint32 rxqed; /* pdus buffered before sending up */ ++ uint32 rxdup; /* duplicate pdus */ ++ uint32 rxnobuf; /* pdus discarded due to no buf */ ++ uint32 rxaddbareq; /* addba req recd */ ++ uint32 rxaddbaresp; /* addba resp recd */ ++ uint32 rxdelba; /* delba recd */ ++ uint32 rxba; /* ba recd */ ++ uint32 rxbar; /* bar recd */ ++ uint32 rxinvba; /* invalid ba recd */ ++ uint32 rxbaholes; /* ba recd with holes */ ++ uint32 rxunexp; /* unexpected packets */ ++ uint32 rxpad[4]; /* future */ ++} wlc_ba_cnt_t; ++#endif /* WLBA */ ++ ++/* structure for per-tid ampdu control */ ++struct ampdu_tid_control { ++ uint8 tid; /* tid */ ++ uint8 enable; /* enable/disable */ ++}; ++ ++/* structure for identifying ea/tid for sending addba/delba */ ++struct ampdu_ea_tid { ++ struct ether_addr ea; /* Station address */ ++ uint8 tid; /* tid */ ++}; ++/* structure for identifying retry/tid for retry_limit_tid/rr_retry_limit_tid */ ++struct ampdu_retry_tid { ++ uint8 tid; /* tid */ ++ uint8 retry; /* retry value */ ++}; ++ ++/* Different discovery modes for dpt */ ++#define DPT_DISCOVERY_MANUAL 0x01 /* manual discovery mode */ ++#define DPT_DISCOVERY_AUTO 0x02 /* auto discovery mode */ ++#define DPT_DISCOVERY_SCAN 0x04 /* scan-based discovery mode */ ++ ++/* different path selection values */ ++#define DPT_PATHSEL_AUTO 0 /* auto mode for path selection */ ++#define DPT_PATHSEL_DIRECT 1 /* always use direct DPT path */ ++#define DPT_PATHSEL_APPATH 2 /* always use AP path */ ++ ++/* different ops for deny list */ ++#define DPT_DENY_LIST_ADD 1 /* add to dpt deny list */ ++#define DPT_DENY_LIST_REMOVE 2 /* remove from dpt deny list */ ++ ++/* different ops for manual end point */ ++#define DPT_MANUAL_EP_CREATE 1 /* create manual dpt endpoint */ ++#define DPT_MANUAL_EP_MODIFY 2 /* modify manual dpt endpoint */ ++#define DPT_MANUAL_EP_DELETE 3 /* delete manual dpt endpoint */ ++ ++/* structure for dpt iovars */ ++typedef struct dpt_iovar { ++ struct ether_addr ea; /* Station address */ ++ uint8 mode; /* mode: depends on iovar */ ++ uint32 pad; /* future */ ++} dpt_iovar_t; ++ ++/* flags to indicate DPT status */ ++#define DPT_STATUS_ACTIVE 0x01 /* link active (though may be suspended) */ ++#define DPT_STATUS_AES 0x02 /* link secured through AES encryption */ ++#define DPT_STATUS_FAILED 0x04 /* DPT link failed */ ++ ++#define DPT_FNAME_LEN 48 /* Max length of friendly name */ ++ ++typedef struct dpt_status { ++ uint8 status; /* flags to indicate status */ ++ uint8 fnlen; /* length of friendly name */ ++ uchar name[DPT_FNAME_LEN]; /* friendly name */ ++ uint32 rssi; /* RSSI of the link */ ++ sta_info_t sta; /* sta info */ ++} dpt_status_t; ++ ++/* structure for dpt list */ ++typedef struct dpt_list { ++ uint32 num; /* number of entries in struct */ ++ dpt_status_t status[1]; /* per station info */ ++} dpt_list_t; ++ ++/* structure for dpt friendly name */ ++typedef struct dpt_fname { ++ uint8 len; /* length of friendly name */ ++ uchar name[DPT_FNAME_LEN]; /* friendly name */ ++} dpt_fname_t; ++ ++#define BDD_FNAME_LEN 32 /* Max length of friendly name */ ++typedef struct bdd_fname { ++ uint8 len; /* length of friendly name */ ++ uchar name[BDD_FNAME_LEN]; /* friendly name */ ++} bdd_fname_t; ++ ++/* structure for addts arguments */ ++/* For ioctls that take a list of TSPEC */ ++struct tslist { ++ int count; /* number of tspecs */ ++ struct tsinfo_arg tsinfo[1]; /* variable length array of tsinfo */ ++}; ++ ++#ifdef WLTDLS ++/* different ops for manual end point */ ++#define TDLS_MANUAL_EP_CREATE 1 /* create manual dpt endpoint */ ++#define TDLS_MANUAL_EP_MODIFY 2 /* modify manual dpt endpoint */ ++#define TDLS_MANUAL_EP_DELETE 3 /* delete manual dpt endpoint */ ++#define TDLS_MANUAL_EP_PM 4 /* put dpt endpoint in PM mode */ ++#define TDLS_MANUAL_EP_WAKE 5 /* wake up dpt endpoint from PM */ ++#define TDLS_MANUAL_EP_DISCOVERY 6 /* discover if endpoint is TDLS capable */ ++#define TDLS_MANUAL_EP_CHSW 7 /* channel switch */ ++ ++/* structure for tdls iovars */ ++typedef struct tdls_iovar { ++ struct ether_addr ea; /* Station address */ ++ uint8 mode; /* mode: depends on iovar */ ++ chanspec_t chanspec; ++ uint32 pad; /* future */ ++} tdls_iovar_t; ++ ++/* modes */ ++#define TDLS_WFD_IE_TX 0 ++#define TDLS_WFD_IE_RX 1 ++#define TDLS_WFD_IE_SIZE 255 ++/* structure for tdls wfd ie */ ++typedef struct tdls_wfd_ie_iovar { ++ struct ether_addr ea; /* Station address */ ++ uint8 mode; ++ uint8 length; ++ uint8 data[TDLS_WFD_IE_SIZE]; ++} tdls_wfd_ie_iovar_t; ++#endif /* WLTDLS */ ++ ++/* structure for addts/delts arguments */ ++typedef struct tspec_arg { ++ uint16 version; /* see definition of TSPEC_ARG_VERSION */ ++ uint16 length; /* length of entire structure */ ++ uint flag; /* bit field */ ++ /* TSPEC Arguments */ ++ struct tsinfo_arg tsinfo; /* TS Info bit field */ ++ uint16 nom_msdu_size; /* (Nominal or fixed) MSDU Size (bytes) */ ++ uint16 max_msdu_size; /* Maximum MSDU Size (bytes) */ ++ uint min_srv_interval; /* Minimum Service Interval (us) */ ++ uint max_srv_interval; /* Maximum Service Interval (us) */ ++ uint inactivity_interval; /* Inactivity Interval (us) */ ++ uint suspension_interval; /* Suspension Interval (us) */ ++ uint srv_start_time; /* Service Start Time (us) */ ++ uint min_data_rate; /* Minimum Data Rate (bps) */ ++ uint mean_data_rate; /* Mean Data Rate (bps) */ ++ uint peak_data_rate; /* Peak Data Rate (bps) */ ++ uint max_burst_size; /* Maximum Burst Size (bytes) */ ++ uint delay_bound; /* Delay Bound (us) */ ++ uint min_phy_rate; /* Minimum PHY Rate (bps) */ ++ uint16 surplus_bw; /* Surplus Bandwidth Allowance (range 1.0 to 8.0) */ ++ uint16 medium_time; /* Medium Time (32 us/s periods) */ ++ uint8 dialog_token; /* dialog token */ ++} tspec_arg_t; ++ ++/* tspec arg for desired station */ ++typedef struct tspec_per_sta_arg { ++ struct ether_addr ea; ++ struct tspec_arg ts; ++} tspec_per_sta_arg_t; ++ ++/* structure for max bandwidth for each access category */ ++typedef struct wme_max_bandwidth { ++ uint32 ac[AC_COUNT]; /* max bandwidth for each access category */ ++} wme_max_bandwidth_t; ++ ++#define WL_WME_MBW_PARAMS_IO_BYTES (sizeof(wme_max_bandwidth_t)) ++ ++/* current version of wl_tspec_arg_t struct */ ++#define TSPEC_ARG_VERSION 2 /* current version of wl_tspec_arg_t struct */ ++#define TSPEC_ARG_LENGTH 55 /* argument length from tsinfo to medium_time */ ++#define TSPEC_DEFAULT_DIALOG_TOKEN 42 /* default dialog token */ ++#define TSPEC_DEFAULT_SBW_FACTOR 0x3000 /* default surplus bw */ ++ ++ ++#define WL_WOWL_KEEPALIVE_MAX_PACKET_SIZE 80 ++#define WLC_WOWL_MAX_KEEPALIVE 2 ++ ++/* define for flag */ ++#define TSPEC_PENDING 0 /* TSPEC pending */ ++#define TSPEC_ACCEPTED 1 /* TSPEC accepted */ ++#define TSPEC_REJECTED 2 /* TSPEC rejected */ ++#define TSPEC_UNKNOWN 3 /* TSPEC unknown */ ++#define TSPEC_STATUS_MASK 7 /* TSPEC status mask */ ++ ++ ++/* Software feature flag defines used by wlfeatureflag */ ++#ifdef WLAFTERBURNER ++#define WL_SWFL_ABBFL 0x0001 /* Allow Afterburner on systems w/o hardware BFL */ ++#define WL_SWFL_ABENCORE 0x0002 /* Allow AB on non-4318E chips */ ++#endif /* WLAFTERBURNER */ ++#define WL_SWFL_NOHWRADIO 0x0004 ++#define WL_SWFL_FLOWCONTROL 0x0008 /* Enable backpressure to OS stack */ ++#define WL_SWFL_WLBSSSORT 0x0010 /* Per-port supports sorting of BSS */ ++ ++#define WL_LIFETIME_MAX 0xFFFF /* Max value in ms */ ++ ++/* Packet lifetime configuration per ac */ ++typedef struct wl_lifetime { ++ uint32 ac; /* access class */ ++ uint32 lifetime; /* Packet lifetime value in ms */ ++} wl_lifetime_t; ++ ++/* Channel Switch Announcement param */ ++typedef struct wl_chan_switch { ++ uint8 mode; /* value 0 or 1 */ ++ uint8 count; /* count # of beacons before switching */ ++ chanspec_t chspec; /* chanspec */ ++ uint8 reg; /* regulatory class */ ++} wl_chan_switch_t; ++ ++/* Roaming trigger definitions for WLC_SET_ROAM_TRIGGER. ++ * ++ * (-100 < value < 0) value is used directly as a roaming trigger in dBm ++ * (0 <= value) value specifies a logical roaming trigger level from ++ * the list below ++ * ++ * WLC_GET_ROAM_TRIGGER always returns roaming trigger value in dBm, never ++ * the logical roam trigger value. ++ */ ++#define WLC_ROAM_TRIGGER_DEFAULT 0 /* default roaming trigger */ ++#define WLC_ROAM_TRIGGER_BANDWIDTH 1 /* optimize for bandwidth roaming trigger */ ++#define WLC_ROAM_TRIGGER_DISTANCE 2 /* optimize for distance roaming trigger */ ++#define WLC_ROAM_TRIGGER_AUTO 3 /* auto-detect environment */ ++#define WLC_ROAM_TRIGGER_MAX_VALUE 3 /* max. valid value */ ++ ++#define WLC_ROAM_NEVER_ROAM_TRIGGER (-100) /* Avoid Roaming by setting a large value */ ++ ++/* Preferred Network Offload (PNO, formerly PFN) defines */ ++#define WPA_AUTH_PFN_ANY 0xffffffff /* for PFN, match only ssid */ ++ ++enum { ++ PFN_LIST_ORDER, ++ PFN_RSSI ++}; ++ ++enum { ++ DISABLE, ++ ENABLE ++}; ++ ++enum { ++ OFF_ADAPT, ++ SMART_ADAPT, ++ STRICT_ADAPT, ++ SLOW_ADAPT ++}; ++ ++#define SORT_CRITERIA_BIT 0 ++#define AUTO_NET_SWITCH_BIT 1 ++#define ENABLE_BKGRD_SCAN_BIT 2 ++#define IMMEDIATE_SCAN_BIT 3 ++#define AUTO_CONNECT_BIT 4 ++#define ENABLE_BD_SCAN_BIT 5 ++#define ENABLE_ADAPTSCAN_BIT 6 ++#define IMMEDIATE_EVENT_BIT 8 ++#define SUPPRESS_SSID_BIT 9 ++#define ENABLE_NET_OFFLOAD_BIT 10 ++ ++#define SORT_CRITERIA_MASK 0x0001 ++#define AUTO_NET_SWITCH_MASK 0x0002 ++#define ENABLE_BKGRD_SCAN_MASK 0x0004 ++#define IMMEDIATE_SCAN_MASK 0x0008 ++#define AUTO_CONNECT_MASK 0x0010 ++ ++#define ENABLE_BD_SCAN_MASK 0x0020 ++#define ENABLE_ADAPTSCAN_MASK 0x00c0 ++#define IMMEDIATE_EVENT_MASK 0x0100 ++#define SUPPRESS_SSID_MASK 0x0200 ++#define ENABLE_NET_OFFLOAD_MASK 0x0400 ++ ++#define PFN_VERSION 2 ++#define PFN_SCANRESULT_VERSION 1 ++#define MAX_PFN_LIST_COUNT 16 ++ ++#define PFN_COMPLETE 1 ++#define PFN_INCOMPLETE 0 ++ ++#define DEFAULT_BESTN 2 ++#define DEFAULT_MSCAN 0 ++#define DEFAULT_REPEAT 10 ++#define DEFAULT_EXP 2 ++ ++/* PFN network info structure */ ++typedef struct wl_pfn_subnet_info { ++ struct ether_addr BSSID; ++ uint8 channel; /* channel number only */ ++ uint8 SSID_len; ++ uint8 SSID[32]; ++} wl_pfn_subnet_info_t; ++ ++typedef struct wl_pfn_net_info { ++ wl_pfn_subnet_info_t pfnsubnet; ++ int16 RSSI; /* receive signal strength (in dBm) */ ++ uint16 timestamp; /* age in seconds */ ++} wl_pfn_net_info_t; ++ ++typedef struct wl_pfn_scanresults { ++ uint32 version; ++ uint32 status; ++ uint32 count; ++ wl_pfn_net_info_t netinfo[1]; ++} wl_pfn_scanresults_t; ++ ++/* PFN data structure */ ++typedef struct wl_pfn_param { ++ int32 version; /* PNO parameters version */ ++ int32 scan_freq; /* Scan frequency */ ++ int32 lost_network_timeout; /* Timeout in sec. to declare ++ * discovered network as lost ++ */ ++ int16 flags; /* Bit field to control features ++ * of PFN such as sort criteria auto ++ * enable switch and background scan ++ */ ++ int16 rssi_margin; /* Margin to avoid jitter for choosing a ++ * PFN based on RSSI sort criteria ++ */ ++ uint8 bestn; /* number of best networks in each scan */ ++ uint8 mscan; /* number of scans recorded */ ++ uint8 repeat; /* Minimum number of scan intervals ++ *before scan frequency changes in adaptive scan ++ */ ++ uint8 exp; /* Exponent of 2 for maximum scan interval */ ++ int32 slow_freq; /* slow scan period */ ++} wl_pfn_param_t; ++ ++typedef struct wl_pfn_bssid { ++ struct ether_addr macaddr; ++ /* Bit4: suppress_lost, Bit3: suppress_found */ ++ uint16 flags; ++} wl_pfn_bssid_t; ++#define WL_PFN_SUPPRESSFOUND_MASK 0x08 ++#define WL_PFN_SUPPRESSLOST_MASK 0x10 ++ ++typedef struct wl_pfn_cfg { ++ uint32 reporttype; ++ int32 channel_num; ++ uint16 channel_list[WL_NUMCHANNELS]; ++} wl_pfn_cfg_t; ++#define WL_PFN_REPORT_ALLNET 0 ++#define WL_PFN_REPORT_SSIDNET 1 ++#define WL_PFN_REPORT_BSSIDNET 2 ++ ++typedef struct wl_pfn { ++ wlc_ssid_t ssid; /* ssid name and its length */ ++ int32 flags; /* bit2: hidden */ ++ int32 infra; /* BSS Vs IBSS */ ++ int32 auth; /* Open Vs Closed */ ++ int32 wpa_auth; /* WPA type */ ++ int32 wsec; /* wsec value */ ++} wl_pfn_t; ++#define WL_PFN_HIDDEN_BIT 2 ++#define PNO_SCAN_MAX_FW 508*1000 /* max time scan time in msec */ ++#define PNO_SCAN_MAX_FW_SEC PNO_SCAN_MAX_FW/1000 /* max time scan time in SEC */ ++#define PNO_SCAN_MIN_FW_SEC 10 /* min time scan time in SEC */ ++#define WL_PFN_HIDDEN_MASK 0x4 ++ ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++/* TCP Checksum Offload defines */ ++#define TOE_TX_CSUM_OL 0x00000001 ++#define TOE_RX_CSUM_OL 0x00000002 ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++/* TCP Checksum Offload error injection for testing */ ++#define TOE_ERRTEST_TX_CSUM 0x00000001 ++#define TOE_ERRTEST_RX_CSUM 0x00000002 ++#define TOE_ERRTEST_RX_CSUM2 0x00000004 ++ ++struct toe_ol_stats_t { ++ /* Num of tx packets that don't need to be checksummed */ ++ uint32 tx_summed; ++ ++ /* Num of tx packets where checksum is filled by offload engine */ ++ uint32 tx_iph_fill; ++ uint32 tx_tcp_fill; ++ uint32 tx_udp_fill; ++ uint32 tx_icmp_fill; ++ ++ /* Num of rx packets where toe finds out if checksum is good or bad */ ++ uint32 rx_iph_good; ++ uint32 rx_iph_bad; ++ uint32 rx_tcp_good; ++ uint32 rx_tcp_bad; ++ uint32 rx_udp_good; ++ uint32 rx_udp_bad; ++ uint32 rx_icmp_good; ++ uint32 rx_icmp_bad; ++ ++ /* Num of tx packets in which csum error is injected */ ++ uint32 tx_tcp_errinj; ++ uint32 tx_udp_errinj; ++ uint32 tx_icmp_errinj; ++ ++ /* Num of rx packets in which csum error is injected */ ++ uint32 rx_tcp_errinj; ++ uint32 rx_udp_errinj; ++ uint32 rx_icmp_errinj; ++}; ++ ++/* ARP Offload feature flags for arp_ol iovar */ ++#define ARP_OL_AGENT 0x00000001 ++#define ARP_OL_SNOOP 0x00000002 ++#define ARP_OL_HOST_AUTO_REPLY 0x00000004 ++#define ARP_OL_PEER_AUTO_REPLY 0x00000008 ++ ++/* ARP Offload error injection */ ++#define ARP_ERRTEST_REPLY_PEER 0x1 ++#define ARP_ERRTEST_REPLY_HOST 0x2 ++ ++#define ARP_MULTIHOMING_MAX 8 /* Maximum local host IP addresses */ ++#define ND_MULTIHOMING_MAX 8 /* Maximum local host IP addresses */ ++ ++/* Arp offload statistic counts */ ++struct arp_ol_stats_t { ++ uint32 host_ip_entries; /* Host IP table addresses (more than one if multihomed) */ ++ uint32 host_ip_overflow; /* Host IP table additions skipped due to overflow */ ++ ++ uint32 arp_table_entries; /* ARP table entries */ ++ uint32 arp_table_overflow; /* ARP table additions skipped due to overflow */ ++ ++ uint32 host_request; /* ARP requests from host */ ++ uint32 host_reply; /* ARP replies from host */ ++ uint32 host_service; /* ARP requests from host serviced by ARP Agent */ ++ ++ uint32 peer_request; /* ARP requests received from network */ ++ uint32 peer_request_drop; /* ARP requests from network that were dropped */ ++ uint32 peer_reply; /* ARP replies received from network */ ++ uint32 peer_reply_drop; /* ARP replies from network that were dropped */ ++ uint32 peer_service; /* ARP request from host serviced by ARP Agent */ ++}; ++ ++/* NS offload statistic counts */ ++struct nd_ol_stats_t { ++ uint32 host_ip_entries; /* Host IP table addresses (more than one if multihomed) */ ++ uint32 host_ip_overflow; /* Host IP table additions skipped due to overflow */ ++ uint32 peer_request; /* NS requests received from network */ ++ uint32 peer_request_drop; /* NS requests from network that were dropped */ ++ uint32 peer_reply_drop; /* NA replies from network that were dropped */ ++ uint32 peer_service; /* NS request from host serviced by firmware */ ++}; ++ ++/* ++ * Keep-alive packet offloading. ++ */ ++ ++/* NAT keep-alive packets format: specifies the re-transmission period, the packet ++ * length, and packet contents. ++ */ ++typedef struct wl_keep_alive_pkt { ++ uint32 period_msec; /* Retransmission period (0 to disable packet re-transmits) */ ++ uint16 len_bytes; /* Size of packet to transmit (0 to disable packet re-transmits) */ ++ uint8 data[1]; /* Variable length packet to transmit. Contents should include ++ * entire ethernet packet (enet header, IP header, UDP header, ++ * and UDP payload) in network byte order. ++ */ ++} wl_keep_alive_pkt_t; ++ ++#define WL_KEEP_ALIVE_FIXED_LEN OFFSETOF(wl_keep_alive_pkt_t, data) ++ ++/* ++ * Dongle pattern matching filter. ++ */ ++ ++/* Packet filter types. Currently, only pattern matching is supported. */ ++typedef enum wl_pkt_filter_type { ++ WL_PKT_FILTER_TYPE_PATTERN_MATCH /* Pattern matching filter */ ++} wl_pkt_filter_type_t; ++ ++#define WL_PKT_FILTER_TYPE wl_pkt_filter_type_t ++ ++/* Pattern matching filter. Specifies an offset within received packets to ++ * start matching, the pattern to match, the size of the pattern, and a bitmask ++ * that indicates which bits within the pattern should be matched. ++ */ ++typedef struct wl_pkt_filter_pattern { ++ uint32 offset; /* Offset within received packet to start pattern matching. ++ * Offset '0' is the first byte of the ethernet header. ++ */ ++ uint32 size_bytes; /* Size of the pattern. Bitmask must be the same size. */ ++ uint8 mask_and_pattern[1]; /* Variable length mask and pattern data. mask starts ++ * at offset 0. Pattern immediately follows mask. ++ */ ++} wl_pkt_filter_pattern_t; ++ ++/* IOVAR "pkt_filter_add" parameter. Used to install packet filters. */ ++typedef struct wl_pkt_filter { ++ uint32 id; /* Unique filter id, specified by app. */ ++ uint32 type; /* Filter type (WL_PKT_FILTER_TYPE_xxx). */ ++ uint32 negate_match; /* Negate the result of filter matches */ ++ union { /* Filter definitions */ ++ wl_pkt_filter_pattern_t pattern; /* Pattern matching filter */ ++ } u; ++} wl_pkt_filter_t; ++ ++#define WL_PKT_FILTER_FIXED_LEN OFFSETOF(wl_pkt_filter_t, u) ++#define WL_PKT_FILTER_PATTERN_FIXED_LEN OFFSETOF(wl_pkt_filter_pattern_t, mask_and_pattern) ++ ++/* IOVAR "pkt_filter_enable" parameter. */ ++typedef struct wl_pkt_filter_enable { ++ uint32 id; /* Unique filter id */ ++ uint32 enable; /* Enable/disable bool */ ++} wl_pkt_filter_enable_t; ++ ++/* IOVAR "pkt_filter_list" parameter. Used to retrieve a list of installed filters. */ ++typedef struct wl_pkt_filter_list { ++ uint32 num; /* Number of installed packet filters */ ++ wl_pkt_filter_t filter[1]; /* Variable array of packet filters. */ ++} wl_pkt_filter_list_t; ++ ++#define WL_PKT_FILTER_LIST_FIXED_LEN OFFSETOF(wl_pkt_filter_list_t, filter) ++ ++/* IOVAR "pkt_filter_stats" parameter. Used to retrieve debug statistics. */ ++typedef struct wl_pkt_filter_stats { ++ uint32 num_pkts_matched; /* # filter matches for specified filter id */ ++ uint32 num_pkts_forwarded; /* # packets fwded from dongle to host for all filters */ ++ uint32 num_pkts_discarded; /* # packets discarded by dongle for all filters */ ++} wl_pkt_filter_stats_t; ++ ++/* Sequential Commands ioctl */ ++typedef struct wl_seq_cmd_ioctl { ++ uint32 cmd; /* common ioctl definition */ ++ uint32 len; /* length of user buffer */ ++} wl_seq_cmd_ioctl_t; ++ ++#define WL_SEQ_CMD_ALIGN_BYTES 4 ++ ++/* These are the set of get IOCTLs that should be allowed when using ++ * IOCTL sequence commands. These are issued implicitly by wl.exe each time ++ * it is invoked. We never want to buffer these, or else wl.exe will stop working. ++ */ ++#define WL_SEQ_CMDS_GET_IOCTL_FILTER(cmd) \ ++ (((cmd) == WLC_GET_MAGIC) || \ ++ ((cmd) == WLC_GET_VERSION) || \ ++ ((cmd) == WLC_GET_AP) || \ ++ ((cmd) == WLC_GET_INSTANCE)) ++ ++/* ++ * Packet engine interface ++ */ ++ ++#define WL_PKTENG_PER_TX_START 0x01 ++#define WL_PKTENG_PER_TX_STOP 0x02 ++#define WL_PKTENG_PER_RX_START 0x04 ++#define WL_PKTENG_PER_RX_WITH_ACK_START 0x05 ++#define WL_PKTENG_PER_TX_WITH_ACK_START 0x06 ++#define WL_PKTENG_PER_RX_STOP 0x08 ++#define WL_PKTENG_PER_MASK 0xff ++ ++#define WL_PKTENG_SYNCHRONOUS 0x100 /* synchronous flag */ ++ ++typedef struct wl_pkteng { ++ uint32 flags; ++ uint32 delay; /* Inter-packet delay */ ++ uint32 nframes; /* Number of frames */ ++ uint32 length; /* Packet length */ ++ uint8 seqno; /* Enable/disable sequence no. */ ++ struct ether_addr dest; /* Destination address */ ++ struct ether_addr src; /* Source address */ ++} wl_pkteng_t; ++ ++#define NUM_80211b_RATES 4 ++#define NUM_80211ag_RATES 8 ++#define NUM_80211n_RATES 32 ++#define NUM_80211_RATES (NUM_80211b_RATES+NUM_80211ag_RATES+NUM_80211n_RATES) ++typedef struct wl_pkteng_stats { ++ uint32 lostfrmcnt; /* RX PER test: no of frames lost (skip seqno) */ ++ int32 rssi; /* RSSI */ ++ int32 snr; /* signal to noise ratio */ ++ uint16 rxpktcnt[NUM_80211_RATES+1]; ++} wl_pkteng_stats_t; ++ ++ ++#define WL_WOWL_MAGIC (1 << 0) /* Wakeup on Magic packet */ ++#define WL_WOWL_NET (1 << 1) /* Wakeup on Netpattern */ ++#define WL_WOWL_DIS (1 << 2) /* Wakeup on loss-of-link due to Disassoc/Deauth */ ++#define WL_WOWL_RETR (1 << 3) /* Wakeup on retrograde TSF */ ++#define WL_WOWL_BCN (1 << 4) /* Wakeup on loss of beacon */ ++#define WL_WOWL_TST (1 << 5) /* Wakeup after test */ ++#define WL_WOWL_M1 (1 << 6) /* Wakeup after PTK refresh */ ++#define WL_WOWL_EAPID (1 << 7) /* Wakeup after receipt of EAP-Identity Req */ ++#define WL_WOWL_PME_GPIO (1 << 8) /* Wakeind via PME(0) or GPIO(1) */ ++#define WL_WOWL_NEEDTKIP1 (1 << 9) /* need tkip phase 1 key to be updated by the driver */ ++#define WL_WOWL_GTK_FAILURE (1 << 10) /* enable wakeup if GTK fails */ ++#define WL_WOWL_EXTMAGPAT (1 << 11) /* support extended magic packets */ ++#define WL_WOWL_ARPOFFLOAD (1 << 12) /* support ARP/NS/keepalive offloading */ ++#define WL_WOWL_WPA2 (1 << 13) /* read protocol version for EAPOL frames */ ++#define WL_WOWL_KEYROT (1 << 14) /* If the bit is set, use key rotaton */ ++#define WL_WOWL_BCAST (1 << 15) /* If the bit is set, frm received was bcast frame */ ++ ++#define MAGIC_PKT_MINLEN 102 /* Magic pkt min length is 6 * 0xFF + 16 * ETHER_ADDR_LEN */ ++ ++#define WOWL_PATTEN_TYPE_ARP (1 << 0) /* ARP offload Pattern */ ++#define WOWL_PATTEN_TYPE_NA (1 << 1) /* NA offload Pattern */ ++ ++typedef struct { ++ uint32 masksize; /* Size of the mask in #of bytes */ ++ uint32 offset; /* Offset to start looking for the packet in # of bytes */ ++ uint32 patternoffset; /* Offset of start of pattern in the structure */ ++ uint32 patternsize; /* Size of the pattern itself in #of bytes */ ++ uint32 id; /* id */ ++ uint32 reasonsize; /* Size of the wakeup reason code */ ++ uint32 flags; /* Flags to tell the pattern type and other properties */ ++ /* Mask follows the structure above */ ++ /* Pattern follows the mask is at 'patternoffset' from the start */ ++} wl_wowl_pattern_t; ++ ++typedef struct { ++ uint count; ++ wl_wowl_pattern_t pattern[1]; ++} wl_wowl_pattern_list_t; ++ ++typedef struct { ++ uint8 pci_wakeind; /* Whether PCI PMECSR PMEStatus bit was set */ ++ uint16 ucode_wakeind; /* What wakeup-event indication was set by ucode */ ++} wl_wowl_wakeind_t; ++ ++ ++/* per AC rate control related data structure */ ++typedef struct wl_txrate_class { ++ uint8 init_rate; ++ uint8 min_rate; ++ uint8 max_rate; ++} wl_txrate_class_t; ++ ++ ++ ++/* Overlap BSS Scan parameters default, minimum, maximum */ ++#define WLC_OBSS_SCAN_PASSIVE_DWELL_DEFAULT 20 /* unit TU */ ++#define WLC_OBSS_SCAN_PASSIVE_DWELL_MIN 5 /* unit TU */ ++#define WLC_OBSS_SCAN_PASSIVE_DWELL_MAX 1000 /* unit TU */ ++#define WLC_OBSS_SCAN_ACTIVE_DWELL_DEFAULT 10 /* unit TU */ ++#define WLC_OBSS_SCAN_ACTIVE_DWELL_MIN 10 /* unit TU */ ++#define WLC_OBSS_SCAN_ACTIVE_DWELL_MAX 1000 /* unit TU */ ++#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_DEFAULT 300 /* unit Sec */ ++#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_MIN 10 /* unit Sec */ ++#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_MAX 900 /* unit Sec */ ++#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_DEFAULT 5 ++#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_MIN 5 ++#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_MAX 100 ++#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_DEFAULT 200 /* unit TU */ ++#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_MIN 200 /* unit TU */ ++#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_MAX 10000 /* unit TU */ ++#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_DEFAULT 20 /* unit TU */ ++#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_MIN 20 /* unit TU */ ++#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_MAX 10000 /* unit TU */ ++#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_DEFAULT 25 /* unit percent */ ++#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_MIN 0 /* unit percent */ ++#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_MAX 100 /* unit percent */ ++ ++/* structure for Overlap BSS scan arguments */ ++typedef struct wl_obss_scan_arg { ++ int16 passive_dwell; ++ int16 active_dwell; ++ int16 bss_widthscan_interval; ++ int16 passive_total; ++ int16 active_total; ++ int16 chanwidth_transition_delay; ++ int16 activity_threshold; ++} wl_obss_scan_arg_t; ++ ++#define WL_OBSS_SCAN_PARAM_LEN sizeof(wl_obss_scan_arg_t) ++#define WL_MIN_NUM_OBSS_SCAN_ARG 7 /* minimum number of arguments required for OBSS Scan */ ++ ++#define WL_COEX_INFO_MASK 0x07 ++#define WL_COEX_INFO_REQ 0x01 ++#define WL_COEX_40MHZ_INTOLERANT 0x02 ++#define WL_COEX_WIDTH20 0x04 ++ ++#define WLC_RSSI_INVALID 0 /* invalid RSSI value */ ++ ++#define MAX_RSSI_LEVELS 8 ++ ++/* RSSI event notification configuration. */ ++typedef struct wl_rssi_event { ++ uint32 rate_limit_msec; /* # of events posted to application will be limited to ++ * one per specified period (0 to disable rate limit). ++ */ ++ uint8 num_rssi_levels; /* Number of entries in rssi_levels[] below */ ++ int8 rssi_levels[MAX_RSSI_LEVELS]; /* Variable number of RSSI levels. An event ++ * will be posted each time the RSSI of received ++ * beacons/packets crosses a level. ++ */ ++} wl_rssi_event_t; ++ ++typedef struct wl_action_obss_coex_req { ++ uint8 info; ++ uint8 num; ++ uint8 ch_list[1]; ++} wl_action_obss_coex_req_t; ++ ++ ++/* IOVar parameter block for small MAC address array with type indicator */ ++#define WL_IOV_MAC_PARAM_LEN 4 ++ ++#define WL_IOV_PKTQ_LOG_PRECS 16 ++ ++typedef struct { ++ uint32 num_addrs; ++ char addr_type[WL_IOV_MAC_PARAM_LEN]; ++ struct ether_addr ea[WL_IOV_MAC_PARAM_LEN]; ++} wl_iov_mac_params_t; ++ ++ ++/* Parameter block for PKTQ_LOG statistics */ ++typedef struct { ++ uint32 requested; /* packets requested to be stored */ ++ uint32 stored; /* packets stored */ ++ uint32 saved; /* packets saved, ++ because a lowest priority queue has given away one packet ++ */ ++ uint32 selfsaved; /* packets saved, ++ because an older packet from the same queue has been dropped ++ */ ++ uint32 full_dropped; /* packets dropped, ++ because pktq is full with higher precedence packets ++ */ ++ uint32 dropped; /* packets dropped because pktq per that precedence is full */ ++ uint32 sacrificed; /* packets dropped, ++ in order to save one from a queue of a highest priority ++ */ ++ uint32 busy; /* packets droped because of hardware/transmission error */ ++ uint32 retry; /* packets re-sent because they were not received */ ++ uint32 ps_retry; /* packets retried again prior to moving power save mode */ ++ uint32 retry_drop; /* packets finally dropped after retry limit */ ++ uint32 max_avail; /* the high-water mark of the queue capacity for packets - ++ goes to zero as queue fills ++ */ ++ uint32 max_used; /* the high-water mark of the queue utilisation for packets - ++ increases with use ('inverse' of max_avail) ++ */ ++ uint32 queue_capacity; /* the maximum capacity of the queue */ ++} pktq_log_counters_v01_t; ++ ++#define sacrified sacrificed ++ ++typedef struct { ++ uint8 num_prec[WL_IOV_MAC_PARAM_LEN]; ++ pktq_log_counters_v01_t counters[WL_IOV_MAC_PARAM_LEN][WL_IOV_PKTQ_LOG_PRECS]; ++ char headings[1]; ++} pktq_log_format_v01_t; ++ ++ ++typedef struct { ++ uint32 version; ++ wl_iov_mac_params_t params; ++ union { ++ pktq_log_format_v01_t v01; ++ } pktq_log; ++} wl_iov_pktq_log_t; ++ ++ ++/* **** EXTLOG **** */ ++#define EXTLOG_CUR_VER 0x0100 ++ ++#define MAX_ARGSTR_LEN 18 /* At least big enough for storing ETHER_ADDR_STR_LEN */ ++ ++/* log modules (bitmap) */ ++#define LOG_MODULE_COMMON 0x0001 ++#define LOG_MODULE_ASSOC 0x0002 ++#define LOG_MODULE_EVENT 0x0004 ++#define LOG_MODULE_MAX 3 /* Update when adding module */ ++ ++/* log levels */ ++#define WL_LOG_LEVEL_DISABLE 0 ++#define WL_LOG_LEVEL_ERR 1 ++#define WL_LOG_LEVEL_WARN 2 ++#define WL_LOG_LEVEL_INFO 3 ++#define WL_LOG_LEVEL_MAX WL_LOG_LEVEL_INFO /* Update when adding level */ ++ ++/* flag */ ++#define LOG_FLAG_EVENT 1 ++ ++/* log arg_type */ ++#define LOG_ARGTYPE_NULL 0 ++#define LOG_ARGTYPE_STR 1 /* %s */ ++#define LOG_ARGTYPE_INT 2 /* %d */ ++#define LOG_ARGTYPE_INT_STR 3 /* %d...%s */ ++#define LOG_ARGTYPE_STR_INT 4 /* %s...%d */ ++ ++typedef struct wlc_extlog_cfg { ++ int max_number; ++ uint16 module; /* bitmap */ ++ uint8 level; ++ uint8 flag; ++ uint16 version; ++} wlc_extlog_cfg_t; ++ ++typedef struct log_record { ++ uint32 time; ++ uint16 module; ++ uint16 id; ++ uint8 level; ++ uint8 sub_unit; ++ uint8 seq_num; ++ int32 arg; ++ char str[MAX_ARGSTR_LEN]; ++} log_record_t; ++ ++typedef struct wlc_extlog_req { ++ uint32 from_last; ++ uint32 num; ++} wlc_extlog_req_t; ++ ++typedef struct wlc_extlog_results { ++ uint16 version; ++ uint16 record_len; ++ uint32 num; ++ log_record_t logs[1]; ++} wlc_extlog_results_t; ++ ++typedef struct log_idstr { ++ uint16 id; ++ uint16 flag; ++ uint8 arg_type; ++ const char *fmt_str; ++} log_idstr_t; ++ ++#define FMTSTRF_USER 1 ++ ++/* flat ID definitions ++ * New definitions HAVE TO BE ADDED at the end of the table. Otherwise, it will ++ * affect backward compatibility with pre-existing apps ++ */ ++typedef enum { ++ FMTSTR_DRIVER_UP_ID = 0, ++ FMTSTR_DRIVER_DOWN_ID = 1, ++ FMTSTR_SUSPEND_MAC_FAIL_ID = 2, ++ FMTSTR_NO_PROGRESS_ID = 3, ++ FMTSTR_RFDISABLE_ID = 4, ++ FMTSTR_REG_PRINT_ID = 5, ++ FMTSTR_EXPTIME_ID = 6, ++ FMTSTR_JOIN_START_ID = 7, ++ FMTSTR_JOIN_COMPLETE_ID = 8, ++ FMTSTR_NO_NETWORKS_ID = 9, ++ FMTSTR_SECURITY_MISMATCH_ID = 10, ++ FMTSTR_RATE_MISMATCH_ID = 11, ++ FMTSTR_AP_PRUNED_ID = 12, ++ FMTSTR_KEY_INSERTED_ID = 13, ++ FMTSTR_DEAUTH_ID = 14, ++ FMTSTR_DISASSOC_ID = 15, ++ FMTSTR_LINK_UP_ID = 16, ++ FMTSTR_LINK_DOWN_ID = 17, ++ FMTSTR_RADIO_HW_OFF_ID = 18, ++ FMTSTR_RADIO_HW_ON_ID = 19, ++ FMTSTR_EVENT_DESC_ID = 20, ++ FMTSTR_PNP_SET_POWER_ID = 21, ++ FMTSTR_RADIO_SW_OFF_ID = 22, ++ FMTSTR_RADIO_SW_ON_ID = 23, ++ FMTSTR_PWD_MISMATCH_ID = 24, ++ FMTSTR_FATAL_ERROR_ID = 25, ++ FMTSTR_AUTH_FAIL_ID = 26, ++ FMTSTR_ASSOC_FAIL_ID = 27, ++ FMTSTR_IBSS_FAIL_ID = 28, ++ FMTSTR_EXTAP_FAIL_ID = 29, ++ FMTSTR_MAX_ID ++} log_fmtstr_id_t; ++ ++#ifdef DONGLEOVERLAYS ++typedef struct { ++ uint32 flags_idx; /* lower 8 bits: overlay index; upper 24 bits: flags */ ++ uint32 offset; /* offset into overlay region to write code */ ++ uint32 len; /* overlay code len */ ++ /* overlay code follows this struct */ ++} wl_ioctl_overlay_t; ++ ++#define OVERLAY_IDX_MASK 0x000000ff ++#define OVERLAY_IDX_SHIFT 0 ++#define OVERLAY_FLAGS_MASK 0xffffff00 ++#define OVERLAY_FLAGS_SHIFT 8 ++/* overlay written to device memory immediately after loading the base image */ ++#define OVERLAY_FLAG_POSTLOAD 0x100 ++/* defer overlay download until the device responds w/WLC_E_OVL_DOWNLOAD event */ ++#define OVERLAY_FLAG_DEFER_DL 0x200 ++/* overlay downloaded prior to the host going to sleep */ ++#define OVERLAY_FLAG_PRESLEEP 0x400 ++ ++#define OVERLAY_DOWNLOAD_CHUNKSIZE 1024 ++#endif /* DONGLEOVERLAYS */ ++ ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++/* no default structure packing */ ++#include ++ ++/* require strict packing */ ++#include ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++ ++/* Structures and constants used for "vndr_ie" IOVar interface */ ++#define VNDR_IE_CMD_LEN 4 /* length of the set command string: ++ * "add", "del" (+ NUL) ++ */ ++ ++/* 802.11 Mgmt Packet flags */ ++#define VNDR_IE_BEACON_FLAG 0x1 ++#define VNDR_IE_PRBRSP_FLAG 0x2 ++#define VNDR_IE_ASSOCRSP_FLAG 0x4 ++#define VNDR_IE_AUTHRSP_FLAG 0x8 ++#define VNDR_IE_PRBREQ_FLAG 0x10 ++#define VNDR_IE_ASSOCREQ_FLAG 0x20 ++#define VNDR_IE_IWAPID_FLAG 0x40 /* vendor IE in IW advertisement protocol ID field */ ++#define VNDR_IE_CUSTOM_FLAG 0x100 /* allow custom IE id */ ++ ++#define VNDR_IE_INFO_HDR_LEN (sizeof(uint32)) ++ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ uint32 pktflag; /* bitmask indicating which packet(s) contain this IE */ ++ vndr_ie_t vndr_ie_data; /* vendor IE data */ ++} BWL_POST_PACKED_STRUCT vndr_ie_info_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ int iecount; /* number of entries in the vndr_ie_list[] array */ ++ vndr_ie_info_t vndr_ie_list[1]; /* variable size list of vndr_ie_info_t structs */ ++} BWL_POST_PACKED_STRUCT vndr_ie_buf_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ char cmd[VNDR_IE_CMD_LEN]; /* vndr_ie IOVar set command : "add", "del" + NUL */ ++ vndr_ie_buf_t vndr_ie_buffer; /* buffer containing Vendor IE list information */ ++} BWL_POST_PACKED_STRUCT vndr_ie_setbuf_t; ++ ++/* tag_ID/length/value_buffer tuple */ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ uint8 id; ++ uint8 len; ++ uint8 data[1]; ++} BWL_POST_PACKED_STRUCT tlv_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ uint32 pktflag; /* bitmask indicating which packet(s) contain this IE */ ++ tlv_t ie_data; /* IE data */ ++} BWL_POST_PACKED_STRUCT ie_info_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ int iecount; /* number of entries in the ie_list[] array */ ++ ie_info_t ie_list[1]; /* variable size list of ie_info_t structs */ ++} BWL_POST_PACKED_STRUCT ie_buf_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ char cmd[VNDR_IE_CMD_LEN]; /* ie IOVar set command : "add" + NUL */ ++ ie_buf_t ie_buffer; /* buffer containing IE list information */ ++} BWL_POST_PACKED_STRUCT ie_setbuf_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct { ++ uint32 pktflag; /* bitmask indicating which packet(s) contain this IE */ ++ uint8 id; /* IE type */ ++} BWL_POST_PACKED_STRUCT ie_getbuf_t; ++ ++/* structures used to define format of wps ie data from probe requests */ ++/* passed up to applications via iovar "prbreq_wpsie" */ ++typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_hdr { ++ struct ether_addr staAddr; ++ uint16 ieLen; ++} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_hdr_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_data { ++ sta_prbreq_wps_ie_hdr_t hdr; ++ uint8 ieData[1]; ++} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_data_t; ++ ++typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_list { ++ uint32 totLen; ++ uint8 ieDataList[1]; ++} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_list_t; ++ ++ ++#ifdef WLMEDIA_TXFAILEVENT ++typedef BWL_PRE_PACKED_STRUCT struct { ++ char dest[ETHER_ADDR_LEN]; /* destination MAC */ ++ uint8 prio; /* Packet Priority */ ++ uint8 flags; /* Flags */ ++ uint32 tsf_l; /* TSF timer low */ ++ uint32 tsf_h; /* TSF timer high */ ++ uint16 rates; /* Main Rates */ ++ uint16 txstatus; /* TX Status */ ++} BWL_POST_PACKED_STRUCT txfailinfo_t; ++#endif /* WLMEDIA_TXFAILEVENT */ ++ ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ ++/* no strict structure packing */ ++#include ++ ++#ifndef LINUX_POSTMOGRIFY_REMOVAL ++/* Global ASSERT Logging */ ++#define ASSERTLOG_CUR_VER 0x0100 ++#define MAX_ASSRTSTR_LEN 64 ++ ++typedef struct assert_record { ++ uint32 time; ++ uint8 seq_num; ++ char str[MAX_ASSRTSTR_LEN]; ++} assert_record_t; ++ ++typedef struct assertlog_results { ++ uint16 version; ++ uint16 record_len; ++ uint32 num; ++ assert_record_t logs[1]; ++} assertlog_results_t; ++ ++#define LOGRRC_FIX_LEN 8 ++#define IOBUF_ALLOWED_NUM_OF_LOGREC(type, len) ((len - LOGRRC_FIX_LEN)/sizeof(type)) ++ ++ ++/* channel interference measurement (chanim) related defines */ ++ ++/* chanim mode */ ++#define CHANIM_DISABLE 0 /* disabled */ ++#define CHANIM_DETECT 1 /* detection only */ ++#define CHANIM_EXT 2 /* external state machine */ ++#define CHANIM_ACT 3 /* full internal state machine, detect + act */ ++#define CHANIM_MODE_MAX 4 ++ ++/* define for apcs reason code */ ++#define APCS_INIT 0 ++#define APCS_IOCTL 1 ++#define APCS_CHANIM 2 ++#define APCS_CSTIMER 3 ++#define APCS_BTA 4 ++ ++/* number of ACS record entries */ ++#define CHANIM_ACS_RECORD 10 ++ ++/* CHANIM */ ++#define CCASTATS_TXDUR 0 ++#define CCASTATS_INBSS 1 ++#define CCASTATS_OBSS 2 ++#define CCASTATS_NOCTG 3 ++#define CCASTATS_NOPKT 4 ++#define CCASTATS_DOZE 5 ++#define CCASTATS_TXOP 6 ++#define CCASTATS_GDTXDUR 7 ++#define CCASTATS_BDTXDUR 8 ++#define CCASTATS_MAX 9 ++ ++/* chanim acs record */ ++typedef struct { ++ bool valid; ++ uint8 trigger; ++ chanspec_t selected_chspc; ++ int8 bgnoise; ++ uint32 glitch_cnt; ++ uint8 ccastats; ++ uint timestamp; ++} chanim_acs_record_t; ++ ++typedef struct { ++ chanim_acs_record_t acs_record[CHANIM_ACS_RECORD]; ++ uint8 count; ++ uint timestamp; ++} wl_acs_record_t; ++ ++typedef struct chanim_stats { ++ uint32 glitchcnt; /* normalized as per second count */ ++ uint32 badplcp; /* normalized as per second count */ ++ uint8 ccastats[CCASTATS_MAX]; /* normalized as 0-255 */ ++ int8 bgnoise; /* background noise level (in dBm) */ ++ chanspec_t chanspec; ++ uint32 timestamp; ++} chanim_stats_t; ++ ++#define WL_CHANIM_STATS_VERSION 1 ++#define WL_CHANIM_COUNT_ALL 0xff ++#define WL_CHANIM_COUNT_ONE 0x1 ++ ++typedef struct { ++ uint32 buflen; ++ uint32 version; ++ uint32 count; ++ chanim_stats_t stats[1]; ++} wl_chanim_stats_t; ++ ++#define WL_CHANIM_STATS_FIXED_LEN OFFSETOF(wl_chanim_stats_t, stats) ++ ++/* Noise measurement metrics. */ ++#define NOISE_MEASURE_KNOISE 0x1 ++ ++/* scb probe parameter */ ++typedef struct { ++ uint32 scb_timeout; ++ uint32 scb_activity_time; ++ uint32 scb_max_probe; ++} wl_scb_probe_t; ++ ++/* ap tpc modes */ ++#define AP_TPC_OFF 0 ++#define AP_TPC_BSS_PWR 1 /* BSS power control */ ++#define AP_TPC_AP_PWR 2 /* AP power control */ ++#define AP_TPC_AP_BSS_PWR 3 /* Both AP and BSS power control */ ++#define AP_TPC_MAX_LINK_MARGIN 127 ++ ++/* ap tpc modes */ ++#define AP_TPC_OFF 0 ++#define AP_TPC_BSS_PWR 1 /* BSS power control */ ++#define AP_TPC_AP_PWR 2 /* AP power control */ ++#define AP_TPC_AP_BSS_PWR 3 /* Both AP and BSS power control */ ++#define AP_TPC_MAX_LINK_MARGIN 127 ++ ++/* structure/defines for selective mgmt frame (smf) stats support */ ++ ++#define SMFS_VERSION 1 ++/* selected mgmt frame (smf) stats element */ ++typedef struct wl_smfs_elem { ++ uint32 count; ++ uint16 code; /* SC or RC code */ ++} wl_smfs_elem_t; ++ ++typedef struct wl_smf_stats { ++ uint32 version; ++ uint16 length; /* reserved for future usage */ ++ uint8 type; ++ uint8 codetype; ++ uint32 ignored_cnt; ++ uint32 malformed_cnt; ++ uint32 count_total; /* count included the interested group */ ++ wl_smfs_elem_t elem[1]; ++} wl_smf_stats_t; ++ ++#define WL_SMFSTATS_FIXED_LEN OFFSETOF(wl_smf_stats_t, elem); ++ ++enum { ++ SMFS_CODETYPE_SC, ++ SMFS_CODETYPE_RC ++}; ++ ++/* reuse two number in the sc/rc space */ ++#define SMFS_CODE_MALFORMED 0xFFFE ++#define SMFS_CODE_IGNORED 0xFFFD ++ ++typedef enum smfs_type { ++ SMFS_TYPE_AUTH, ++ SMFS_TYPE_ASSOC, ++ SMFS_TYPE_REASSOC, ++ SMFS_TYPE_DISASSOC_TX, ++ SMFS_TYPE_DISASSOC_RX, ++ SMFS_TYPE_DEAUTH_TX, ++ SMFS_TYPE_DEAUTH_RX, ++ SMFS_TYPE_MAX ++} smfs_type_t; ++ ++#ifdef PHYMON ++ ++#define PHYMON_VERSION 1 ++ ++typedef struct wl_phycal_core_state { ++ /* Tx IQ/LO calibration coeffs */ ++ int16 tx_iqlocal_a; ++ int16 tx_iqlocal_b; ++ int8 tx_iqlocal_ci; ++ int8 tx_iqlocal_cq; ++ int8 tx_iqlocal_di; ++ int8 tx_iqlocal_dq; ++ int8 tx_iqlocal_ei; ++ int8 tx_iqlocal_eq; ++ int8 tx_iqlocal_fi; ++ int8 tx_iqlocal_fq; ++ ++ /* Rx IQ calibration coeffs */ ++ int16 rx_iqcal_a; ++ int16 rx_iqcal_b; ++ ++ uint8 tx_iqlocal_pwridx; /* Tx Power Index for Tx IQ/LO calibration */ ++ uint32 papd_epsilon_table[64]; /* PAPD epsilon table */ ++ int16 papd_epsilon_offset; /* PAPD epsilon offset */ ++ uint8 curr_tx_pwrindex; /* Tx power index */ ++ int8 idle_tssi; /* Idle TSSI */ ++ int8 est_tx_pwr; /* Estimated Tx Power (dB) */ ++ int8 est_rx_pwr; /* Estimated Rx Power (dB) from RSSI */ ++ uint16 rx_gaininfo; /* Rx gain applied on last Rx pkt */ ++ uint16 init_gaincode; /* initgain required for ACI */ ++ int8 estirr_tx; ++ int8 estirr_rx; ++ ++} wl_phycal_core_state_t; ++ ++typedef struct wl_phycal_state { ++ int version; ++ int8 num_phy_cores; /* number of cores */ ++ int8 curr_temperature; /* on-chip temperature sensor reading */ ++ chanspec_t chspec; /* channspec for this state */ ++ bool aci_state; /* ACI state: ON/OFF */ ++ uint16 crsminpower; /* crsminpower required for ACI */ ++ uint16 crsminpowerl; /* crsminpowerl required for ACI */ ++ uint16 crsminpoweru; /* crsminpoweru required for ACI */ ++ wl_phycal_core_state_t phycal_core[1]; ++} wl_phycal_state_t; ++ ++#define WL_PHYCAL_STAT_FIXED_LEN OFFSETOF(wl_phycal_state_t, phycal_core) ++#endif /* PHYMON */ ++ ++/* discovery state */ ++typedef struct wl_p2p_disc_st { ++ uint8 state; /* see state */ ++ chanspec_t chspec; /* valid in listen state */ ++ uint16 dwell; /* valid in listen state, in ms */ ++} wl_p2p_disc_st_t; ++ ++/* state */ ++#define WL_P2P_DISC_ST_SCAN 0 ++#define WL_P2P_DISC_ST_LISTEN 1 ++#define WL_P2P_DISC_ST_SEARCH 2 ++ ++/* scan request */ ++typedef struct wl_p2p_scan { ++ uint8 type; /* 'S' for WLC_SCAN, 'E' for "escan" */ ++ uint8 reserved[3]; ++ /* scan or escan parms... */ ++} wl_p2p_scan_t; ++ ++/* i/f request */ ++typedef struct wl_p2p_if { ++ struct ether_addr addr; ++ uint8 type; /* see i/f type */ ++ chanspec_t chspec; /* for p2p_ifadd GO */ ++} wl_p2p_if_t; ++ ++/* i/f type */ ++#define WL_P2P_IF_CLIENT 0 ++#define WL_P2P_IF_GO 1 ++#define WL_P2P_IF_DYNBCN_GO 2 ++#define WL_P2P_IF_DEV 3 ++ ++/* i/f query */ ++typedef struct wl_p2p_ifq { ++ uint bsscfgidx; ++ char ifname[BCM_MSG_IFNAME_MAX]; ++} wl_p2p_ifq_t; ++ ++/* OppPS & CTWindow */ ++typedef struct wl_p2p_ops { ++ uint8 ops; /* 0: disable 1: enable */ ++ uint8 ctw; /* >= 10 */ ++} wl_p2p_ops_t; ++ ++/* absence and presence request */ ++typedef struct wl_p2p_sched_desc { ++ uint32 start; ++ uint32 interval; ++ uint32 duration; ++ uint32 count; /* see count */ ++} wl_p2p_sched_desc_t; ++ ++/* count */ ++#define WL_P2P_SCHED_RSVD 0 ++#define WL_P2P_SCHED_REPEAT 255 /* anything > 255 will be treated as 255 */ ++ ++typedef struct wl_p2p_sched { ++ uint8 type; /* see schedule type */ ++ uint8 action; /* see schedule action */ ++ uint8 option; /* see schedule option */ ++ wl_p2p_sched_desc_t desc[1]; ++} wl_p2p_sched_t; ++#define WL_P2P_SCHED_FIXED_LEN 3 ++ ++/* schedule type */ ++#define WL_P2P_SCHED_TYPE_ABS 0 /* Scheduled Absence */ ++#define WL_P2P_SCHED_TYPE_REQ_ABS 1 /* Requested Absence */ ++ ++/* schedule action during absence periods (for WL_P2P_SCHED_ABS type) */ ++#define WL_P2P_SCHED_ACTION_NONE 0 /* no action */ ++#define WL_P2P_SCHED_ACTION_DOZE 1 /* doze */ ++/* schedule option - WL_P2P_SCHED_TYPE_REQ_ABS */ ++#define WL_P2P_SCHED_ACTION_GOOFF 2 /* turn off GO beacon/prbrsp functions */ ++/* schedule option - WL_P2P_SCHED_TYPE_XXX */ ++#define WL_P2P_SCHED_ACTION_RESET 255 /* reset */ ++ ++/* schedule option - WL_P2P_SCHED_TYPE_ABS */ ++#define WL_P2P_SCHED_OPTION_NORMAL 0 /* normal start/interval/duration/count */ ++#define WL_P2P_SCHED_OPTION_BCNPCT 1 /* percentage of beacon interval */ ++/* schedule option - WL_P2P_SCHED_TYPE_REQ_ABS */ ++#define WL_P2P_SCHED_OPTION_TSFOFS 2 /* normal start/internal/duration/count with ++ * start being an offset of the 'current' TSF ++ */ ++ ++/* feature flags */ ++#define WL_P2P_FEAT_GO_CSA (1 << 0) /* GO moves with the STA using CSA method */ ++#define WL_P2P_FEAT_GO_NOLEGACY (1 << 1) /* GO does not probe respond to non-p2p probe ++ * requests ++ */ ++#define WL_P2P_FEAT_RESTRICT_DEV_RESP (1 << 2) /* Restrict p2p dev interface from responding */ ++ ++#ifdef WLNIC ++/* nic_cnx iovar */ ++typedef struct wl_nic_cnx { ++ uint8 opcode; ++ struct ether_addr addr; ++ /* the following are valid for WL_NIC_CNX_CONN */ ++ uint8 SSID_len; ++ uint8 SSID[32]; ++ struct ether_addr abssid; ++ uint8 join_period; ++} wl_nic_cnx_t; ++ ++/* opcode */ ++#define WL_NIC_CNX_ADD 0 /* add NIC connection */ ++#define WL_NIC_CNX_DEL 1 /* delete NIC connection */ ++#define WL_NIC_CNX_IDX 2 /* query NIC connection index */ ++#define WL_NIC_CNX_CONN 3 /* join/create network */ ++#define WL_NIC_CNX_DIS 4 /* disconnect from network */ ++ ++/* nic_cfg iovar */ ++typedef struct wl_nic_cfg { ++ uint8 version; ++ uint8 beacon_mode; ++ uint16 beacon_interval; ++ uint8 diluted_beacon_period; ++ uint8 repeat_EQC; ++ uint8 scan_length; ++ uint8 scan_interval; ++ uint8 scan_probability; ++ uint8 awake_window_length; ++ int8 TSF_correction; ++ uint8 ASID; ++ uint8 channel_usage_mode; ++} wl_nic_cfg_t; ++ ++/* version */ ++#define WL_NIC_CFG_VER 1 ++ ++/* beacon_mode */ ++#define WL_NIC_BCN_NORM 0 ++#define WL_NIC_BCN_DILUTED 1 ++ ++/* channel_usage_mode */ ++#define WL_NIC_CHAN_STATIC 0 ++#define WL_NIC_CHAN_CYCLE 1 ++ ++/* nic_cfg iovar */ ++typedef struct wl_nic_frm { ++ uint8 type; ++ struct ether_addr da; ++ uint8 body[1]; ++} wl_nic_frm_t; ++ ++/* type */ ++#define WL_NIC_FRM_MYNET 1 ++#define WL_NIC_FRM_ACTION 2 ++ ++/* i/f query */ ++typedef struct wl_nic_ifq { ++ uint bsscfgidx; ++ char ifname[BCM_MSG_IFNAME_MAX]; ++} wl_nic_ifq_t; ++ ++/* data mode */ ++/* nic_dm iovar */ ++typedef struct wl_nic_dm { ++ uint8 enab; ++ chanspec_t chspec; ++} wl_nic_dm_t; ++#endif /* WLNIC */ ++ ++/* RFAWARE def */ ++#define BCM_ACTION_RFAWARE 0x77 ++#define BCM_ACTION_RFAWARE_DCS 0x01 ++ ++/* DCS reason code define */ ++#define BCM_DCS_IOVAR 0x1 ++#define BCM_DCS_UNKNOWN 0xFF ++ ++typedef struct wl_bcmdcs_data { ++ uint reason; ++ chanspec_t chspec; ++} wl_bcmdcs_data_t; ++ ++/* n-mode support capability */ ++/* 2x2 includes both 1x1 & 2x2 devices ++ * reserved #define 2 for future when we want to separate 1x1 & 2x2 and ++ * control it independently ++ */ ++#define WL_11N_2x2 1 ++#define WL_11N_3x3 3 ++#define WL_11N_4x4 4 ++ ++/* define 11n feature disable flags */ ++#define WLFEATURE_DISABLE_11N 0x00000001 ++#define WLFEATURE_DISABLE_11N_STBC_TX 0x00000002 ++#define WLFEATURE_DISABLE_11N_STBC_RX 0x00000004 ++#define WLFEATURE_DISABLE_11N_SGI_TX 0x00000008 ++#define WLFEATURE_DISABLE_11N_SGI_RX 0x00000010 ++#define WLFEATURE_DISABLE_11N_AMPDU_TX 0x00000020 ++#define WLFEATURE_DISABLE_11N_AMPDU_RX 0x00000040 ++#define WLFEATURE_DISABLE_11N_GF 0x00000080 ++ ++/* Proxy STA modes */ ++#define PSTA_MODE_DISABLED 0 ++#define PSTA_MODE_PROXY 1 ++#define PSTA_MODE_REPEATER 2 ++ ++ ++/* NAT configuration */ ++typedef struct { ++ uint32 ipaddr; /* interface ip address */ ++ uint32 ipaddr_mask; /* interface ip address mask */ ++ uint32 ipaddr_gateway; /* gateway ip address */ ++ uint8 mac_gateway[6]; /* gateway mac address */ ++ uint32 ipaddr_dns; /* DNS server ip address, valid only for public if */ ++ uint8 mac_dns[6]; /* DNS server mac address, valid only for public if */ ++ uint8 GUID[38]; /* interface GUID */ ++} nat_if_info_t; ++ ++typedef struct { ++ uint op; /* operation code */ ++ bool pub_if; /* set for public if, clear for private if */ ++ nat_if_info_t if_info; /* interface info */ ++} nat_cfg_t; ++ ++/* op code in nat_cfg */ ++#define NAT_OP_ENABLE 1 /* enable NAT on given interface */ ++#define NAT_OP_DISABLE 2 /* disable NAT on given interface */ ++#define NAT_OP_DISABLE_ALL 3 /* disable NAT on all interfaces */ ++ ++/* NAT state */ ++#define NAT_STATE_ENABLED 1 /* NAT is enabled */ ++#define NAT_STATE_DISABLED 2 /* NAT is disabled */ ++ ++typedef struct { ++ int state; /* NAT state returned */ ++} nat_state_t; ++ ++#ifdef PROP_TXSTATUS ++/* Bit definitions for tlv iovar */ ++/* ++ * enable RSSI signals: ++ * WLFC_CTL_TYPE_RSSI ++ */ ++#define WLFC_FLAGS_RSSI_SIGNALS 0x0001 ++ ++/* enable (if/mac_open, if/mac_close,, mac_add, mac_del) signals: ++ * ++ * WLFC_CTL_TYPE_MAC_OPEN ++ * WLFC_CTL_TYPE_MAC_CLOSE ++ * ++ * WLFC_CTL_TYPE_INTERFACE_OPEN ++ * WLFC_CTL_TYPE_INTERFACE_CLOSE ++ * ++ * WLFC_CTL_TYPE_MACDESC_ADD ++ * WLFC_CTL_TYPE_MACDESC_DEL ++ * ++ */ ++#define WLFC_FLAGS_XONXOFF_SIGNALS 0x0002 ++ ++/* enable (status, fifo_credit, mac_credit) signals ++ * WLFC_CTL_TYPE_MAC_REQUEST_CREDIT ++ * WLFC_CTL_TYPE_TXSTATUS ++ * WLFC_CTL_TYPE_FIFO_CREDITBACK ++ */ ++#define WLFC_FLAGS_CREDIT_STATUS_SIGNALS 0x0004 ++ ++#define WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008 ++#define WLFC_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010 ++#define WLFC_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020 ++#define WLFC_FLAGS_HOST_RXRERODER_ACTIVE 0x0040 ++#endif /* PROP_TXSTATUS */ ++ ++#define BTA_STATE_LOG_SZ 64 ++ ++/* BTAMP Statemachine states */ ++enum { ++ HCIReset = 1, ++ HCIReadLocalAMPInfo, ++ HCIReadLocalAMPASSOC, ++ HCIWriteRemoteAMPASSOC, ++ HCICreatePhysicalLink, ++ HCIAcceptPhysicalLinkRequest, ++ HCIDisconnectPhysicalLink, ++ HCICreateLogicalLink, ++ HCIAcceptLogicalLink, ++ HCIDisconnectLogicalLink, ++ HCILogicalLinkCancel, ++ HCIAmpStateChange, ++ HCIWriteLogicalLinkAcceptTimeout ++}; ++ ++typedef struct flush_txfifo { ++ uint32 txfifobmp; ++ uint32 hwtxfifoflush; ++ struct ether_addr ea; ++} flush_txfifo_t; ++ ++#define CHANNEL_5G_LOW_START 36 /* 5G low (36..48) CDD enable/disable bit mask */ ++#define CHANNEL_5G_MID_START 52 /* 5G mid (52..64) CDD enable/disable bit mask */ ++#define CHANNEL_5G_HIGH_START 100 /* 5G high (100..140) CDD enable/disable bit mask */ ++#define CHANNEL_5G_UPPER_START 149 /* 5G upper (149..161) CDD enable/disable bit mask */ ++ ++enum { ++ SPATIAL_MODE_2G_IDX = 0, ++ SPATIAL_MODE_5G_LOW_IDX, ++ SPATIAL_MODE_5G_MID_IDX, ++ SPATIAL_MODE_5G_HIGH_IDX, ++ SPATIAL_MODE_5G_UPPER_IDX, ++ SPATIAL_MODE_MAX_IDX ++}; ++ ++/* IOVAR "mempool" parameter. Used to retrieve a list of memory pool statistics. */ ++typedef struct wl_mempool_stats { ++ int num; /* Number of memory pools */ ++ bcm_mp_stats_t s[1]; /* Variable array of memory pool stats. */ ++} wl_mempool_stats_t; ++ ++ ++/* D0 Coalescing */ ++#define IPV4_ARP_FILTER 0x0001 ++#define IPV4_NETBT_FILTER 0x0002 ++#define IPV4_LLMNR_FILTER 0x0004 ++#define IPV4_SSDP_FILTER 0x0008 ++#define IPV4_WSD_FILTER 0x0010 ++#define IPV6_NETBT_FILTER 0x0200 ++#define IPV6_LLMNR_FILTER 0x0400 ++#define IPV6_SSDP_FILTER 0x0800 ++#define IPV6_WSD_FILTER 0x1000 ++ ++/* Network Offload Engine */ ++#define NWOE_OL_ENABLE 0x00000001 ++ ++typedef struct { ++ uint32 ipaddr; ++ uint32 ipaddr_netmask; ++ uint32 ipaddr_gateway; ++} nwoe_ifconfig_t; ++ ++/* ++ * Traffic management structures/defines. ++ */ ++ ++/* Traffic management bandwidth parameters */ ++#define TRF_MGMT_MAX_PRIORITIES 3 ++ ++#define TRF_MGMT_FLAG_ADD_DSCP 0x0001 /* Add DSCP to IP TOS field */ ++#define TRF_MGMT_FLAG_DISABLE_SHAPING 0x0002 /* Only support traffic clasification */ ++#define TRF_MGMT_FLAG_DISABLE_PRIORITY_TAGGING 0x0004 /* Don't override packet's priority */ ++ ++/* Traffic management priority classes */ ++typedef enum trf_mgmt_priority_class { ++ trf_mgmt_priority_low = 0, /* Maps to 802.1p BO */ ++ trf_mgmt_priority_medium = 1, /* Maps to 802.1p BE */ ++ trf_mgmt_priority_high = 2, /* Maps to 802.1p VI */ ++ trf_mgmt_priority_invalid = (trf_mgmt_priority_high + 1) ++} trf_mgmt_priority_class_t; ++ ++/* Traffic management configuration parameters */ ++typedef struct trf_mgmt_config { ++ uint32 trf_mgmt_enabled; /* 0 - disabled, 1 - enabled */ ++ uint32 flags; /* See TRF_MGMT_FLAG_xxx defines */ ++ uint32 host_ip_addr; /* My IP address to determine subnet */ ++ uint32 host_subnet_mask; /* My subnet mask */ ++ uint32 downlink_bandwidth; /* In units of kbps */ ++ uint32 uplink_bandwidth; /* In units of kbps */ ++ uint32 min_tx_bandwidth[TRF_MGMT_MAX_PRIORITIES]; /* Minimum guaranteed tx bandwidth */ ++ uint32 min_rx_bandwidth[TRF_MGMT_MAX_PRIORITIES]; /* Minimum guaranteed rx bandwidth */ ++} trf_mgmt_config_t; ++ ++/* Traffic management filter */ ++typedef struct trf_mgmt_filter { ++ struct ether_addr dst_ether_addr; /* His L2 address */ ++ uint32 dst_ip_addr; /* His IP address */ ++ uint16 dst_port; /* His L4 port */ ++ uint16 src_port; /* My L4 port */ ++ uint16 prot; /* L4 protocol (only TCP or UDP) */ ++ uint16 flags; /* TBD. For now, this must be zero. */ ++ trf_mgmt_priority_class_t priority; /* Priority for filtered packets */ ++} trf_mgmt_filter_t; ++ ++/* Traffic management filter list (variable length) */ ++typedef struct trf_mgmt_filter_list { ++ uint32 num_filters; ++ trf_mgmt_filter_t filter[1]; ++} trf_mgmt_filter_list_t; ++ ++/* Traffic management global info used for all queues */ ++typedef struct trf_mgmt_global_info { ++ uint32 maximum_bytes_per_second; ++ uint32 maximum_bytes_per_sampling_period; ++ uint32 total_bytes_consumed_per_second; ++ uint32 total_bytes_consumed_per_sampling_period; ++ uint32 total_unused_bytes_per_sampling_period; ++} trf_mgmt_global_info_t; ++ ++/* Traffic management shaping info per priority queue */ ++typedef struct trf_mgmt_shaping_info { ++ uint32 gauranteed_bandwidth_percentage; ++ uint32 guaranteed_bytes_per_second; ++ uint32 guaranteed_bytes_per_sampling_period; ++ uint32 num_bytes_produced_per_second; ++ uint32 num_bytes_consumed_per_second; ++ uint32 num_queued_packets; /* Number of packets in queue */ ++ uint32 num_queued_bytes; /* Number of bytes in queue */ ++} trf_mgmt_shaping_info_t; ++ ++/* Traffic management shaping info array */ ++typedef struct trf_mgmt_shaping_info_array { ++ trf_mgmt_global_info_t tx_global_shaping_info; ++ trf_mgmt_shaping_info_t tx_queue_shaping_info[TRF_MGMT_MAX_PRIORITIES]; ++ trf_mgmt_global_info_t rx_global_shaping_info; ++ trf_mgmt_shaping_info_t rx_queue_shaping_info[TRF_MGMT_MAX_PRIORITIES]; ++} trf_mgmt_shaping_info_array_t; ++ ++ ++/* Traffic management statistical counters */ ++typedef struct trf_mgmt_stats { ++ uint32 num_processed_packets; /* Number of packets processed */ ++ uint32 num_processed_bytes; /* Number of bytes processed */ ++ uint32 num_discarded_packets; /* Number of packets discarded from queue */ ++} trf_mgmt_stats_t; ++ ++/* Traffic management statisics array */ ++typedef struct trf_mgmt_stats_array { ++ trf_mgmt_stats_t tx_queue_stats[TRF_MGMT_MAX_PRIORITIES]; ++ trf_mgmt_stats_t rx_queue_stats[TRF_MGMT_MAX_PRIORITIES]; ++} trf_mgmt_stats_array_t; ++ ++typedef struct powersel_params { ++ /* LPC Params exposed via IOVAR */ ++ int32 tp_ratio_thresh; /* Throughput ratio threshold */ ++ uint8 rate_stab_thresh; /* Thresh for rate stability based on nupd */ ++ uint8 pwr_stab_thresh; /* Number of successes before power step down */ ++ uint8 pwr_sel_exp_time; /* Time lapse for expiry of database */ ++} powersel_params_t; ++ ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++#endif /* _wlioctl_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/linux_osl.c b/drivers/net/wireless/bcmdhd/linux_osl.c +new file mode 100644 +index 00000000..93990d9b +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/linux_osl.c +@@ -0,0 +1,1126 @@ ++/* ++ * Linux OS Independent Layer ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: linux_osl.c 373382 2012-12-07 07:59:52Z $ ++ */ ++ ++#define LINUX_PORT ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include ++ ++#define PCI_CFG_RETRY 10 ++ ++#define OS_HANDLE_MAGIC 0x1234abcd /* Magic # to recognize osh */ ++#define BCM_MEM_FILENAME_LEN 24 /* Mem. filename length */ ++ ++#ifdef CONFIG_DHD_USE_STATIC_BUF ++#define DHD_SKB_HDRSIZE 336 ++#define DHD_SKB_1PAGE_BUFSIZE ((PAGE_SIZE*1)-DHD_SKB_HDRSIZE) ++#define DHD_SKB_2PAGE_BUFSIZE ((PAGE_SIZE*2)-DHD_SKB_HDRSIZE) ++#define DHD_SKB_4PAGE_BUFSIZE ((PAGE_SIZE*4)-DHD_SKB_HDRSIZE) ++ ++#define STATIC_BUF_MAX_NUM 16 ++#define STATIC_BUF_SIZE (PAGE_SIZE*2) ++#define STATIC_BUF_TOTAL_LEN (STATIC_BUF_MAX_NUM * STATIC_BUF_SIZE) ++ ++typedef struct bcm_static_buf { ++ struct semaphore static_sem; ++ unsigned char *buf_ptr; ++ unsigned char buf_use[STATIC_BUF_MAX_NUM]; ++} bcm_static_buf_t; ++ ++static bcm_static_buf_t *bcm_static_buf = 0; ++ ++#define STATIC_PKT_MAX_NUM 8 ++#if defined(ENHANCED_STATIC_BUF) ++#define STATIC_PKT_4PAGE_NUM 1 ++#define DHD_SKB_MAX_BUFSIZE DHD_SKB_4PAGE_BUFSIZE ++#else ++#define STATIC_PKT_4PAGE_NUM 0 ++#define DHD_SKB_MAX_BUFSIZE DHD_SKB_2PAGE_BUFSIZE ++#endif ++ ++typedef struct bcm_static_pkt { ++ struct sk_buff *skb_4k[STATIC_PKT_MAX_NUM]; ++ struct sk_buff *skb_8k[STATIC_PKT_MAX_NUM]; ++#ifdef ENHANCED_STATIC_BUF ++ struct sk_buff *skb_16k; ++#endif ++ struct semaphore osl_pkt_sem; ++ unsigned char pkt_use[STATIC_PKT_MAX_NUM * 2 + STATIC_PKT_4PAGE_NUM]; ++} bcm_static_pkt_t; ++ ++static bcm_static_pkt_t *bcm_static_skb = 0; ++#endif ++ ++typedef struct bcm_mem_link { ++ struct bcm_mem_link *prev; ++ struct bcm_mem_link *next; ++ uint size; ++ int line; ++ void *osh; ++ char file[BCM_MEM_FILENAME_LEN]; ++} bcm_mem_link_t; ++ ++struct osl_info { ++ osl_pubinfo_t pub; ++#ifdef CTFPOOL ++ ctfpool_t *ctfpool; ++#endif ++ uint magic; ++ void *pdev; ++ atomic_t malloced; ++ uint failed; ++ uint bustype; ++ bcm_mem_link_t *dbgmem_list; ++ spinlock_t dbgmem_lock; ++ spinlock_t pktalloc_lock; ++}; ++ ++ ++ ++ ++uint32 g_assert_type = FALSE; ++ ++static int16 linuxbcmerrormap[] = ++{ 0, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -E2BIG, ++ -E2BIG, ++ -EBUSY, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EFAULT, ++ -ENOMEM, ++ -EOPNOTSUPP, ++ -EMSGSIZE, ++ -EINVAL, ++ -EPERM, ++ -ENOMEM, ++ -EINVAL, ++ -ERANGE, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EINVAL, ++ -EIO, ++ -ENODEV, ++ -EINVAL, ++ -EIO, ++ -EIO, ++ -ENODEV, ++ -EINVAL, ++ -ENODATA, ++ ++ ++ ++#if BCME_LAST != -42 ++#error "You need to add a OS error translation in the linuxbcmerrormap \ ++ for new error code defined in bcmutils.h" ++#endif ++}; ++ ++ ++int ++osl_error(int bcmerror) ++{ ++ if (bcmerror > 0) ++ bcmerror = 0; ++ else if (bcmerror < BCME_LAST) ++ bcmerror = BCME_ERROR; ++ ++ ++ return linuxbcmerrormap[-bcmerror]; ++} ++ ++extern uint8* dhd_os_prealloc(void *osh, int section, int size); ++ ++osl_t * ++osl_attach(void *pdev, uint bustype, bool pkttag) ++{ ++ osl_t *osh; ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ gfp_t flags; ++ ++ flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; ++ osh = kmalloc(sizeof(osl_t), flags); ++#else ++ osh = kmalloc(sizeof(osl_t), GFP_ATOMIC); ++#endif ++ ++ ASSERT(osh); ++ ++ bzero(osh, sizeof(osl_t)); ++ ++ ++ ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(linuxbcmerrormap) - 1)); ++ ++ osh->magic = OS_HANDLE_MAGIC; ++ atomic_set(&osh->malloced, 0); ++ osh->failed = 0; ++ osh->dbgmem_list = NULL; ++ spin_lock_init(&(osh->dbgmem_lock)); ++ osh->pdev = pdev; ++ osh->pub.pkttag = pkttag; ++ osh->bustype = bustype; ++ ++ switch (bustype) { ++ case PCI_BUS: ++ case SI_BUS: ++ case PCMCIA_BUS: ++ osh->pub.mmbus = TRUE; ++ break; ++ case JTAG_BUS: ++ case SDIO_BUS: ++ case USB_BUS: ++ case SPI_BUS: ++ case RPC_BUS: ++ osh->pub.mmbus = FALSE; ++ break; ++ default: ++ ASSERT(FALSE); ++ break; ++ } ++ ++#if defined(CONFIG_DHD_USE_STATIC_BUF) ++ if (!bcm_static_buf) { ++ if (!(bcm_static_buf = (bcm_static_buf_t *)dhd_os_prealloc(osh, 3, STATIC_BUF_SIZE+ ++ STATIC_BUF_TOTAL_LEN))) { ++ printk("can not alloc static buf!\n"); ++ } ++ else ++ printk("alloc static buf at %x!\n", (unsigned int)bcm_static_buf); ++ ++ ++ sema_init(&bcm_static_buf->static_sem, 1); ++ ++ bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE; ++ } ++ ++ if (!bcm_static_skb) { ++ int i; ++ void *skb_buff_ptr = 0; ++ bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048); ++ skb_buff_ptr = dhd_os_prealloc(osh, 4, 0); ++ ++ bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *)* ++ (STATIC_PKT_MAX_NUM * 2 + STATIC_PKT_4PAGE_NUM)); ++ for (i = 0; i < (STATIC_PKT_MAX_NUM * 2 + STATIC_PKT_4PAGE_NUM); i++) ++ bcm_static_skb->pkt_use[i] = 0; ++ ++ sema_init(&bcm_static_skb->osl_pkt_sem, 1); ++ } ++#endif ++ ++ spin_lock_init(&(osh->pktalloc_lock)); ++ ++ return osh; ++} ++ ++void ++osl_detach(osl_t *osh) ++{ ++ if (osh == NULL) ++ return; ++ ++#ifdef CONFIG_DHD_USE_STATIC_BUF ++ if (bcm_static_buf) { ++ bcm_static_buf = 0; ++ } ++ if (bcm_static_skb) { ++ bcm_static_skb = 0; ++ } ++#endif ++ ++ ASSERT(osh->magic == OS_HANDLE_MAGIC); ++ kfree(osh); ++} ++ ++static struct sk_buff *osl_alloc_skb(unsigned int len) ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) ++ gfp_t flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; ++ ++ return __dev_alloc_skb(len, flags); ++#else ++ return dev_alloc_skb(len); ++#endif ++} ++ ++#ifdef CTFPOOL ++ ++#ifdef CTFPOOL_SPINLOCK ++#define CTFPOOL_LOCK(ctfpool, flags) spin_lock_irqsave(&(ctfpool)->lock, flags) ++#define CTFPOOL_UNLOCK(ctfpool, flags) spin_unlock_irqrestore(&(ctfpool)->lock, flags) ++#else ++#define CTFPOOL_LOCK(ctfpool, flags) spin_lock_bh(&(ctfpool)->lock) ++#define CTFPOOL_UNLOCK(ctfpool, flags) spin_unlock_bh(&(ctfpool)->lock) ++#endif ++ ++void * ++osl_ctfpool_add(osl_t *osh) ++{ ++ struct sk_buff *skb; ++#ifdef CTFPOOL_SPINLOCK ++ unsigned long flags; ++#endif ++ ++ if ((osh == NULL) || (osh->ctfpool == NULL)) ++ return NULL; ++ ++ CTFPOOL_LOCK(osh->ctfpool, flags); ++ ASSERT(osh->ctfpool->curr_obj <= osh->ctfpool->max_obj); ++ ++ ++ if (osh->ctfpool->curr_obj == osh->ctfpool->max_obj) { ++ CTFPOOL_UNLOCK(osh->ctfpool, flags); ++ return NULL; ++ } ++ ++ ++ skb = osl_alloc_skb(osh->ctfpool->obj_size); ++ if (skb == NULL) { ++ printf("%s: skb alloc of len %d failed\n", __FUNCTION__, ++ osh->ctfpool->obj_size); ++ CTFPOOL_UNLOCK(osh->ctfpool, flags); ++ return NULL; ++ } ++ ++ ++ skb->next = (struct sk_buff *)osh->ctfpool->head; ++ osh->ctfpool->head = skb; ++ osh->ctfpool->fast_frees++; ++ osh->ctfpool->curr_obj++; ++ ++ ++ CTFPOOLPTR(osh, skb) = (void *)osh->ctfpool; ++ ++ ++ PKTFAST(osh, skb) = FASTBUF; ++ ++ CTFPOOL_UNLOCK(osh->ctfpool, flags); ++ ++ return skb; ++} ++ ++ ++void ++osl_ctfpool_replenish(osl_t *osh, uint thresh) ++{ ++ if ((osh == NULL) || (osh->ctfpool == NULL)) ++ return; ++ ++ ++ while ((osh->ctfpool->refills > 0) && (thresh--)) { ++ osl_ctfpool_add(osh); ++ osh->ctfpool->refills--; ++ } ++} ++ ++ ++int32 ++osl_ctfpool_init(osl_t *osh, uint numobj, uint size) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ gfp_t flags; ++ ++ flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; ++ osh->ctfpool = kmalloc(sizeof(ctfpool_t), flags); ++#else ++ osh->ctfpool = kmalloc(sizeof(ctfpool_t), GFP_ATOMIC); ++#endif ++ ASSERT(osh->ctfpool); ++ bzero(osh->ctfpool, sizeof(ctfpool_t)); ++ ++ osh->ctfpool->max_obj = numobj; ++ osh->ctfpool->obj_size = size; ++ ++ spin_lock_init(&osh->ctfpool->lock); ++ ++ while (numobj--) { ++ if (!osl_ctfpool_add(osh)) ++ return -1; ++ osh->ctfpool->fast_frees--; ++ } ++ ++ return 0; ++} ++ ++ ++void ++osl_ctfpool_cleanup(osl_t *osh) ++{ ++ struct sk_buff *skb, *nskb; ++#ifdef CTFPOOL_SPINLOCK ++ unsigned long flags; ++#endif ++ ++ if ((osh == NULL) || (osh->ctfpool == NULL)) ++ return; ++ ++ CTFPOOL_LOCK(osh->ctfpool, flags); ++ ++ skb = osh->ctfpool->head; ++ ++ while (skb != NULL) { ++ nskb = skb->next; ++ dev_kfree_skb(skb); ++ skb = nskb; ++ osh->ctfpool->curr_obj--; ++ } ++ ++ ASSERT(osh->ctfpool->curr_obj == 0); ++ osh->ctfpool->head = NULL; ++ CTFPOOL_UNLOCK(osh->ctfpool, flags); ++ ++ kfree(osh->ctfpool); ++ osh->ctfpool = NULL; ++} ++ ++void ++osl_ctfpool_stats(osl_t *osh, void *b) ++{ ++ struct bcmstrbuf *bb; ++ ++ if ((osh == NULL) || (osh->ctfpool == NULL)) ++ return; ++ ++#ifdef CONFIG_DHD_USE_STATIC_BUF ++ if (bcm_static_buf) { ++ bcm_static_buf = 0; ++ } ++ if (bcm_static_skb) { ++ bcm_static_skb = 0; ++ } ++#endif ++ ++ bb = b; ++ ++ ASSERT((osh != NULL) && (bb != NULL)); ++ ++ bcm_bprintf(bb, "max_obj %d obj_size %d curr_obj %d refills %d\n", ++ osh->ctfpool->max_obj, osh->ctfpool->obj_size, ++ osh->ctfpool->curr_obj, osh->ctfpool->refills); ++ bcm_bprintf(bb, "fast_allocs %d fast_frees %d slow_allocs %d\n", ++ osh->ctfpool->fast_allocs, osh->ctfpool->fast_frees, ++ osh->ctfpool->slow_allocs); ++} ++ ++static inline struct sk_buff * ++osl_pktfastget(osl_t *osh, uint len) ++{ ++ struct sk_buff *skb; ++#ifdef CTFPOOL_SPINLOCK ++ unsigned long flags; ++#endif ++ ++ ++ if (osh->ctfpool == NULL) ++ return NULL; ++ ++ CTFPOOL_LOCK(osh->ctfpool, flags); ++ if (osh->ctfpool->head == NULL) { ++ ASSERT(osh->ctfpool->curr_obj == 0); ++ osh->ctfpool->slow_allocs++; ++ CTFPOOL_UNLOCK(osh->ctfpool, flags); ++ return NULL; ++ } ++ ++ ASSERT(len <= osh->ctfpool->obj_size); ++ ++ ++ skb = (struct sk_buff *)osh->ctfpool->head; ++ osh->ctfpool->head = (void *)skb->next; ++ ++ osh->ctfpool->fast_allocs++; ++ osh->ctfpool->curr_obj--; ++ ASSERT(CTFPOOLHEAD(osh, skb) == (struct sock *)osh->ctfpool->head); ++ CTFPOOL_UNLOCK(osh->ctfpool, flags); ++ ++ ++ skb->next = skb->prev = NULL; ++ skb->data = skb->head + 16; ++ skb->tail = skb->head + 16; ++ ++ skb->len = 0; ++ skb->cloned = 0; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) ++ skb->list = NULL; ++#endif ++ atomic_set(&skb->users, 1); ++ ++ return skb; ++} ++#endif ++ ++struct sk_buff * BCMFASTPATH ++osl_pkt_tonative(osl_t *osh, void *pkt) ++{ ++#ifndef WL_UMK ++ struct sk_buff *nskb; ++ unsigned long flags; ++#endif ++ ++ if (osh->pub.pkttag) ++ bzero((void*)((struct sk_buff *)pkt)->cb, OSL_PKTTAG_SZ); ++ ++#ifndef WL_UMK ++ ++ for (nskb = (struct sk_buff *)pkt; nskb; nskb = nskb->next) { ++ spin_lock_irqsave(&osh->pktalloc_lock, flags); ++ osh->pub.pktalloced--; ++ spin_unlock_irqrestore(&osh->pktalloc_lock, flags); ++ } ++#endif ++ return (struct sk_buff *)pkt; ++} ++ ++ ++void * BCMFASTPATH ++osl_pkt_frmnative(osl_t *osh, void *pkt) ++{ ++#ifndef WL_UMK ++ struct sk_buff *nskb; ++ unsigned long flags; ++#endif ++ ++ if (osh->pub.pkttag) ++ bzero((void*)((struct sk_buff *)pkt)->cb, OSL_PKTTAG_SZ); ++ ++#ifndef WL_UMK ++ ++ for (nskb = (struct sk_buff *)pkt; nskb; nskb = nskb->next) { ++ spin_lock_irqsave(&osh->pktalloc_lock, flags); ++ osh->pub.pktalloced++; ++ spin_unlock_irqrestore(&osh->pktalloc_lock, flags); ++ } ++#endif ++ return (void *)pkt; ++} ++ ++ ++void * BCMFASTPATH ++osl_pktget(osl_t *osh, uint len) ++{ ++ struct sk_buff *skb; ++ unsigned long flags; ++ ++#ifdef CTFPOOL ++ ++ skb = osl_pktfastget(osh, len); ++ if ((skb != NULL) || ((skb = osl_alloc_skb(len)) != NULL)) { ++#else ++ if ((skb = osl_alloc_skb(len))) { ++#endif ++ skb_put(skb, len); ++ skb->priority = 0; ++ ++ ++ spin_lock_irqsave(&osh->pktalloc_lock, flags); ++ osh->pub.pktalloced++; ++ spin_unlock_irqrestore(&osh->pktalloc_lock, flags); ++ } ++ ++ return ((void*) skb); ++} ++ ++#ifdef CTFPOOL ++static inline void ++osl_pktfastfree(osl_t *osh, struct sk_buff *skb) ++{ ++ ctfpool_t *ctfpool; ++#ifdef CTFPOOL_SPINLOCK ++ unsigned long flags; ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) ++ skb->tstamp.tv.sec = 0; ++#else ++ skb->stamp.tv_sec = 0; ++#endif ++ ++ ++ skb->dev = NULL; ++ skb->dst = NULL; ++ memset(skb->cb, 0, sizeof(skb->cb)); ++ skb->ip_summed = 0; ++ skb->destructor = NULL; ++ ++ ctfpool = (ctfpool_t *)CTFPOOLPTR(osh, skb); ++ ASSERT(ctfpool != NULL); ++ ++ ++ CTFPOOL_LOCK(ctfpool, flags); ++ skb->next = (struct sk_buff *)ctfpool->head; ++ ctfpool->head = (void *)skb; ++ ++ ctfpool->fast_frees++; ++ ctfpool->curr_obj++; ++ ++ ASSERT(ctfpool->curr_obj <= ctfpool->max_obj); ++ CTFPOOL_UNLOCK(ctfpool, flags); ++} ++#endif ++ ++ ++void BCMFASTPATH ++osl_pktfree(osl_t *osh, void *p, bool send) ++{ ++ struct sk_buff *skb, *nskb; ++ unsigned long flags; ++ ++ skb = (struct sk_buff*) p; ++ ++ if (send && osh->pub.tx_fn) ++ osh->pub.tx_fn(osh->pub.tx_ctx, p, 0); ++ ++ PKTDBG_TRACE(osh, (void *) skb, PKTLIST_PKTFREE); ++ ++ ++ while (skb) { ++ nskb = skb->next; ++ skb->next = NULL; ++ ++ ++ ++#ifdef CTFPOOL ++ if ((PKTISFAST(osh, skb)) && (atomic_read(&skb->users) == 1)) ++ osl_pktfastfree(osh, skb); ++ else { ++#else ++ { ++#endif ++ ++ if (skb->destructor) ++ ++ dev_kfree_skb_any(skb); ++ else ++ ++ dev_kfree_skb(skb); ++ } ++ spin_lock_irqsave(&osh->pktalloc_lock, flags); ++ osh->pub.pktalloced--; ++ spin_unlock_irqrestore(&osh->pktalloc_lock, flags); ++ skb = nskb; ++ } ++} ++ ++#ifdef CONFIG_DHD_USE_STATIC_BUF ++void* ++osl_pktget_static(osl_t *osh, uint len) ++{ ++ int i = 0; ++ struct sk_buff *skb; ++ ++ ++ if (len > DHD_SKB_MAX_BUFSIZE) { ++ printk("osl_pktget_static: Do we really need this big skb??" ++ " len=%d\n", len); ++ return osl_pktget(osh, len); ++ } ++ ++ down(&bcm_static_skb->osl_pkt_sem); ++ ++ if (len <= DHD_SKB_1PAGE_BUFSIZE) { ++ for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { ++ if (bcm_static_skb->pkt_use[i] == 0) ++ break; ++ } ++ ++ if (i != STATIC_PKT_MAX_NUM) { ++ bcm_static_skb->pkt_use[i] = 1; ++ ++ skb = bcm_static_skb->skb_4k[i]; ++ skb->tail = skb->data + len; ++ skb->len = len; ++ ++ up(&bcm_static_skb->osl_pkt_sem); ++ return skb; ++ } ++ } ++ ++ if (len <= DHD_SKB_2PAGE_BUFSIZE) { ++ ++ for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { ++ if (bcm_static_skb->pkt_use[i + STATIC_PKT_MAX_NUM] ++ == 0) ++ break; ++ } ++ ++ if (i != STATIC_PKT_MAX_NUM) { ++ bcm_static_skb->pkt_use[i + STATIC_PKT_MAX_NUM] = 1; ++ skb = bcm_static_skb->skb_8k[i]; ++ skb->tail = skb->data + len; ++ skb->len = len; ++ ++ up(&bcm_static_skb->osl_pkt_sem); ++ return skb; ++ } ++ } ++ ++#if defined(ENHANCED_STATIC_BUF) ++ if (bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM * 2] == 0) { ++ bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM * 2] = 1; ++ ++ skb = bcm_static_skb->skb_16k; ++ skb->tail = skb->data + len; ++ skb->len = len; ++ ++ up(&bcm_static_skb->osl_pkt_sem); ++ return skb; ++ } ++#endif ++ ++ up(&bcm_static_skb->osl_pkt_sem); ++ printk("osl_pktget_static: all static pkt in use!\n"); ++ return osl_pktget(osh, len); ++} ++ ++void ++osl_pktfree_static(osl_t *osh, void *p, bool send) ++{ ++ int i; ++ if (!bcm_static_skb) { ++ osl_pktfree(osh, p, send); ++ return; ++ } ++ ++ down(&bcm_static_skb->osl_pkt_sem); ++ for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { ++ if (p == bcm_static_skb->skb_4k[i]) { ++ bcm_static_skb->pkt_use[i] = 0; ++ up(&bcm_static_skb->osl_pkt_sem); ++ return; ++ } ++ } ++ ++ for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { ++ if (p == bcm_static_skb->skb_8k[i]) { ++ bcm_static_skb->pkt_use[i + STATIC_PKT_MAX_NUM] = 0; ++ up(&bcm_static_skb->osl_pkt_sem); ++ return; ++ } ++ } ++#ifdef ENHANCED_STATIC_BUF ++ if (p == bcm_static_skb->skb_16k) { ++ bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM*2] = 0; ++ up(&bcm_static_skb->osl_pkt_sem); ++ return; ++ } ++#endif ++ up(&bcm_static_skb->osl_pkt_sem); ++ ++ osl_pktfree(osh, p, send); ++ return; ++} ++#endif ++ ++uint32 ++osl_pci_read_config(osl_t *osh, uint offset, uint size) ++{ ++ uint val = 0; ++ uint retry = PCI_CFG_RETRY; ++ ++ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); ++ ++ ++ ASSERT(size == 4); ++ ++ do { ++ pci_read_config_dword(osh->pdev, offset, &val); ++ if (val != 0xffffffff) ++ break; ++ } while (retry--); ++ ++ ++ return (val); ++} ++ ++void ++osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val) ++{ ++ uint retry = PCI_CFG_RETRY; ++ ++ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); ++ ++ ++ ASSERT(size == 4); ++ ++ do { ++ pci_write_config_dword(osh->pdev, offset, val); ++ if (offset != PCI_BAR0_WIN) ++ break; ++ if (osl_pci_read_config(osh, offset, size) == val) ++ break; ++ } while (retry--); ++ ++} ++ ++ ++uint ++osl_pci_bus(osl_t *osh) ++{ ++ ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev); ++ ++ return ((struct pci_dev *)osh->pdev)->bus->number; ++} ++ ++ ++uint ++osl_pci_slot(osl_t *osh) ++{ ++ ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev); ++ ++ return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn); ++} ++ ++ ++struct pci_dev * ++osl_pci_device(osl_t *osh) ++{ ++ ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev); ++ ++ return osh->pdev; ++} ++ ++static void ++osl_pcmcia_attr(osl_t *osh, uint offset, char *buf, int size, bool write) ++{ ++} ++ ++void ++osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size) ++{ ++ osl_pcmcia_attr(osh, offset, (char *) buf, size, FALSE); ++} ++ ++void ++osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size) ++{ ++ osl_pcmcia_attr(osh, offset, (char *) buf, size, TRUE); ++} ++ ++void * ++osl_malloc(osl_t *osh, uint size) ++{ ++ void *addr; ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ gfp_t flags; ++#endif ++ ++ ++ if (osh) ++ ASSERT(osh->magic == OS_HANDLE_MAGIC); ++ ++#ifdef CONFIG_DHD_USE_STATIC_BUF ++ if (bcm_static_buf) ++ { ++ int i = 0; ++ if ((size >= PAGE_SIZE)&&(size <= STATIC_BUF_SIZE)) ++ { ++ down(&bcm_static_buf->static_sem); ++ ++ for (i = 0; i < STATIC_BUF_MAX_NUM; i++) ++ { ++ if (bcm_static_buf->buf_use[i] == 0) ++ break; ++ } ++ ++ if (i == STATIC_BUF_MAX_NUM) ++ { ++ up(&bcm_static_buf->static_sem); ++ printk("all static buff in use!\n"); ++ goto original; ++ } ++ ++ bcm_static_buf->buf_use[i] = 1; ++ up(&bcm_static_buf->static_sem); ++ ++ bzero(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i, size); ++ if (osh) ++ atomic_add(size, &osh->malloced); ++ ++ return ((void *)(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i)); ++ } ++ } ++original: ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; ++ if ((addr = kmalloc(size, flags)) == NULL) { ++#else ++ if ((addr = kmalloc(size, GFP_ATOMIC)) == NULL) { ++#endif ++ if (osh) ++ osh->failed++; ++ return (NULL); ++ } ++ if (osh) ++ atomic_add(size, &osh->malloced); ++ ++ return (addr); ++} ++ ++void ++osl_mfree(osl_t *osh, void *addr, uint size) ++{ ++#ifdef CONFIG_DHD_USE_STATIC_BUF ++ if (bcm_static_buf) ++ { ++ if ((addr > (void *)bcm_static_buf) && ((unsigned char *)addr ++ <= ((unsigned char *)bcm_static_buf + STATIC_BUF_TOTAL_LEN))) ++ { ++ int buf_idx = 0; ++ ++ buf_idx = ((unsigned char *)addr - bcm_static_buf->buf_ptr)/STATIC_BUF_SIZE; ++ ++ down(&bcm_static_buf->static_sem); ++ bcm_static_buf->buf_use[buf_idx] = 0; ++ up(&bcm_static_buf->static_sem); ++ ++ if (osh) { ++ ASSERT(osh->magic == OS_HANDLE_MAGIC); ++ atomic_sub(size, &osh->malloced); ++ } ++ return; ++ } ++ } ++#endif ++ if (osh) { ++ ASSERT(osh->magic == OS_HANDLE_MAGIC); ++ atomic_sub(size, &osh->malloced); ++ } ++ kfree(addr); ++} ++ ++uint ++osl_malloced(osl_t *osh) ++{ ++ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); ++ return (atomic_read(&osh->malloced)); ++} ++ ++uint ++osl_malloc_failed(osl_t *osh) ++{ ++ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); ++ return (osh->failed); ++} ++ ++ ++uint ++osl_dma_consistent_align(void) ++{ ++ return (PAGE_SIZE); ++} ++ ++void* ++osl_dma_alloc_consistent(osl_t *osh, uint size, uint16 align_bits, uint *alloced, ulong *pap) ++{ ++ uint16 align = (1 << align_bits); ++ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); ++ ++ if (!ISALIGNED(DMA_CONSISTENT_ALIGN, align)) ++ size += align; ++ *alloced = size; ++ ++ return (pci_alloc_consistent(osh->pdev, size, (dma_addr_t*)pap)); ++} ++ ++void ++osl_dma_free_consistent(osl_t *osh, void *va, uint size, ulong pa) ++{ ++ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); ++ ++ pci_free_consistent(osh->pdev, size, va, (dma_addr_t)pa); ++} ++ ++uint BCMFASTPATH ++osl_dma_map(osl_t *osh, void *va, uint size, int direction) ++{ ++ int dir; ++ ++ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); ++ dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE; ++ return (pci_map_single(osh->pdev, va, size, dir)); ++} ++ ++void BCMFASTPATH ++osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction) ++{ ++ int dir; ++ ++ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); ++ dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE; ++ pci_unmap_single(osh->pdev, (uint32)pa, size, dir); ++} ++ ++#if defined(BCMASSERT_LOG) ++void ++osl_assert(const char *exp, const char *file, int line) ++{ ++ char tempbuf[256]; ++ const char *basename; ++ ++ basename = strrchr(file, '/'); ++ /* skip the '/' */ ++ if (basename) ++ basename++; ++ ++ if (!basename) ++ basename = file; ++ ++ snprintf(tempbuf, 64, "\"%s\": file \"%s\", line %d\n", ++ exp, basename, line); ++ ++ printk("%s", tempbuf); ++ ++ ++} ++#endif ++ ++void ++osl_delay(uint usec) ++{ ++ uint d; ++ ++ while (usec > 0) { ++ d = MIN(usec, 1000); ++ udelay(d); ++ usec -= d; ++ } ++} ++ ++ ++ ++void * ++osl_pktdup(osl_t *osh, void *skb) ++{ ++ void * p; ++ unsigned long irqflags; ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ gfp_t flags; ++#endif ++ ++ ++ PKTCTFMAP(osh, skb); ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; ++ if ((p = skb_clone((struct sk_buff *)skb, flags)) == NULL) ++#else ++ if ((p = skb_clone((struct sk_buff*)skb, GFP_ATOMIC)) == NULL) ++#endif ++ return NULL; ++ ++#ifdef CTFPOOL ++ if (PKTISFAST(osh, skb)) { ++ ctfpool_t *ctfpool; ++ ++ ++ ctfpool = (ctfpool_t *)CTFPOOLPTR(osh, skb); ++ ASSERT(ctfpool != NULL); ++ PKTCLRFAST(osh, p); ++ PKTCLRFAST(osh, skb); ++ ctfpool->refills++; ++ } ++#endif ++ ++ ++ if (osh->pub.pkttag) ++ bzero((void*)((struct sk_buff *)p)->cb, OSL_PKTTAG_SZ); ++ ++ ++ spin_lock_irqsave(&osh->pktalloc_lock, irqflags); ++ osh->pub.pktalloced++; ++ spin_unlock_irqrestore(&osh->pktalloc_lock, irqflags); ++ return (p); ++} ++ ++ ++ ++ ++ ++ ++ ++void * ++osl_os_open_image(char *filename) ++{ ++ struct file *fp; ++ ++ fp = filp_open(filename, O_RDONLY, 0); ++ ++ if (IS_ERR(fp)) ++ fp = NULL; ++ ++ return fp; ++} ++ ++int ++osl_os_get_image_block(char *buf, int len, void *image) ++{ ++ struct file *fp = (struct file *)image; ++ int rdlen; ++ ++ if (!image) ++ return 0; ++ ++ rdlen = kernel_read(fp, fp->f_pos, buf, len); ++ if (rdlen > 0) ++ fp->f_pos += rdlen; ++ ++ return rdlen; ++} ++ ++void ++osl_os_close_image(void *image) ++{ ++ if (image) ++ filp_close((struct file *)image, NULL); ++} +diff --git a/drivers/net/wireless/bcmdhd/sbutils.c b/drivers/net/wireless/bcmdhd/sbutils.c +new file mode 100644 +index 00000000..68cfcb27 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/sbutils.c +@@ -0,0 +1,1001 @@ ++/* ++ * Misc utility routines for accessing chip-specific features ++ * of the SiliconBackplane-based Broadcom chips. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: sbutils.c 310902 2012-01-26 19:45:33Z $ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "siutils_priv.h" ++ ++ ++/* local prototypes */ ++static uint _sb_coreidx(si_info_t *sii, uint32 sba); ++static uint _sb_scan(si_info_t *sii, uint32 sba, void *regs, uint bus, uint32 sbba, ++ uint ncores); ++static uint32 _sb_coresba(si_info_t *sii); ++static void *_sb_setcoreidx(si_info_t *sii, uint coreidx); ++ ++#define SET_SBREG(sii, r, mask, val) \ ++ W_SBREG((sii), (r), ((R_SBREG((sii), (r)) & ~(mask)) | (val))) ++#define REGS2SB(va) (sbconfig_t*) ((int8*)(va) + SBCONFIGOFF) ++ ++/* sonicsrev */ ++#define SONICS_2_2 (SBIDL_RV_2_2 >> SBIDL_RV_SHIFT) ++#define SONICS_2_3 (SBIDL_RV_2_3 >> SBIDL_RV_SHIFT) ++ ++#define R_SBREG(sii, sbr) sb_read_sbreg((sii), (sbr)) ++#define W_SBREG(sii, sbr, v) sb_write_sbreg((sii), (sbr), (v)) ++#define AND_SBREG(sii, sbr, v) W_SBREG((sii), (sbr), (R_SBREG((sii), (sbr)) & (v))) ++#define OR_SBREG(sii, sbr, v) W_SBREG((sii), (sbr), (R_SBREG((sii), (sbr)) | (v))) ++ ++static uint32 ++sb_read_sbreg(si_info_t *sii, volatile uint32 *sbr) ++{ ++ uint8 tmp; ++ uint32 val, intr_val = 0; ++ ++ ++ /* ++ * compact flash only has 11 bits address, while we needs 12 bits address. ++ * MEM_SEG will be OR'd with other 11 bits address in hardware, ++ * so we program MEM_SEG with 12th bit when necessary(access sb regsiters). ++ * For normal PCMCIA bus(CFTable_regwinsz > 2k), do nothing special ++ */ ++ if (PCMCIA(sii)) { ++ INTR_OFF(sii, intr_val); ++ tmp = 1; ++ OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1); ++ sbr = (volatile uint32 *)((uintptr)sbr & ~(1 << 11)); /* mask out bit 11 */ ++ } ++ ++ val = R_REG(sii->osh, sbr); ++ ++ if (PCMCIA(sii)) { ++ tmp = 0; ++ OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1); ++ INTR_RESTORE(sii, intr_val); ++ } ++ ++ return (val); ++} ++ ++static void ++sb_write_sbreg(si_info_t *sii, volatile uint32 *sbr, uint32 v) ++{ ++ uint8 tmp; ++ volatile uint32 dummy; ++ uint32 intr_val = 0; ++ ++ ++ /* ++ * compact flash only has 11 bits address, while we needs 12 bits address. ++ * MEM_SEG will be OR'd with other 11 bits address in hardware, ++ * so we program MEM_SEG with 12th bit when necessary(access sb regsiters). ++ * For normal PCMCIA bus(CFTable_regwinsz > 2k), do nothing special ++ */ ++ if (PCMCIA(sii)) { ++ INTR_OFF(sii, intr_val); ++ tmp = 1; ++ OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1); ++ sbr = (volatile uint32 *)((uintptr)sbr & ~(1 << 11)); /* mask out bit 11 */ ++ } ++ ++ if (BUSTYPE(sii->pub.bustype) == PCMCIA_BUS) { ++ dummy = R_REG(sii->osh, sbr); ++ BCM_REFERENCE(dummy); ++ W_REG(sii->osh, (volatile uint16 *)sbr, (uint16)(v & 0xffff)); ++ dummy = R_REG(sii->osh, sbr); ++ BCM_REFERENCE(dummy); ++ W_REG(sii->osh, ((volatile uint16 *)sbr + 1), (uint16)((v >> 16) & 0xffff)); ++ } else ++ W_REG(sii->osh, sbr, v); ++ ++ if (PCMCIA(sii)) { ++ tmp = 0; ++ OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1); ++ INTR_RESTORE(sii, intr_val); ++ } ++} ++ ++uint ++sb_coreid(si_t *sih) ++{ ++ si_info_t *sii; ++ sbconfig_t *sb; ++ ++ sii = SI_INFO(sih); ++ sb = REGS2SB(sii->curmap); ++ ++ return ((R_SBREG(sii, &sb->sbidhigh) & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT); ++} ++ ++uint ++sb_intflag(si_t *sih) ++{ ++ si_info_t *sii; ++ void *corereg; ++ sbconfig_t *sb; ++ uint origidx, intflag, intr_val = 0; ++ ++ sii = SI_INFO(sih); ++ ++ INTR_OFF(sii, intr_val); ++ origidx = si_coreidx(sih); ++ corereg = si_setcore(sih, CC_CORE_ID, 0); ++ ASSERT(corereg != NULL); ++ sb = REGS2SB(corereg); ++ intflag = R_SBREG(sii, &sb->sbflagst); ++ sb_setcoreidx(sih, origidx); ++ INTR_RESTORE(sii, intr_val); ++ ++ return intflag; ++} ++ ++uint ++sb_flag(si_t *sih) ++{ ++ si_info_t *sii; ++ sbconfig_t *sb; ++ ++ sii = SI_INFO(sih); ++ sb = REGS2SB(sii->curmap); ++ ++ return R_SBREG(sii, &sb->sbtpsflag) & SBTPS_NUM0_MASK; ++} ++ ++void ++sb_setint(si_t *sih, int siflag) ++{ ++ si_info_t *sii; ++ sbconfig_t *sb; ++ uint32 vec; ++ ++ sii = SI_INFO(sih); ++ sb = REGS2SB(sii->curmap); ++ ++ if (siflag == -1) ++ vec = 0; ++ else ++ vec = 1 << siflag; ++ W_SBREG(sii, &sb->sbintvec, vec); ++} ++ ++/* return core index of the core with address 'sba' */ ++static uint ++_sb_coreidx(si_info_t *sii, uint32 sba) ++{ ++ uint i; ++ ++ for (i = 0; i < sii->numcores; i ++) ++ if (sba == sii->coresba[i]) ++ return i; ++ return BADIDX; ++} ++ ++/* return core address of the current core */ ++static uint32 ++_sb_coresba(si_info_t *sii) ++{ ++ uint32 sbaddr; ++ ++ ++ switch (BUSTYPE(sii->pub.bustype)) { ++ case SI_BUS: { ++ sbconfig_t *sb = REGS2SB(sii->curmap); ++ sbaddr = sb_base(R_SBREG(sii, &sb->sbadmatch0)); ++ break; ++ } ++ ++ case PCI_BUS: ++ sbaddr = OSL_PCI_READ_CONFIG(sii->osh, PCI_BAR0_WIN, sizeof(uint32)); ++ break; ++ ++ case PCMCIA_BUS: { ++ uint8 tmp = 0; ++ OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR0, &tmp, 1); ++ sbaddr = (uint32)tmp << 12; ++ OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR1, &tmp, 1); ++ sbaddr |= (uint32)tmp << 16; ++ OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR2, &tmp, 1); ++ sbaddr |= (uint32)tmp << 24; ++ break; ++ } ++ ++ case SPI_BUS: ++ case SDIO_BUS: ++ sbaddr = (uint32)(uintptr)sii->curmap; ++ break; ++ ++ ++ default: ++ sbaddr = BADCOREADDR; ++ break; ++ } ++ ++ return sbaddr; ++} ++ ++uint ++sb_corevendor(si_t *sih) ++{ ++ si_info_t *sii; ++ sbconfig_t *sb; ++ ++ sii = SI_INFO(sih); ++ sb = REGS2SB(sii->curmap); ++ ++ return ((R_SBREG(sii, &sb->sbidhigh) & SBIDH_VC_MASK) >> SBIDH_VC_SHIFT); ++} ++ ++uint ++sb_corerev(si_t *sih) ++{ ++ si_info_t *sii; ++ sbconfig_t *sb; ++ uint sbidh; ++ ++ sii = SI_INFO(sih); ++ sb = REGS2SB(sii->curmap); ++ sbidh = R_SBREG(sii, &sb->sbidhigh); ++ ++ return (SBCOREREV(sbidh)); ++} ++ ++/* set core-specific control flags */ ++void ++sb_core_cflags_wo(si_t *sih, uint32 mask, uint32 val) ++{ ++ si_info_t *sii; ++ sbconfig_t *sb; ++ uint32 w; ++ ++ sii = SI_INFO(sih); ++ sb = REGS2SB(sii->curmap); ++ ++ ASSERT((val & ~mask) == 0); ++ ++ /* mask and set */ ++ w = (R_SBREG(sii, &sb->sbtmstatelow) & ~(mask << SBTML_SICF_SHIFT)) | ++ (val << SBTML_SICF_SHIFT); ++ W_SBREG(sii, &sb->sbtmstatelow, w); ++} ++ ++/* set/clear core-specific control flags */ ++uint32 ++sb_core_cflags(si_t *sih, uint32 mask, uint32 val) ++{ ++ si_info_t *sii; ++ sbconfig_t *sb; ++ uint32 w; ++ ++ sii = SI_INFO(sih); ++ sb = REGS2SB(sii->curmap); ++ ++ ASSERT((val & ~mask) == 0); ++ ++ /* mask and set */ ++ if (mask || val) { ++ w = (R_SBREG(sii, &sb->sbtmstatelow) & ~(mask << SBTML_SICF_SHIFT)) | ++ (val << SBTML_SICF_SHIFT); ++ W_SBREG(sii, &sb->sbtmstatelow, w); ++ } ++ ++ /* return the new value ++ * for write operation, the following readback ensures the completion of write opration. ++ */ ++ return (R_SBREG(sii, &sb->sbtmstatelow) >> SBTML_SICF_SHIFT); ++} ++ ++/* set/clear core-specific status flags */ ++uint32 ++sb_core_sflags(si_t *sih, uint32 mask, uint32 val) ++{ ++ si_info_t *sii; ++ sbconfig_t *sb; ++ uint32 w; ++ ++ sii = SI_INFO(sih); ++ sb = REGS2SB(sii->curmap); ++ ++ ASSERT((val & ~mask) == 0); ++ ASSERT((mask & ~SISF_CORE_BITS) == 0); ++ ++ /* mask and set */ ++ if (mask || val) { ++ w = (R_SBREG(sii, &sb->sbtmstatehigh) & ~(mask << SBTMH_SISF_SHIFT)) | ++ (val << SBTMH_SISF_SHIFT); ++ W_SBREG(sii, &sb->sbtmstatehigh, w); ++ } ++ ++ /* return the new value */ ++ return (R_SBREG(sii, &sb->sbtmstatehigh) >> SBTMH_SISF_SHIFT); ++} ++ ++bool ++sb_iscoreup(si_t *sih) ++{ ++ si_info_t *sii; ++ sbconfig_t *sb; ++ ++ sii = SI_INFO(sih); ++ sb = REGS2SB(sii->curmap); ++ ++ return ((R_SBREG(sii, &sb->sbtmstatelow) & ++ (SBTML_RESET | SBTML_REJ_MASK | (SICF_CLOCK_EN << SBTML_SICF_SHIFT))) == ++ (SICF_CLOCK_EN << SBTML_SICF_SHIFT)); ++} ++ ++/* ++ * Switch to 'coreidx', issue a single arbitrary 32bit register mask&set operation, ++ * switch back to the original core, and return the new value. ++ * ++ * When using the silicon backplane, no fidleing with interrupts or core switches are needed. ++ * ++ * Also, when using pci/pcie, we can optimize away the core switching for pci registers ++ * and (on newer pci cores) chipcommon registers. ++ */ ++uint ++sb_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) ++{ ++ uint origidx = 0; ++ uint32 *r = NULL; ++ uint w; ++ uint intr_val = 0; ++ bool fast = FALSE; ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ ++ ASSERT(GOODIDX(coreidx)); ++ ASSERT(regoff < SI_CORE_SIZE); ++ ASSERT((val & ~mask) == 0); ++ ++ if (coreidx >= SI_MAXCORES) ++ return 0; ++ ++ if (BUSTYPE(sii->pub.bustype) == SI_BUS) { ++ /* If internal bus, we can always get at everything */ ++ fast = TRUE; ++ /* map if does not exist */ ++ if (!sii->regs[coreidx]) { ++ sii->regs[coreidx] = REG_MAP(sii->coresba[coreidx], ++ SI_CORE_SIZE); ++ ASSERT(GOODREGS(sii->regs[coreidx])); ++ } ++ r = (uint32 *)((uchar *)sii->regs[coreidx] + regoff); ++ } else if (BUSTYPE(sii->pub.bustype) == PCI_BUS) { ++ /* If pci/pcie, we can get at pci/pcie regs and on newer cores to chipc */ ++ ++ if ((sii->coreid[coreidx] == CC_CORE_ID) && SI_FAST(sii)) { ++ /* Chipc registers are mapped at 12KB */ ++ ++ fast = TRUE; ++ r = (uint32 *)((char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff); ++ } else if (sii->pub.buscoreidx == coreidx) { ++ /* pci registers are at either in the last 2KB of an 8KB window ++ * or, in pcie and pci rev 13 at 8KB ++ */ ++ fast = TRUE; ++ if (SI_FAST(sii)) ++ r = (uint32 *)((char *)sii->curmap + ++ PCI_16KB0_PCIREGS_OFFSET + regoff); ++ else ++ r = (uint32 *)((char *)sii->curmap + ++ ((regoff >= SBCONFIGOFF) ? ++ PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) + ++ regoff); ++ } ++ } ++ ++ if (!fast) { ++ INTR_OFF(sii, intr_val); ++ ++ /* save current core index */ ++ origidx = si_coreidx(&sii->pub); ++ ++ /* switch core */ ++ r = (uint32*) ((uchar*)sb_setcoreidx(&sii->pub, coreidx) + regoff); ++ } ++ ASSERT(r != NULL); ++ ++ /* mask and set */ ++ if (mask || val) { ++ if (regoff >= SBCONFIGOFF) { ++ w = (R_SBREG(sii, r) & ~mask) | val; ++ W_SBREG(sii, r, w); ++ } else { ++ w = (R_REG(sii->osh, r) & ~mask) | val; ++ W_REG(sii->osh, r, w); ++ } ++ } ++ ++ /* readback */ ++ if (regoff >= SBCONFIGOFF) ++ w = R_SBREG(sii, r); ++ else { ++ if ((CHIPID(sii->pub.chip) == BCM5354_CHIP_ID) && ++ (coreidx == SI_CC_IDX) && ++ (regoff == OFFSETOF(chipcregs_t, watchdog))) { ++ w = val; ++ } else ++ w = R_REG(sii->osh, r); ++ } ++ ++ if (!fast) { ++ /* restore core index */ ++ if (origidx != coreidx) ++ sb_setcoreidx(&sii->pub, origidx); ++ ++ INTR_RESTORE(sii, intr_val); ++ } ++ ++ return (w); ++} ++ ++/* Scan the enumeration space to find all cores starting from the given ++ * bus 'sbba'. Append coreid and other info to the lists in 'si'. 'sba' ++ * is the default core address at chip POR time and 'regs' is the virtual ++ * address that the default core is mapped at. 'ncores' is the number of ++ * cores expected on bus 'sbba'. It returns the total number of cores ++ * starting from bus 'sbba', inclusive. ++ */ ++#define SB_MAXBUSES 2 ++static uint ++_sb_scan(si_info_t *sii, uint32 sba, void *regs, uint bus, uint32 sbba, uint numcores) ++{ ++ uint next; ++ uint ncc = 0; ++ uint i; ++ ++ if (bus >= SB_MAXBUSES) { ++ SI_ERROR(("_sb_scan: bus 0x%08x at level %d is too deep to scan\n", sbba, bus)); ++ return 0; ++ } ++ SI_MSG(("_sb_scan: scan bus 0x%08x assume %u cores\n", sbba, numcores)); ++ ++ /* Scan all cores on the bus starting from core 0. ++ * Core addresses must be contiguous on each bus. ++ */ ++ for (i = 0, next = sii->numcores; i < numcores && next < SB_BUS_MAXCORES; i++, next++) { ++ sii->coresba[next] = sbba + (i * SI_CORE_SIZE); ++ ++ /* keep and reuse the initial register mapping */ ++ if ((BUSTYPE(sii->pub.bustype) == SI_BUS) && (sii->coresba[next] == sba)) { ++ SI_VMSG(("_sb_scan: reuse mapped regs %p for core %u\n", regs, next)); ++ sii->regs[next] = regs; ++ } ++ ++ /* change core to 'next' and read its coreid */ ++ sii->curmap = _sb_setcoreidx(sii, next); ++ sii->curidx = next; ++ ++ sii->coreid[next] = sb_coreid(&sii->pub); ++ ++ /* core specific processing... */ ++ /* chipc provides # cores */ ++ if (sii->coreid[next] == CC_CORE_ID) { ++ chipcregs_t *cc = (chipcregs_t *)sii->curmap; ++ uint32 ccrev = sb_corerev(&sii->pub); ++ ++ /* determine numcores - this is the total # cores in the chip */ ++ if (((ccrev == 4) || (ccrev >= 6))) ++ numcores = (R_REG(sii->osh, &cc->chipid) & CID_CC_MASK) >> ++ CID_CC_SHIFT; ++ else { ++ /* Older chips */ ++ uint chip = CHIPID(sii->pub.chip); ++ ++ if (chip == BCM4306_CHIP_ID) /* < 4306c0 */ ++ numcores = 6; ++ else if (chip == BCM4704_CHIP_ID) ++ numcores = 9; ++ else if (chip == BCM5365_CHIP_ID) ++ numcores = 7; ++ else { ++ SI_ERROR(("sb_chip2numcores: unsupported chip 0x%x\n", ++ chip)); ++ ASSERT(0); ++ numcores = 1; ++ } ++ } ++ SI_VMSG(("_sb_scan: there are %u cores in the chip %s\n", numcores, ++ sii->pub.issim ? "QT" : "")); ++ } ++ /* scan bridged SB(s) and add results to the end of the list */ ++ else if (sii->coreid[next] == OCP_CORE_ID) { ++ sbconfig_t *sb = REGS2SB(sii->curmap); ++ uint32 nsbba = R_SBREG(sii, &sb->sbadmatch1); ++ uint nsbcc; ++ ++ sii->numcores = next + 1; ++ ++ if ((nsbba & 0xfff00000) != SI_ENUM_BASE) ++ continue; ++ nsbba &= 0xfffff000; ++ if (_sb_coreidx(sii, nsbba) != BADIDX) ++ continue; ++ ++ nsbcc = (R_SBREG(sii, &sb->sbtmstatehigh) & 0x000f0000) >> 16; ++ nsbcc = _sb_scan(sii, sba, regs, bus + 1, nsbba, nsbcc); ++ if (sbba == SI_ENUM_BASE) ++ numcores -= nsbcc; ++ ncc += nsbcc; ++ } ++ } ++ ++ SI_MSG(("_sb_scan: found %u cores on bus 0x%08x\n", i, sbba)); ++ ++ sii->numcores = i + ncc; ++ return sii->numcores; ++} ++ ++/* scan the sb enumerated space to identify all cores */ ++void ++sb_scan(si_t *sih, void *regs, uint devid) ++{ ++ si_info_t *sii; ++ uint32 origsba; ++ sbconfig_t *sb; ++ ++ sii = SI_INFO(sih); ++ sb = REGS2SB(sii->curmap); ++ ++ sii->pub.socirev = (R_SBREG(sii, &sb->sbidlow) & SBIDL_RV_MASK) >> SBIDL_RV_SHIFT; ++ ++ /* Save the current core info and validate it later till we know ++ * for sure what is good and what is bad. ++ */ ++ origsba = _sb_coresba(sii); ++ ++ /* scan all SB(s) starting from SI_ENUM_BASE */ ++ sii->numcores = _sb_scan(sii, origsba, regs, 0, SI_ENUM_BASE, 1); ++} ++ ++/* ++ * This function changes logical "focus" to the indicated core; ++ * must be called with interrupts off. ++ * Moreover, callers should keep interrupts off during switching out of and back to d11 core ++ */ ++void * ++sb_setcoreidx(si_t *sih, uint coreidx) ++{ ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ ++ if (coreidx >= sii->numcores) ++ return (NULL); ++ ++ /* ++ * If the user has provided an interrupt mask enabled function, ++ * then assert interrupts are disabled before switching the core. ++ */ ++ ASSERT((sii->intrsenabled_fn == NULL) || !(*(sii)->intrsenabled_fn)((sii)->intr_arg)); ++ ++ sii->curmap = _sb_setcoreidx(sii, coreidx); ++ sii->curidx = coreidx; ++ ++ return (sii->curmap); ++} ++ ++/* This function changes the logical "focus" to the indicated core. ++ * Return the current core's virtual address. ++ */ ++static void * ++_sb_setcoreidx(si_info_t *sii, uint coreidx) ++{ ++ uint32 sbaddr = sii->coresba[coreidx]; ++ void *regs; ++ ++ switch (BUSTYPE(sii->pub.bustype)) { ++ case SI_BUS: ++ /* map new one */ ++ if (!sii->regs[coreidx]) { ++ sii->regs[coreidx] = REG_MAP(sbaddr, SI_CORE_SIZE); ++ ASSERT(GOODREGS(sii->regs[coreidx])); ++ } ++ regs = sii->regs[coreidx]; ++ break; ++ ++ case PCI_BUS: ++ /* point bar0 window */ ++ OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, sbaddr); ++ regs = sii->curmap; ++ break; ++ ++ case PCMCIA_BUS: { ++ uint8 tmp = (sbaddr >> 12) & 0x0f; ++ OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR0, &tmp, 1); ++ tmp = (sbaddr >> 16) & 0xff; ++ OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR1, &tmp, 1); ++ tmp = (sbaddr >> 24) & 0xff; ++ OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR2, &tmp, 1); ++ regs = sii->curmap; ++ break; ++ } ++ case SPI_BUS: ++ case SDIO_BUS: ++ /* map new one */ ++ if (!sii->regs[coreidx]) { ++ sii->regs[coreidx] = (void *)(uintptr)sbaddr; ++ ASSERT(GOODREGS(sii->regs[coreidx])); ++ } ++ regs = sii->regs[coreidx]; ++ break; ++ ++ ++ default: ++ ASSERT(0); ++ regs = NULL; ++ break; ++ } ++ ++ return regs; ++} ++ ++/* Return the address of sbadmatch0/1/2/3 register */ ++static volatile uint32 * ++sb_admatch(si_info_t *sii, uint asidx) ++{ ++ sbconfig_t *sb; ++ volatile uint32 *addrm; ++ ++ sb = REGS2SB(sii->curmap); ++ ++ switch (asidx) { ++ case 0: ++ addrm = &sb->sbadmatch0; ++ break; ++ ++ case 1: ++ addrm = &sb->sbadmatch1; ++ break; ++ ++ case 2: ++ addrm = &sb->sbadmatch2; ++ break; ++ ++ case 3: ++ addrm = &sb->sbadmatch3; ++ break; ++ ++ default: ++ SI_ERROR(("%s: Address space index (%d) out of range\n", __FUNCTION__, asidx)); ++ return 0; ++ } ++ ++ return (addrm); ++} ++ ++/* Return the number of address spaces in current core */ ++int ++sb_numaddrspaces(si_t *sih) ++{ ++ si_info_t *sii; ++ sbconfig_t *sb; ++ ++ sii = SI_INFO(sih); ++ sb = REGS2SB(sii->curmap); ++ ++ /* + 1 because of enumeration space */ ++ return ((R_SBREG(sii, &sb->sbidlow) & SBIDL_AR_MASK) >> SBIDL_AR_SHIFT) + 1; ++} ++ ++/* Return the address of the nth address space in the current core */ ++uint32 ++sb_addrspace(si_t *sih, uint asidx) ++{ ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ ++ return (sb_base(R_SBREG(sii, sb_admatch(sii, asidx)))); ++} ++ ++/* Return the size of the nth address space in the current core */ ++uint32 ++sb_addrspacesize(si_t *sih, uint asidx) ++{ ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ ++ return (sb_size(R_SBREG(sii, sb_admatch(sii, asidx)))); ++} ++ ++ ++/* do buffered registers update */ ++void ++sb_commit(si_t *sih) ++{ ++ si_info_t *sii; ++ uint origidx; ++ uint intr_val = 0; ++ ++ sii = SI_INFO(sih); ++ ++ origidx = sii->curidx; ++ ASSERT(GOODIDX(origidx)); ++ ++ INTR_OFF(sii, intr_val); ++ ++ /* switch over to chipcommon core if there is one, else use pci */ ++ if (sii->pub.ccrev != NOREV) { ++ chipcregs_t *ccregs = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ ASSERT(ccregs != NULL); ++ ++ /* do the buffer registers update */ ++ W_REG(sii->osh, &ccregs->broadcastaddress, SB_COMMIT); ++ W_REG(sii->osh, &ccregs->broadcastdata, 0x0); ++ } else ++ ASSERT(0); ++ ++ /* restore core index */ ++ sb_setcoreidx(sih, origidx); ++ INTR_RESTORE(sii, intr_val); ++} ++ ++void ++sb_core_disable(si_t *sih, uint32 bits) ++{ ++ si_info_t *sii; ++ volatile uint32 dummy; ++ sbconfig_t *sb; ++ ++ sii = SI_INFO(sih); ++ ++ ASSERT(GOODREGS(sii->curmap)); ++ sb = REGS2SB(sii->curmap); ++ ++ /* if core is already in reset, just return */ ++ if (R_SBREG(sii, &sb->sbtmstatelow) & SBTML_RESET) ++ return; ++ ++ /* if clocks are not enabled, put into reset and return */ ++ if ((R_SBREG(sii, &sb->sbtmstatelow) & (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) == 0) ++ goto disable; ++ ++ /* set target reject and spin until busy is clear (preserve core-specific bits) */ ++ OR_SBREG(sii, &sb->sbtmstatelow, SBTML_REJ); ++ dummy = R_SBREG(sii, &sb->sbtmstatelow); ++ BCM_REFERENCE(dummy); ++ OSL_DELAY(1); ++ SPINWAIT((R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_BUSY), 100000); ++ if (R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_BUSY) ++ SI_ERROR(("%s: target state still busy\n", __FUNCTION__)); ++ ++ if (R_SBREG(sii, &sb->sbidlow) & SBIDL_INIT) { ++ OR_SBREG(sii, &sb->sbimstate, SBIM_RJ); ++ dummy = R_SBREG(sii, &sb->sbimstate); ++ BCM_REFERENCE(dummy); ++ OSL_DELAY(1); ++ SPINWAIT((R_SBREG(sii, &sb->sbimstate) & SBIM_BY), 100000); ++ } ++ ++ /* set reset and reject while enabling the clocks */ ++ W_SBREG(sii, &sb->sbtmstatelow, ++ (((bits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) | ++ SBTML_REJ | SBTML_RESET)); ++ dummy = R_SBREG(sii, &sb->sbtmstatelow); ++ BCM_REFERENCE(dummy); ++ OSL_DELAY(10); ++ ++ /* don't forget to clear the initiator reject bit */ ++ if (R_SBREG(sii, &sb->sbidlow) & SBIDL_INIT) ++ AND_SBREG(sii, &sb->sbimstate, ~SBIM_RJ); ++ ++disable: ++ /* leave reset and reject asserted */ ++ W_SBREG(sii, &sb->sbtmstatelow, ((bits << SBTML_SICF_SHIFT) | SBTML_REJ | SBTML_RESET)); ++ OSL_DELAY(1); ++} ++ ++/* reset and re-enable a core ++ * inputs: ++ * bits - core specific bits that are set during and after reset sequence ++ * resetbits - core specific bits that are set only during reset sequence ++ */ ++void ++sb_core_reset(si_t *sih, uint32 bits, uint32 resetbits) ++{ ++ si_info_t *sii; ++ sbconfig_t *sb; ++ volatile uint32 dummy; ++ ++ sii = SI_INFO(sih); ++ ASSERT(GOODREGS(sii->curmap)); ++ sb = REGS2SB(sii->curmap); ++ ++ /* ++ * Must do the disable sequence first to work for arbitrary current core state. ++ */ ++ sb_core_disable(sih, (bits | resetbits)); ++ ++ /* ++ * Now do the initialization sequence. ++ */ ++ ++ /* set reset while enabling the clock and forcing them on throughout the core */ ++ W_SBREG(sii, &sb->sbtmstatelow, ++ (((bits | resetbits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) | ++ SBTML_RESET)); ++ dummy = R_SBREG(sii, &sb->sbtmstatelow); ++ BCM_REFERENCE(dummy); ++ OSL_DELAY(1); ++ ++ if (R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_SERR) { ++ W_SBREG(sii, &sb->sbtmstatehigh, 0); ++ } ++ if ((dummy = R_SBREG(sii, &sb->sbimstate)) & (SBIM_IBE | SBIM_TO)) { ++ AND_SBREG(sii, &sb->sbimstate, ~(SBIM_IBE | SBIM_TO)); ++ } ++ ++ /* clear reset and allow it to propagate throughout the core */ ++ W_SBREG(sii, &sb->sbtmstatelow, ++ ((bits | resetbits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT)); ++ dummy = R_SBREG(sii, &sb->sbtmstatelow); ++ BCM_REFERENCE(dummy); ++ OSL_DELAY(1); ++ ++ /* leave clock enabled */ ++ W_SBREG(sii, &sb->sbtmstatelow, ((bits | SICF_CLOCK_EN) << SBTML_SICF_SHIFT)); ++ dummy = R_SBREG(sii, &sb->sbtmstatelow); ++ BCM_REFERENCE(dummy); ++ OSL_DELAY(1); ++} ++ ++/* ++ * Set the initiator timeout for the "master core". ++ * The master core is defined to be the core in control ++ * of the chip and so it issues accesses to non-memory ++ * locations (Because of dma *any* core can access memeory). ++ * ++ * The routine uses the bus to decide who is the master: ++ * SI_BUS => mips ++ * JTAG_BUS => chipc ++ * PCI_BUS => pci or pcie ++ * PCMCIA_BUS => pcmcia ++ * SDIO_BUS => pcmcia ++ * ++ * This routine exists so callers can disable initiator ++ * timeouts so accesses to very slow devices like otp ++ * won't cause an abort. The routine allows arbitrary ++ * settings of the service and request timeouts, though. ++ * ++ * Returns the timeout state before changing it or -1 ++ * on error. ++ */ ++ ++#define TO_MASK (SBIMCL_RTO_MASK | SBIMCL_STO_MASK) ++ ++uint32 ++sb_set_initiator_to(si_t *sih, uint32 to, uint idx) ++{ ++ si_info_t *sii; ++ uint origidx; ++ uint intr_val = 0; ++ uint32 tmp, ret = 0xffffffff; ++ sbconfig_t *sb; ++ ++ sii = SI_INFO(sih); ++ ++ if ((to & ~TO_MASK) != 0) ++ return ret; ++ ++ /* Figure out the master core */ ++ if (idx == BADIDX) { ++ switch (BUSTYPE(sii->pub.bustype)) { ++ case PCI_BUS: ++ idx = sii->pub.buscoreidx; ++ break; ++ case JTAG_BUS: ++ idx = SI_CC_IDX; ++ break; ++ case PCMCIA_BUS: ++ case SDIO_BUS: ++ idx = si_findcoreidx(sih, PCMCIA_CORE_ID, 0); ++ break; ++ case SI_BUS: ++ idx = si_findcoreidx(sih, MIPS33_CORE_ID, 0); ++ break; ++ default: ++ ASSERT(0); ++ } ++ if (idx == BADIDX) ++ return ret; ++ } ++ ++ INTR_OFF(sii, intr_val); ++ origidx = si_coreidx(sih); ++ ++ sb = REGS2SB(sb_setcoreidx(sih, idx)); ++ ++ tmp = R_SBREG(sii, &sb->sbimconfiglow); ++ ret = tmp & TO_MASK; ++ W_SBREG(sii, &sb->sbimconfiglow, (tmp & ~TO_MASK) | to); ++ ++ sb_commit(sih); ++ sb_setcoreidx(sih, origidx); ++ INTR_RESTORE(sii, intr_val); ++ return ret; ++} ++ ++uint32 ++sb_base(uint32 admatch) ++{ ++ uint32 base; ++ uint type; ++ ++ type = admatch & SBAM_TYPE_MASK; ++ ASSERT(type < 3); ++ ++ base = 0; ++ ++ if (type == 0) { ++ base = admatch & SBAM_BASE0_MASK; ++ } else if (type == 1) { ++ ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ ++ base = admatch & SBAM_BASE1_MASK; ++ } else if (type == 2) { ++ ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ ++ base = admatch & SBAM_BASE2_MASK; ++ } ++ ++ return (base); ++} ++ ++uint32 ++sb_size(uint32 admatch) ++{ ++ uint32 size; ++ uint type; ++ ++ type = admatch & SBAM_TYPE_MASK; ++ ASSERT(type < 3); ++ ++ size = 0; ++ ++ if (type == 0) { ++ size = 1 << (((admatch & SBAM_ADINT0_MASK) >> SBAM_ADINT0_SHIFT) + 1); ++ } else if (type == 1) { ++ ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ ++ size = 1 << (((admatch & SBAM_ADINT1_MASK) >> SBAM_ADINT1_SHIFT) + 1); ++ } else if (type == 2) { ++ ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ ++ size = 1 << (((admatch & SBAM_ADINT2_MASK) >> SBAM_ADINT2_SHIFT) + 1); ++ } ++ ++ return (size); ++} +diff --git a/drivers/net/wireless/bcmdhd/siutils.c b/drivers/net/wireless/bcmdhd/siutils.c +new file mode 100644 +index 00000000..fef3cbd2 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/siutils.c +@@ -0,0 +1,2466 @@ ++/* ++ * Misc utility routines for accessing chip-specific features ++ * of the SiliconBackplane-based Broadcom chips. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: siutils.c 347632 2012-07-27 11:00:35Z $ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "siutils_priv.h" ++ ++/* local prototypes */ ++static si_info_t *si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs, ++ uint bustype, void *sdh, char **vars, uint *varsz); ++static bool si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh); ++static bool si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin, ++ uint *origidx, void *regs); ++ ++ ++ ++/* global variable to indicate reservation/release of gpio's */ ++static uint32 si_gpioreservation = 0; ++ ++/* global flag to prevent shared resources from being initialized multiple times in si_attach() */ ++ ++int do_4360_pcie2_war = 0; ++ ++/* ++ * Allocate a si handle. ++ * devid - pci device id (used to determine chip#) ++ * osh - opaque OS handle ++ * regs - virtual address of initial core registers ++ * bustype - pci/pcmcia/sb/sdio/etc ++ * vars - pointer to a pointer area for "environment" variables ++ * varsz - pointer to int to return the size of the vars ++ */ ++si_t * ++si_attach(uint devid, osl_t *osh, void *regs, ++ uint bustype, void *sdh, char **vars, uint *varsz) ++{ ++ si_info_t *sii; ++ ++ /* alloc si_info_t */ ++ if ((sii = MALLOC(osh, sizeof (si_info_t))) == NULL) { ++ SI_ERROR(("si_attach: malloc failed! malloced %d bytes\n", MALLOCED(osh))); ++ return (NULL); ++ } ++ ++ if (si_doattach(sii, devid, osh, regs, bustype, sdh, vars, varsz) == NULL) { ++ MFREE(osh, sii, sizeof(si_info_t)); ++ return (NULL); ++ } ++ sii->vars = vars ? *vars : NULL; ++ sii->varsz = varsz ? *varsz : 0; ++ ++ return (si_t *)sii; ++} ++ ++/* global kernel resource */ ++static si_info_t ksii; ++ ++static uint32 wd_msticks; /* watchdog timer ticks normalized to ms */ ++ ++/* generic kernel variant of si_attach() */ ++si_t * ++si_kattach(osl_t *osh) ++{ ++ static bool ksii_attached = FALSE; ++ ++ if (!ksii_attached) { ++ void *regs; ++ regs = REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE); ++ ++ if (si_doattach(&ksii, BCM4710_DEVICE_ID, osh, regs, ++ SI_BUS, NULL, ++ osh != SI_OSH ? &ksii.vars : NULL, ++ osh != SI_OSH ? &ksii.varsz : NULL) == NULL) { ++ SI_ERROR(("si_kattach: si_doattach failed\n")); ++ REG_UNMAP(regs); ++ return NULL; ++ } ++ REG_UNMAP(regs); ++ ++ /* save ticks normalized to ms for si_watchdog_ms() */ ++ if (PMUCTL_ENAB(&ksii.pub)) { ++ /* based on 32KHz ILP clock */ ++ wd_msticks = 32; ++ } else { ++ wd_msticks = ALP_CLOCK / 1000; ++ } ++ ++ ksii_attached = TRUE; ++ SI_MSG(("si_kattach done. ccrev = %d, wd_msticks = %d\n", ++ ksii.pub.ccrev, wd_msticks)); ++ } ++ ++ return &ksii.pub; ++} ++ ++ ++static bool ++si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh) ++{ ++ /* need to set memseg flag for CF card first before any sb registers access */ ++ if (BUSTYPE(bustype) == PCMCIA_BUS) ++ sii->memseg = TRUE; ++ ++ ++ if (BUSTYPE(bustype) == SDIO_BUS) { ++ int err; ++ uint8 clkset; ++ ++ /* Try forcing SDIO core to do ALPAvail request only */ ++ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); ++ if (!err) { ++ uint8 clkval; ++ ++ /* If register supported, wait for ALPAvail and then force ALP */ ++ clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, NULL); ++ if ((clkval & ~SBSDIO_AVBITS) == clkset) { ++ SPINWAIT(((clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, ++ SBSDIO_FUNC1_CHIPCLKCSR, NULL)), !SBSDIO_ALPAV(clkval)), ++ PMU_MAX_TRANSITION_DLY); ++ if (!SBSDIO_ALPAV(clkval)) { ++ SI_ERROR(("timeout on ALPAV wait, clkval 0x%02x\n", ++ clkval)); ++ return FALSE; ++ } ++ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, ++ clkset, &err); ++ OSL_DELAY(65); ++ } ++ } ++ ++ /* Also, disable the extra SDIO pull-ups */ ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); ++ } ++ ++ ++ return TRUE; ++} ++ ++static bool ++si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin, ++ uint *origidx, void *regs) ++{ ++ bool pci, pcie, pcie_gen2 = FALSE; ++ uint i; ++ uint pciidx, pcieidx, pcirev, pcierev; ++ ++ cc = si_setcoreidx(&sii->pub, SI_CC_IDX); ++ ASSERT((uintptr)cc); ++ ++ /* get chipcommon rev */ ++ sii->pub.ccrev = (int)si_corerev(&sii->pub); ++ ++ /* get chipcommon chipstatus */ ++ if (sii->pub.ccrev >= 11) ++ sii->pub.chipst = R_REG(sii->osh, &cc->chipstatus); ++ ++ /* get chipcommon capabilites */ ++ sii->pub.cccaps = R_REG(sii->osh, &cc->capabilities); ++ /* get chipcommon extended capabilities */ ++ ++ if (sii->pub.ccrev >= 35) ++ sii->pub.cccaps_ext = R_REG(sii->osh, &cc->capabilities_ext); ++ ++ /* get pmu rev and caps */ ++ if (sii->pub.cccaps & CC_CAP_PMU) { ++ sii->pub.pmucaps = R_REG(sii->osh, &cc->pmucapabilities); ++ sii->pub.pmurev = sii->pub.pmucaps & PCAP_REV_MASK; ++ } ++ ++ SI_MSG(("Chipc: rev %d, caps 0x%x, chipst 0x%x pmurev %d, pmucaps 0x%x\n", ++ sii->pub.ccrev, sii->pub.cccaps, sii->pub.chipst, sii->pub.pmurev, ++ sii->pub.pmucaps)); ++ ++ /* figure out bus/orignal core idx */ ++ sii->pub.buscoretype = NODEV_CORE_ID; ++ sii->pub.buscorerev = (uint)NOREV; ++ sii->pub.buscoreidx = BADIDX; ++ ++ pci = pcie = FALSE; ++ pcirev = pcierev = (uint)NOREV; ++ pciidx = pcieidx = BADIDX; ++ ++ for (i = 0; i < sii->numcores; i++) { ++ uint cid, crev; ++ ++ si_setcoreidx(&sii->pub, i); ++ cid = si_coreid(&sii->pub); ++ crev = si_corerev(&sii->pub); ++ ++ /* Display cores found */ ++ SI_VMSG(("CORE[%d]: id 0x%x rev %d base 0x%x regs 0x%p\n", ++ i, cid, crev, sii->coresba[i], sii->regs[i])); ++ ++ if (BUSTYPE(bustype) == PCI_BUS) { ++ if (cid == PCI_CORE_ID) { ++ pciidx = i; ++ pcirev = crev; ++ pci = TRUE; ++ } else if ((cid == PCIE_CORE_ID) || (cid == PCIE2_CORE_ID)) { ++ pcieidx = i; ++ pcierev = crev; ++ pcie = TRUE; ++ if (cid == PCIE2_CORE_ID) ++ pcie_gen2 = TRUE; ++ } ++ } else if ((BUSTYPE(bustype) == PCMCIA_BUS) && ++ (cid == PCMCIA_CORE_ID)) { ++ sii->pub.buscorerev = crev; ++ sii->pub.buscoretype = cid; ++ sii->pub.buscoreidx = i; ++ } ++ else if (((BUSTYPE(bustype) == SDIO_BUS) || ++ (BUSTYPE(bustype) == SPI_BUS)) && ++ ((cid == PCMCIA_CORE_ID) || ++ (cid == SDIOD_CORE_ID))) { ++ sii->pub.buscorerev = crev; ++ sii->pub.buscoretype = cid; ++ sii->pub.buscoreidx = i; ++ } ++ ++ /* find the core idx before entering this func. */ ++ if ((savewin && (savewin == sii->coresba[i])) || ++ (regs == sii->regs[i])) ++ *origidx = i; ++ } ++ ++ if (pci) { ++ sii->pub.buscoretype = PCI_CORE_ID; ++ sii->pub.buscorerev = pcirev; ++ sii->pub.buscoreidx = pciidx; ++ } else if (pcie) { ++ if (pcie_gen2) ++ sii->pub.buscoretype = PCIE2_CORE_ID; ++ else ++ sii->pub.buscoretype = PCIE_CORE_ID; ++ sii->pub.buscorerev = pcierev; ++ sii->pub.buscoreidx = pcieidx; ++ } ++ ++ SI_VMSG(("Buscore id/type/rev %d/0x%x/%d\n", sii->pub.buscoreidx, sii->pub.buscoretype, ++ sii->pub.buscorerev)); ++ ++ if (BUSTYPE(sii->pub.bustype) == SI_BUS && (CHIPID(sii->pub.chip) == BCM4712_CHIP_ID) && ++ (sii->pub.chippkg != BCM4712LARGE_PKG_ID) && (CHIPREV(sii->pub.chiprev) <= 3)) ++ OR_REG(sii->osh, &cc->slow_clk_ctl, SCC_SS_XTAL); ++ ++ ++ /* Make sure any on-chip ARM is off (in case strapping is wrong), or downloaded code was ++ * already running. ++ */ ++ if ((BUSTYPE(bustype) == SDIO_BUS) || (BUSTYPE(bustype) == SPI_BUS)) { ++ if (si_setcore(&sii->pub, ARM7S_CORE_ID, 0) || ++ si_setcore(&sii->pub, ARMCM3_CORE_ID, 0)) ++ si_core_disable(&sii->pub, 0); ++ } ++ ++ /* return to the original core */ ++ si_setcoreidx(&sii->pub, *origidx); ++ ++ return TRUE; ++} ++ ++ ++ ++ ++static si_info_t * ++si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs, ++ uint bustype, void *sdh, char **vars, uint *varsz) ++{ ++ struct si_pub *sih = &sii->pub; ++ uint32 w, savewin; ++ chipcregs_t *cc; ++ char *pvars = NULL; ++ uint origidx; ++ ++ ASSERT(GOODREGS(regs)); ++ ++ bzero((uchar*)sii, sizeof(si_info_t)); ++ ++ savewin = 0; ++ ++ sih->buscoreidx = BADIDX; ++ ++ sii->curmap = regs; ++ sii->sdh = sdh; ++ sii->osh = osh; ++ ++ ++ ++ /* find Chipcommon address */ ++ if (bustype == PCI_BUS) { ++ savewin = OSL_PCI_READ_CONFIG(sii->osh, PCI_BAR0_WIN, sizeof(uint32)); ++ if (!GOODCOREADDR(savewin, SI_ENUM_BASE)) ++ savewin = SI_ENUM_BASE; ++ OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, SI_ENUM_BASE); ++ if (!regs) ++ return NULL; ++ cc = (chipcregs_t *)regs; ++ } else if ((bustype == SDIO_BUS) || (bustype == SPI_BUS)) { ++ cc = (chipcregs_t *)sii->curmap; ++ } else { ++ cc = (chipcregs_t *)REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE); ++ } ++ ++ sih->bustype = bustype; ++ if (bustype != BUSTYPE(bustype)) { ++ SI_ERROR(("si_doattach: bus type %d does not match configured bus type %d\n", ++ bustype, BUSTYPE(bustype))); ++ return NULL; ++ } ++ ++ /* bus/core/clk setup for register access */ ++ if (!si_buscore_prep(sii, bustype, devid, sdh)) { ++ SI_ERROR(("si_doattach: si_core_clk_prep failed %d\n", bustype)); ++ return NULL; ++ } ++ ++ /* ChipID recognition. ++ * We assume we can read chipid at offset 0 from the regs arg. ++ * If we add other chiptypes (or if we need to support old sdio hosts w/o chipcommon), ++ * some way of recognizing them needs to be added here. ++ */ ++ if (!cc) { ++ SI_ERROR(("%s: chipcommon register space is null \n", __FUNCTION__)); ++ return NULL; ++ } ++ w = R_REG(osh, &cc->chipid); ++ sih->socitype = (w & CID_TYPE_MASK) >> CID_TYPE_SHIFT; ++ /* Might as wll fill in chip id rev & pkg */ ++ sih->chip = w & CID_ID_MASK; ++ sih->chiprev = (w & CID_REV_MASK) >> CID_REV_SHIFT; ++ sih->chippkg = (w & CID_PKG_MASK) >> CID_PKG_SHIFT; ++ ++ if ((CHIPID(sih->chip) == BCM4329_CHIP_ID) && (sih->chiprev == 0) && ++ (sih->chippkg != BCM4329_289PIN_PKG_ID)) { ++ sih->chippkg = BCM4329_182PIN_PKG_ID; ++ } ++ sih->issim = IS_SIM(sih->chippkg); ++ ++ /* scan for cores */ ++ if (CHIPTYPE(sii->pub.socitype) == SOCI_SB) { ++ SI_MSG(("Found chip type SB (0x%08x)\n", w)); ++ sb_scan(&sii->pub, regs, devid); ++ } else if (CHIPTYPE(sii->pub.socitype) == SOCI_AI) { ++ SI_MSG(("Found chip type AI (0x%08x)\n", w)); ++ /* pass chipc address instead of original core base */ ++ ai_scan(&sii->pub, (void *)(uintptr)cc, devid); ++ } else if (CHIPTYPE(sii->pub.socitype) == SOCI_UBUS) { ++ SI_MSG(("Found chip type UBUS (0x%08x), chip id = 0x%4x\n", w, sih->chip)); ++ /* pass chipc address instead of original core base */ ++ ub_scan(&sii->pub, (void *)(uintptr)cc, devid); ++ } else { ++ SI_ERROR(("Found chip of unknown type (0x%08x)\n", w)); ++ return NULL; ++ } ++ /* no cores found, bail out */ ++ if (sii->numcores == 0) { ++ SI_ERROR(("si_doattach: could not find any cores\n")); ++ return NULL; ++ } ++ /* bus/core/clk setup */ ++ origidx = SI_CC_IDX; ++ if (!si_buscore_setup(sii, cc, bustype, savewin, &origidx, regs)) { ++ SI_ERROR(("si_doattach: si_buscore_setup failed\n")); ++ goto exit; ++ } ++ ++ if (CHIPID(sih->chip) == BCM4322_CHIP_ID && (((sih->chipst & CST4322_SPROM_OTP_SEL_MASK) ++ >> CST4322_SPROM_OTP_SEL_SHIFT) == (CST4322_OTP_PRESENT | ++ CST4322_SPROM_PRESENT))) { ++ SI_ERROR(("%s: Invalid setting: both SPROM and OTP strapped.\n", __FUNCTION__)); ++ return NULL; ++ } ++ ++ /* assume current core is CC */ ++ if ((sii->pub.ccrev == 0x25) && ((CHIPID(sih->chip) == BCM43236_CHIP_ID || ++ CHIPID(sih->chip) == BCM43235_CHIP_ID || ++ CHIPID(sih->chip) == BCM43234_CHIP_ID || ++ CHIPID(sih->chip) == BCM43238_CHIP_ID) && ++ (CHIPREV(sii->pub.chiprev) <= 2))) { ++ ++ if ((cc->chipstatus & CST43236_BP_CLK) != 0) { ++ uint clkdiv; ++ clkdiv = R_REG(osh, &cc->clkdiv); ++ /* otp_clk_div is even number, 120/14 < 9mhz */ ++ clkdiv = (clkdiv & ~CLKD_OTP) | (14 << CLKD_OTP_SHIFT); ++ W_REG(osh, &cc->clkdiv, clkdiv); ++ SI_ERROR(("%s: set clkdiv to %x\n", __FUNCTION__, clkdiv)); ++ } ++ OSL_DELAY(10); ++ } ++ ++ if (bustype == PCI_BUS) { ++ ++ } ++ ++ pvars = NULL; ++ BCM_REFERENCE(pvars); ++ ++ ++ ++ if (sii->pub.ccrev >= 20) { ++ uint32 gpiopullup = 0, gpiopulldown = 0; ++ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ ASSERT(cc != NULL); ++ ++ /* 4314/43142 has pin muxing, don't clear gpio bits */ ++ if ((CHIPID(sih->chip) == BCM4314_CHIP_ID) || ++ (CHIPID(sih->chip) == BCM43142_CHIP_ID)) { ++ gpiopullup |= 0x402e0; ++ gpiopulldown |= 0x20500; ++ } ++ ++ W_REG(osh, &cc->gpiopullup, gpiopullup); ++ W_REG(osh, &cc->gpiopulldown, gpiopulldown); ++ si_setcoreidx(sih, origidx); ++ } ++ ++ ++ /* clear any previous epidiag-induced target abort */ ++ ASSERT(!si_taclear(sih, FALSE)); ++ ++ return (sii); ++ ++exit: ++ ++ return NULL; ++} ++ ++/* may be called with core in reset */ ++void ++si_detach(si_t *sih) ++{ ++ si_info_t *sii; ++ uint idx; ++ ++ ++ sii = SI_INFO(sih); ++ ++ if (sii == NULL) ++ return; ++ ++ if (BUSTYPE(sih->bustype) == SI_BUS) ++ for (idx = 0; idx < SI_MAXCORES; idx++) ++ if (sii->regs[idx]) { ++ REG_UNMAP(sii->regs[idx]); ++ sii->regs[idx] = NULL; ++ } ++ ++ ++ ++#if !defined(BCMBUSTYPE) || (BCMBUSTYPE == SI_BUS) ++ if (sii != &ksii) ++#endif /* !BCMBUSTYPE || (BCMBUSTYPE == SI_BUS) */ ++ MFREE(sii->osh, sii, sizeof(si_info_t)); ++} ++ ++void * ++si_osh(si_t *sih) ++{ ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ return sii->osh; ++} ++ ++void ++si_setosh(si_t *sih, osl_t *osh) ++{ ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ if (sii->osh != NULL) { ++ SI_ERROR(("osh is already set....\n")); ++ ASSERT(!sii->osh); ++ } ++ sii->osh = osh; ++} ++ ++/* register driver interrupt disabling and restoring callback functions */ ++void ++si_register_intr_callback(si_t *sih, void *intrsoff_fn, void *intrsrestore_fn, ++ void *intrsenabled_fn, void *intr_arg) ++{ ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ sii->intr_arg = intr_arg; ++ sii->intrsoff_fn = (si_intrsoff_t)intrsoff_fn; ++ sii->intrsrestore_fn = (si_intrsrestore_t)intrsrestore_fn; ++ sii->intrsenabled_fn = (si_intrsenabled_t)intrsenabled_fn; ++ /* save current core id. when this function called, the current core ++ * must be the core which provides driver functions(il, et, wl, etc.) ++ */ ++ sii->dev_coreid = sii->coreid[sii->curidx]; ++} ++ ++void ++si_deregister_intr_callback(si_t *sih) ++{ ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ sii->intrsoff_fn = NULL; ++} ++ ++uint ++si_intflag(si_t *sih) ++{ ++ si_info_t *sii = SI_INFO(sih); ++ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_intflag(sih); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return R_REG(sii->osh, ((uint32 *)(uintptr) ++ (sii->oob_router + OOB_STATUSA))); ++ else { ++ ASSERT(0); ++ return 0; ++ } ++} ++ ++uint ++si_flag(si_t *sih) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_flag(sih); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return ai_flag(sih); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ return ub_flag(sih); ++ else { ++ ASSERT(0); ++ return 0; ++ } ++} ++ ++void ++si_setint(si_t *sih, int siflag) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ sb_setint(sih, siflag); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ ai_setint(sih, siflag); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ ub_setint(sih, siflag); ++ else ++ ASSERT(0); ++} ++ ++uint ++si_coreid(si_t *sih) ++{ ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ return sii->coreid[sii->curidx]; ++} ++ ++uint ++si_coreidx(si_t *sih) ++{ ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ return sii->curidx; ++} ++ ++/* return the core-type instantiation # of the current core */ ++uint ++si_coreunit(si_t *sih) ++{ ++ si_info_t *sii; ++ uint idx; ++ uint coreid; ++ uint coreunit; ++ uint i; ++ ++ sii = SI_INFO(sih); ++ coreunit = 0; ++ ++ idx = sii->curidx; ++ ++ ASSERT(GOODREGS(sii->curmap)); ++ coreid = si_coreid(sih); ++ ++ /* count the cores of our type */ ++ for (i = 0; i < idx; i++) ++ if (sii->coreid[i] == coreid) ++ coreunit++; ++ ++ return (coreunit); ++} ++ ++uint ++si_corevendor(si_t *sih) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_corevendor(sih); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return ai_corevendor(sih); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ return ub_corevendor(sih); ++ else { ++ ASSERT(0); ++ return 0; ++ } ++} ++ ++bool ++si_backplane64(si_t *sih) ++{ ++ return ((sih->cccaps & CC_CAP_BKPLN64) != 0); ++} ++ ++uint ++si_corerev(si_t *sih) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_corerev(sih); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return ai_corerev(sih); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ return ub_corerev(sih); ++ else { ++ ASSERT(0); ++ return 0; ++ } ++} ++ ++/* return index of coreid or BADIDX if not found */ ++uint ++si_findcoreidx(si_t *sih, uint coreid, uint coreunit) ++{ ++ si_info_t *sii; ++ uint found; ++ uint i; ++ ++ sii = SI_INFO(sih); ++ ++ found = 0; ++ ++ for (i = 0; i < sii->numcores; i++) ++ if (sii->coreid[i] == coreid) { ++ if (found == coreunit) ++ return (i); ++ found++; ++ } ++ ++ return (BADIDX); ++} ++ ++/* return list of found cores */ ++uint ++si_corelist(si_t *sih, uint coreid[]) ++{ ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ ++ bcopy((uchar*)sii->coreid, (uchar*)coreid, (sii->numcores * sizeof(uint))); ++ return (sii->numcores); ++} ++ ++/* return current register mapping */ ++void * ++si_coreregs(si_t *sih) ++{ ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ ASSERT(GOODREGS(sii->curmap)); ++ ++ return (sii->curmap); ++} ++ ++/* ++ * This function changes logical "focus" to the indicated core; ++ * must be called with interrupts off. ++ * Moreover, callers should keep interrupts off during switching out of and back to d11 core ++ */ ++void * ++si_setcore(si_t *sih, uint coreid, uint coreunit) ++{ ++ uint idx; ++ ++ idx = si_findcoreidx(sih, coreid, coreunit); ++ if (!GOODIDX(idx)) ++ return (NULL); ++ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_setcoreidx(sih, idx); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return ai_setcoreidx(sih, idx); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ return ub_setcoreidx(sih, idx); ++ else { ++ ASSERT(0); ++ return NULL; ++ } ++} ++ ++void * ++si_setcoreidx(si_t *sih, uint coreidx) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_setcoreidx(sih, coreidx); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return ai_setcoreidx(sih, coreidx); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ return ub_setcoreidx(sih, coreidx); ++ else { ++ ASSERT(0); ++ return NULL; ++ } ++} ++ ++/* Turn off interrupt as required by sb_setcore, before switch core */ ++void * ++si_switch_core(si_t *sih, uint coreid, uint *origidx, uint *intr_val) ++{ ++ void *cc; ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ ++ if (SI_FAST(sii)) { ++ /* Overloading the origidx variable to remember the coreid, ++ * this works because the core ids cannot be confused with ++ * core indices. ++ */ ++ *origidx = coreid; ++ if (coreid == CC_CORE_ID) ++ return (void *)CCREGS_FAST(sii); ++ else if (coreid == sih->buscoretype) ++ return (void *)PCIEREGS(sii); ++ } ++ INTR_OFF(sii, *intr_val); ++ *origidx = sii->curidx; ++ cc = si_setcore(sih, coreid, 0); ++ ASSERT(cc != NULL); ++ ++ return cc; ++} ++ ++/* restore coreidx and restore interrupt */ ++void ++si_restore_core(si_t *sih, uint coreid, uint intr_val) ++{ ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ if (SI_FAST(sii) && ((coreid == CC_CORE_ID) || (coreid == sih->buscoretype))) ++ return; ++ ++ si_setcoreidx(sih, coreid); ++ INTR_RESTORE(sii, intr_val); ++} ++ ++int ++si_numaddrspaces(si_t *sih) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_numaddrspaces(sih); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return ai_numaddrspaces(sih); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ return ub_numaddrspaces(sih); ++ else { ++ ASSERT(0); ++ return 0; ++ } ++} ++ ++uint32 ++si_addrspace(si_t *sih, uint asidx) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_addrspace(sih, asidx); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return ai_addrspace(sih, asidx); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ return ub_addrspace(sih, asidx); ++ else { ++ ASSERT(0); ++ return 0; ++ } ++} ++ ++uint32 ++si_addrspacesize(si_t *sih, uint asidx) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_addrspacesize(sih, asidx); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return ai_addrspacesize(sih, asidx); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ return ub_addrspacesize(sih, asidx); ++ else { ++ ASSERT(0); ++ return 0; ++ } ++} ++ ++void ++si_coreaddrspaceX(si_t *sih, uint asidx, uint32 *addr, uint32 *size) ++{ ++ /* Only supported for SOCI_AI */ ++ if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ ai_coreaddrspaceX(sih, asidx, addr, size); ++ else ++ *size = 0; ++} ++ ++uint32 ++si_core_cflags(si_t *sih, uint32 mask, uint32 val) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_core_cflags(sih, mask, val); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return ai_core_cflags(sih, mask, val); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ return ub_core_cflags(sih, mask, val); ++ else { ++ ASSERT(0); ++ return 0; ++ } ++} ++ ++void ++si_core_cflags_wo(si_t *sih, uint32 mask, uint32 val) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ sb_core_cflags_wo(sih, mask, val); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ ai_core_cflags_wo(sih, mask, val); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ ub_core_cflags_wo(sih, mask, val); ++ else ++ ASSERT(0); ++} ++ ++uint32 ++si_core_sflags(si_t *sih, uint32 mask, uint32 val) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_core_sflags(sih, mask, val); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return ai_core_sflags(sih, mask, val); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ return ub_core_sflags(sih, mask, val); ++ else { ++ ASSERT(0); ++ return 0; ++ } ++} ++ ++bool ++si_iscoreup(si_t *sih) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_iscoreup(sih); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return ai_iscoreup(sih); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ return ub_iscoreup(sih); ++ else { ++ ASSERT(0); ++ return FALSE; ++ } ++} ++ ++uint ++si_wrapperreg(si_t *sih, uint32 offset, uint32 mask, uint32 val) ++{ ++ /* only for AI back plane chips */ ++ if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return (ai_wrap_reg(sih, offset, mask, val)); ++ return 0; ++} ++ ++uint ++si_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ return sb_corereg(sih, coreidx, regoff, mask, val); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ return ai_corereg(sih, coreidx, regoff, mask, val); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ return ub_corereg(sih, coreidx, regoff, mask, val); ++ else { ++ ASSERT(0); ++ return 0; ++ } ++} ++ ++void ++si_core_disable(si_t *sih, uint32 bits) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ sb_core_disable(sih, bits); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ ai_core_disable(sih, bits); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ ub_core_disable(sih, bits); ++} ++ ++void ++si_core_reset(si_t *sih, uint32 bits, uint32 resetbits) ++{ ++ if (CHIPTYPE(sih->socitype) == SOCI_SB) ++ sb_core_reset(sih, bits, resetbits); ++ else if (CHIPTYPE(sih->socitype) == SOCI_AI) ++ ai_core_reset(sih, bits, resetbits); ++ else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) ++ ub_core_reset(sih, bits, resetbits); ++} ++ ++/* Run bist on current core. Caller needs to take care of core-specific bist hazards */ ++int ++si_corebist(si_t *sih) ++{ ++ uint32 cflags; ++ int result = 0; ++ ++ /* Read core control flags */ ++ cflags = si_core_cflags(sih, 0, 0); ++ ++ /* Set bist & fgc */ ++ si_core_cflags(sih, ~0, (SICF_BIST_EN | SICF_FGC)); ++ ++ /* Wait for bist done */ ++ SPINWAIT(((si_core_sflags(sih, 0, 0) & SISF_BIST_DONE) == 0), 100000); ++ ++ if (si_core_sflags(sih, 0, 0) & SISF_BIST_ERROR) ++ result = BCME_ERROR; ++ ++ /* Reset core control flags */ ++ si_core_cflags(sih, 0xffff, cflags); ++ ++ return result; ++} ++ ++static uint32 ++factor6(uint32 x) ++{ ++ switch (x) { ++ case CC_F6_2: return 2; ++ case CC_F6_3: return 3; ++ case CC_F6_4: return 4; ++ case CC_F6_5: return 5; ++ case CC_F6_6: return 6; ++ case CC_F6_7: return 7; ++ default: return 0; ++ } ++} ++ ++/* calculate the speed the SI would run at given a set of clockcontrol values */ ++uint32 ++si_clock_rate(uint32 pll_type, uint32 n, uint32 m) ++{ ++ uint32 n1, n2, clock, m1, m2, m3, mc; ++ ++ n1 = n & CN_N1_MASK; ++ n2 = (n & CN_N2_MASK) >> CN_N2_SHIFT; ++ ++ if (pll_type == PLL_TYPE6) { ++ if (m & CC_T6_MMASK) ++ return CC_T6_M1; ++ else ++ return CC_T6_M0; ++ } else if ((pll_type == PLL_TYPE1) || ++ (pll_type == PLL_TYPE3) || ++ (pll_type == PLL_TYPE4) || ++ (pll_type == PLL_TYPE7)) { ++ n1 = factor6(n1); ++ n2 += CC_F5_BIAS; ++ } else if (pll_type == PLL_TYPE2) { ++ n1 += CC_T2_BIAS; ++ n2 += CC_T2_BIAS; ++ ASSERT((n1 >= 2) && (n1 <= 7)); ++ ASSERT((n2 >= 5) && (n2 <= 23)); ++ } else if (pll_type == PLL_TYPE5) { ++ return (100000000); ++ } else ++ ASSERT(0); ++ /* PLL types 3 and 7 use BASE2 (25Mhz) */ ++ if ((pll_type == PLL_TYPE3) || ++ (pll_type == PLL_TYPE7)) { ++ clock = CC_CLOCK_BASE2 * n1 * n2; ++ } else ++ clock = CC_CLOCK_BASE1 * n1 * n2; ++ ++ if (clock == 0) ++ return 0; ++ ++ m1 = m & CC_M1_MASK; ++ m2 = (m & CC_M2_MASK) >> CC_M2_SHIFT; ++ m3 = (m & CC_M3_MASK) >> CC_M3_SHIFT; ++ mc = (m & CC_MC_MASK) >> CC_MC_SHIFT; ++ ++ if ((pll_type == PLL_TYPE1) || ++ (pll_type == PLL_TYPE3) || ++ (pll_type == PLL_TYPE4) || ++ (pll_type == PLL_TYPE7)) { ++ m1 = factor6(m1); ++ if ((pll_type == PLL_TYPE1) || (pll_type == PLL_TYPE3)) ++ m2 += CC_F5_BIAS; ++ else ++ m2 = factor6(m2); ++ m3 = factor6(m3); ++ ++ switch (mc) { ++ case CC_MC_BYPASS: return (clock); ++ case CC_MC_M1: return (clock / m1); ++ case CC_MC_M1M2: return (clock / (m1 * m2)); ++ case CC_MC_M1M2M3: return (clock / (m1 * m2 * m3)); ++ case CC_MC_M1M3: return (clock / (m1 * m3)); ++ default: return (0); ++ } ++ } else { ++ ASSERT(pll_type == PLL_TYPE2); ++ ++ m1 += CC_T2_BIAS; ++ m2 += CC_T2M2_BIAS; ++ m3 += CC_T2_BIAS; ++ ASSERT((m1 >= 2) && (m1 <= 7)); ++ ASSERT((m2 >= 3) && (m2 <= 10)); ++ ASSERT((m3 >= 2) && (m3 <= 7)); ++ ++ if ((mc & CC_T2MC_M1BYP) == 0) ++ clock /= m1; ++ if ((mc & CC_T2MC_M2BYP) == 0) ++ clock /= m2; ++ if ((mc & CC_T2MC_M3BYP) == 0) ++ clock /= m3; ++ ++ return (clock); ++ } ++} ++ ++ ++/* set chip watchdog reset timer to fire in 'ticks' */ ++void ++si_watchdog(si_t *sih, uint ticks) ++{ ++ uint nb, maxt; ++ ++ if (PMUCTL_ENAB(sih)) { ++ ++ if ((CHIPID(sih->chip) == BCM4319_CHIP_ID) && ++ (CHIPREV(sih->chiprev) == 0) && (ticks != 0)) { ++ si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, clk_ctl_st), ~0, 0x2); ++ si_setcore(sih, USB20D_CORE_ID, 0); ++ si_core_disable(sih, 1); ++ si_setcore(sih, CC_CORE_ID, 0); ++ } ++ ++ nb = (sih->ccrev < 26) ? 16 : ((sih->ccrev >= 37) ? 32 : 24); ++ /* The mips compiler uses the sllv instruction, ++ * so we specially handle the 32-bit case. ++ */ ++ if (nb == 32) ++ maxt = 0xffffffff; ++ else ++ maxt = ((1 << nb) - 1); ++ ++ if (ticks == 1) ++ ticks = 2; ++ else if (ticks > maxt) ++ ticks = maxt; ++ ++ si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, pmuwatchdog), ~0, ticks); ++ } else { ++ maxt = (1 << 28) - 1; ++ if (ticks > maxt) ++ ticks = maxt; ++ ++ si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, watchdog), ~0, ticks); ++ } ++} ++ ++/* trigger watchdog reset after ms milliseconds */ ++void ++si_watchdog_ms(si_t *sih, uint32 ms) ++{ ++ si_watchdog(sih, wd_msticks * ms); ++} ++ ++uint32 si_watchdog_msticks(void) ++{ ++ return wd_msticks; ++} ++ ++bool ++si_taclear(si_t *sih, bool details) ++{ ++ return FALSE; ++} ++ ++ ++ ++/* return the slow clock source - LPO, XTAL, or PCI */ ++static uint ++si_slowclk_src(si_info_t *sii) ++{ ++ chipcregs_t *cc; ++ ++ ASSERT(SI_FAST(sii) || si_coreid(&sii->pub) == CC_CORE_ID); ++ ++ if (sii->pub.ccrev < 6) { ++ if ((BUSTYPE(sii->pub.bustype) == PCI_BUS) && ++ (OSL_PCI_READ_CONFIG(sii->osh, PCI_GPIO_OUT, sizeof(uint32)) & ++ PCI_CFG_GPIO_SCS)) ++ return (SCC_SS_PCI); ++ else ++ return (SCC_SS_XTAL); ++ } else if (sii->pub.ccrev < 10) { ++ cc = (chipcregs_t *)si_setcoreidx(&sii->pub, sii->curidx); ++ return (R_REG(sii->osh, &cc->slow_clk_ctl) & SCC_SS_MASK); ++ } else /* Insta-clock */ ++ return (SCC_SS_XTAL); ++} ++ ++/* return the ILP (slowclock) min or max frequency */ ++static uint ++si_slowclk_freq(si_info_t *sii, bool max_freq, chipcregs_t *cc) ++{ ++ uint32 slowclk; ++ uint div; ++ ++ ASSERT(SI_FAST(sii) || si_coreid(&sii->pub) == CC_CORE_ID); ++ ++ /* shouldn't be here unless we've established the chip has dynamic clk control */ ++ ASSERT(R_REG(sii->osh, &cc->capabilities) & CC_CAP_PWR_CTL); ++ ++ slowclk = si_slowclk_src(sii); ++ if (sii->pub.ccrev < 6) { ++ if (slowclk == SCC_SS_PCI) ++ return (max_freq ? (PCIMAXFREQ / 64) : (PCIMINFREQ / 64)); ++ else ++ return (max_freq ? (XTALMAXFREQ / 32) : (XTALMINFREQ / 32)); ++ } else if (sii->pub.ccrev < 10) { ++ div = 4 * ++ (((R_REG(sii->osh, &cc->slow_clk_ctl) & SCC_CD_MASK) >> SCC_CD_SHIFT) + 1); ++ if (slowclk == SCC_SS_LPO) ++ return (max_freq ? LPOMAXFREQ : LPOMINFREQ); ++ else if (slowclk == SCC_SS_XTAL) ++ return (max_freq ? (XTALMAXFREQ / div) : (XTALMINFREQ / div)); ++ else if (slowclk == SCC_SS_PCI) ++ return (max_freq ? (PCIMAXFREQ / div) : (PCIMINFREQ / div)); ++ else ++ ASSERT(0); ++ } else { ++ /* Chipc rev 10 is InstaClock */ ++ div = R_REG(sii->osh, &cc->system_clk_ctl) >> SYCC_CD_SHIFT; ++ div = 4 * (div + 1); ++ return (max_freq ? XTALMAXFREQ : (XTALMINFREQ / div)); ++ } ++ return (0); ++} ++ ++static void ++si_clkctl_setdelay(si_info_t *sii, void *chipcregs) ++{ ++ chipcregs_t *cc = (chipcregs_t *)chipcregs; ++ uint slowmaxfreq, pll_delay, slowclk; ++ uint pll_on_delay, fref_sel_delay; ++ ++ pll_delay = PLL_DELAY; ++ ++ /* If the slow clock is not sourced by the xtal then add the xtal_on_delay ++ * since the xtal will also be powered down by dynamic clk control logic. ++ */ ++ ++ slowclk = si_slowclk_src(sii); ++ if (slowclk != SCC_SS_XTAL) ++ pll_delay += XTAL_ON_DELAY; ++ ++ /* Starting with 4318 it is ILP that is used for the delays */ ++ slowmaxfreq = si_slowclk_freq(sii, (sii->pub.ccrev >= 10) ? FALSE : TRUE, cc); ++ ++ pll_on_delay = ((slowmaxfreq * pll_delay) + 999999) / 1000000; ++ fref_sel_delay = ((slowmaxfreq * FREF_DELAY) + 999999) / 1000000; ++ ++ W_REG(sii->osh, &cc->pll_on_delay, pll_on_delay); ++ W_REG(sii->osh, &cc->fref_sel_delay, fref_sel_delay); ++} ++ ++/* initialize power control delay registers */ ++void ++si_clkctl_init(si_t *sih) ++{ ++ si_info_t *sii; ++ uint origidx = 0; ++ chipcregs_t *cc; ++ bool fast; ++ ++ if (!CCCTL_ENAB(sih)) ++ return; ++ ++ sii = SI_INFO(sih); ++ fast = SI_FAST(sii); ++ if (!fast) { ++ origidx = sii->curidx; ++ if ((cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0)) == NULL) ++ return; ++ } else if ((cc = (chipcregs_t *)CCREGS_FAST(sii)) == NULL) ++ return; ++ ASSERT(cc != NULL); ++ ++ /* set all Instaclk chip ILP to 1 MHz */ ++ if (sih->ccrev >= 10) ++ SET_REG(sii->osh, &cc->system_clk_ctl, SYCC_CD_MASK, ++ (ILP_DIV_1MHZ << SYCC_CD_SHIFT)); ++ ++ si_clkctl_setdelay(sii, (void *)(uintptr)cc); ++ ++ if (!fast) ++ si_setcoreidx(sih, origidx); ++} ++ ++ ++/* change logical "focus" to the gpio core for optimized access */ ++void * ++si_gpiosetcore(si_t *sih) ++{ ++ return (si_setcoreidx(sih, SI_CC_IDX)); ++} ++ ++/* ++ * mask & set gpiocontrol bits. ++ * If a gpiocontrol bit is set to 0, chipcommon controls the corresponding GPIO pin. ++ * If a gpiocontrol bit is set to 1, the GPIO pin is no longer a GPIO and becomes dedicated ++ * to some chip-specific purpose. ++ */ ++uint32 ++si_gpiocontrol(si_t *sih, uint32 mask, uint32 val, uint8 priority) ++{ ++ uint regoff; ++ ++ regoff = 0; ++ ++ /* gpios could be shared on router platforms ++ * ignore reservation if it's high priority (e.g., test apps) ++ */ ++ if ((priority != GPIO_HI_PRIORITY) && ++ (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { ++ mask = priority ? (si_gpioreservation & mask) : ++ ((si_gpioreservation | mask) & ~(si_gpioreservation)); ++ val &= mask; ++ } ++ ++ regoff = OFFSETOF(chipcregs_t, gpiocontrol); ++ return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); ++} ++ ++/* mask&set gpio output enable bits */ ++uint32 ++si_gpioouten(si_t *sih, uint32 mask, uint32 val, uint8 priority) ++{ ++ uint regoff; ++ ++ regoff = 0; ++ ++ /* gpios could be shared on router platforms ++ * ignore reservation if it's high priority (e.g., test apps) ++ */ ++ if ((priority != GPIO_HI_PRIORITY) && ++ (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { ++ mask = priority ? (si_gpioreservation & mask) : ++ ((si_gpioreservation | mask) & ~(si_gpioreservation)); ++ val &= mask; ++ } ++ ++ regoff = OFFSETOF(chipcregs_t, gpioouten); ++ return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); ++} ++ ++/* mask&set gpio output bits */ ++uint32 ++si_gpioout(si_t *sih, uint32 mask, uint32 val, uint8 priority) ++{ ++ uint regoff; ++ ++ regoff = 0; ++ ++ /* gpios could be shared on router platforms ++ * ignore reservation if it's high priority (e.g., test apps) ++ */ ++ if ((priority != GPIO_HI_PRIORITY) && ++ (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { ++ mask = priority ? (si_gpioreservation & mask) : ++ ((si_gpioreservation | mask) & ~(si_gpioreservation)); ++ val &= mask; ++ } ++ ++ regoff = OFFSETOF(chipcregs_t, gpioout); ++ return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); ++} ++ ++/* reserve one gpio */ ++uint32 ++si_gpioreserve(si_t *sih, uint32 gpio_bitmask, uint8 priority) ++{ ++ /* only cores on SI_BUS share GPIO's and only applcation users need to ++ * reserve/release GPIO ++ */ ++ if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) { ++ ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority)); ++ return 0xffffffff; ++ } ++ /* make sure only one bit is set */ ++ if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) { ++ ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1))); ++ return 0xffffffff; ++ } ++ ++ /* already reserved */ ++ if (si_gpioreservation & gpio_bitmask) ++ return 0xffffffff; ++ /* set reservation */ ++ si_gpioreservation |= gpio_bitmask; ++ ++ return si_gpioreservation; ++} ++ ++/* release one gpio */ ++/* ++ * releasing the gpio doesn't change the current value on the GPIO last write value ++ * persists till some one overwrites it ++ */ ++ ++uint32 ++si_gpiorelease(si_t *sih, uint32 gpio_bitmask, uint8 priority) ++{ ++ /* only cores on SI_BUS share GPIO's and only applcation users need to ++ * reserve/release GPIO ++ */ ++ if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) { ++ ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority)); ++ return 0xffffffff; ++ } ++ /* make sure only one bit is set */ ++ if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) { ++ ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1))); ++ return 0xffffffff; ++ } ++ ++ /* already released */ ++ if (!(si_gpioreservation & gpio_bitmask)) ++ return 0xffffffff; ++ ++ /* clear reservation */ ++ si_gpioreservation &= ~gpio_bitmask; ++ ++ return si_gpioreservation; ++} ++ ++/* return the current gpioin register value */ ++uint32 ++si_gpioin(si_t *sih) ++{ ++ uint regoff; ++ ++ regoff = OFFSETOF(chipcregs_t, gpioin); ++ return (si_corereg(sih, SI_CC_IDX, regoff, 0, 0)); ++} ++ ++/* mask&set gpio interrupt polarity bits */ ++uint32 ++si_gpiointpolarity(si_t *sih, uint32 mask, uint32 val, uint8 priority) ++{ ++ uint regoff; ++ ++ /* gpios could be shared on router platforms */ ++ if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { ++ mask = priority ? (si_gpioreservation & mask) : ++ ((si_gpioreservation | mask) & ~(si_gpioreservation)); ++ val &= mask; ++ } ++ ++ regoff = OFFSETOF(chipcregs_t, gpiointpolarity); ++ return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); ++} ++ ++/* mask&set gpio interrupt mask bits */ ++uint32 ++si_gpiointmask(si_t *sih, uint32 mask, uint32 val, uint8 priority) ++{ ++ uint regoff; ++ ++ /* gpios could be shared on router platforms */ ++ if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { ++ mask = priority ? (si_gpioreservation & mask) : ++ ((si_gpioreservation | mask) & ~(si_gpioreservation)); ++ val &= mask; ++ } ++ ++ regoff = OFFSETOF(chipcregs_t, gpiointmask); ++ return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); ++} ++ ++/* assign the gpio to an led */ ++uint32 ++si_gpioled(si_t *sih, uint32 mask, uint32 val) ++{ ++ if (sih->ccrev < 16) ++ return 0xffffffff; ++ ++ /* gpio led powersave reg */ ++ return (si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, gpiotimeroutmask), mask, val)); ++} ++ ++/* mask&set gpio timer val */ ++uint32 ++si_gpiotimerval(si_t *sih, uint32 mask, uint32 gpiotimerval) ++{ ++ if (sih->ccrev < 16) ++ return 0xffffffff; ++ ++ return (si_corereg(sih, SI_CC_IDX, ++ OFFSETOF(chipcregs_t, gpiotimerval), mask, gpiotimerval)); ++} ++ ++uint32 ++si_gpiopull(si_t *sih, bool updown, uint32 mask, uint32 val) ++{ ++ uint offs; ++ ++ if (sih->ccrev < 20) ++ return 0xffffffff; ++ ++ offs = (updown ? OFFSETOF(chipcregs_t, gpiopulldown) : OFFSETOF(chipcregs_t, gpiopullup)); ++ return (si_corereg(sih, SI_CC_IDX, offs, mask, val)); ++} ++ ++uint32 ++si_gpioevent(si_t *sih, uint regtype, uint32 mask, uint32 val) ++{ ++ uint offs; ++ ++ if (sih->ccrev < 11) ++ return 0xffffffff; ++ ++ if (regtype == GPIO_REGEVT) ++ offs = OFFSETOF(chipcregs_t, gpioevent); ++ else if (regtype == GPIO_REGEVT_INTMSK) ++ offs = OFFSETOF(chipcregs_t, gpioeventintmask); ++ else if (regtype == GPIO_REGEVT_INTPOL) ++ offs = OFFSETOF(chipcregs_t, gpioeventintpolarity); ++ else ++ return 0xffffffff; ++ ++ return (si_corereg(sih, SI_CC_IDX, offs, mask, val)); ++} ++ ++void * ++si_gpio_handler_register(si_t *sih, uint32 event, ++ bool level, gpio_handler_t cb, void *arg) ++{ ++ si_info_t *sii; ++ gpioh_item_t *gi; ++ ++ ASSERT(event); ++ ASSERT(cb != NULL); ++ ++ sii = SI_INFO(sih); ++ if (sih->ccrev < 11) ++ return NULL; ++ ++ if ((gi = MALLOC(sii->osh, sizeof(gpioh_item_t))) == NULL) ++ return NULL; ++ ++ bzero(gi, sizeof(gpioh_item_t)); ++ gi->event = event; ++ gi->handler = cb; ++ gi->arg = arg; ++ gi->level = level; ++ ++ gi->next = sii->gpioh_head; ++ sii->gpioh_head = gi; ++ ++ return (void *)(gi); ++} ++ ++void ++si_gpio_handler_unregister(si_t *sih, void *gpioh) ++{ ++ si_info_t *sii; ++ gpioh_item_t *p, *n; ++ ++ sii = SI_INFO(sih); ++ if (sih->ccrev < 11) ++ return; ++ ++ ASSERT(sii->gpioh_head != NULL); ++ if ((void*)sii->gpioh_head == gpioh) { ++ sii->gpioh_head = sii->gpioh_head->next; ++ MFREE(sii->osh, gpioh, sizeof(gpioh_item_t)); ++ return; ++ } else { ++ p = sii->gpioh_head; ++ n = p->next; ++ while (n) { ++ if ((void*)n == gpioh) { ++ p->next = n->next; ++ MFREE(sii->osh, gpioh, sizeof(gpioh_item_t)); ++ return; ++ } ++ p = n; ++ n = n->next; ++ } ++ } ++ ++ ASSERT(0); /* Not found in list */ ++} ++ ++void ++si_gpio_handler_process(si_t *sih) ++{ ++ si_info_t *sii; ++ gpioh_item_t *h; ++ uint32 level = si_gpioin(sih); ++ uint32 levelp = si_gpiointpolarity(sih, 0, 0, 0); ++ uint32 edge = si_gpioevent(sih, GPIO_REGEVT, 0, 0); ++ uint32 edgep = si_gpioevent(sih, GPIO_REGEVT_INTPOL, 0, 0); ++ ++ sii = SI_INFO(sih); ++ for (h = sii->gpioh_head; h != NULL; h = h->next) { ++ if (h->handler) { ++ uint32 status = (h->level ? level : edge) & h->event; ++ uint32 polarity = (h->level ? levelp : edgep) & h->event; ++ ++ /* polarity bitval is opposite of status bitval */ ++ if (status ^ polarity) ++ h->handler(status, h->arg); ++ } ++ } ++ ++ si_gpioevent(sih, GPIO_REGEVT, edge, edge); /* clear edge-trigger status */ ++} ++ ++uint32 ++si_gpio_int_enable(si_t *sih, bool enable) ++{ ++ uint offs; ++ ++ if (sih->ccrev < 11) ++ return 0xffffffff; ++ ++ offs = OFFSETOF(chipcregs_t, intmask); ++ return (si_corereg(sih, SI_CC_IDX, offs, CI_GPIO, (enable ? CI_GPIO : 0))); ++} ++ ++ ++/* Return the size of the specified SOCRAM bank */ ++static uint ++socram_banksize(si_info_t *sii, sbsocramregs_t *regs, uint8 idx, uint8 mem_type) ++{ ++ uint banksize, bankinfo; ++ uint bankidx = idx | (mem_type << SOCRAM_BANKIDX_MEMTYPE_SHIFT); ++ ++ ASSERT(mem_type <= SOCRAM_MEMTYPE_DEVRAM); ++ ++ W_REG(sii->osh, ®s->bankidx, bankidx); ++ bankinfo = R_REG(sii->osh, ®s->bankinfo); ++ banksize = SOCRAM_BANKINFO_SZBASE * ((bankinfo & SOCRAM_BANKINFO_SZMASK) + 1); ++ return banksize; ++} ++ ++void ++si_socdevram(si_t *sih, bool set, uint8 *enable, uint8 *protect, uint8 *remap) ++{ ++ si_info_t *sii; ++ uint origidx; ++ uint intr_val = 0; ++ sbsocramregs_t *regs; ++ bool wasup; ++ uint corerev; ++ ++ sii = SI_INFO(sih); ++ ++ /* Block ints and save current core */ ++ INTR_OFF(sii, intr_val); ++ origidx = si_coreidx(sih); ++ ++ if (!set) ++ *enable = *protect = *remap = 0; ++ ++ /* Switch to SOCRAM core */ ++ if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) ++ goto done; ++ ++ /* Get info for determining size */ ++ if (!(wasup = si_iscoreup(sih))) ++ si_core_reset(sih, 0, 0); ++ ++ corerev = si_corerev(sih); ++ if (corerev >= 10) { ++ uint32 extcinfo; ++ uint8 nb; ++ uint8 i; ++ uint32 bankidx, bankinfo; ++ ++ extcinfo = R_REG(sii->osh, ®s->extracoreinfo); ++ nb = ((extcinfo & SOCRAM_DEVRAMBANK_MASK) >> SOCRAM_DEVRAMBANK_SHIFT); ++ for (i = 0; i < nb; i++) { ++ bankidx = i | (SOCRAM_MEMTYPE_DEVRAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); ++ W_REG(sii->osh, ®s->bankidx, bankidx); ++ bankinfo = R_REG(sii->osh, ®s->bankinfo); ++ if (set) { ++ bankinfo &= ~SOCRAM_BANKINFO_DEVRAMSEL_MASK; ++ bankinfo &= ~SOCRAM_BANKINFO_DEVRAMPRO_MASK; ++ bankinfo &= ~SOCRAM_BANKINFO_DEVRAMREMAP_MASK; ++ if (*enable) { ++ bankinfo |= (1 << SOCRAM_BANKINFO_DEVRAMSEL_SHIFT); ++ if (*protect) ++ bankinfo |= (1 << SOCRAM_BANKINFO_DEVRAMPRO_SHIFT); ++ if ((corerev >= 16) && *remap) ++ bankinfo |= ++ (1 << SOCRAM_BANKINFO_DEVRAMREMAP_SHIFT); ++ } ++ W_REG(sii->osh, ®s->bankinfo, bankinfo); ++ } ++ else if (i == 0) { ++ if (bankinfo & SOCRAM_BANKINFO_DEVRAMSEL_MASK) { ++ *enable = 1; ++ if (bankinfo & SOCRAM_BANKINFO_DEVRAMPRO_MASK) ++ *protect = 1; ++ if (bankinfo & SOCRAM_BANKINFO_DEVRAMREMAP_MASK) ++ *remap = 1; ++ } ++ } ++ } ++ } ++ ++ /* Return to previous state and core */ ++ if (!wasup) ++ si_core_disable(sih, 0); ++ si_setcoreidx(sih, origidx); ++ ++done: ++ INTR_RESTORE(sii, intr_val); ++} ++ ++bool ++si_socdevram_remap_isenb(si_t *sih) ++{ ++ si_info_t *sii; ++ uint origidx; ++ uint intr_val = 0; ++ sbsocramregs_t *regs; ++ bool wasup, remap = FALSE; ++ uint corerev; ++ uint32 extcinfo; ++ uint8 nb; ++ uint8 i; ++ uint32 bankidx, bankinfo; ++ ++ sii = SI_INFO(sih); ++ ++ /* Block ints and save current core */ ++ INTR_OFF(sii, intr_val); ++ origidx = si_coreidx(sih); ++ ++ /* Switch to SOCRAM core */ ++ if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) ++ goto done; ++ ++ /* Get info for determining size */ ++ if (!(wasup = si_iscoreup(sih))) ++ si_core_reset(sih, 0, 0); ++ ++ corerev = si_corerev(sih); ++ if (corerev >= 16) { ++ extcinfo = R_REG(sii->osh, ®s->extracoreinfo); ++ nb = ((extcinfo & SOCRAM_DEVRAMBANK_MASK) >> SOCRAM_DEVRAMBANK_SHIFT); ++ for (i = 0; i < nb; i++) { ++ bankidx = i | (SOCRAM_MEMTYPE_DEVRAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); ++ W_REG(sii->osh, ®s->bankidx, bankidx); ++ bankinfo = R_REG(sii->osh, ®s->bankinfo); ++ if (bankinfo & SOCRAM_BANKINFO_DEVRAMREMAP_MASK) { ++ remap = TRUE; ++ break; ++ } ++ } ++ } ++ ++ /* Return to previous state and core */ ++ if (!wasup) ++ si_core_disable(sih, 0); ++ si_setcoreidx(sih, origidx); ++ ++done: ++ INTR_RESTORE(sii, intr_val); ++ return remap; ++} ++ ++bool ++si_socdevram_pkg(si_t *sih) ++{ ++ if (si_socdevram_size(sih) > 0) ++ return TRUE; ++ else ++ return FALSE; ++} ++ ++uint32 ++si_socdevram_size(si_t *sih) ++{ ++ si_info_t *sii; ++ uint origidx; ++ uint intr_val = 0; ++ uint32 memsize = 0; ++ sbsocramregs_t *regs; ++ bool wasup; ++ uint corerev; ++ ++ sii = SI_INFO(sih); ++ ++ /* Block ints and save current core */ ++ INTR_OFF(sii, intr_val); ++ origidx = si_coreidx(sih); ++ ++ /* Switch to SOCRAM core */ ++ if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) ++ goto done; ++ ++ /* Get info for determining size */ ++ if (!(wasup = si_iscoreup(sih))) ++ si_core_reset(sih, 0, 0); ++ ++ corerev = si_corerev(sih); ++ if (corerev >= 10) { ++ uint32 extcinfo; ++ uint8 nb; ++ uint8 i; ++ ++ extcinfo = R_REG(sii->osh, ®s->extracoreinfo); ++ nb = (((extcinfo & SOCRAM_DEVRAMBANK_MASK) >> SOCRAM_DEVRAMBANK_SHIFT)); ++ for (i = 0; i < nb; i++) ++ memsize += socram_banksize(sii, regs, i, SOCRAM_MEMTYPE_DEVRAM); ++ } ++ ++ /* Return to previous state and core */ ++ if (!wasup) ++ si_core_disable(sih, 0); ++ si_setcoreidx(sih, origidx); ++ ++done: ++ INTR_RESTORE(sii, intr_val); ++ ++ return memsize; ++} ++ ++uint32 ++si_socdevram_remap_size(si_t *sih) ++{ ++ si_info_t *sii; ++ uint origidx; ++ uint intr_val = 0; ++ uint32 memsize = 0, banksz; ++ sbsocramregs_t *regs; ++ bool wasup; ++ uint corerev; ++ uint32 extcinfo; ++ uint8 nb; ++ uint8 i; ++ uint32 bankidx, bankinfo; ++ ++ sii = SI_INFO(sih); ++ ++ /* Block ints and save current core */ ++ INTR_OFF(sii, intr_val); ++ origidx = si_coreidx(sih); ++ ++ /* Switch to SOCRAM core */ ++ if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) ++ goto done; ++ ++ /* Get info for determining size */ ++ if (!(wasup = si_iscoreup(sih))) ++ si_core_reset(sih, 0, 0); ++ ++ corerev = si_corerev(sih); ++ if (corerev >= 16) { ++ extcinfo = R_REG(sii->osh, ®s->extracoreinfo); ++ nb = (((extcinfo & SOCRAM_DEVRAMBANK_MASK) >> SOCRAM_DEVRAMBANK_SHIFT)); ++ ++ /* ++ * FIX: A0 Issue: Max addressable is 512KB, instead 640KB ++ * Only four banks are accessible to ARM ++ */ ++ if ((corerev == 16) && (nb == 5)) ++ nb = 4; ++ ++ for (i = 0; i < nb; i++) { ++ bankidx = i | (SOCRAM_MEMTYPE_DEVRAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); ++ W_REG(sii->osh, ®s->bankidx, bankidx); ++ bankinfo = R_REG(sii->osh, ®s->bankinfo); ++ if (bankinfo & SOCRAM_BANKINFO_DEVRAMREMAP_MASK) { ++ banksz = socram_banksize(sii, regs, i, SOCRAM_MEMTYPE_DEVRAM); ++ memsize += banksz; ++ } else { ++ /* Account only consecutive banks for now */ ++ break; ++ } ++ } ++ } ++ ++ /* Return to previous state and core */ ++ if (!wasup) ++ si_core_disable(sih, 0); ++ si_setcoreidx(sih, origidx); ++ ++done: ++ INTR_RESTORE(sii, intr_val); ++ ++ return memsize; ++} ++ ++/* Return the RAM size of the SOCRAM core */ ++uint32 ++si_socram_size(si_t *sih) ++{ ++ si_info_t *sii; ++ uint origidx; ++ uint intr_val = 0; ++ ++ sbsocramregs_t *regs; ++ bool wasup; ++ uint corerev; ++ uint32 coreinfo; ++ uint memsize = 0; ++ ++ sii = SI_INFO(sih); ++ ++ /* Block ints and save current core */ ++ INTR_OFF(sii, intr_val); ++ origidx = si_coreidx(sih); ++ ++ /* Switch to SOCRAM core */ ++ if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) ++ goto done; ++ ++ /* Get info for determining size */ ++ if (!(wasup = si_iscoreup(sih))) ++ si_core_reset(sih, 0, 0); ++ corerev = si_corerev(sih); ++ coreinfo = R_REG(sii->osh, ®s->coreinfo); ++ ++ /* Calculate size from coreinfo based on rev */ ++ if (corerev == 0) ++ memsize = 1 << (16 + (coreinfo & SRCI_MS0_MASK)); ++ else if (corerev < 3) { ++ memsize = 1 << (SR_BSZ_BASE + (coreinfo & SRCI_SRBSZ_MASK)); ++ memsize *= (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; ++ } else if ((corerev <= 7) || (corerev == 12)) { ++ uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; ++ uint bsz = (coreinfo & SRCI_SRBSZ_MASK); ++ uint lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT; ++ if (lss != 0) ++ nb --; ++ memsize = nb * (1 << (bsz + SR_BSZ_BASE)); ++ if (lss != 0) ++ memsize += (1 << ((lss - 1) + SR_BSZ_BASE)); ++ } else { ++ uint8 i; ++ uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; ++ for (i = 0; i < nb; i++) ++ memsize += socram_banksize(sii, regs, i, SOCRAM_MEMTYPE_RAM); ++ } ++ ++ /* Return to previous state and core */ ++ if (!wasup) ++ si_core_disable(sih, 0); ++ si_setcoreidx(sih, origidx); ++ ++done: ++ INTR_RESTORE(sii, intr_val); ++ ++ return memsize; ++} ++ ++ ++/* Return the TCM-RAM size of the ARMCR4 core. */ ++uint32 ++si_tcm_size(si_t *sih) ++{ ++ si_info_t *sii; ++ uint origidx; ++ uint intr_val = 0; ++ uint8 *regs; ++ bool wasup; ++ uint32 corecap; ++ uint memsize = 0; ++ uint32 nab = 0; ++ uint32 nbb = 0; ++ uint32 totb = 0; ++ uint32 bxinfo = 0; ++ uint32 idx = 0; ++ uint32 *arm_cap_reg; ++ uint32 *arm_bidx; ++ uint32 *arm_binfo; ++ ++ sii = SI_INFO(sih); ++ ++ /* Block ints and save current core */ ++ INTR_OFF(sii, intr_val); ++ origidx = si_coreidx(sih); ++ ++ /* Switch to CR4 core */ ++ if (!(regs = si_setcore(sih, ARMCR4_CORE_ID, 0))) ++ goto done; ++ ++ /* Get info for determining size. If in reset, come out of reset, ++ * but remain in halt ++ */ ++ if (!(wasup = si_iscoreup(sih))) ++ si_core_reset(sih, SICF_CPUHALT, SICF_CPUHALT); ++ ++ arm_cap_reg = (uint32 *)(regs + SI_CR4_CAP); ++ corecap = R_REG(sii->osh, arm_cap_reg); ++ ++ nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT; ++ nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT; ++ totb = nab + nbb; ++ ++ arm_bidx = (uint32 *)(regs + SI_CR4_BANKIDX); ++ arm_binfo = (uint32 *)(regs + SI_CR4_BANKINFO); ++ for (idx = 0; idx < totb; idx++) { ++ W_REG(sii->osh, arm_bidx, idx); ++ ++ bxinfo = R_REG(sii->osh, arm_binfo); ++ memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT; ++ } ++ ++ /* Return to previous state and core */ ++ if (!wasup) ++ si_core_disable(sih, 0); ++ si_setcoreidx(sih, origidx); ++ ++done: ++ INTR_RESTORE(sii, intr_val); ++ ++ return memsize; ++} ++ ++uint32 ++si_socram_srmem_size(si_t *sih) ++{ ++ si_info_t *sii; ++ uint origidx; ++ uint intr_val = 0; ++ ++ sbsocramregs_t *regs; ++ bool wasup; ++ uint corerev; ++ uint32 coreinfo; ++ uint memsize = 0; ++ ++ if ((CHIPID(sih->chip) == BCM4334_CHIP_ID) && (CHIPREV(sih->chiprev) < 2)) { ++ return (32 * 1024); ++ } ++ ++ sii = SI_INFO(sih); ++ ++ /* Block ints and save current core */ ++ INTR_OFF(sii, intr_val); ++ origidx = si_coreidx(sih); ++ ++ /* Switch to SOCRAM core */ ++ if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) ++ goto done; ++ ++ /* Get info for determining size */ ++ if (!(wasup = si_iscoreup(sih))) ++ si_core_reset(sih, 0, 0); ++ corerev = si_corerev(sih); ++ coreinfo = R_REG(sii->osh, ®s->coreinfo); ++ ++ /* Calculate size from coreinfo based on rev */ ++ if (corerev >= 16) { ++ uint8 i; ++ uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; ++ for (i = 0; i < nb; i++) { ++ W_REG(sii->osh, ®s->bankidx, i); ++ if (R_REG(sii->osh, ®s->bankinfo) & SOCRAM_BANKINFO_RETNTRAM_MASK) ++ memsize += socram_banksize(sii, regs, i, SOCRAM_MEMTYPE_RAM); ++ } ++ } ++ ++ /* Return to previous state and core */ ++ if (!wasup) ++ si_core_disable(sih, 0); ++ si_setcoreidx(sih, origidx); ++ ++done: ++ INTR_RESTORE(sii, intr_val); ++ ++ return memsize; ++} ++ ++ ++void ++si_btcgpiowar(si_t *sih) ++{ ++ si_info_t *sii; ++ uint origidx; ++ uint intr_val = 0; ++ chipcregs_t *cc; ++ ++ sii = SI_INFO(sih); ++ ++ /* Make sure that there is ChipCommon core present && ++ * UART_TX is strapped to 1 ++ */ ++ if (!(sih->cccaps & CC_CAP_UARTGPIO)) ++ return; ++ ++ /* si_corereg cannot be used as we have to guarantee 8-bit read/writes */ ++ INTR_OFF(sii, intr_val); ++ ++ origidx = si_coreidx(sih); ++ ++ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ ASSERT(cc != NULL); ++ ++ W_REG(sii->osh, &cc->uart0mcr, R_REG(sii->osh, &cc->uart0mcr) | 0x04); ++ ++ /* restore the original index */ ++ si_setcoreidx(sih, origidx); ++ ++ INTR_RESTORE(sii, intr_val); ++} ++ ++void ++si_chipcontrl_btshd0_4331(si_t *sih, bool on) ++{ ++ si_info_t *sii; ++ chipcregs_t *cc; ++ uint origidx; ++ uint32 val; ++ uint intr_val = 0; ++ ++ sii = SI_INFO(sih); ++ ++ INTR_OFF(sii, intr_val); ++ ++ origidx = si_coreidx(sih); ++ ++ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ ++ val = R_REG(sii->osh, &cc->chipcontrol); ++ ++ /* bt_shd0 controls are same for 4331 chiprevs 0 and 1, packages 12x9 and 12x12 */ ++ if (on) { ++ /* Enable bt_shd0 on gpio4: */ ++ val |= (CCTRL4331_BT_SHD0_ON_GPIO4); ++ W_REG(sii->osh, &cc->chipcontrol, val); ++ } else { ++ val &= ~(CCTRL4331_BT_SHD0_ON_GPIO4); ++ W_REG(sii->osh, &cc->chipcontrol, val); ++ } ++ ++ /* restore the original index */ ++ si_setcoreidx(sih, origidx); ++ ++ INTR_RESTORE(sii, intr_val); ++} ++ ++void ++si_chipcontrl_restore(si_t *sih, uint32 val) ++{ ++ si_info_t *sii; ++ chipcregs_t *cc; ++ uint origidx; ++ ++ sii = SI_INFO(sih); ++ origidx = si_coreidx(sih); ++ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ W_REG(sii->osh, &cc->chipcontrol, val); ++ si_setcoreidx(sih, origidx); ++} ++ ++uint32 ++si_chipcontrl_read(si_t *sih) ++{ ++ si_info_t *sii; ++ chipcregs_t *cc; ++ uint origidx; ++ uint32 val; ++ ++ sii = SI_INFO(sih); ++ origidx = si_coreidx(sih); ++ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ val = R_REG(sii->osh, &cc->chipcontrol); ++ si_setcoreidx(sih, origidx); ++ return val; ++} ++ ++void ++si_chipcontrl_epa4331(si_t *sih, bool on) ++{ ++ si_info_t *sii; ++ chipcregs_t *cc; ++ uint origidx; ++ uint32 val; ++ ++ sii = SI_INFO(sih); ++ origidx = si_coreidx(sih); ++ ++ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ ++ val = R_REG(sii->osh, &cc->chipcontrol); ++ ++ if (on) { ++ if (sih->chippkg == 9 || sih->chippkg == 0xb) { ++ val |= (CCTRL4331_EXTPA_EN | CCTRL4331_EXTPA_ON_GPIO2_5); ++ /* Ext PA Controls for 4331 12x9 Package */ ++ W_REG(sii->osh, &cc->chipcontrol, val); ++ } else { ++ /* Ext PA Controls for 4331 12x12 Package */ ++ if (sih->chiprev > 0) { ++ W_REG(sii->osh, &cc->chipcontrol, val | ++ (CCTRL4331_EXTPA_EN) | (CCTRL4331_EXTPA_EN2)); ++ } else { ++ W_REG(sii->osh, &cc->chipcontrol, val | (CCTRL4331_EXTPA_EN)); ++ } ++ } ++ } else { ++ val &= ~(CCTRL4331_EXTPA_EN | CCTRL4331_EXTPA_EN2 | CCTRL4331_EXTPA_ON_GPIO2_5); ++ W_REG(sii->osh, &cc->chipcontrol, val); ++ } ++ ++ si_setcoreidx(sih, origidx); ++} ++ ++/* switch muxed pins, on: SROM, off: FEMCTRL */ ++void ++si_chipcontrl_srom4360(si_t *sih, bool on) ++{ ++ si_info_t *sii; ++ chipcregs_t *cc; ++ uint origidx; ++ uint32 val; ++ ++ sii = SI_INFO(sih); ++ origidx = si_coreidx(sih); ++ ++ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ ++ val = R_REG(sii->osh, &cc->chipcontrol); ++ ++ if (on) { ++ val &= ~(CCTRL4360_SECI_MODE | ++ CCTRL4360_BTSWCTRL_MODE | ++ CCTRL4360_EXTRA_FEMCTRL_MODE | ++ CCTRL4360_BT_LGCY_MODE | ++ CCTRL4360_CORE2FEMCTRL4_ON); ++ ++ W_REG(sii->osh, &cc->chipcontrol, val); ++ } else { ++ } ++ ++ si_setcoreidx(sih, origidx); ++} ++ ++void ++si_chipcontrl_epa4331_wowl(si_t *sih, bool enter_wowl) ++{ ++ si_info_t *sii; ++ chipcregs_t *cc; ++ uint origidx; ++ uint32 val; ++ bool sel_chip; ++ ++ sel_chip = (CHIPID(sih->chip) == BCM4331_CHIP_ID) || ++ (CHIPID(sih->chip) == BCM43431_CHIP_ID); ++ sel_chip &= ((sih->chippkg == 9 || sih->chippkg == 0xb)); ++ ++ if (!sel_chip) ++ return; ++ ++ sii = SI_INFO(sih); ++ origidx = si_coreidx(sih); ++ ++ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ ++ val = R_REG(sii->osh, &cc->chipcontrol); ++ ++ if (enter_wowl) { ++ val |= CCTRL4331_EXTPA_EN; ++ W_REG(sii->osh, &cc->chipcontrol, val); ++ } else { ++ val |= (CCTRL4331_EXTPA_EN | CCTRL4331_EXTPA_ON_GPIO2_5); ++ W_REG(sii->osh, &cc->chipcontrol, val); ++ } ++ si_setcoreidx(sih, origidx); ++} ++ ++uint ++si_pll_reset(si_t *sih) ++{ ++ uint err = 0; ++ ++ return (err); ++} ++ ++/* Enable BT-COEX & Ex-PA for 4313 */ ++void ++si_epa_4313war(si_t *sih) ++{ ++ si_info_t *sii; ++ chipcregs_t *cc; ++ uint origidx; ++ ++ sii = SI_INFO(sih); ++ origidx = si_coreidx(sih); ++ ++ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ ++ /* EPA Fix */ ++ W_REG(sii->osh, &cc->gpiocontrol, ++ R_REG(sii->osh, &cc->gpiocontrol) | GPIO_CTRL_EPA_EN_MASK); ++ ++ si_setcoreidx(sih, origidx); ++} ++ ++void ++si_clk_pmu_htavail_set(si_t *sih, bool set_clear) ++{ ++} ++ ++/* WL/BT control for 4313 btcombo boards >= P250 */ ++void ++si_btcombo_p250_4313_war(si_t *sih) ++{ ++ si_info_t *sii; ++ chipcregs_t *cc; ++ uint origidx; ++ ++ sii = SI_INFO(sih); ++ origidx = si_coreidx(sih); ++ ++ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ W_REG(sii->osh, &cc->gpiocontrol, ++ R_REG(sii->osh, &cc->gpiocontrol) | GPIO_CTRL_5_6_EN_MASK); ++ ++ W_REG(sii->osh, &cc->gpioouten, ++ R_REG(sii->osh, &cc->gpioouten) | GPIO_CTRL_5_6_EN_MASK); ++ ++ si_setcoreidx(sih, origidx); ++} ++void ++si_btc_enable_chipcontrol(si_t *sih) ++{ ++ si_info_t *sii; ++ chipcregs_t *cc; ++ uint origidx; ++ ++ sii = SI_INFO(sih); ++ origidx = si_coreidx(sih); ++ ++ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ ++ /* BT fix */ ++ W_REG(sii->osh, &cc->chipcontrol, ++ R_REG(sii->osh, &cc->chipcontrol) | CC_BTCOEX_EN_MASK); ++ ++ si_setcoreidx(sih, origidx); ++} ++void ++si_btcombo_43228_war(si_t *sih) ++{ ++ si_info_t *sii; ++ chipcregs_t *cc; ++ uint origidx; ++ ++ sii = SI_INFO(sih); ++ origidx = si_coreidx(sih); ++ ++ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); ++ ++ W_REG(sii->osh, &cc->gpioouten, GPIO_CTRL_7_6_EN_MASK); ++ W_REG(sii->osh, &cc->gpioout, GPIO_OUT_7_EN_MASK); ++ ++ si_setcoreidx(sih, origidx); ++} ++ ++/* check if the device is removed */ ++bool ++si_deviceremoved(si_t *sih) ++{ ++ uint32 w; ++ si_info_t *sii; ++ ++ sii = SI_INFO(sih); ++ ++ switch (BUSTYPE(sih->bustype)) { ++ case PCI_BUS: ++ ASSERT(sii->osh != NULL); ++ w = OSL_PCI_READ_CONFIG(sii->osh, PCI_CFG_VID, sizeof(uint32)); ++ if ((w & 0xFFFF) != VENDOR_BROADCOM) ++ return TRUE; ++ break; ++ } ++ return FALSE; ++} ++ ++bool ++si_is_sprom_available(si_t *sih) ++{ ++ if (sih->ccrev >= 31) { ++ si_info_t *sii; ++ uint origidx; ++ chipcregs_t *cc; ++ uint32 sromctrl; ++ ++ if ((sih->cccaps & CC_CAP_SROM) == 0) ++ return FALSE; ++ ++ sii = SI_INFO(sih); ++ origidx = sii->curidx; ++ cc = si_setcoreidx(sih, SI_CC_IDX); ++ sromctrl = R_REG(sii->osh, &cc->sromcontrol); ++ si_setcoreidx(sih, origidx); ++ return (sromctrl & SRC_PRESENT); ++ } ++ ++ switch (CHIPID(sih->chip)) { ++ case BCM4312_CHIP_ID: ++ return ((sih->chipst & CST4312_SPROM_OTP_SEL_MASK) != CST4312_OTP_SEL); ++ case BCM4325_CHIP_ID: ++ return (sih->chipst & CST4325_SPROM_SEL) != 0; ++ case BCM4322_CHIP_ID: case BCM43221_CHIP_ID: case BCM43231_CHIP_ID: ++ case BCM43222_CHIP_ID: case BCM43111_CHIP_ID: case BCM43112_CHIP_ID: ++ case BCM4342_CHIP_ID: { ++ uint32 spromotp; ++ spromotp = (sih->chipst & CST4322_SPROM_OTP_SEL_MASK) >> ++ CST4322_SPROM_OTP_SEL_SHIFT; ++ return (spromotp & CST4322_SPROM_PRESENT) != 0; ++ } ++ case BCM4329_CHIP_ID: ++ return (sih->chipst & CST4329_SPROM_SEL) != 0; ++ case BCM4315_CHIP_ID: ++ return (sih->chipst & CST4315_SPROM_SEL) != 0; ++ case BCM4319_CHIP_ID: ++ return (sih->chipst & CST4319_SPROM_SEL) != 0; ++ case BCM4336_CHIP_ID: ++ case BCM43362_CHIP_ID: ++ return (sih->chipst & CST4336_SPROM_PRESENT) != 0; ++ case BCM4330_CHIP_ID: ++ return (sih->chipst & CST4330_SPROM_PRESENT) != 0; ++ case BCM4313_CHIP_ID: ++ return (sih->chipst & CST4313_SPROM_PRESENT) != 0; ++ case BCM4331_CHIP_ID: ++ case BCM43431_CHIP_ID: ++ return (sih->chipst & CST4331_SPROM_PRESENT) != 0; ++ case BCM43239_CHIP_ID: ++ return ((sih->chipst & CST43239_SPROM_MASK) && ++ !(sih->chipst & CST43239_SFLASH_MASK)); ++ case BCM4324_CHIP_ID: ++ return ((sih->chipst & CST4324_SPROM_MASK) && ++ !(sih->chipst & CST4324_SFLASH_MASK)); ++ case BCM4335_CHIP_ID: ++ return ((sih->chipst & CST4335_SPROM_MASK) && ++ !(sih->chipst & CST4335_SFLASH_MASK)); ++ case BCM43131_CHIP_ID: ++ case BCM43217_CHIP_ID: ++ case BCM43227_CHIP_ID: ++ case BCM43228_CHIP_ID: ++ case BCM43428_CHIP_ID: ++ return (sih->chipst & CST43228_OTP_PRESENT) != CST43228_OTP_PRESENT; ++ default: ++ return TRUE; ++ } ++} ++ ++ ++uint32 si_get_sromctl(si_t *sih) ++{ ++ chipcregs_t *cc; ++ uint origidx; ++ uint32 sromctl; ++ osl_t *osh; ++ ++ osh = si_osh(sih); ++ origidx = si_coreidx(sih); ++ cc = si_setcoreidx(sih, SI_CC_IDX); ++ ASSERT((uintptr)cc); ++ ++ sromctl = R_REG(osh, &cc->sromcontrol); ++ ++ /* return to the original core */ ++ si_setcoreidx(sih, origidx); ++ return sromctl; ++} ++ ++int si_set_sromctl(si_t *sih, uint32 value) ++{ ++ chipcregs_t *cc; ++ uint origidx; ++ osl_t *osh; ++ ++ osh = si_osh(sih); ++ origidx = si_coreidx(sih); ++ cc = si_setcoreidx(sih, SI_CC_IDX); ++ ASSERT((uintptr)cc); ++ ++ /* get chipcommon rev */ ++ if (si_corerev(sih) < 32) ++ return BCME_UNSUPPORTED; ++ ++ W_REG(osh, &cc->sromcontrol, value); ++ ++ /* return to the original core */ ++ si_setcoreidx(sih, origidx); ++ return BCME_OK; ++ ++} +diff --git a/drivers/net/wireless/bcmdhd/siutils_priv.h b/drivers/net/wireless/bcmdhd/siutils_priv.h +new file mode 100644 +index 00000000..9a3270f7 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/siutils_priv.h +@@ -0,0 +1,246 @@ ++/* ++ * Include file private to the SOC Interconnect support files. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: siutils_priv.h 309193 2012-01-19 00:03:57Z $ ++ */ ++ ++#ifndef _siutils_priv_h_ ++#define _siutils_priv_h_ ++ ++#define SI_ERROR(args) ++ ++#define SI_MSG(args) ++ ++#ifdef BCMDBG_SI ++#define SI_VMSG(args) printf args ++#else ++#define SI_VMSG(args) ++#endif ++ ++#define IS_SIM(chippkg) ((chippkg == HDLSIM_PKG_ID) || (chippkg == HWSIM_PKG_ID)) ++ ++typedef uint32 (*si_intrsoff_t)(void *intr_arg); ++typedef void (*si_intrsrestore_t)(void *intr_arg, uint32 arg); ++typedef bool (*si_intrsenabled_t)(void *intr_arg); ++ ++typedef struct gpioh_item { ++ void *arg; ++ bool level; ++ gpio_handler_t handler; ++ uint32 event; ++ struct gpioh_item *next; ++} gpioh_item_t; ++ ++/* misc si info needed by some of the routines */ ++typedef struct si_info { ++ struct si_pub pub; /* back plane public state (must be first field) */ ++ ++ void *osh; /* osl os handle */ ++ void *sdh; /* bcmsdh handle */ ++ ++ uint dev_coreid; /* the core provides driver functions */ ++ void *intr_arg; /* interrupt callback function arg */ ++ si_intrsoff_t intrsoff_fn; /* turns chip interrupts off */ ++ si_intrsrestore_t intrsrestore_fn; /* restore chip interrupts */ ++ si_intrsenabled_t intrsenabled_fn; /* check if interrupts are enabled */ ++ ++ void *pch; /* PCI/E core handle */ ++ ++ gpioh_item_t *gpioh_head; /* GPIO event handlers list */ ++ ++ bool memseg; /* flag to toggle MEM_SEG register */ ++ ++ char *vars; ++ uint varsz; ++ ++ void *curmap; /* current regs va */ ++ void *regs[SI_MAXCORES]; /* other regs va */ ++ ++ uint curidx; /* current core index */ ++ uint numcores; /* # discovered cores */ ++ uint coreid[SI_MAXCORES]; /* id of each core */ ++ uint32 coresba[SI_MAXCORES]; /* backplane address of each core */ ++ void *regs2[SI_MAXCORES]; /* va of each core second register set (usbh20) */ ++ uint32 coresba2[SI_MAXCORES]; /* address of each core second register set (usbh20) */ ++ uint32 coresba_size[SI_MAXCORES]; /* backplane address space size */ ++ uint32 coresba2_size[SI_MAXCORES]; /* second address space size */ ++ ++ void *curwrap; /* current wrapper va */ ++ void *wrappers[SI_MAXCORES]; /* other cores wrapper va */ ++ uint32 wrapba[SI_MAXCORES]; /* address of controlling wrapper */ ++ ++ uint32 cia[SI_MAXCORES]; /* erom cia entry for each core */ ++ uint32 cib[SI_MAXCORES]; /* erom cia entry for each core */ ++ uint32 oob_router; /* oob router registers for axi */ ++} si_info_t; ++ ++#define SI_INFO(sih) (si_info_t *)(uintptr)sih ++ ++#define GOODCOREADDR(x, b) (((x) >= (b)) && ((x) < ((b) + SI_MAXCORES * SI_CORE_SIZE)) && \ ++ ISALIGNED((x), SI_CORE_SIZE)) ++#define GOODREGS(regs) ((regs) != NULL && ISALIGNED((uintptr)(regs), SI_CORE_SIZE)) ++#define BADCOREADDR 0 ++#define GOODIDX(idx) (((uint)idx) < SI_MAXCORES) ++#define NOREV -1 /* Invalid rev */ ++ ++#define PCI(si) ((BUSTYPE((si)->pub.bustype) == PCI_BUS) && \ ++ ((si)->pub.buscoretype == PCI_CORE_ID)) ++ ++#define PCIE_GEN1(si) ((BUSTYPE((si)->pub.bustype) == PCI_BUS) && \ ++ ((si)->pub.buscoretype == PCIE_CORE_ID)) ++ ++#define PCIE_GEN2(si) ((BUSTYPE((si)->pub.bustype) == PCI_BUS) && \ ++ ((si)->pub.buscoretype == PCIE2_CORE_ID)) ++ ++#define PCIE(si) (PCIE_GEN1(si) || PCIE_GEN2(si)) ++ ++#define PCMCIA(si) ((BUSTYPE((si)->pub.bustype) == PCMCIA_BUS) && ((si)->memseg == TRUE)) ++ ++/* Newer chips can access PCI/PCIE and CC core without requiring to change ++ * PCI BAR0 WIN ++ */ ++#define SI_FAST(si) (PCIE(si) || (PCI(si) && ((si)->pub.buscorerev >= 13))) ++ ++#define PCIEREGS(si) (((char *)((si)->curmap) + PCI_16KB0_PCIREGS_OFFSET)) ++#define CCREGS_FAST(si) (((char *)((si)->curmap) + PCI_16KB0_CCREGS_OFFSET)) ++ ++/* ++ * Macros to disable/restore function core(D11, ENET, ILINE20, etc) interrupts before/ ++ * after core switching to avoid invalid register accesss inside ISR. ++ */ ++#define INTR_OFF(si, intr_val) \ ++ if ((si)->intrsoff_fn && (si)->coreid[(si)->curidx] == (si)->dev_coreid) { \ ++ intr_val = (*(si)->intrsoff_fn)((si)->intr_arg); } ++#define INTR_RESTORE(si, intr_val) \ ++ if ((si)->intrsrestore_fn && (si)->coreid[(si)->curidx] == (si)->dev_coreid) { \ ++ (*(si)->intrsrestore_fn)((si)->intr_arg, intr_val); } ++ ++/* dynamic clock control defines */ ++#define LPOMINFREQ 25000 /* low power oscillator min */ ++#define LPOMAXFREQ 43000 /* low power oscillator max */ ++#define XTALMINFREQ 19800000 /* 20 MHz - 1% */ ++#define XTALMAXFREQ 20200000 /* 20 MHz + 1% */ ++#define PCIMINFREQ 25000000 /* 25 MHz */ ++#define PCIMAXFREQ 34000000 /* 33 MHz + fudge */ ++ ++#define ILP_DIV_5MHZ 0 /* ILP = 5 MHz */ ++#define ILP_DIV_1MHZ 4 /* ILP = 1 MHz */ ++ ++#define PCI_FORCEHT(si) \ ++ (((PCIE_GEN1(si)) && (si->pub.chip == BCM4311_CHIP_ID) && ((si->pub.chiprev <= 1))) || \ ++ ((PCI(si) || PCIE_GEN1(si)) && (si->pub.chip == BCM4321_CHIP_ID)) || \ ++ (PCIE_GEN1(si) && (si->pub.chip == BCM4716_CHIP_ID)) || \ ++ (PCIE_GEN1(si) && (si->pub.chip == BCM4748_CHIP_ID))) ++ ++/* GPIO Based LED powersave defines */ ++#define DEFAULT_GPIO_ONTIME 10 /* Default: 10% on */ ++#define DEFAULT_GPIO_OFFTIME 90 /* Default: 10% on */ ++ ++#ifndef DEFAULT_GPIOTIMERVAL ++#define DEFAULT_GPIOTIMERVAL ((DEFAULT_GPIO_ONTIME << GPIO_ONTIME_SHIFT) | DEFAULT_GPIO_OFFTIME) ++#endif ++ ++/* Silicon Backplane externs */ ++extern void sb_scan(si_t *sih, void *regs, uint devid); ++extern uint sb_coreid(si_t *sih); ++extern uint sb_intflag(si_t *sih); ++extern uint sb_flag(si_t *sih); ++extern void sb_setint(si_t *sih, int siflag); ++extern uint sb_corevendor(si_t *sih); ++extern uint sb_corerev(si_t *sih); ++extern uint sb_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val); ++extern bool sb_iscoreup(si_t *sih); ++extern void *sb_setcoreidx(si_t *sih, uint coreidx); ++extern uint32 sb_core_cflags(si_t *sih, uint32 mask, uint32 val); ++extern void sb_core_cflags_wo(si_t *sih, uint32 mask, uint32 val); ++extern uint32 sb_core_sflags(si_t *sih, uint32 mask, uint32 val); ++extern void sb_commit(si_t *sih); ++extern uint32 sb_base(uint32 admatch); ++extern uint32 sb_size(uint32 admatch); ++extern void sb_core_reset(si_t *sih, uint32 bits, uint32 resetbits); ++extern void sb_core_disable(si_t *sih, uint32 bits); ++extern uint32 sb_addrspace(si_t *sih, uint asidx); ++extern uint32 sb_addrspacesize(si_t *sih, uint asidx); ++extern int sb_numaddrspaces(si_t *sih); ++ ++extern uint32 sb_set_initiator_to(si_t *sih, uint32 to, uint idx); ++ ++extern bool sb_taclear(si_t *sih, bool details); ++ ++ ++/* Wake-on-wireless-LAN (WOWL) */ ++extern bool sb_pci_pmecap(si_t *sih); ++struct osl_info; ++extern bool sb_pci_fastpmecap(struct osl_info *osh); ++extern bool sb_pci_pmeclr(si_t *sih); ++extern void sb_pci_pmeen(si_t *sih); ++extern uint sb_pcie_readreg(void *sih, uint addrtype, uint offset); ++ ++/* AMBA Interconnect exported externs */ ++extern si_t *ai_attach(uint pcidev, osl_t *osh, void *regs, uint bustype, ++ void *sdh, char **vars, uint *varsz); ++extern si_t *ai_kattach(osl_t *osh); ++extern void ai_scan(si_t *sih, void *regs, uint devid); ++ ++extern uint ai_flag(si_t *sih); ++extern void ai_setint(si_t *sih, int siflag); ++extern uint ai_coreidx(si_t *sih); ++extern uint ai_corevendor(si_t *sih); ++extern uint ai_corerev(si_t *sih); ++extern bool ai_iscoreup(si_t *sih); ++extern void *ai_setcoreidx(si_t *sih, uint coreidx); ++extern uint32 ai_core_cflags(si_t *sih, uint32 mask, uint32 val); ++extern void ai_core_cflags_wo(si_t *sih, uint32 mask, uint32 val); ++extern uint32 ai_core_sflags(si_t *sih, uint32 mask, uint32 val); ++extern uint ai_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val); ++extern void ai_core_reset(si_t *sih, uint32 bits, uint32 resetbits); ++extern void ai_core_disable(si_t *sih, uint32 bits); ++extern int ai_numaddrspaces(si_t *sih); ++extern uint32 ai_addrspace(si_t *sih, uint asidx); ++extern uint32 ai_addrspacesize(si_t *sih, uint asidx); ++extern void ai_coreaddrspaceX(si_t *sih, uint asidx, uint32 *addr, uint32 *size); ++extern uint ai_wrap_reg(si_t *sih, uint32 offset, uint32 mask, uint32 val); ++ ++ ++ ++#define ub_scan(a, b, c) do {} while (0) ++#define ub_flag(a) (0) ++#define ub_setint(a, b) do {} while (0) ++#define ub_coreidx(a) (0) ++#define ub_corevendor(a) (0) ++#define ub_corerev(a) (0) ++#define ub_iscoreup(a) (0) ++#define ub_setcoreidx(a, b) (0) ++#define ub_core_cflags(a, b, c) (0) ++#define ub_core_cflags_wo(a, b, c) do {} while (0) ++#define ub_core_sflags(a, b, c) (0) ++#define ub_corereg(a, b, c, d, e) (0) ++#define ub_core_reset(a, b, c) do {} while (0) ++#define ub_core_disable(a, b) do {} while (0) ++#define ub_numaddrspaces(a) (0) ++#define ub_addrspace(a, b) (0) ++#define ub_addrspacesize(a, b) (0) ++#define ub_view(a, b) do {} while (0) ++#define ub_dumpregs(a, b) do {} while (0) ++ ++#endif /* _siutils_priv_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/uamp_api.h b/drivers/net/wireless/bcmdhd/uamp_api.h +new file mode 100644 +index 00000000..673dce08 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/uamp_api.h +@@ -0,0 +1,176 @@ ++/* ++ * Name: uamp_api.h ++ * ++ * Description: Universal AMP API ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: uamp_api.h 294267 2011-11-04 23:41:52Z $ ++ * ++ */ ++#ifndef UAMP_API_H ++#define UAMP_API_H ++ ++ ++#include "typedefs.h" ++ ++ ++/***************************************************************************** ++** Constant and Type Definitions ++****************************************************************************** ++*/ ++ ++#define BT_API ++ ++/* Types. */ ++typedef bool BOOLEAN; ++typedef uint8 UINT8; ++typedef uint16 UINT16; ++ ++ ++/* UAMP identifiers */ ++#define UAMP_ID_1 1 ++#define UAMP_ID_2 2 ++typedef UINT8 tUAMP_ID; ++ ++/* UAMP event ids (used by UAMP_CBACK) */ ++#define UAMP_EVT_RX_READY 0 /* Data from AMP controller is ready to be read */ ++#define UAMP_EVT_CTLR_REMOVED 1 /* Controller removed */ ++#define UAMP_EVT_CTLR_READY 2 /* Controller added/ready */ ++typedef UINT8 tUAMP_EVT; ++ ++ ++/* UAMP Channels */ ++#define UAMP_CH_HCI_CMD 0 /* HCI Command channel */ ++#define UAMP_CH_HCI_EVT 1 /* HCI Event channel */ ++#define UAMP_CH_HCI_DATA 2 /* HCI ACL Data channel */ ++typedef UINT8 tUAMP_CH; ++ ++/* tUAMP_EVT_DATA: union for event-specific data, used by UAMP_CBACK */ ++typedef union { ++ tUAMP_CH channel; /* UAMP_EVT_RX_READY: channel for which rx occured */ ++} tUAMP_EVT_DATA; ++ ++ ++/***************************************************************************** ++** ++** Function: UAMP_CBACK ++** ++** Description: Callback for events. Register callback using UAMP_Init. ++** ++** Parameters amp_id: AMP device identifier that generated the event ++** amp_evt: event id ++** p_amp_evt_data: pointer to event-specific data ++** ++****************************************************************************** ++*/ ++typedef void (*tUAMP_CBACK)(tUAMP_ID amp_id, tUAMP_EVT amp_evt, tUAMP_EVT_DATA *p_amp_evt_data); ++ ++/***************************************************************************** ++** external function declarations ++****************************************************************************** ++*/ ++#ifdef __cplusplus ++extern "C" ++{ ++#endif ++ ++/***************************************************************************** ++** ++** Function: UAMP_Init ++** ++** Description: Initialize UAMP driver ++** ++** Parameters p_cback: Callback function for UAMP event notification ++** ++****************************************************************************** ++*/ ++BT_API BOOLEAN UAMP_Init(tUAMP_CBACK p_cback); ++ ++ ++/***************************************************************************** ++** ++** Function: UAMP_Open ++** ++** Description: Open connection to local AMP device. ++** ++** Parameters app_id: Application specific AMP identifer. This value ++** will be included in AMP messages sent to the ++** BTU task, to identify source of the message ++** ++****************************************************************************** ++*/ ++BT_API BOOLEAN UAMP_Open(tUAMP_ID amp_id); ++ ++/***************************************************************************** ++** ++** Function: UAMP_Close ++** ++** Description: Close connection to local AMP device. ++** ++** Parameters app_id: Application specific AMP identifer. ++** ++****************************************************************************** ++*/ ++BT_API void UAMP_Close(tUAMP_ID amp_id); ++ ++ ++/***************************************************************************** ++** ++** Function: UAMP_Write ++** ++** Description: Send buffer to AMP device. Frees GKI buffer when done. ++** ++** ++** Parameters: app_id: AMP identifer. ++** p_buf: pointer to buffer to write ++** num_bytes: number of bytes to write ++** channel: UAMP_CH_HCI_ACL, or UAMP_CH_HCI_CMD ++** ++** Returns: number of bytes written ++** ++****************************************************************************** ++*/ ++BT_API UINT16 UAMP_Write(tUAMP_ID amp_id, UINT8 *p_buf, UINT16 num_bytes, tUAMP_CH channel); ++ ++/***************************************************************************** ++** ++** Function: UAMP_Read ++** ++** Description: Read incoming data from AMP. Call after receiving a ++** UAMP_EVT_RX_READY callback event. ++** ++** Parameters: app_id: AMP identifer. ++** p_buf: pointer to buffer for holding incoming AMP data ++** buf_size: size of p_buf ++** channel: UAMP_CH_HCI_ACL, or UAMP_CH_HCI_EVT ++** ++** Returns: number of bytes read ++** ++****************************************************************************** ++*/ ++BT_API UINT16 UAMP_Read(tUAMP_ID amp_id, UINT8 *p_buf, UINT16 buf_size, tUAMP_CH channel); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* UAMP_API_H */ +diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c +new file mode 100644 +index 00000000..26f88e24 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/wl_android.c +@@ -0,0 +1,908 @@ ++/* ++ * Linux cfg80211 driver - Android related functions ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wl_android.c 379859 2013-01-19 13:16:55Z $ ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef WL_CFG80211 ++#include ++#endif ++#if defined(CONFIG_WIFI_CONTROL_FUNC) ++#include ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) ++#include ++#else ++#include ++#endif ++#endif /* CONFIG_WIFI_CONTROL_FUNC */ ++ ++/* ++ * Android private command strings, PLEASE define new private commands here ++ * so they can be updated easily in the future (if needed) ++ */ ++ ++#define CMD_START "START" ++#define CMD_STOP "STOP" ++#define CMD_SCAN_ACTIVE "SCAN-ACTIVE" ++#define CMD_SCAN_PASSIVE "SCAN-PASSIVE" ++#define CMD_RSSI "RSSI" ++#define CMD_LINKSPEED "LINKSPEED" ++#define CMD_RXFILTER_START "RXFILTER-START" ++#define CMD_RXFILTER_STOP "RXFILTER-STOP" ++#define CMD_RXFILTER_ADD "RXFILTER-ADD" ++#define CMD_RXFILTER_REMOVE "RXFILTER-REMOVE" ++#define CMD_BTCOEXSCAN_START "BTCOEXSCAN-START" ++#define CMD_BTCOEXSCAN_STOP "BTCOEXSCAN-STOP" ++#define CMD_BTCOEXMODE "BTCOEXMODE" ++#define CMD_SETSUSPENDOPT "SETSUSPENDOPT" ++#define CMD_SETSUSPENDMODE "SETSUSPENDMODE" ++#define CMD_P2P_DEV_ADDR "P2P_DEV_ADDR" ++#define CMD_SETFWPATH "SETFWPATH" ++#define CMD_SETBAND "SETBAND" ++#define CMD_GETBAND "GETBAND" ++#define CMD_COUNTRY "COUNTRY" ++#define CMD_P2P_SET_NOA "P2P_SET_NOA" ++#if !defined WL_ENABLE_P2P_IF ++#define CMD_P2P_GET_NOA "P2P_GET_NOA" ++#endif ++#define CMD_P2P_SET_PS "P2P_SET_PS" ++#define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE" ++ ++ ++#ifdef PNO_SUPPORT ++#define CMD_PNOSSIDCLR_SET "PNOSSIDCLR" ++#define CMD_PNOSETUP_SET "PNOSETUP " ++#define CMD_PNOENABLE_SET "PNOFORCE" ++#define CMD_PNODEBUG_SET "PNODEBUG" ++ ++#define PNO_TLV_PREFIX 'S' ++#define PNO_TLV_VERSION '1' ++#define PNO_TLV_SUBVERSION '2' ++#define PNO_TLV_RESERVED '0' ++#define PNO_TLV_TYPE_SSID_IE 'S' ++#define PNO_TLV_TYPE_TIME 'T' ++#define PNO_TLV_FREQ_REPEAT 'R' ++#define PNO_TLV_FREQ_EXPO_MAX 'M' ++ ++typedef struct cmd_tlv { ++ char prefix; ++ char version; ++ char subver; ++ char reserved; ++} cmd_tlv_t; ++#endif /* PNO_SUPPORT */ ++ ++typedef struct android_wifi_priv_cmd { ++ char *buf; ++ int used_len; ++ int total_len; ++} android_wifi_priv_cmd; ++ ++/** ++ * Extern function declarations (TODO: move them to dhd_linux.h) ++ */ ++void dhd_customer_gpio_wlan_ctrl(int onoff); ++int dhd_dev_reset(struct net_device *dev, uint8 flag); ++int dhd_dev_init_ioctl(struct net_device *dev); ++#ifdef WL_CFG80211 ++int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); ++int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command); ++#else ++int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) ++{ return 0; } ++int wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len) ++{ return 0; } ++int wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len) ++{ return 0; } ++int wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len) ++{ return 0; } ++#endif /* WL_CFG80211 */ ++extern int dhd_os_check_if_up(void *dhdp); ++extern void *bcmsdh_get_drvdata(void); ++#if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) ++extern int dhd_wlfc_init(dhd_pub_t *dhd); ++extern void dhd_wlfc_deinit(dhd_pub_t *dhd); ++#endif ++ ++extern bool ap_fw_loaded; ++#if defined(CUSTOMER_HW2) ++extern char iface_name[IFNAMSIZ]; ++#endif ++ ++#define WIFI_TURNOFF_DELAY 0 ++/** ++ * Local (static) functions and variables ++ */ ++ ++/* Initialize g_wifi_on to 1 so dhd_bus_start will be called for the first ++ * time (only) in dhd_open, subsequential wifi on will be handled by ++ * wl_android_wifi_on ++ */ ++static int g_wifi_on = TRUE; ++ ++/** ++ * Local (static) function definitions ++ */ ++static int wl_android_get_link_speed(struct net_device *net, char *command, int total_len) ++{ ++ int link_speed; ++ int bytes_written; ++ int error; ++ ++ error = wldev_get_link_speed(net, &link_speed); ++ if (error) ++ return -1; ++ ++ /* Convert Kbps to Android Mbps */ ++ link_speed = link_speed / 1000; ++ bytes_written = snprintf(command, total_len, "LinkSpeed %d", link_speed); ++ DHD_INFO(("%s: command result is %s\n", __FUNCTION__, command)); ++ return bytes_written; ++} ++ ++static int wl_android_get_rssi(struct net_device *net, char *command, int total_len) ++{ ++ wlc_ssid_t ssid = {0}; ++ int rssi; ++ int bytes_written = 0; ++ int error; ++ ++ error = wldev_get_rssi(net, &rssi); ++ if (error) ++ return -1; ++ ++ error = wldev_get_ssid(net, &ssid); ++ if (error) ++ return -1; ++ if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) { ++ DHD_ERROR(("%s: wldev_get_ssid failed\n", __FUNCTION__)); ++ } else { ++ memcpy(command, ssid.SSID, ssid.SSID_len); ++ bytes_written = ssid.SSID_len; ++ } ++ bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", rssi); ++ DHD_INFO(("%s: command result is %s (%d)\n", __FUNCTION__, command, bytes_written)); ++ return bytes_written; ++} ++ ++static int wl_android_set_suspendopt(struct net_device *dev, char *command, int total_len) ++{ ++ int suspend_flag; ++ int ret_now; ++ int ret = 0; ++ ++ suspend_flag = *(command + strlen(CMD_SETSUSPENDOPT) + 1) - '0'; ++ ++ if (suspend_flag != 0) ++ suspend_flag = 1; ++ ret_now = net_os_set_suspend_disable(dev, suspend_flag); ++ ++ if (ret_now != suspend_flag) { ++ if (!(ret = net_os_set_suspend(dev, ret_now, 1))) ++ DHD_INFO(("%s: Suspend Flag %d -> %d\n", ++ __FUNCTION__, ret_now, suspend_flag)); ++ else ++ DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); ++ } ++ return ret; ++} ++ ++static int wl_android_set_suspendmode(struct net_device *dev, char *command, int total_len) ++{ ++ int ret = 0; ++ ++#if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(DHD_USE_EARLYSUSPEND) ++ int suspend_flag; ++ ++ suspend_flag = *(command + strlen(CMD_SETSUSPENDMODE) + 1) - '0'; ++ ++ if (suspend_flag != 0) ++ suspend_flag = 1; ++ ++ if (!(ret = net_os_set_suspend(dev, suspend_flag, 0))) ++ DHD_INFO(("%s: Suspend Mode %d\n", __FUNCTION__, suspend_flag)); ++ else ++ DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); ++#endif ++ return ret; ++} ++ ++static int wl_android_get_band(struct net_device *dev, char *command, int total_len) ++{ ++ uint band; ++ int bytes_written; ++ int error; ++ ++ error = wldev_get_band(dev, &band); ++ if (error) ++ return -1; ++ bytes_written = snprintf(command, total_len, "Band %d", band); ++ return bytes_written; ++} ++ ++#if defined(PNO_SUPPORT) && !defined(WL_SCHED_SCAN) ++static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len) ++{ ++ wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; ++ int res = -1; ++ int nssid = 0; ++ cmd_tlv_t *cmd_tlv_temp; ++ char *str_ptr; ++ int tlv_size_left; ++ int pno_time = 0; ++ int pno_repeat = 0; ++ int pno_freq_expo_max = 0; ++ ++#ifdef PNO_SET_DEBUG ++ int i; ++ char pno_in_example[] = { ++ 'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ', ++ 'S', '1', '2', '0', ++ 'S', ++ 0x05, ++ 'd', 'l', 'i', 'n', 'k', ++ 'S', ++ 0x04, ++ 'G', 'O', 'O', 'G', ++ 'T', ++ '0', 'B', ++ 'R', ++ '2', ++ 'M', ++ '2', ++ 0x00 ++ }; ++#endif /* PNO_SET_DEBUG */ ++ ++ DHD_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); ++ ++ if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) { ++ DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len)); ++ goto exit_proc; ++ } ++ ++ ++#ifdef PNO_SET_DEBUG ++ memcpy(command, pno_in_example, sizeof(pno_in_example)); ++ for (i = 0; i < sizeof(pno_in_example); i++) ++ printf("%02X ", command[i]); ++ printf("\n"); ++ total_len = sizeof(pno_in_example); ++#endif ++ ++ str_ptr = command + strlen(CMD_PNOSETUP_SET); ++ tlv_size_left = total_len - strlen(CMD_PNOSETUP_SET); ++ ++ cmd_tlv_temp = (cmd_tlv_t *)str_ptr; ++ memset(ssids_local, 0, sizeof(ssids_local)); ++ ++ if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) && ++ (cmd_tlv_temp->version == PNO_TLV_VERSION) && ++ (cmd_tlv_temp->subver == PNO_TLV_SUBVERSION)) { ++ ++ str_ptr += sizeof(cmd_tlv_t); ++ tlv_size_left -= sizeof(cmd_tlv_t); ++ ++ if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, ++ MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) { ++ DHD_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid)); ++ goto exit_proc; ++ } else { ++ if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) { ++ DHD_ERROR(("%s scan duration corrupted field size %d\n", ++ __FUNCTION__, tlv_size_left)); ++ goto exit_proc; ++ } ++ str_ptr++; ++ pno_time = simple_strtoul(str_ptr, &str_ptr, 16); ++ DHD_INFO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); ++ ++ if (str_ptr[0] != 0) { ++ if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) { ++ DHD_ERROR(("%s pno repeat : corrupted field\n", ++ __FUNCTION__)); ++ goto exit_proc; ++ } ++ str_ptr++; ++ pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16); ++ DHD_INFO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); ++ if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) { ++ DHD_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n", ++ __FUNCTION__)); ++ goto exit_proc; ++ } ++ str_ptr++; ++ pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16); ++ DHD_INFO(("%s: pno_freq_expo_max=%d\n", ++ __FUNCTION__, pno_freq_expo_max)); ++ } ++ } ++ } else { ++ DHD_ERROR(("%s get wrong TLV command\n", __FUNCTION__)); ++ goto exit_proc; ++ } ++ ++ res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time, pno_repeat, pno_freq_expo_max); ++ ++exit_proc: ++ return res; ++} ++#endif /* PNO_SUPPORT */ ++ ++static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len) ++{ ++ int ret; ++ int bytes_written = 0; ++ ++ ret = wl_cfg80211_get_p2p_dev_addr(ndev, (struct ether_addr*)command); ++ if (ret) ++ return 0; ++ bytes_written = sizeof(struct ether_addr); ++ return bytes_written; ++} ++ ++/** ++ * Global function definitions (declared in wl_android.h) ++ */ ++ ++int wl_android_wifi_on(struct net_device *dev) ++{ ++ int ret = 0; ++ int retry = POWERUP_MAX_RETRY; ++ ++ printk("%s in\n", __FUNCTION__); ++ if (!dev) { ++ DHD_ERROR(("%s: dev is null\n", __FUNCTION__)); ++ return -EINVAL; ++ } ++ ++ dhd_net_if_lock(dev); ++ if (!g_wifi_on) { ++ do { ++ dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON); ++ ret = sdioh_start(NULL, 0); ++ if (ret == 0) ++ break; ++ DHD_ERROR(("\nfailed to power up wifi chip, retry again (%d left) **\n\n", ++ retry+1)); ++ dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); ++ } while (retry-- >= 0); ++ if (ret != 0) { ++ DHD_ERROR(("\nfailed to power up wifi chip, max retry reached **\n\n")); ++ goto exit; ++ } ++ ret = dhd_dev_reset(dev, FALSE); ++ sdioh_start(NULL, 1); ++ if (!ret) { ++ if (dhd_dev_init_ioctl(dev) < 0) ++ ret = -EFAULT; ++ } ++#if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) ++ dhd_wlfc_init(bcmsdh_get_drvdata()); ++#endif ++ g_wifi_on = TRUE; ++ } ++ ++exit: ++ dhd_net_if_unlock(dev); ++ ++ return ret; ++} ++ ++int wl_android_wifi_off(struct net_device *dev) ++{ ++ int ret = 0; ++ ++ printk("%s in\n", __FUNCTION__); ++ if (!dev) { ++ DHD_TRACE(("%s: dev is null\n", __FUNCTION__)); ++ return -EINVAL; ++ } ++ ++ dhd_net_if_lock(dev); ++ if (g_wifi_on) { ++#if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) ++ dhd_wlfc_deinit(bcmsdh_get_drvdata()); ++#endif ++ ret = dhd_dev_reset(dev, TRUE); ++ sdioh_stop(NULL); ++ dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); ++ g_wifi_on = FALSE; ++ } ++ dhd_net_if_unlock(dev); ++ ++ return ret; ++} ++ ++static int wl_android_set_fwpath(struct net_device *net, char *command, int total_len) ++{ ++ if ((strlen(command) - strlen(CMD_SETFWPATH)) > MOD_PARAM_PATHLEN) ++ return -1; ++ bcm_strncpy_s(fw_path, sizeof(fw_path), ++ command + strlen(CMD_SETFWPATH) + 1, MOD_PARAM_PATHLEN - 1); ++ if (strstr(fw_path, "apsta") != NULL) { ++ DHD_INFO(("GOT APSTA FIRMWARE\n")); ++ ap_fw_loaded = TRUE; ++ } else { ++ DHD_INFO(("GOT STA FIRMWARE\n")); ++ ap_fw_loaded = FALSE; ++ } ++ return 0; ++} ++ ++int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) ++{ ++#define PRIVATE_COMMAND_MAX_LEN 8192 ++ int ret = 0; ++ char *command = NULL; ++ int bytes_written = 0; ++ android_wifi_priv_cmd priv_cmd; ++ ++ net_os_wake_lock(net); ++ ++ if (!ifr->ifr_data) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) { ++ ret = -EFAULT; ++ goto exit; ++ } ++ if (priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN) ++ { ++ DHD_ERROR(("%s: too long priavte command\n", __FUNCTION__)); ++ ret = -EINVAL; ++ } ++ command = kmalloc(priv_cmd.total_len, GFP_KERNEL); ++ if (!command) ++ { ++ DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__)); ++ ret = -ENOMEM; ++ goto exit; ++ } ++ if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) { ++ ret = -EFAULT; ++ goto exit; ++ } ++ ++ DHD_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name)); ++ ++ if (strnicmp(command, CMD_START, strlen(CMD_START)) == 0) { ++ DHD_INFO(("%s, Received regular START command\n", __FUNCTION__)); ++ bytes_written = wl_android_wifi_on(net); ++ } ++ else if (strnicmp(command, CMD_SETFWPATH, strlen(CMD_SETFWPATH)) == 0) { ++ bytes_written = wl_android_set_fwpath(net, command, priv_cmd.total_len); ++ } ++ ++ if (!g_wifi_on) { ++ DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface %s is down\n", ++ __FUNCTION__, command, ifr->ifr_name)); ++ ret = 0; ++ goto exit; ++ } ++ ++ if (strnicmp(command, CMD_STOP, strlen(CMD_STOP)) == 0) { ++ bytes_written = wl_android_wifi_off(net); ++ } ++ else if (strnicmp(command, CMD_SCAN_ACTIVE, strlen(CMD_SCAN_ACTIVE)) == 0) { ++ /* TBD: SCAN-ACTIVE */ ++ } ++ else if (strnicmp(command, CMD_SCAN_PASSIVE, strlen(CMD_SCAN_PASSIVE)) == 0) { ++ /* TBD: SCAN-PASSIVE */ ++ } ++ else if (strnicmp(command, CMD_RSSI, strlen(CMD_RSSI)) == 0) { ++ bytes_written = wl_android_get_rssi(net, command, priv_cmd.total_len); ++ } ++ else if (strnicmp(command, CMD_LINKSPEED, strlen(CMD_LINKSPEED)) == 0) { ++ bytes_written = wl_android_get_link_speed(net, command, priv_cmd.total_len); ++ } ++#ifdef PKT_FILTER_SUPPORT ++ else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) { ++ bytes_written = net_os_enable_packet_filter(net, 1); ++ } ++ else if (strnicmp(command, CMD_RXFILTER_STOP, strlen(CMD_RXFILTER_STOP)) == 0) { ++ bytes_written = net_os_enable_packet_filter(net, 0); ++ } ++ else if (strnicmp(command, CMD_RXFILTER_ADD, strlen(CMD_RXFILTER_ADD)) == 0) { ++ int filter_num = *(command + strlen(CMD_RXFILTER_ADD) + 1) - '0'; ++ bytes_written = net_os_rxfilter_add_remove(net, TRUE, filter_num); ++ } ++ else if (strnicmp(command, CMD_RXFILTER_REMOVE, strlen(CMD_RXFILTER_REMOVE)) == 0) { ++ int filter_num = *(command + strlen(CMD_RXFILTER_REMOVE) + 1) - '0'; ++ bytes_written = net_os_rxfilter_add_remove(net, FALSE, filter_num); ++ } ++#endif /* PKT_FILTER_SUPPORT */ ++ else if (strnicmp(command, CMD_BTCOEXSCAN_START, strlen(CMD_BTCOEXSCAN_START)) == 0) { ++ /* TBD: BTCOEXSCAN-START */ ++ } ++ else if (strnicmp(command, CMD_BTCOEXSCAN_STOP, strlen(CMD_BTCOEXSCAN_STOP)) == 0) { ++ /* TBD: BTCOEXSCAN-STOP */ ++ } ++ else if (strnicmp(command, CMD_BTCOEXMODE, strlen(CMD_BTCOEXMODE)) == 0) { ++#ifdef WL_CFG80211 ++ bytes_written = wl_cfg80211_set_btcoex_dhcp(net, command); ++#else ++#ifdef PKT_FILTER_SUPPORT ++ uint mode = *(command + strlen(CMD_BTCOEXMODE) + 1) - '0'; ++ ++ if (mode == 1) ++ net_os_enable_packet_filter(net, 0); /* DHCP starts */ ++ else ++ net_os_enable_packet_filter(net, 1); /* DHCP ends */ ++#endif /* PKT_FILTER_SUPPORT */ ++#endif /* WL_CFG80211 */ ++ } ++ else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) { ++ bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len); ++ } ++ else if (strnicmp(command, CMD_SETSUSPENDMODE, strlen(CMD_SETSUSPENDMODE)) == 0) { ++ bytes_written = wl_android_set_suspendmode(net, command, priv_cmd.total_len); ++ } ++ else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) { ++ uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; ++#ifdef WL_HOST_BAND_MGMT ++ if (wl_cfg80211_set_band(net, band) < 0) { ++ bytes_written = -1; ++ goto exit; ++ } ++ if (band == WLC_BAND_AUTO) ++ bytes_written = wldev_set_band(net, band); ++#else ++ bytes_written = wldev_set_band(net, band); ++#endif /* WL_HOST_BAND_MGMT */ ++ } ++ else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) { ++ bytes_written = wl_android_get_band(net, command, priv_cmd.total_len); ++ } ++#ifdef WL_CFG80211 ++ else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) { ++ char *country_code = command + strlen(CMD_COUNTRY) + 1; ++ bytes_written = wldev_set_country(net, country_code, true, true); ++ } ++#endif /* WL_CFG80211 */ ++#if defined(PNO_SUPPORT) && !defined(WL_SCHED_SCAN) ++ else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) { ++ bytes_written = dhd_dev_pno_reset(net); ++ } ++ else if (strnicmp(command, CMD_PNOSETUP_SET, strlen(CMD_PNOSETUP_SET)) == 0) { ++ bytes_written = wl_android_set_pno_setup(net, command, priv_cmd.total_len); ++ } ++ else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) { ++ uint pfn_enabled = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0'; ++ bytes_written = dhd_dev_pno_enable(net, pfn_enabled); ++ } ++#endif /* PNO_SUPPORT && !WL_SCHED_SCAN */ ++ else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) { ++ bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len); ++ } ++ else if (strnicmp(command, CMD_P2P_SET_NOA, strlen(CMD_P2P_SET_NOA)) == 0) { ++ int skip = strlen(CMD_P2P_SET_NOA) + 1; ++ bytes_written = wl_cfg80211_set_p2p_noa(net, command + skip, ++ priv_cmd.total_len - skip); ++ } ++#if !defined WL_ENABLE_P2P_IF ++ else if (strnicmp(command, CMD_P2P_GET_NOA, strlen(CMD_P2P_GET_NOA)) == 0) { ++ bytes_written = wl_cfg80211_get_p2p_noa(net, command, priv_cmd.total_len); ++ } ++#endif /* SUPPORT_GET_NOA */ ++ else if (strnicmp(command, CMD_P2P_SET_PS, strlen(CMD_P2P_SET_PS)) == 0) { ++ int skip = strlen(CMD_P2P_SET_PS) + 1; ++ bytes_written = wl_cfg80211_set_p2p_ps(net, command + skip, ++ priv_cmd.total_len - skip); ++ } ++#ifdef WL_CFG80211 ++ else if (strnicmp(command, CMD_SET_AP_WPS_P2P_IE, ++ strlen(CMD_SET_AP_WPS_P2P_IE)) == 0) { ++ int skip = strlen(CMD_SET_AP_WPS_P2P_IE) + 3; ++ bytes_written = wl_cfg80211_set_wps_p2p_ie(net, command + skip, ++ priv_cmd.total_len - skip, *(command + skip - 2) - '0'); ++ } ++#endif /* WL_CFG80211 */ ++ else { ++ DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command)); ++ snprintf(command, 3, "OK"); ++ bytes_written = strlen("OK"); ++ } ++ ++ if (bytes_written >= 0) { ++ if ((bytes_written == 0) && (priv_cmd.total_len > 0)) ++ command[0] = '\0'; ++ if (bytes_written >= priv_cmd.total_len) { ++ DHD_ERROR(("%s: bytes_written = %d\n", __FUNCTION__, bytes_written)); ++ bytes_written = priv_cmd.total_len; ++ } else { ++ bytes_written++; ++ } ++ priv_cmd.used_len = bytes_written; ++ if (copy_to_user(priv_cmd.buf, command, bytes_written)) { ++ DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__)); ++ ret = -EFAULT; ++ } ++ } ++ else { ++ ret = bytes_written; ++ } ++ ++exit: ++ net_os_wake_unlock(net); ++ if (command) { ++ kfree(command); ++ } ++ ++ return ret; ++} ++ ++int wl_android_init(void) ++{ ++ int ret = 0; ++ ++ dhd_msg_level |= DHD_ERROR_VAL; ++#ifdef ENABLE_INSMOD_NO_FW_LOAD ++ dhd_download_fw_on_driverload = FALSE; ++#endif /* ENABLE_INSMOD_NO_FW_LOAD */ ++#if defined(CUSTOMER_HW2) ++ if (!iface_name[0]) { ++ memset(iface_name, 0, IFNAMSIZ); ++ bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ); ++ } ++#endif ++ return ret; ++} ++ ++int wl_android_exit(void) ++{ ++ int ret = 0; ++ ++ return ret; ++} ++ ++void wl_android_post_init(void) ++{ ++ if (!dhd_download_fw_on_driverload) { ++ /* Call customer gpio to turn off power with WL_REG_ON signal */ ++ dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); ++ g_wifi_on = 0; ++ } ++} ++/** ++ * Functions for Android WiFi card detection ++ */ ++#if defined(CONFIG_WIFI_CONTROL_FUNC) ++ ++static int g_wifidev_registered = 0; ++static struct semaphore wifi_control_sem; ++static struct wifi_platform_data *wifi_control_data = NULL; ++static struct resource *wifi_irqres = NULL; ++ ++static int wifi_add_dev(void); ++static void wifi_del_dev(void); ++ ++int wl_android_wifictrl_func_add(void) ++{ ++ int ret = 0; ++ sema_init(&wifi_control_sem, 0); ++ ++ ret = wifi_add_dev(); ++ if (ret) { ++ DHD_ERROR(("%s: platform_driver_register failed\n", __FUNCTION__)); ++ return ret; ++ } ++ g_wifidev_registered = 1; ++ ++ /* Waiting callback after platform_driver_register is done or exit with error */ ++ if (down_timeout(&wifi_control_sem, msecs_to_jiffies(1000)) != 0) { ++ ret = -EINVAL; ++ DHD_ERROR(("%s: platform_driver_register timeout\n", __FUNCTION__)); ++ } ++ ++ return ret; ++} ++ ++void wl_android_wifictrl_func_del(void) ++{ ++ if (g_wifidev_registered) ++ { ++ wifi_del_dev(); ++ g_wifidev_registered = 0; ++ } ++} ++ ++void* wl_android_prealloc(int section, unsigned long size) ++{ ++ void *alloc_ptr = NULL; ++ if (wifi_control_data && wifi_control_data->mem_prealloc) { ++ alloc_ptr = wifi_control_data->mem_prealloc(section, size); ++ if (alloc_ptr) { ++ DHD_INFO(("success alloc section %d\n", section)); ++ if (size != 0L) ++ bzero(alloc_ptr, size); ++ return alloc_ptr; ++ } ++ } ++ ++ DHD_ERROR(("can't alloc section %d\n", section)); ++ return NULL; ++} ++ ++int wifi_get_irq_number(unsigned long *irq_flags_ptr) ++{ ++ if (wifi_irqres) { ++ *irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK; ++ return (int)wifi_irqres->start; ++ } ++#ifdef CUSTOM_OOB_GPIO_NUM ++ return CUSTOM_OOB_GPIO_NUM; ++#else ++ return -1; ++#endif ++} ++ ++int wifi_set_power(int on, unsigned long msec) ++{ ++ DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); ++ if (wifi_control_data && wifi_control_data->set_power) { ++ wifi_control_data->set_power(on); ++ } ++ if (msec) ++ msleep(msec); ++ return 0; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) ++int wifi_get_mac_addr(unsigned char *buf) ++{ ++ DHD_ERROR(("%s\n", __FUNCTION__)); ++ if (!buf) ++ return -EINVAL; ++ if (wifi_control_data && wifi_control_data->get_mac_addr) { ++ return wifi_control_data->get_mac_addr(buf); ++ } ++ return -EOPNOTSUPP; ++} ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) */ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++void *wifi_get_country_code(char *ccode) ++{ ++ DHD_TRACE(("%s\n", __FUNCTION__)); ++ if (!ccode) ++ return NULL; ++ if (wifi_control_data && wifi_control_data->get_country_code) { ++ return wifi_control_data->get_country_code(ccode); ++ } ++ return NULL; ++} ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */ ++ ++static int wifi_set_carddetect(int on) ++{ ++ DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); ++ if (wifi_control_data && wifi_control_data->set_carddetect) { ++ wifi_control_data->set_carddetect(on); ++ } ++ return 0; ++} ++ ++static int wifi_probe(struct platform_device *pdev) ++{ ++ struct wifi_platform_data *wifi_ctrl = ++ (struct wifi_platform_data *)(pdev->dev.platform_data); ++ ++ wifi_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq"); ++ if (wifi_irqres == NULL) ++ wifi_irqres = platform_get_resource_byname(pdev, ++ IORESOURCE_IRQ, "bcm4329_wlan_irq"); ++ wifi_control_data = wifi_ctrl; ++ wifi_set_power(1, 0); /* Power On */ ++ wifi_set_carddetect(1); /* CardDetect (0->1) */ ++ ++ up(&wifi_control_sem); ++ return 0; ++} ++ ++static int wifi_remove(struct platform_device *pdev) ++{ ++ struct wifi_platform_data *wifi_ctrl = ++ (struct wifi_platform_data *)(pdev->dev.platform_data); ++ ++ DHD_ERROR(("## %s\n", __FUNCTION__)); ++ wifi_control_data = wifi_ctrl; ++ ++ wifi_set_power(0, WIFI_TURNOFF_DELAY); /* Power Off */ ++ wifi_set_carddetect(0); /* CardDetect (1->0) */ ++ ++ up(&wifi_control_sem); ++ return 0; ++} ++ ++static int wifi_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ DHD_TRACE(("##> %s\n", __FUNCTION__)); ++#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && 1 ++ bcmsdh_oob_intr_set(0); ++#endif /* (OOB_INTR_ONLY) */ ++ return 0; ++} ++ ++static int wifi_resume(struct platform_device *pdev) ++{ ++ DHD_TRACE(("##> %s\n", __FUNCTION__)); ++#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && 1 ++ if (dhd_os_check_if_up(bcmsdh_get_drvdata())) ++ bcmsdh_oob_intr_set(1); ++#endif /* (OOB_INTR_ONLY) */ ++ return 0; ++} ++ ++static struct platform_driver wifi_device = { ++ .probe = wifi_probe, ++ .remove = wifi_remove, ++ .suspend = wifi_suspend, ++ .resume = wifi_resume, ++ .driver = { ++ .name = "bcmdhd_wlan", ++ } ++}; ++ ++static struct platform_driver wifi_device_legacy = { ++ .probe = wifi_probe, ++ .remove = wifi_remove, ++ .suspend = wifi_suspend, ++ .resume = wifi_resume, ++ .driver = { ++ .name = "bcm4329_wlan", ++ } ++}; ++ ++static int wifi_add_dev(void) ++{ ++ int ret; ++ DHD_TRACE(("## Calling platform_driver_register\n")); ++ ret = platform_driver_register(&wifi_device); ++ if (ret) ++ return ret; ++ ++ ret = platform_driver_register(&wifi_device_legacy); ++ return ret; ++} ++ ++static void wifi_del_dev(void) ++{ ++ DHD_TRACE(("## Unregister platform_driver_register\n")); ++ platform_driver_unregister(&wifi_device); ++ platform_driver_unregister(&wifi_device_legacy); ++} ++#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ +diff --git a/drivers/net/wireless/bcmdhd/wl_android.h b/drivers/net/wireless/bcmdhd/wl_android.h +new file mode 100644 +index 00000000..583a1673 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/wl_android.h +@@ -0,0 +1,57 @@ ++/* ++ * Linux cfg80211 driver - Android related functions ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wl_android.h 307885 2012-01-12 23:30:48Z $ ++ */ ++ ++#include ++#include ++#include ++ ++/** ++ * Android platform dependent functions, feel free to add Android specific functions here ++ * (save the macros in dhd). Please do NOT declare functions that are NOT exposed to dhd ++ * or cfg, define them as static in wl_android.c ++ */ ++ ++/** ++ * wl_android_init will be called from module init function (dhd_module_init now), similarly ++ * wl_android_exit will be called from module exit function (dhd_module_cleanup now) ++ */ ++int wl_android_init(void); ++int wl_android_exit(void); ++void wl_android_post_init(void); ++int wl_android_wifi_on(struct net_device *dev); ++int wl_android_wifi_off(struct net_device *dev); ++int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd); ++ ++#if defined(CONFIG_WIFI_CONTROL_FUNC) ++int wl_android_wifictrl_func_add(void); ++void wl_android_wifictrl_func_del(void); ++void* wl_android_prealloc(int section, unsigned long size); ++ ++int wifi_get_irq_number(unsigned long *irq_flags_ptr); ++int wifi_set_power(int on, unsigned long msec); ++int wifi_get_mac_addr(unsigned char *buf); ++void *wifi_get_country_code(char *ccode); ++#endif /* CONFIG_WIFI_CONTROL_FUNC */ +diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c +new file mode 100644 +index 00000000..193dbe05 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c +@@ -0,0 +1,9750 @@ ++/* ++ * Linux cfg80211 driver ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wl_cfg80211.c 381665 2013-01-29 02:34:22Z $ ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef PROP_TXSTATUS ++#include ++#endif ++ ++ ++#define IW_WSEC_ENABLED(wsec) ((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED)) ++ ++static struct device *cfg80211_parent_dev = NULL; ++struct wl_priv *wlcfg_drv_priv = NULL; ++u32 wl_dbg_level = WL_DBG_ERR; ++ ++#define MAX_WAIT_TIME 1500 ++ ++#ifdef VSDB ++/* sleep time to keep STA's connecting or connection for continuous af tx or finding a peer */ ++#define DEFAULT_SLEEP_TIME_VSDB 200 ++#define OFF_CHAN_TIME_THRESHOLD_MS 200 ++ ++/* if sta is connected or connecting, sleep for a while before retry af tx or finding a peer */ ++#define WL_AF_TX_KEEP_PRI_CONNECTION_VSDB(wl) \ ++ do { \ ++ if (wl_get_drv_status(wl, CONNECTED, wl_to_prmry_ndev(wl)) || \ ++ wl_get_drv_status(wl, CONNECTING, wl_to_prmry_ndev(wl))) { \ ++ msleep(DEFAULT_SLEEP_TIME_VSDB); \ ++ } \ ++ } while (0) ++#else /* VSDB */ ++/* if not VSDB, do nothing */ ++#define WL_AF_TX_KEEP_PRI_CONNECTION_VSDB(wl) ++#endif /* VSDB */ ++ ++#ifdef WL_CFG80211_SYNC_GON ++#define WL_DRV_STATUS_SENDING_AF_FRM_EXT(wl) \ ++ (wl_get_drv_status_all(wl, SENDING_ACT_FRM) || \ ++ wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM_LISTEN)) ++#else ++#define WL_DRV_STATUS_SENDING_AF_FRM_EXT(wl) wl_get_drv_status_all(wl, SENDING_ACT_FRM) ++#endif /* WL_CFG80211_SYNC_GON */ ++ ++#define WL_CHANSPEC_CTL_SB_NONE WL_CHANSPEC_CTL_SB_LLL ++ ++ ++#define DNGL_FUNC(func, parameters) func parameters; ++#define COEX_DHCP ++ ++#define WLAN_EID_SSID 0 ++#define CH_MIN_5G_CHANNEL 34 ++#define CH_MIN_2G_CHANNEL 1 ++ ++/* This is to override regulatory domains defined in cfg80211 module (reg.c) ++ * By default world regulatory domain defined in reg.c puts the flags NL80211_RRF_PASSIVE_SCAN ++ * and NL80211_RRF_NO_IBSS for 5GHz channels (for 36..48 and 149..165). ++ * With respect to these flags, wpa_supplicant doesn't start p2p operations on 5GHz channels. ++ * All the chnages in world regulatory domain are to be done here. ++ */ ++static const struct ieee80211_regdomain brcm_regdom = { ++ .n_reg_rules = 4, ++ .alpha2 = "99", ++ .reg_rules = { ++ /* IEEE 802.11b/g, channels 1..11 */ ++ REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), ++ /* If any */ ++ /* IEEE 802.11 channel 14 - Only JP enables ++ * this and for 802.11b only ++ */ ++ REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), ++ /* IEEE 802.11a, channel 36..64 */ ++ REG_RULE(5150-10, 5350+10, 40, 6, 20, 0), ++ /* IEEE 802.11a, channel 100..165 */ ++ REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), } ++}; ++ ++ ++/* Data Element Definitions */ ++#define WPS_ID_CONFIG_METHODS 0x1008 ++#define WPS_ID_REQ_TYPE 0x103A ++#define WPS_ID_DEVICE_NAME 0x1011 ++#define WPS_ID_VERSION 0x104A ++#define WPS_ID_DEVICE_PWD_ID 0x1012 ++#define WPS_ID_REQ_DEV_TYPE 0x106A ++#define WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS 0x1053 ++#define WPS_ID_PRIM_DEV_TYPE 0x1054 ++ ++/* Device Password ID */ ++#define DEV_PW_DEFAULT 0x0000 ++#define DEV_PW_USER_SPECIFIED 0x0001, ++#define DEV_PW_MACHINE_SPECIFIED 0x0002 ++#define DEV_PW_REKEY 0x0003 ++#define DEV_PW_PUSHBUTTON 0x0004 ++#define DEV_PW_REGISTRAR_SPECIFIED 0x0005 ++ ++/* Config Methods */ ++#define WPS_CONFIG_USBA 0x0001 ++#define WPS_CONFIG_ETHERNET 0x0002 ++#define WPS_CONFIG_LABEL 0x0004 ++#define WPS_CONFIG_DISPLAY 0x0008 ++#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010 ++#define WPS_CONFIG_INT_NFC_TOKEN 0x0020 ++#define WPS_CONFIG_NFC_INTERFACE 0x0040 ++#define WPS_CONFIG_PUSHBUTTON 0x0080 ++#define WPS_CONFIG_KEYPAD 0x0100 ++#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280 ++#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480 ++#define WPS_CONFIG_VIRT_DISPLAY 0x2008 ++#define WPS_CONFIG_PHY_DISPLAY 0x4008 ++ ++#define PM_BLOCK 1 ++#define PM_ENABLE 0 ++ ++#ifndef RSSI_OFFSET ++#define RSSI_OFFSET 0 ++#endif ++/* ++ * cfg80211_ops api/callback list ++ */ ++static s32 wl_frame_get_mgmt(u16 fc, const struct ether_addr *da, ++ const struct ether_addr *sa, const struct ether_addr *bssid, ++ u8 **pheader, u32 *body_len, u8 *pbody); ++static s32 __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, ++ struct cfg80211_scan_request *request, ++ struct cfg80211_ssid *this_ssid); ++static s32 wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, ++ struct cfg80211_scan_request *request); ++static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed); ++static s32 wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, ++ struct cfg80211_ibss_params *params); ++static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, ++ struct net_device *dev); ++static s32 wl_cfg80211_get_station(struct wiphy *wiphy, ++ struct net_device *dev, u8 *mac, ++ struct station_info *sinfo); ++static s32 wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, ++ struct net_device *dev, bool enabled, ++ s32 timeout); ++static int wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, ++ struct cfg80211_connect_params *sme); ++static s32 wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, ++ u16 reason_code); ++static s32 wl_cfg80211_set_tx_power(struct wiphy *wiphy, ++ enum nl80211_tx_power_setting type, ++ s32 dbm); ++static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm); ++static s32 wl_cfg80211_config_default_key(struct wiphy *wiphy, ++ struct net_device *dev, ++ u8 key_idx, bool unicast, bool multicast); ++static s32 wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, ++ u8 key_idx, bool pairwise, const u8 *mac_addr, ++ struct key_params *params); ++static s32 wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, ++ u8 key_idx, bool pairwise, const u8 *mac_addr); ++static s32 wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, ++ u8 key_idx, bool pairwise, const u8 *mac_addr, ++ void *cookie, void (*callback) (void *cookie, ++ struct key_params *params)); ++static s32 wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, ++ struct net_device *dev, u8 key_idx); ++static s32 wl_cfg80211_resume(struct wiphy *wiphy); ++static s32 wl_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, ++ struct net_device *dev, u64 cookie); ++static s32 wl_cfg80211_del_station(struct wiphy *wiphy, ++ struct net_device *ndev, u8* mac_addr); ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) ++static s32 wl_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow); ++#else ++static s32 wl_cfg80211_suspend(struct wiphy *wiphy); ++#endif ++static s32 wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, ++ struct cfg80211_pmksa *pmksa); ++static s32 wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, ++ struct cfg80211_pmksa *pmksa); ++static s32 wl_cfg80211_flush_pmksa(struct wiphy *wiphy, ++ struct net_device *dev); ++static s32 wl_notify_escan_complete(struct wl_priv *wl, ++ struct net_device *ndev, bool aborted, bool fw_abort); ++/* ++ * event & event Q handlers for cfg80211 interfaces ++ */ ++static s32 wl_create_event_handler(struct wl_priv *wl); ++static void wl_destroy_event_handler(struct wl_priv *wl); ++static s32 wl_event_handler(void *data); ++static void wl_init_eq(struct wl_priv *wl); ++static void wl_flush_eq(struct wl_priv *wl); ++static unsigned long wl_lock_eq(struct wl_priv *wl); ++static void wl_unlock_eq(struct wl_priv *wl, unsigned long flags); ++static void wl_init_eq_lock(struct wl_priv *wl); ++static void wl_init_event_handler(struct wl_priv *wl); ++static struct wl_event_q *wl_deq_event(struct wl_priv *wl); ++static s32 wl_enq_event(struct wl_priv *wl, struct net_device *ndev, u32 type, ++ const wl_event_msg_t *msg, void *data); ++static void wl_put_event(struct wl_event_q *e); ++static void wl_wakeup_event(struct wl_priv *wl); ++static s32 wl_notify_connect_status_ap(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data); ++static s32 wl_notify_connect_status(struct wl_priv *wl, ++ struct net_device *ndev, ++ const wl_event_msg_t *e, void *data); ++static s32 wl_notify_roaming_status(struct wl_priv *wl, ++ struct net_device *ndev, ++ const wl_event_msg_t *e, void *data); ++static s32 wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data); ++static s32 wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data, bool completed); ++static s32 wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data); ++static s32 wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data); ++#ifdef WL_SCHED_SCAN ++static s32 ++wl_notify_sched_scan_results(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data); ++#endif /* WL_SCHED_SCAN */ ++#ifdef PNO_SUPPORT ++static s32 wl_notify_pfn_status(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data); ++#endif /* PNO_SUPPORT */ ++static s32 wl_notifier_change_state(struct wl_priv *wl, struct net_info *_net_info, ++ enum wl_status state, bool set); ++/* ++ * register/deregister parent device ++ */ ++static void wl_cfg80211_clear_parent_dev(void); ++ ++/* ++ * ioctl utilites ++ */ ++ ++/* ++ * cfg80211 set_wiphy_params utilities ++ */ ++static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold); ++static s32 wl_set_rts(struct net_device *dev, u32 frag_threshold); ++static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l); ++ ++/* ++ * wl profile utilities ++ */ ++static s32 wl_update_prof(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data, s32 item); ++static void *wl_read_prof(struct wl_priv *wl, struct net_device *ndev, s32 item); ++static void wl_init_prof(struct wl_priv *wl, struct net_device *ndev); ++ ++/* ++ * cfg80211 connect utilites ++ */ ++static s32 wl_set_wpa_version(struct net_device *dev, ++ struct cfg80211_connect_params *sme); ++static s32 wl_set_auth_type(struct net_device *dev, ++ struct cfg80211_connect_params *sme); ++static s32 wl_set_set_cipher(struct net_device *dev, ++ struct cfg80211_connect_params *sme); ++static s32 wl_set_key_mgmt(struct net_device *dev, ++ struct cfg80211_connect_params *sme); ++static s32 wl_set_set_sharedkey(struct net_device *dev, ++ struct cfg80211_connect_params *sme); ++static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev); ++static void wl_ch_to_chanspec(int ch, ++ struct wl_join_params *join_params, size_t *join_params_size); ++ ++/* ++ * information element utilities ++ */ ++static void wl_rst_ie(struct wl_priv *wl); ++static __used s32 wl_add_ie(struct wl_priv *wl, u8 t, u8 l, u8 *v); ++static s32 wl_mrg_ie(struct wl_priv *wl, u8 *ie_stream, u16 ie_size); ++static s32 wl_cp_ie(struct wl_priv *wl, u8 *dst, u16 dst_size); ++static u32 wl_get_ielen(struct wl_priv *wl); ++ ++ ++static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *dev); ++static void wl_free_wdev(struct wl_priv *wl); ++#ifdef CONFIG_CFG80211_INTERNAL_REGDB ++static int ++wl_cfg80211_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request); ++#endif /* CONFIG_CFG80211_INTERNAL_REGDB */ ++ ++static s32 wl_inform_bss(struct wl_priv *wl); ++static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi, u8 is_roam_done); ++static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev, u8 is_roam_done); ++static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); ++s32 wl_cfg80211_channel_to_freq(u32 channel); ++ ++#if defined(DHCP_SCAN_SUPPRESS) ++static void wl_cfg80211_work_handler(struct work_struct *work); ++static void wl_cfg80211_scan_supp_timerfunc(ulong data); ++#endif /* DHCP_SCAN_SUPPRESS */ ++ ++static s32 wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, ++ u8 key_idx, const u8 *mac_addr, ++ struct key_params *params); ++/* ++ * key indianess swap utilities ++ */ ++static void swap_key_from_BE(struct wl_wsec_key *key); ++static void swap_key_to_BE(struct wl_wsec_key *key); ++ ++/* ++ * wl_priv memory init/deinit utilities ++ */ ++static s32 wl_init_priv_mem(struct wl_priv *wl); ++static void wl_deinit_priv_mem(struct wl_priv *wl); ++ ++static void wl_delay(u32 ms); ++ ++/* ++ * ibss mode utilities ++ */ ++static bool wl_is_ibssmode(struct wl_priv *wl, struct net_device *ndev); ++static __used bool wl_is_ibssstarter(struct wl_priv *wl); ++ ++/* ++ * link up/down , default configuration utilities ++ */ ++static s32 __wl_cfg80211_up(struct wl_priv *wl); ++static s32 __wl_cfg80211_down(struct wl_priv *wl); ++static bool wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e); ++static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net_device *ndev); ++static bool wl_is_nonetwork(struct wl_priv *wl, const wl_event_msg_t *e); ++static void wl_link_up(struct wl_priv *wl); ++static void wl_link_down(struct wl_priv *wl); ++static s32 wl_config_ifmode(struct wl_priv *wl, struct net_device *ndev, s32 iftype); ++static void wl_init_conf(struct wl_conf *conf); ++ ++/* ++ * iscan handler ++ */ ++static void wl_iscan_timer(unsigned long data); ++static void wl_term_iscan(struct wl_priv *wl); ++static s32 wl_init_scan(struct wl_priv *wl); ++static s32 wl_iscan_thread(void *data); ++static s32 wl_run_iscan(struct wl_iscan_ctrl *iscan, struct cfg80211_scan_request *request, ++ u16 action); ++static s32 wl_do_iscan(struct wl_priv *wl, struct cfg80211_scan_request *request); ++static s32 wl_wakeup_iscan(struct wl_iscan_ctrl *iscan); ++static s32 wl_invoke_iscan(struct wl_priv *wl); ++static s32 wl_get_iscan_results(struct wl_iscan_ctrl *iscan, u32 *status, ++ struct wl_scan_results **bss_list); ++static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted); ++static void wl_init_iscan_handler(struct wl_iscan_ctrl *iscan); ++static s32 wl_iscan_done(struct wl_priv *wl); ++static s32 wl_iscan_pending(struct wl_priv *wl); ++static s32 wl_iscan_inprogress(struct wl_priv *wl); ++static s32 wl_iscan_aborted(struct wl_priv *wl); ++ ++/* ++ * find most significant bit set ++ */ ++static __used u32 wl_find_msb(u16 bit16); ++ ++/* ++ * rfkill support ++ */ ++static int wl_setup_rfkill(struct wl_priv *wl, bool setup); ++static int wl_rfkill_set(void *data, bool blocked); ++ ++static wl_scan_params_t *wl_cfg80211_scan_alloc_params(int channel, ++ int nprobes, int *out_params_size); ++static void get_primary_mac(struct wl_priv *wl, struct ether_addr *mac); ++ ++/* ++ * Some external functions, TODO: move them to dhd_linux.h ++ */ ++int dhd_add_monitor(char *name, struct net_device **new_ndev); ++int dhd_del_monitor(struct net_device *ndev); ++int dhd_monitor_init(void *dhd_pub); ++int dhd_monitor_uninit(void); ++int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); ++ ++ ++ ++#define CHECK_SYS_UP(wlpriv) \ ++do { \ ++ struct net_device *ndev = wl_to_prmry_ndev(wlpriv); \ ++ if (unlikely(!wl_get_drv_status(wlpriv, READY, ndev))) { \ ++ WL_INFO(("device is not ready\n")); \ ++ return -EIO; \ ++ } \ ++} while (0) ++ ++ ++#define IS_WPA_AKM(akm) ((akm) == RSN_AKM_NONE || \ ++ (akm) == RSN_AKM_UNSPECIFIED || \ ++ (akm) == RSN_AKM_PSK) ++ ++ ++extern int dhd_wait_pend8021x(struct net_device *dev); ++#ifdef PROP_TXSTATUS_VSDB ++extern int disable_proptx; ++extern int dhd_wlfc_init(dhd_pub_t *dhd); ++extern void dhd_wlfc_deinit(dhd_pub_t *dhd); ++#endif /* PROP_TXSTATUS_VSDB */ ++ ++#if (WL_DBG_LEVEL > 0) ++#define WL_DBG_ESTR_MAX 50 ++static s8 wl_dbg_estr[][WL_DBG_ESTR_MAX] = { ++ "SET_SSID", "JOIN", "START", "AUTH", "AUTH_IND", ++ "DEAUTH", "DEAUTH_IND", "ASSOC", "ASSOC_IND", "REASSOC", ++ "REASSOC_IND", "DISASSOC", "DISASSOC_IND", "QUIET_START", "QUIET_END", ++ "BEACON_RX", "LINK", "MIC_ERROR", "NDIS_LINK", "ROAM", ++ "TXFAIL", "PMKID_CACHE", "RETROGRADE_TSF", "PRUNE", "AUTOAUTH", ++ "EAPOL_MSG", "SCAN_COMPLETE", "ADDTS_IND", "DELTS_IND", "BCNSENT_IND", ++ "BCNRX_MSG", "BCNLOST_MSG", "ROAM_PREP", "PFN_NET_FOUND", ++ "PFN_NET_LOST", ++ "RESET_COMPLETE", "JOIN_START", "ROAM_START", "ASSOC_START", ++ "IBSS_ASSOC", ++ "RADIO", "PSM_WATCHDOG", "WLC_E_CCX_ASSOC_START", "WLC_E_CCX_ASSOC_ABORT", ++ "PROBREQ_MSG", ++ "SCAN_CONFIRM_IND", "PSK_SUP", "COUNTRY_CODE_CHANGED", ++ "EXCEEDED_MEDIUM_TIME", "ICV_ERROR", ++ "UNICAST_DECODE_ERROR", "MULTICAST_DECODE_ERROR", "TRACE", ++ "WLC_E_BTA_HCI_EVENT", "IF", "WLC_E_P2P_DISC_LISTEN_COMPLETE", ++ "RSSI", "PFN_SCAN_COMPLETE", "WLC_E_EXTLOG_MSG", ++ "ACTION_FRAME", "ACTION_FRAME_COMPLETE", "WLC_E_PRE_ASSOC_IND", ++ "WLC_E_PRE_REASSOC_IND", "WLC_E_CHANNEL_ADOPTED", "WLC_E_AP_STARTED", ++ "WLC_E_DFS_AP_STOP", "WLC_E_DFS_AP_RESUME", "WLC_E_WAI_STA_EVENT", ++ "WLC_E_WAI_MSG", "WLC_E_ESCAN_RESULT", "WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE", ++ "WLC_E_PROBRESP_MSG", "WLC_E_P2P_PROBREQ_MSG", "WLC_E_DCS_REQUEST", "WLC_E_FIFO_CREDIT_MAP", ++ "WLC_E_ACTION_FRAME_RX", "WLC_E_WAKE_EVENT", "WLC_E_RM_COMPLETE" ++}; ++#endif /* WL_DBG_LEVEL */ ++ ++#define CHAN2G(_channel, _freq, _flags) { \ ++ .band = IEEE80211_BAND_2GHZ, \ ++ .center_freq = (_freq), \ ++ .hw_value = (_channel), \ ++ .flags = (_flags), \ ++ .max_antenna_gain = 0, \ ++ .max_power = 30, \ ++} ++ ++#define CHAN5G(_channel, _flags) { \ ++ .band = IEEE80211_BAND_5GHZ, \ ++ .center_freq = 5000 + (5 * (_channel)), \ ++ .hw_value = (_channel), \ ++ .flags = (_flags), \ ++ .max_antenna_gain = 0, \ ++ .max_power = 30, \ ++} ++ ++#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) ++#define RATETAB_ENT(_rateid, _flags) \ ++ { \ ++ .bitrate = RATE_TO_BASE100KBPS(_rateid), \ ++ .hw_value = (_rateid), \ ++ .flags = (_flags), \ ++ } ++ ++static struct ieee80211_rate __wl_rates[] = { ++ RATETAB_ENT(WLC_RATE_1M, 0), ++ RATETAB_ENT(WLC_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE), ++ RATETAB_ENT(WLC_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE), ++ RATETAB_ENT(WLC_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE), ++ RATETAB_ENT(WLC_RATE_6M, 0), ++ RATETAB_ENT(WLC_RATE_9M, 0), ++ RATETAB_ENT(WLC_RATE_12M, 0), ++ RATETAB_ENT(WLC_RATE_18M, 0), ++ RATETAB_ENT(WLC_RATE_24M, 0), ++ RATETAB_ENT(WLC_RATE_36M, 0), ++ RATETAB_ENT(WLC_RATE_48M, 0), ++ RATETAB_ENT(WLC_RATE_54M, 0) ++}; ++ ++#define wl_a_rates (__wl_rates + 4) ++#define wl_a_rates_size 8 ++#define wl_g_rates (__wl_rates + 0) ++#define wl_g_rates_size 12 ++ ++static struct ieee80211_channel __wl_2ghz_channels[] = { ++ CHAN2G(1, 2412, 0), ++ CHAN2G(2, 2417, 0), ++ CHAN2G(3, 2422, 0), ++ CHAN2G(4, 2427, 0), ++ CHAN2G(5, 2432, 0), ++ CHAN2G(6, 2437, 0), ++ CHAN2G(7, 2442, 0), ++ CHAN2G(8, 2447, 0), ++ CHAN2G(9, 2452, 0), ++ CHAN2G(10, 2457, 0), ++ CHAN2G(11, 2462, 0), ++ CHAN2G(12, 2467, 0), ++ CHAN2G(13, 2472, 0), ++ CHAN2G(14, 2484, 0) ++}; ++ ++static struct ieee80211_channel __wl_5ghz_a_channels[] = { ++ CHAN5G(34, 0), CHAN5G(36, 0), ++ CHAN5G(38, 0), CHAN5G(40, 0), ++ CHAN5G(42, 0), CHAN5G(44, 0), ++ CHAN5G(46, 0), CHAN5G(48, 0), ++ CHAN5G(52, 0), CHAN5G(56, 0), ++ CHAN5G(60, 0), CHAN5G(64, 0), ++ CHAN5G(100, 0), CHAN5G(104, 0), ++ CHAN5G(108, 0), CHAN5G(112, 0), ++ CHAN5G(116, 0), CHAN5G(120, 0), ++ CHAN5G(124, 0), CHAN5G(128, 0), ++ CHAN5G(132, 0), CHAN5G(136, 0), ++ CHAN5G(140, 0), CHAN5G(149, 0), ++ CHAN5G(153, 0), CHAN5G(157, 0), ++ CHAN5G(161, 0), CHAN5G(165, 0) ++}; ++ ++static struct ieee80211_supported_band __wl_band_2ghz = { ++ .band = IEEE80211_BAND_2GHZ, ++ .channels = __wl_2ghz_channels, ++ .n_channels = ARRAY_SIZE(__wl_2ghz_channels), ++ .bitrates = wl_g_rates, ++ .n_bitrates = wl_g_rates_size ++}; ++ ++static struct ieee80211_supported_band __wl_band_5ghz_a = { ++ .band = IEEE80211_BAND_5GHZ, ++ .channels = __wl_5ghz_a_channels, ++ .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels), ++ .bitrates = wl_a_rates, ++ .n_bitrates = wl_a_rates_size ++}; ++ ++static const u32 __wl_cipher_suites[] = { ++ WLAN_CIPHER_SUITE_WEP40, ++ WLAN_CIPHER_SUITE_WEP104, ++ WLAN_CIPHER_SUITE_TKIP, ++ WLAN_CIPHER_SUITE_CCMP, ++ WLAN_CIPHER_SUITE_AES_CMAC, ++}; ++ ++ ++/* IOCtl version read from targeted driver */ ++static int ioctl_version; ++ ++/* Return a new chanspec given a legacy chanspec ++ * Returns INVCHANSPEC on error ++ */ ++static chanspec_t ++wl_chspec_from_legacy(chanspec_t legacy_chspec) ++{ ++ chanspec_t chspec; ++ ++ /* get the channel number */ ++ chspec = LCHSPEC_CHANNEL(legacy_chspec); ++ ++ /* convert the band */ ++ if (LCHSPEC_IS2G(legacy_chspec)) { ++ chspec |= WL_CHANSPEC_BAND_2G; ++ } else { ++ chspec |= WL_CHANSPEC_BAND_5G; ++ } ++ ++ /* convert the bw and sideband */ ++ if (LCHSPEC_IS20(legacy_chspec)) { ++ chspec |= WL_CHANSPEC_BW_20; ++ } else { ++ chspec |= WL_CHANSPEC_BW_40; ++ if (LCHSPEC_CTL_SB(legacy_chspec) == WL_LCHANSPEC_CTL_SB_LOWER) { ++ chspec |= WL_CHANSPEC_CTL_SB_L; ++ } else { ++ chspec |= WL_CHANSPEC_CTL_SB_U; ++ } ++ } ++ ++ if (wf_chspec_malformed(chspec)) { ++ WL_ERR(("wl_chspec_from_legacy: output chanspec (0x%04X) malformed\n", ++ chspec)); ++ return INVCHANSPEC; ++ } ++ ++ return chspec; ++} ++ ++/* Return a legacy chanspec given a new chanspec ++ * Returns INVCHANSPEC on error ++ */ ++static chanspec_t ++wl_chspec_to_legacy(chanspec_t chspec) ++{ ++ chanspec_t lchspec; ++ ++ if (wf_chspec_malformed(chspec)) { ++ WL_ERR(("wl_chspec_to_legacy: input chanspec (0x%04X) malformed\n", ++ chspec)); ++ return INVCHANSPEC; ++ } ++ ++ /* get the channel number */ ++ lchspec = CHSPEC_CHANNEL(chspec); ++ ++ /* convert the band */ ++ if (CHSPEC_IS2G(chspec)) { ++ lchspec |= WL_LCHANSPEC_BAND_2G; ++ } else { ++ lchspec |= WL_LCHANSPEC_BAND_5G; ++ } ++ ++ /* convert the bw and sideband */ ++ if (CHSPEC_IS20(chspec)) { ++ lchspec |= WL_LCHANSPEC_BW_20; ++ lchspec |= WL_LCHANSPEC_CTL_SB_NONE; ++ } else if (CHSPEC_IS40(chspec)) { ++ lchspec |= WL_LCHANSPEC_BW_40; ++ if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_L) { ++ lchspec |= WL_LCHANSPEC_CTL_SB_LOWER; ++ } else { ++ lchspec |= WL_LCHANSPEC_CTL_SB_UPPER; ++ } ++ } else { ++ /* cannot express the bandwidth */ ++ char chanbuf[CHANSPEC_STR_LEN]; ++ WL_ERR(( ++ "wl_chspec_to_legacy: unable to convert chanspec %s (0x%04X) " ++ "to pre-11ac format\n", ++ wf_chspec_ntoa(chspec, chanbuf), chspec)); ++ return INVCHANSPEC; ++ } ++ ++ return lchspec; ++} ++ ++/* given a chanspec value, do the endian and chanspec version conversion to ++ * a chanspec_t value ++ * Returns INVCHANSPEC on error ++ */ ++static chanspec_t ++wl_chspec_host_to_driver(chanspec_t chanspec) ++{ ++ if (ioctl_version == 1) { ++ chanspec = wl_chspec_to_legacy(chanspec); ++ if (chanspec == INVCHANSPEC) { ++ return chanspec; ++ } ++ } ++ chanspec = htodchanspec(chanspec); ++ ++ return chanspec; ++} ++ ++/* given a channel value, do the endian and chanspec version conversion to ++ * a chanspec_t value ++ * Returns INVCHANSPEC on error ++ */ ++chanspec_t ++wl_ch_host_to_driver(u16 channel) ++{ ++ ++ chanspec_t chanspec; ++ ++ chanspec = channel & WL_CHANSPEC_CHAN_MASK; ++ ++ if (channel <= CH_MAX_2G_CHANNEL) ++ chanspec |= WL_CHANSPEC_BAND_2G; ++ else ++ chanspec |= WL_CHANSPEC_BAND_5G; ++ ++ chanspec |= WL_CHANSPEC_BW_20; ++ chanspec |= WL_CHANSPEC_CTL_SB_NONE; ++ ++ return wl_chspec_host_to_driver(chanspec); ++} ++ ++/* given a chanspec value from the driver, do the endian and chanspec version conversion to ++ * a chanspec_t value ++ * Returns INVCHANSPEC on error ++ */ ++static chanspec_t ++wl_chspec_driver_to_host(chanspec_t chanspec) ++{ ++ chanspec = dtohchanspec(chanspec); ++ if (ioctl_version == 1) { ++ chanspec = wl_chspec_from_legacy(chanspec); ++ } ++ ++ return chanspec; ++} ++ ++/* There isn't a lot of sense in it, but you can transmit anything you like */ ++static const struct ieee80211_txrx_stypes ++wl_cfg80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { ++ [NL80211_IFTYPE_ADHOC] = { ++ .tx = 0xffff, ++ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) ++ }, ++ [NL80211_IFTYPE_STATION] = { ++ .tx = 0xffff, ++ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | ++ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) ++ }, ++ [NL80211_IFTYPE_AP] = { ++ .tx = 0xffff, ++ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | ++ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | ++ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | ++ BIT(IEEE80211_STYPE_DISASSOC >> 4) | ++ BIT(IEEE80211_STYPE_AUTH >> 4) | ++ BIT(IEEE80211_STYPE_DEAUTH >> 4) | ++ BIT(IEEE80211_STYPE_ACTION >> 4) ++ }, ++ [NL80211_IFTYPE_AP_VLAN] = { ++ /* copy AP */ ++ .tx = 0xffff, ++ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | ++ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | ++ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | ++ BIT(IEEE80211_STYPE_DISASSOC >> 4) | ++ BIT(IEEE80211_STYPE_AUTH >> 4) | ++ BIT(IEEE80211_STYPE_DEAUTH >> 4) | ++ BIT(IEEE80211_STYPE_ACTION >> 4) ++ }, ++ [NL80211_IFTYPE_P2P_CLIENT] = { ++ .tx = 0xffff, ++ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | ++ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) ++ }, ++ [NL80211_IFTYPE_P2P_GO] = { ++ .tx = 0xffff, ++ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | ++ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | ++ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | ++ BIT(IEEE80211_STYPE_DISASSOC >> 4) | ++ BIT(IEEE80211_STYPE_AUTH >> 4) | ++ BIT(IEEE80211_STYPE_DEAUTH >> 4) | ++ BIT(IEEE80211_STYPE_ACTION >> 4) ++ } ++}; ++ ++static void swap_key_from_BE(struct wl_wsec_key *key) ++{ ++ key->index = htod32(key->index); ++ key->len = htod32(key->len); ++ key->algo = htod32(key->algo); ++ key->flags = htod32(key->flags); ++ key->rxiv.hi = htod32(key->rxiv.hi); ++ key->rxiv.lo = htod16(key->rxiv.lo); ++ key->iv_initialized = htod32(key->iv_initialized); ++} ++ ++static void swap_key_to_BE(struct wl_wsec_key *key) ++{ ++ key->index = dtoh32(key->index); ++ key->len = dtoh32(key->len); ++ key->algo = dtoh32(key->algo); ++ key->flags = dtoh32(key->flags); ++ key->rxiv.hi = dtoh32(key->rxiv.hi); ++ key->rxiv.lo = dtoh16(key->rxiv.lo); ++ key->iv_initialized = dtoh32(key->iv_initialized); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) ++/* For debug: Dump the contents of the encoded wps ie buffe */ ++static void ++wl_validate_wps_ie(char *wps_ie, s32 wps_ie_len, bool *pbc) ++{ ++ #define WPS_IE_FIXED_LEN 6 ++ u16 len; ++ u8 *subel = NULL; ++ u16 subelt_id; ++ u16 subelt_len; ++ u16 val; ++ u8 *valptr = (uint8*) &val; ++ if (wps_ie == NULL || wps_ie_len < WPS_IE_FIXED_LEN) { ++ WL_ERR(("invalid argument : NULL\n")); ++ return; ++ } ++ len = (u16)wps_ie[TLV_LEN_OFF]; ++ ++ if (len > wps_ie_len) { ++ WL_ERR(("invalid length len %d, wps ie len %d\n", len, wps_ie_len)); ++ return; ++ } ++ WL_DBG(("wps_ie len=%d\n", len)); ++ len -= 4; /* for the WPS IE's OUI, oui_type fields */ ++ subel = wps_ie + WPS_IE_FIXED_LEN; ++ while (len >= 4) { /* must have attr id, attr len fields */ ++ valptr[0] = *subel++; ++ valptr[1] = *subel++; ++ subelt_id = HTON16(val); ++ ++ valptr[0] = *subel++; ++ valptr[1] = *subel++; ++ subelt_len = HTON16(val); ++ ++ len -= 4; /* for the attr id, attr len fields */ ++ len -= subelt_len; /* for the remaining fields in this attribute */ ++ WL_DBG((" subel=%p, subelt_id=0x%x subelt_len=%u\n", ++ subel, subelt_id, subelt_len)); ++ ++ if (subelt_id == WPS_ID_VERSION) { ++ WL_DBG((" attr WPS_ID_VERSION: %u\n", *subel)); ++ } else if (subelt_id == WPS_ID_REQ_TYPE) { ++ WL_DBG((" attr WPS_ID_REQ_TYPE: %u\n", *subel)); ++ } else if (subelt_id == WPS_ID_CONFIG_METHODS) { ++ valptr[0] = *subel; ++ valptr[1] = *(subel + 1); ++ WL_DBG((" attr WPS_ID_CONFIG_METHODS: %x\n", HTON16(val))); ++ } else if (subelt_id == WPS_ID_DEVICE_NAME) { ++ char devname[100]; ++ memcpy(devname, subel, subelt_len); ++ devname[subelt_len] = '\0'; ++ WL_DBG((" attr WPS_ID_DEVICE_NAME: %s (len %u)\n", ++ devname, subelt_len)); ++ } else if (subelt_id == WPS_ID_DEVICE_PWD_ID) { ++ valptr[0] = *subel; ++ valptr[1] = *(subel + 1); ++ WL_DBG((" attr WPS_ID_DEVICE_PWD_ID: %u\n", HTON16(val))); ++ *pbc = (HTON16(val) == DEV_PW_PUSHBUTTON) ? true : false; ++ } else if (subelt_id == WPS_ID_PRIM_DEV_TYPE) { ++ valptr[0] = *subel; ++ valptr[1] = *(subel + 1); ++ WL_DBG((" attr WPS_ID_PRIM_DEV_TYPE: cat=%u \n", HTON16(val))); ++ valptr[0] = *(subel + 6); ++ valptr[1] = *(subel + 7); ++ WL_DBG((" attr WPS_ID_PRIM_DEV_TYPE: subcat=%u\n", HTON16(val))); ++ } else if (subelt_id == WPS_ID_REQ_DEV_TYPE) { ++ valptr[0] = *subel; ++ valptr[1] = *(subel + 1); ++ WL_DBG((" attr WPS_ID_REQ_DEV_TYPE: cat=%u\n", HTON16(val))); ++ valptr[0] = *(subel + 6); ++ valptr[1] = *(subel + 7); ++ WL_DBG((" attr WPS_ID_REQ_DEV_TYPE: subcat=%u\n", HTON16(val))); ++ } else if (subelt_id == WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS) { ++ valptr[0] = *subel; ++ valptr[1] = *(subel + 1); ++ WL_DBG((" attr WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS" ++ ": cat=%u\n", HTON16(val))); ++ } else { ++ WL_DBG((" unknown attr 0x%x\n", subelt_id)); ++ } ++ ++ subel += subelt_len; ++ } ++} ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ ++ ++static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy) ++{ ++ chanspec_t chspec; ++ int err = 0; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct net_device *dev = wl_to_prmry_ndev(wl); ++ struct ether_addr bssid; ++ struct wl_bss_info *bss = NULL; ++ ++ if ((err = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, sizeof(bssid), false))) { ++ /* STA interface is not associated. So start the new interface on a temp ++ * channel . Later proper channel will be applied by the above framework ++ * via set_channel (cfg80211 API). ++ */ ++ WL_DBG(("Not associated. Return a temp channel. \n")); ++ return wl_ch_host_to_driver(WL_P2P_TEMP_CHAN); ++ } ++ ++ ++ *(u32 *) wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); ++ if ((err = wldev_ioctl(dev, WLC_GET_BSS_INFO, wl->extra_buf, ++ WL_EXTRA_BUF_MAX, false))) { ++ WL_ERR(("Failed to get associated bss info, use temp channel \n")); ++ chspec = wl_ch_host_to_driver(WL_P2P_TEMP_CHAN); ++ } ++ else { ++ bss = (struct wl_bss_info *) (wl->extra_buf + 4); ++ chspec = bss->chanspec; ++ WL_DBG(("Valid BSS Found. chanspec:%d \n", chspec)); ++ } ++ return chspec; ++} ++ ++static struct net_device* wl_cfg80211_add_monitor_if(char *name) ++{ ++#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) ++ WL_INFO(("wl_cfg80211_add_monitor_if: No more support monitor interface\n")); ++ return ERR_PTR(-EOPNOTSUPP); ++#else ++ struct net_device* ndev = NULL; ++ ++ dhd_add_monitor(name, &ndev); ++ WL_INFO(("wl_cfg80211_add_monitor_if net device returned: 0x%p\n", ndev)); ++ return ndev; ++#endif /* defined(WLP2P) && defined(WL_ENABLE_P2P_IF) */ ++} ++ ++static struct net_device * ++wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, ++ enum nl80211_iftype type, u32 *flags, ++ struct vif_params *params) ++{ ++ s32 err; ++ s32 timeout = -1; ++ s32 wlif_type = -1; ++ s32 mode = 0; ++ s32 val = 0; ++ s32 dhd_mode = 0; ++ chanspec_t chspec; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct net_device *_ndev; ++ struct ether_addr primary_mac; ++ int (*net_attach)(void *dhdp, int ifidx); ++ bool rollback_lock = false; ++#ifdef PROP_TXSTATUS_VSDB ++ s32 up = 1; ++ dhd_pub_t *dhd; ++#endif /* PROP_TXSTATUS_VSDB */ ++ ++ if (!wl) ++ return ERR_PTR(-EINVAL); ++ ++#ifdef PROP_TXSTATUS_VSDB ++ dhd = (dhd_pub_t *)(wl->pub); ++#endif /* PROP_TXSTATUS_VSDB */ ++ ++ ++ /* Use primary I/F for sending cmds down to firmware */ ++ _ndev = wl_to_prmry_ndev(wl); ++ ++ WL_DBG(("if name: %s, type: %d\n", name, type)); ++ switch (type) { ++ case NL80211_IFTYPE_ADHOC: ++ case NL80211_IFTYPE_AP_VLAN: ++ case NL80211_IFTYPE_WDS: ++ case NL80211_IFTYPE_MESH_POINT: ++ WL_ERR(("Unsupported interface type\n")); ++ mode = WL_MODE_IBSS; ++ return NULL; ++ case NL80211_IFTYPE_MONITOR: ++ return wl_cfg80211_add_monitor_if(name); ++ case NL80211_IFTYPE_P2P_CLIENT: ++ case NL80211_IFTYPE_STATION: ++ wlif_type = WL_P2P_IF_CLIENT; ++ mode = WL_MODE_BSS; ++ break; ++ case NL80211_IFTYPE_P2P_GO: ++ case NL80211_IFTYPE_AP: ++ wlif_type = WL_P2P_IF_GO; ++ mode = WL_MODE_AP; ++ break; ++ default: ++ WL_ERR(("Unsupported interface type\n")); ++ return NULL; ++ break; ++ } ++ ++ if (!name) { ++ WL_ERR(("name is NULL\n")); ++ return NULL; ++ } ++ if (wl->p2p_supported && (wlif_type != -1)) { ++ if (wl_get_p2p_status(wl, IF_DELETING)) { ++ /* wait till IF_DEL is complete ++ * release the lock for the unregister to proceed ++ */ ++ if (rtnl_is_locked()) { ++ rtnl_unlock(); ++ rollback_lock = true; ++ } ++ WL_INFO(("%s: Released the lock and wait till IF_DEL is complete\n", ++ __func__)); ++ timeout = wait_event_interruptible_timeout(wl->netif_change_event, ++ (wl_get_p2p_status(wl, IF_DELETING) == false), ++ msecs_to_jiffies(MAX_WAIT_TIME)); ++ ++ /* put back the rtnl_lock again */ ++ if (rollback_lock) { ++ rtnl_lock(); ++ rollback_lock = false; ++ } ++ if (timeout > 0) { ++ WL_ERR(("IF DEL is Success\n")); ++ ++ } else { ++ WL_ERR(("timeount < 0, return -EAGAIN\n")); ++ return ERR_PTR(-EAGAIN); ++ } ++ /* It should be now be safe to put this check here since we are sure ++ * by now netdev_notifier (unregister) would have been called ++ */ ++ if (wl->iface_cnt == IFACE_MAX_CNT) ++ return ERR_PTR(-ENOMEM); ++ } ++ ++#ifdef PROP_TXSTATUS_VSDB ++ if (!dhd) ++ return ERR_PTR(-ENODEV); ++#endif /* PROP_TXSTATUS_VSDB */ ++ if (!wl->p2p) ++ return ERR_PTR(-ENODEV); ++ ++ if (wl->p2p && !wl->p2p->on && strstr(name, WL_P2P_INTERFACE_PREFIX)) { ++ p2p_on(wl) = true; ++ wl_cfgp2p_set_firm_p2p(wl); ++ wl_cfgp2p_init_discovery(wl); ++ get_primary_mac(wl, &primary_mac); ++ wl_cfgp2p_generate_bss_mac(&primary_mac, ++ &wl->p2p->dev_addr, &wl->p2p->int_addr); ++ } ++ ++ memset(wl->p2p->vir_ifname, 0, IFNAMSIZ); ++ strncpy(wl->p2p->vir_ifname, name, IFNAMSIZ - 1); ++ ++ wl_notify_escan_complete(wl, _ndev, true, true); ++#ifdef PROP_TXSTATUS_VSDB ++ if (!wl->wlfc_on && !disable_proptx) { ++ dhd->wlfc_enabled = true; ++ dhd_wlfc_init(dhd); ++ err = wldev_ioctl(_ndev, WLC_UP, &up, sizeof(s32), true); ++ if (err < 0) ++ WL_ERR(("WLC_UP return err:%d\n", err)); ++ wl->wlfc_on = true; ++ } ++#endif /* PROP_TXSTATUS_VSDB */ ++ ++ /* In concurrency case, STA may be already associated in a particular channel. ++ * so retrieve the current channel of primary interface and then start the virtual ++ * interface on that. ++ */ ++ chspec = wl_cfg80211_get_shared_freq(wiphy); ++ ++ /* For P2P mode, use P2P-specific driver features to create the ++ * bss: "wl p2p_ifadd" ++ */ ++ wl_set_p2p_status(wl, IF_ADD); ++ if (wlif_type == WL_P2P_IF_GO) ++ wldev_iovar_setint(_ndev, "mpc", 0); ++ err = wl_cfgp2p_ifadd(wl, &wl->p2p->int_addr, htod32(wlif_type), chspec); ++ ++ if (unlikely(err)) { ++ WL_ERR((" virtual iface add failed (%d) \n", err)); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ timeout = wait_event_interruptible_timeout(wl->netif_change_event, ++ (wl_get_p2p_status(wl, IF_ADD) == false), ++ msecs_to_jiffies(MAX_WAIT_TIME)); ++ if (timeout > 0 && (!wl_get_p2p_status(wl, IF_ADD))) { ++ ++ struct wireless_dev *vwdev; ++ vwdev = kzalloc(sizeof(*vwdev), GFP_KERNEL); ++ if (unlikely(!vwdev)) { ++ WL_ERR(("Could not allocate wireless device\n")); ++ return ERR_PTR(-ENOMEM); ++ } ++ vwdev->wiphy = wl->wdev->wiphy; ++ WL_INFO((" virtual interface(%s) is created memalloc done \n", ++ wl->p2p->vir_ifname)); ++ vwdev->iftype = type; ++ _ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); ++ _ndev->ieee80211_ptr = vwdev; ++ SET_NETDEV_DEV(_ndev, wiphy_dev(vwdev->wiphy)); ++ vwdev->netdev = _ndev; ++ wl_set_drv_status(wl, READY, _ndev); ++ wl->p2p->vif_created = true; ++ wl_set_mode_by_netdev(wl, _ndev, mode); ++ net_attach = wl_to_p2p_bss_private(wl, P2PAPI_BSSCFG_CONNECTION); ++ if (rtnl_is_locked()) { ++ rtnl_unlock(); ++ rollback_lock = true; ++ } ++ if (net_attach && !net_attach(wl->pub, _ndev->ifindex)) { ++ wl_alloc_netinfo(wl, _ndev, vwdev, mode, PM_ENABLE); ++ val = 1; ++ /* Disable firmware roaming for P2P interface */ ++ wldev_iovar_setint(_ndev, "roam_off", val); ++ WL_ERR((" virtual interface(%s) is " ++ "created net attach done\n", wl->p2p->vir_ifname)); ++ if (mode == WL_MODE_AP) ++ wl_set_drv_status(wl, CONNECTED, _ndev); ++ if (type == NL80211_IFTYPE_P2P_CLIENT) ++ dhd_mode = DHD_FLAG_P2P_GC_MODE; ++ else if (type == NL80211_IFTYPE_P2P_GO) ++ dhd_mode = DHD_FLAG_P2P_GO_MODE; ++ DNGL_FUNC(dhd_cfg80211_set_p2p_info, (wl, dhd_mode)); ++ } else { ++ /* put back the rtnl_lock again */ ++ if (rollback_lock) ++ rtnl_lock(); ++ goto fail; ++ } ++ /* put back the rtnl_lock again */ ++ if (rollback_lock) ++ rtnl_lock(); ++ return _ndev; ++ ++ } else { ++ wl_clr_p2p_status(wl, IF_ADD); ++ WL_ERR((" virtual interface(%s) is not created \n", wl->p2p->vir_ifname)); ++ memset(wl->p2p->vir_ifname, '\0', IFNAMSIZ); ++ wl->p2p->vif_created = false; ++#ifdef PROP_TXSTATUS_VSDB ++ if (dhd->wlfc_enabled && wl->wlfc_on) { ++ dhd->wlfc_enabled = false; ++ dhd_wlfc_deinit(dhd); ++ wl->wlfc_on = false; ++ } ++#endif /* PROP_TXSTATUS_VSDB */ ++ } ++ } ++fail: ++ if (wlif_type == WL_P2P_IF_GO) ++ wldev_iovar_setint(_ndev, "mpc", 1); ++ return ERR_PTR(-ENODEV); ++} ++ ++static s32 ++wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, struct net_device *dev) ++{ ++ struct ether_addr p2p_mac; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ s32 timeout = -1; ++ s32 ret = 0; ++ WL_DBG(("Enter\n")); ++ ++ if (wl->p2p_net == dev) { ++ /* Since there is no ifidx corresponding to p2p0, cmds to ++ * firmware should be routed through primary I/F ++ */ ++ dev = wl_to_prmry_ndev(wl); ++ } ++ ++ if (wl->p2p_supported) { ++ memcpy(p2p_mac.octet, wl->p2p->int_addr.octet, ETHER_ADDR_LEN); ++ ++ /* Clear GO_NEG_PHASE bit to take care of GO-NEG-FAIL cases ++ */ ++ WL_DBG(("P2P: GO_NEG_PHASE status cleared ")); ++ wl_clr_p2p_status(wl, GO_NEG_PHASE); ++ if (wl->p2p->vif_created) { ++ if (wl_get_drv_status(wl, SCANNING, dev)) { ++ wl_notify_escan_complete(wl, dev, true, true); ++ } ++ wldev_iovar_setint(dev, "mpc", 1); ++ ++ /* for GC */ ++ if (wl_get_drv_status(wl, DISCONNECTING, dev) && ++ (wl_get_mode_by_netdev(wl, dev) != WL_MODE_AP)) { ++ WL_ERR(("Wait for Link Down event for GC !\n")); ++ wait_for_completion_timeout ++ (&wl->iface_disable, msecs_to_jiffies(500)); ++ } ++ wl_set_p2p_status(wl, IF_DELETING); ++ DNGL_FUNC(dhd_cfg80211_clean_p2p_info, (wl)); ++ ++ /* for GO */ ++ if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP) { ++ wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, false); ++ /* disable interface before bsscfg free */ ++ ret = wl_cfgp2p_ifdisable(wl, &p2p_mac); ++ /* if fw doesn't support "ifdis", ++ do not wait for link down of ap mode ++ */ ++ if (ret == 0) { ++ WL_ERR(("Wait for Link Down event for GO !!!\n")); ++ wait_for_completion_timeout(&wl->iface_disable, ++ msecs_to_jiffies(500)); ++ } else { ++ msleep(300); ++ } ++ } ++ wl_cfgp2p_clear_management_ie(wl, wl_cfgp2p_find_idx(wl, dev)); ++ /* delete interface after link down */ ++ ret = wl_cfgp2p_ifdel(wl, &p2p_mac); ++ /* Firmware could not delete the interface so we will not get WLC_E_IF ++ * event for cleaning the dhd virtual nw interace ++ * So lets do it here. Failures from fw will ensure the application to do ++ * ifconfig down and up sequnce, which will reload the fw ++ * however we should cleanup the linux network virtual interfaces ++ */ ++ /* Request framework to RESET and clean up */ ++ if (ret) { ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ WL_ERR(("Firmware returned an error (%d) from p2p_ifdel" ++ "HANG Notification sent to %s\n", ret, ndev->name)); ++ net_os_send_hang_message(ndev); ++ } ++ /* Wait for IF_DEL operation to be finished in firmware */ ++ timeout = wait_event_interruptible_timeout(wl->netif_change_event, ++ (wl->p2p->vif_created == false), ++ msecs_to_jiffies(MAX_WAIT_TIME)); ++ if (timeout > 0 && (wl->p2p->vif_created == false)) { ++ WL_DBG(("IFDEL operation done\n")); ++ } else { ++ WL_ERR(("IFDEL didn't complete properly\n")); ++ } ++ ret = dhd_del_monitor(dev); ++ } ++ } ++ return ret; ++} ++ ++static s32 ++wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, ++ enum nl80211_iftype type, u32 *flags, ++ struct vif_params *params) ++{ ++ s32 ap = 0; ++ s32 infra = 0; ++ s32 wlif_type; ++ s32 mode = 0; ++ chanspec_t chspec; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); ++ WL_DBG(("Enter type %d\n", type)); ++ switch (type) { ++ case NL80211_IFTYPE_MONITOR: ++ case NL80211_IFTYPE_WDS: ++ case NL80211_IFTYPE_MESH_POINT: ++ ap = 1; ++ WL_ERR(("type (%d) : currently we do not support this type\n", ++ type)); ++ break; ++ case NL80211_IFTYPE_ADHOC: ++ mode = WL_MODE_IBSS; ++ break; ++ case NL80211_IFTYPE_STATION: ++ case NL80211_IFTYPE_P2P_CLIENT: ++ mode = WL_MODE_BSS; ++ infra = 1; ++ break; ++ case NL80211_IFTYPE_AP: ++ case NL80211_IFTYPE_AP_VLAN: ++ case NL80211_IFTYPE_P2P_GO: ++ mode = WL_MODE_AP; ++ ap = 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (!dhd) ++ return -EINVAL; ++ if (ap) { ++ wl_set_mode_by_netdev(wl, ndev, mode); ++ if (wl->p2p_supported && wl->p2p->vif_created) { ++ WL_DBG(("p2p_vif_created (%d) p2p_on (%d)\n", wl->p2p->vif_created, ++ p2p_on(wl))); ++ wldev_iovar_setint(ndev, "mpc", 0); ++ wl_notify_escan_complete(wl, ndev, true, true); ++ ++ /* In concurrency case, STA may be already associated in a particular ++ * channel. so retrieve the current channel of primary interface and ++ * then start the virtual interface on that. ++ */ ++ chspec = wl_cfg80211_get_shared_freq(wiphy); ++ ++ wlif_type = WL_P2P_IF_GO; ++ WL_ERR(("%s : ap (%d), infra (%d), iftype: (%d)\n", ++ ndev->name, ap, infra, type)); ++ wl_set_p2p_status(wl, IF_CHANGING); ++ wl_clr_p2p_status(wl, IF_CHANGED); ++ wl_cfgp2p_ifchange(wl, &wl->p2p->int_addr, htod32(wlif_type), chspec); ++ wait_event_interruptible_timeout(wl->netif_change_event, ++ (wl_get_p2p_status(wl, IF_CHANGED) == true), ++ msecs_to_jiffies(MAX_WAIT_TIME)); ++ wl_set_mode_by_netdev(wl, ndev, mode); ++ dhd->op_mode &= ~DHD_FLAG_P2P_GC_MODE; ++ dhd->op_mode |= DHD_FLAG_P2P_GO_MODE; ++ wl_clr_p2p_status(wl, IF_CHANGING); ++ wl_clr_p2p_status(wl, IF_CHANGED); ++ if (mode == WL_MODE_AP) ++ wl_set_drv_status(wl, CONNECTED, ndev); ++ } else if (ndev == wl_to_prmry_ndev(wl) && ++ !wl_get_drv_status(wl, AP_CREATED, ndev)) { ++ wl_set_drv_status(wl, AP_CREATING, ndev); ++ if (!wl->ap_info && ++ !(wl->ap_info = kzalloc(sizeof(struct ap_info), GFP_KERNEL))) { ++ WL_ERR(("struct ap_saved_ie allocation failed\n")); ++ return -ENOMEM; ++ } ++ } else { ++ WL_ERR(("Cannot change the interface for GO or SOFTAP\n")); ++ return -EINVAL; ++ } ++ } else { ++ WL_DBG(("Change_virtual_iface for transition from GO/AP to client/STA")); ++ } ++ ++ ndev->ieee80211_ptr->iftype = type; ++ return 0; ++} ++ ++s32 ++wl_cfg80211_notify_ifadd(struct net_device *ndev, s32 idx, s32 bssidx, ++ void* _net_attach) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ s32 ret = BCME_OK; ++ WL_DBG(("Enter")); ++ if (!ndev) { ++ WL_ERR(("net is NULL\n")); ++ return 0; ++ } ++ if (wl->p2p_supported && wl_get_p2p_status(wl, IF_ADD)) { ++ WL_DBG(("IF_ADD event called from dongle, old interface name: %s," ++ "new name: %s\n", ndev->name, wl->p2p->vir_ifname)); ++ /* Assign the net device to CONNECT BSSCFG */ ++ strncpy(ndev->name, wl->p2p->vir_ifname, IFNAMSIZ - 1); ++ wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION) = ndev; ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = bssidx; ++ wl_to_p2p_bss_private(wl, P2PAPI_BSSCFG_CONNECTION) = _net_attach; ++ ndev->ifindex = idx; ++ wl_clr_p2p_status(wl, IF_ADD); ++ ++ wake_up_interruptible(&wl->netif_change_event); ++ } else { ++ ret = BCME_NOTREADY; ++ } ++ return ret; ++} ++ ++s32 ++wl_cfg80211_notify_ifdel(void) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ ++ WL_DBG(("Enter \n")); ++ wl_clr_p2p_status(wl, IF_DELETING); ++ wake_up_interruptible(&wl->netif_change_event); ++ return 0; ++} ++ ++s32 ++wl_cfg80211_ifdel_ops(struct net_device *ndev) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ bool rollback_lock = false; ++ s32 index = 0; ++#ifdef PROP_TXSTATUS_VSDB ++ dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); ++#endif /* PROP_TXSTATUS_VSDB */ ++ if (!ndev || (strlen(ndev->name) == 0)) { ++ WL_ERR(("net is NULL\n")); ++ return 0; ++ } ++ ++ if (p2p_is_on(wl) && wl->p2p->vif_created && ++ wl_get_p2p_status(wl, IF_DELETING)) { ++ if (wl->scan_request && ++ (wl->escan_info.ndev == ndev)) { ++ /* Abort any pending scan requests */ ++ wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; ++ if (!rtnl_is_locked()) { ++ rtnl_lock(); ++ rollback_lock = true; ++ } ++ WL_DBG(("ESCAN COMPLETED\n")); ++ wl_notify_escan_complete(wl, ndev, true, false); ++ if (rollback_lock) ++ rtnl_unlock(); ++ } ++ WL_ERR(("IF_DEL event called from dongle, net %x, vif name: %s\n", ++ (unsigned int)ndev, wl->p2p->vir_ifname)); ++ ++ memset(wl->p2p->vir_ifname, '\0', IFNAMSIZ); ++ index = wl_cfgp2p_find_idx(wl, ndev); ++ wl_to_p2p_bss_ndev(wl, index) = NULL; ++ wl_to_p2p_bss_bssidx(wl, index) = WL_INVALID; ++ wl->p2p->vif_created = false; ++ ++ WL_DBG(("index : %d\n", index)); ++#ifdef PROP_TXSTATUS_VSDB ++ if (dhd->wlfc_enabled && wl->wlfc_on) { ++ dhd->wlfc_enabled = false; ++ dhd_wlfc_deinit(dhd); ++ wl->wlfc_on = false; ++ } ++#endif /* PROP_TXSTATUS_VSDB */ ++ wl_clr_drv_status(wl, CONNECTED, ndev); ++ } ++ /* Wake up any waiting thread */ ++ wake_up_interruptible(&wl->netif_change_event); ++ ++ return 0; ++} ++ ++s32 ++wl_cfg80211_is_progress_ifadd(void) ++{ ++ s32 is_progress = 0; ++ struct wl_priv *wl = wlcfg_drv_priv; ++ if (wl_get_p2p_status(wl, IF_ADD)) ++ is_progress = 1; ++ return is_progress; ++} ++ ++s32 ++wl_cfg80211_is_progress_ifchange(void) ++{ ++ s32 is_progress = 0; ++ struct wl_priv *wl = wlcfg_drv_priv; ++ if (wl_get_p2p_status(wl, IF_CHANGING)) ++ is_progress = 1; ++ return is_progress; ++} ++ ++ ++s32 ++wl_cfg80211_notify_ifchange(void) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ if (wl_get_p2p_status(wl, IF_CHANGING)) { ++ wl_set_p2p_status(wl, IF_CHANGED); ++ wake_up_interruptible(&wl->netif_change_event); ++ } ++ return 0; ++} ++ ++/* Find listen channel */ ++static s32 wl_find_listen_channel(struct wl_priv *wl, ++ u8 *ie, u32 ie_len) ++{ ++ wifi_p2p_ie_t *p2p_ie; ++ u8 *end, *pos; ++ s32 listen_channel; ++ ++ p2p_ie = wl_cfgp2p_find_p2pie(ie, ie_len); ++ ++ if (p2p_ie == NULL) ++ return 0; ++ ++ pos = p2p_ie->subelts; ++ end = p2p_ie->subelts + (p2p_ie->len - 4); ++ ++ CFGP2P_DBG((" found p2p ie ! lenth %d \n", ++ p2p_ie->len)); ++ ++ while (pos < end) { ++ uint16 attr_len; ++ if (pos + 2 >= end) { ++ CFGP2P_DBG((" -- Invalid P2P attribute")); ++ return 0; ++ } ++ attr_len = ((uint16) (((pos + 1)[1] << 8) | (pos + 1)[0])); ++ ++ if (pos + 3 + attr_len > end) { ++ CFGP2P_DBG(("P2P: Attribute underflow " ++ "(len=%u left=%d)", ++ attr_len, (int) (end - pos - 3))); ++ return 0; ++ } ++ ++ /* if Listen Channel att id is 6 and the vailue is valid, ++ * return the listen channel ++ */ ++ if (pos[0] == 6) { ++ /* listen channel subel length format ++ * 1(id) + 2(len) + 3(country) + 1(op. class) + 1(chan num) ++ */ ++ listen_channel = pos[1 + 2 + 3 + 1]; ++ ++ if (listen_channel == SOCIAL_CHAN_1 || ++ listen_channel == SOCIAL_CHAN_2 || ++ listen_channel == SOCIAL_CHAN_3) { ++ CFGP2P_DBG((" Found my Listen Channel %d \n", listen_channel)); ++ return listen_channel; ++ } ++ } ++ pos += 3 + attr_len; ++ } ++ return 0; ++} ++ ++static void wl_scan_prep(struct wl_scan_params *params, struct cfg80211_scan_request *request) ++{ ++ u32 n_ssids; ++ u32 n_channels; ++ u16 channel; ++ chanspec_t chanspec; ++ s32 i = 0, j = 0, offset; ++ char *ptr; ++ wlc_ssid_t ssid; ++ struct wl_priv *wl = wlcfg_drv_priv; ++ ++ memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN); ++ params->bss_type = DOT11_BSSTYPE_ANY; ++ params->scan_type = 0; ++ params->nprobes = -1; ++ params->active_time = -1; ++ params->passive_time = -1; ++ params->home_time = -1; ++ params->channel_num = 0; ++ memset(¶ms->ssid, 0, sizeof(wlc_ssid_t)); ++ ++ WL_SCAN(("Preparing Scan request\n")); ++ WL_SCAN(("nprobes=%d\n", params->nprobes)); ++ WL_SCAN(("active_time=%d\n", params->active_time)); ++ WL_SCAN(("passive_time=%d\n", params->passive_time)); ++ WL_SCAN(("home_time=%d\n", params->home_time)); ++ WL_SCAN(("scan_type=%d\n", params->scan_type)); ++ ++ params->nprobes = htod32(params->nprobes); ++ params->active_time = htod32(params->active_time); ++ params->passive_time = htod32(params->passive_time); ++ params->home_time = htod32(params->home_time); ++ ++ /* if request is null just exit so it will be all channel broadcast scan */ ++ if (!request) ++ return; ++ ++ n_ssids = request->n_ssids; ++ n_channels = request->n_channels; ++ ++ /* Copy channel array if applicable */ ++ WL_SCAN(("### List of channelspecs to scan ###\n")); ++ if (n_channels > 0) { ++ for (i = 0; i < n_channels; i++) { ++ chanspec = 0; ++ channel = ieee80211_frequency_to_channel(request->channels[i]->center_freq); ++ /* SKIP DFS channels for Secondary interface */ ++ if ((wl->escan_info.ndev != wl_to_prmry_ndev(wl)) && ++ (request->channels[i]->flags & ++ (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_PASSIVE_SCAN))) ++ continue; ++ ++ if (request->channels[i]->band == IEEE80211_BAND_2GHZ) { ++#ifdef WL_HOST_BAND_MGMT ++ if (wl->curr_band == WLC_BAND_5G) { ++ WL_DBG(("In 5G only mode, omit 2G channel:%d\n", channel)); ++ continue; ++ } ++#endif /* WL_HOST_BAND_MGMT */ ++ chanspec |= WL_CHANSPEC_BAND_2G; ++ } else { ++#ifdef WL_HOST_BAND_MGMT ++ if (wl->curr_band == WLC_BAND_2G) { ++ WL_DBG(("In 2G only mode, omit 5G channel:%d\n", channel)); ++ continue; ++ } ++#endif /* WL_HOST_BAND_MGMT */ ++ chanspec |= WL_CHANSPEC_BAND_5G; ++ } ++ ++ chanspec |= WL_CHANSPEC_BW_20; ++ chanspec |= WL_CHANSPEC_CTL_SB_NONE; ++ ++ params->channel_list[j] = channel; ++ params->channel_list[j] &= WL_CHANSPEC_CHAN_MASK; ++ params->channel_list[j] |= chanspec; ++ WL_SCAN(("Chan : %d, Channel spec: %x \n", ++ channel, params->channel_list[j])); ++ params->channel_list[j] = wl_chspec_host_to_driver(params->channel_list[j]); ++ j++; ++ } ++ } else { ++ WL_SCAN(("Scanning all channels\n")); ++ } ++ n_channels = j; ++ /* Copy ssid array if applicable */ ++ WL_SCAN(("### List of SSIDs to scan ###\n")); ++ if (n_ssids > 0) { ++ offset = offsetof(wl_scan_params_t, channel_list) + n_channels * sizeof(u16); ++ offset = roundup(offset, sizeof(u32)); ++ ptr = (char*)params + offset; ++ for (i = 0; i < n_ssids; i++) { ++ memset(&ssid, 0, sizeof(wlc_ssid_t)); ++ ssid.SSID_len = request->ssids[i].ssid_len; ++ memcpy(ssid.SSID, request->ssids[i].ssid, ssid.SSID_len); ++ if (!ssid.SSID_len) ++ WL_SCAN(("%d: Broadcast scan\n", i)); ++ else ++ WL_SCAN(("%d: scan for %s size =%d\n", i, ++ ssid.SSID, ssid.SSID_len)); ++ memcpy(ptr, &ssid, sizeof(wlc_ssid_t)); ++ ptr += sizeof(wlc_ssid_t); ++ } ++ } else { ++ WL_SCAN(("Broadcast scan\n")); ++ } ++ /* Adding mask to channel numbers */ ++ params->channel_num = ++ htod32((n_ssids << WL_SCAN_PARAMS_NSSID_SHIFT) | ++ (n_channels & WL_SCAN_PARAMS_COUNT_MASK)); ++ ++ if (n_channels == 1 && wl_get_drv_status_all(wl, CONNECTED)) { ++ params->active_time = WL_SCAN_CONNECT_DWELL_TIME_MS; ++ } ++} ++ ++static s32 ++wl_run_iscan(struct wl_iscan_ctrl *iscan, struct cfg80211_scan_request *request, u16 action) ++{ ++ u32 n_channels; ++ u32 n_ssids; ++ s32 params_size = ++ (WL_SCAN_PARAMS_FIXED_SIZE + offsetof(wl_iscan_params_t, params)); ++ struct wl_iscan_params *params = NULL; ++ s32 err = 0; ++ ++ if (request != NULL) { ++ n_channels = request->n_channels; ++ n_ssids = request->n_ssids; ++ /* Allocate space for populating ssids in wl_iscan_params struct */ ++ if (n_channels % 2) ++ /* If n_channels is odd, add a padd of u16 */ ++ params_size += sizeof(u16) * (n_channels + 1); ++ else ++ params_size += sizeof(u16) * n_channels; ++ ++ /* Allocate space for populating ssids in wl_iscan_params struct */ ++ params_size += sizeof(struct wlc_ssid) * n_ssids; ++ } ++ params = (struct wl_iscan_params *)kzalloc(params_size, GFP_KERNEL); ++ if (!params) { ++ err = -ENOMEM; ++ goto done; ++ } ++ wl_scan_prep(¶ms->params, request); ++ ++ params->version = htod32(ISCAN_REQ_VERSION); ++ params->action = htod16(action); ++ params->scan_duration = htod16(0); ++ ++ if (params_size + sizeof("iscan") >= WLC_IOCTL_MEDLEN) { ++ WL_ERR(("ioctl buffer length is not sufficient\n")); ++ err = -ENOMEM; ++ goto done; ++ } ++ err = wldev_iovar_setbuf(iscan->dev, "iscan", params, params_size, ++ iscan->ioctl_buf, WLC_IOCTL_MEDLEN, NULL); ++ if (unlikely(err)) { ++ if (err == -EBUSY) { ++ WL_ERR(("system busy : iscan canceled\n")); ++ } else { ++ WL_ERR(("error (%d)\n", err)); ++ } ++ } ++ ++done: ++ if (params) ++ kfree(params); ++ return err; ++} ++ ++static s32 wl_do_iscan(struct wl_priv *wl, struct cfg80211_scan_request *request) ++{ ++ struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ s32 passive_scan; ++ s32 err = 0; ++ ++ iscan->state = WL_ISCAN_STATE_SCANING; ++ ++ passive_scan = wl->active_scan ? 0 : 1; ++ err = wldev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, ++ &passive_scan, sizeof(passive_scan), true); ++ if (unlikely(err)) { ++ WL_DBG(("error (%d)\n", err)); ++ return err; ++ } ++ wl->iscan_kickstart = true; ++ wl_run_iscan(iscan, request, WL_SCAN_ACTION_START); ++ mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); ++ iscan->timer_on = 1; ++ ++ return err; ++} ++ ++static s32 ++wl_get_valid_channels(struct net_device *ndev, u8 *valid_chan_list, s32 size) ++{ ++ wl_uint32_list_t *list; ++ s32 err = BCME_OK; ++ if (valid_chan_list == NULL || size <= 0) ++ return -ENOMEM; ++ ++ memset(valid_chan_list, 0, size); ++ list = (wl_uint32_list_t *)(void *) valid_chan_list; ++ list->count = htod32(WL_NUMCHANNELS); ++ err = wldev_ioctl(ndev, WLC_GET_VALID_CHANNELS, valid_chan_list, size, false); ++ if (err != 0) { ++ WL_ERR(("get channels failed with %d\n", err)); ++ } ++ ++ return err; ++} ++ ++static s32 ++wl_run_escan(struct wl_priv *wl, struct net_device *ndev, ++ struct cfg80211_scan_request *request, uint16 action) ++{ ++ s32 err = BCME_OK; ++ u32 n_channels; ++ u32 n_ssids; ++ s32 params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_escan_params_t, params)); ++ wl_escan_params_t *params = NULL; ++ u8 chan_buf[sizeof(u32)*(WL_NUMCHANNELS + 1)]; ++ u32 num_chans = 0; ++ s32 channel; ++ s32 n_valid_chan; ++ s32 search_state = WL_P2P_DISC_ST_SCAN; ++ u32 i, j, n_nodfs = 0; ++ u16 *default_chan_list = NULL; ++ wl_uint32_list_t *list; ++ struct net_device *dev = NULL; ++ ++ WL_DBG(("Enter \n")); ++ ++ if (!wl) { ++ err = -EINVAL; ++ goto exit; ++ } ++ if (!wl->p2p_supported || !p2p_scan(wl)) { ++ /* LEGACY SCAN TRIGGER */ ++ WL_SCAN((" LEGACY E-SCAN START\n")); ++ ++ /* if scan request is not empty parse scan request paramters */ ++ if (request != NULL) { ++ n_channels = request->n_channels; ++ n_ssids = request->n_ssids; ++ /* Allocate space for populating ssids in wl_iscan_params struct */ ++ if (n_channels % 2) ++ /* If n_channels is odd, add a padd of u16 */ ++ params_size += sizeof(u16) * (n_channels + 1); ++ else ++ params_size += sizeof(u16) * n_channels; ++ ++ /* Allocate space for populating ssids in wl_iscan_params struct */ ++ params_size += sizeof(struct wlc_ssid) * n_ssids; ++ } ++ params = (wl_escan_params_t *) kzalloc(params_size, GFP_KERNEL); ++ if (params == NULL) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ ++ wl_scan_prep(¶ms->params, request); ++ ++ params->version = htod32(ESCAN_REQ_VERSION); ++ params->action = htod16(action); ++ params->sync_id = htod16(0x1234); ++ if (params_size + sizeof("escan") >= WLC_IOCTL_MEDLEN) { ++ WL_ERR(("ioctl buffer length not sufficient\n")); ++ kfree(params); ++ err = -ENOMEM; ++ goto exit; ++ } ++ err = wldev_iovar_setbuf(ndev, "escan", params, params_size, ++ wl->escan_ioctl_buf, WLC_IOCTL_MEDLEN, NULL); ++ if (unlikely(err)) { ++ if (err == BCME_EPERM) ++ /* Scan Not permitted at this point of time */ ++ WL_DBG((" Escan not permitted at this time (%d)\n", err)); ++ else ++ WL_ERR((" Escan set error (%d)\n", err)); ++ } ++ kfree(params); ++ } ++ else if (p2p_is_on(wl) && p2p_scan(wl)) { ++ /* P2P SCAN TRIGGER */ ++ s32 _freq = 0; ++ n_nodfs = 0; ++ if (request && request->n_channels) { ++ num_chans = request->n_channels; ++ WL_SCAN((" chann number : %d\n", num_chans)); ++ default_chan_list = kzalloc(num_chans * sizeof(*default_chan_list), ++ GFP_KERNEL); ++ if (default_chan_list == NULL) { ++ WL_ERR(("channel list allocation failed \n")); ++ err = -ENOMEM; ++ goto exit; ++ } ++ if (!wl_get_valid_channels(ndev, chan_buf, sizeof(chan_buf))) { ++ list = (wl_uint32_list_t *) chan_buf; ++ n_valid_chan = dtoh32(list->count); ++ for (i = 0; i < num_chans; i++) ++ { ++#ifdef WL_HOST_BAND_MGMT ++ int channel_band = 0; ++#endif /* WL_HOST_BAND_MGMT */ ++ _freq = request->channels[i]->center_freq; ++ channel = ieee80211_frequency_to_channel(_freq); ++#ifdef WL_HOST_BAND_MGMT ++ channel_band = (channel > CH_MAX_2G_CHANNEL) ? ++ WLC_BAND_5G : WLC_BAND_2G; ++ if ((wl->curr_band != WLC_BAND_AUTO) && ++ (wl->curr_band != channel_band) && ++ !IS_P2P_SOCIAL_CHANNEL(channel)) ++ continue; ++#endif /* WL_HOST_BAND_MGMT */ ++ ++ /* ignore DFS channels */ ++ if (request->channels[i]->flags & ++ (IEEE80211_CHAN_RADAR ++ | IEEE80211_CHAN_PASSIVE_SCAN)) ++ continue; ++ ++ for (j = 0; j < n_valid_chan; j++) { ++ /* allows only supported channel on ++ * current reguatory ++ */ ++ if (channel == (dtoh32(list->element[j]))) ++ default_chan_list[n_nodfs++] = ++ channel; ++ } ++ ++ } ++ } ++ if (num_chans == 3 && ( ++ (default_chan_list[0] == SOCIAL_CHAN_1) && ++ (default_chan_list[1] == SOCIAL_CHAN_2) && ++ (default_chan_list[2] == SOCIAL_CHAN_3))) { ++ /* SOCIAL CHANNELS 1, 6, 11 */ ++ search_state = WL_P2P_DISC_ST_SEARCH; ++ WL_INFO(("P2P SEARCH PHASE START \n")); ++ } else if ((dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)) && ++ (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP)) { ++ /* If you are already a GO, then do SEARCH only */ ++ WL_INFO(("Already a GO. Do SEARCH Only")); ++ search_state = WL_P2P_DISC_ST_SEARCH; ++ num_chans = n_nodfs; ++ ++ } else { ++ WL_INFO(("P2P SCAN STATE START \n")); ++ num_chans = n_nodfs; ++ } ++ ++ } ++ err = wl_cfgp2p_escan(wl, ndev, wl->active_scan, num_chans, default_chan_list, ++ search_state, action, ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE), NULL); ++ kfree(default_chan_list); ++ } ++exit: ++ if (unlikely(err)) { ++ /* Don't print Error incase of Scan suppress */ ++ if ((err == BCME_EPERM) && wl->scan_suppressed) ++ WL_DBG(("Escan failed: Scan Suppressed \n")); ++ else ++ WL_ERR(("error (%d)\n", err)); ++ } ++ return err; ++} ++ ++ ++static s32 ++wl_do_escan(struct wl_priv *wl, struct wiphy *wiphy, struct net_device *ndev, ++ struct cfg80211_scan_request *request) ++{ ++ s32 err = BCME_OK; ++ s32 passive_scan; ++ wl_scan_results_t *results; ++ WL_SCAN(("Enter \n")); ++ mutex_lock(&wl->usr_sync); ++ results = (wl_scan_results_t *) wl->escan_info.escan_buf; ++ results->version = 0; ++ results->count = 0; ++ results->buflen = WL_SCAN_RESULTS_FIXED_SIZE; ++ ++ wl->escan_info.ndev = ndev; ++ wl->escan_info.wiphy = wiphy; ++ wl->escan_info.escan_state = WL_ESCAN_STATE_SCANING; ++ passive_scan = wl->active_scan ? 0 : 1; ++ err = wldev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, ++ &passive_scan, sizeof(passive_scan), true); ++ if (unlikely(err)) { ++ WL_ERR(("error (%d)\n", err)); ++ goto exit; ++ } ++ ++ err = wl_run_escan(wl, ndev, request, WL_SCAN_ACTION_START); ++exit: ++ mutex_unlock(&wl->usr_sync); ++ return err; ++} ++ ++static s32 ++__wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, ++ struct cfg80211_scan_request *request, ++ struct cfg80211_ssid *this_ssid) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct cfg80211_ssid *ssids; ++ struct wl_scan_req *sr = wl_to_sr(wl); ++ struct ether_addr primary_mac; ++ s32 passive_scan; ++ bool iscan_req; ++ bool escan_req = false; ++ bool p2p_ssid; ++ s32 err = 0; ++ s32 bssidx = -1; ++ s32 i; ++ ++ unsigned long flags; ++ static s32 busy_count = 0; ++ ++ /* If scan req comes for p2p0, send it over primary I/F ++ * Scan results will be delivered corresponding to cfg80211_scan_request ++ */ ++ if (ndev == wl->p2p_net) { ++ ndev = wl_to_prmry_ndev(wl); ++ } ++ ++ if (WL_DRV_STATUS_SENDING_AF_FRM_EXT(wl)) { ++ WL_ERR(("Sending Action Frames. Try it again.\n")); ++ return -EAGAIN; ++ } ++ ++ WL_DBG(("Enter wiphy (%p)\n", wiphy)); ++ if (wl_get_drv_status_all(wl, SCANNING)) { ++ if (wl->scan_request == NULL) { ++ wl_clr_drv_status_all(wl, SCANNING); ++ WL_DBG(("<<<<<<<<<<>>>>>>>>>>\n")); ++ } else { ++ WL_ERR(("Scanning already\n")); ++ return -EAGAIN; ++ } ++ } ++ if (wl_get_drv_status(wl, SCAN_ABORTING, ndev)) { ++ WL_ERR(("Scanning being aborted\n")); ++ return -EAGAIN; ++ } ++ if (request && request->n_ssids > WL_SCAN_PARAMS_SSID_MAX) { ++ WL_ERR(("request null or n_ssids > WL_SCAN_PARAMS_SSID_MAX\n")); ++ return -EOPNOTSUPP; ++ } ++#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++ if (wl_get_drv_status_all(wl, REMAINING_ON_CHANNEL)) { ++ WL_DBG(("Remain_on_channel bit is set, somehow it didn't get cleared\n")); ++ wl_notify_escan_complete(wl, ndev, true, true); ++ } ++#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ ++ ++ /* Arm scan timeout timer */ ++ mod_timer(&wl->scan_timeout, jiffies + msecs_to_jiffies(WL_SCAN_TIMER_INTERVAL_MS)); ++ iscan_req = false; ++ if (request) { /* scan bss */ ++ ssids = request->ssids; ++ if (wl->iscan_on && (!ssids || !ssids->ssid_len || request->n_ssids != 1)) { ++ iscan_req = true; ++ } else if (wl->escan_on) { ++ escan_req = true; ++ p2p_ssid = false; ++ for (i = 0; i < request->n_ssids; i++) { ++ if (ssids[i].ssid_len && ++ IS_P2P_SSID(ssids[i].ssid, ssids[i].ssid_len)) { ++ p2p_ssid = true; ++ break; ++ } ++ } ++ if (p2p_ssid) { ++ if (wl->p2p_supported) { ++ /* p2p scan trigger */ ++ if (p2p_on(wl) == false) { ++ /* p2p on at the first time */ ++ p2p_on(wl) = true; ++ wl_cfgp2p_set_firm_p2p(wl); ++ get_primary_mac(wl, &primary_mac); ++ wl_cfgp2p_generate_bss_mac(&primary_mac, ++ &wl->p2p->dev_addr, &wl->p2p->int_addr); ++ } ++ wl_clr_p2p_status(wl, GO_NEG_PHASE); ++ WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); ++ p2p_scan(wl) = true; ++ } ++ } else { ++ /* legacy scan trigger ++ * So, we have to disable p2p discovery if p2p discovery is on ++ */ ++ if (wl->p2p_supported) { ++ p2p_scan(wl) = false; ++ /* If Netdevice is not equals to primary and p2p is on ++ * , we will do p2p scan using P2PAPI_BSSCFG_DEVICE. ++ */ ++ ++ if (p2p_scan(wl) == false) { ++ if (wl_get_p2p_status(wl, DISCOVERY_ON)) { ++ err = wl_cfgp2p_discover_enable_search(wl, ++ false); ++ if (unlikely(err)) { ++ goto scan_out; ++ } ++ ++ } ++ } ++ } ++ if (!wl->p2p_supported || !p2p_scan(wl)) { ++ bssidx = wl_cfgp2p_find_idx(wl, ndev); ++ err = wl_cfgp2p_set_management_ie(wl, ndev, bssidx, ++ VNDR_IE_PRBREQ_FLAG, (u8 *)request->ie, ++ request->ie_len); ++ ++ if (unlikely(err)) { ++ goto scan_out; ++ } ++ ++ } ++ } ++ } ++ } else { /* scan in ibss */ ++ /* we don't do iscan in ibss */ ++ ssids = this_ssid; ++ } ++ wl->scan_request = request; ++ wl_set_drv_status(wl, SCANNING, ndev); ++ if (iscan_req) { ++ err = wl_do_iscan(wl, request); ++ if (likely(!err)) ++ goto scan_success; ++ else ++ goto scan_out; ++ } else if (escan_req) { ++ if (wl->p2p_supported) { ++ if (p2p_on(wl) && p2p_scan(wl)) { ++ ++ /* find my listen channel */ ++ wl->afx_hdl->my_listen_chan = ++ wl_find_listen_channel(wl, (u8 *)request->ie, ++ request->ie_len); ++ err = wl_cfgp2p_enable_discovery(wl, ndev, ++ request->ie, request->ie_len); ++ ++ if (unlikely(err)) { ++ goto scan_out; ++ } ++ } ++ } ++ err = wl_do_escan(wl, wiphy, ndev, request); ++ if (likely(!err)) ++ goto scan_success; ++ else ++ goto scan_out; ++ ++ ++ } else { ++ memset(&sr->ssid, 0, sizeof(sr->ssid)); ++ sr->ssid.SSID_len = ++ min_t(u8, sizeof(sr->ssid.SSID), ssids->ssid_len); ++ if (sr->ssid.SSID_len) { ++ memcpy(sr->ssid.SSID, ssids->ssid, sr->ssid.SSID_len); ++ sr->ssid.SSID_len = htod32(sr->ssid.SSID_len); ++ WL_SCAN(("Specific scan ssid=\"%s\" len=%d\n", ++ sr->ssid.SSID, sr->ssid.SSID_len)); ++ } else { ++ WL_SCAN(("Broadcast scan\n")); ++ } ++ WL_SCAN(("sr->ssid.SSID_len (%d)\n", sr->ssid.SSID_len)); ++ passive_scan = wl->active_scan ? 0 : 1; ++ err = wldev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, ++ &passive_scan, sizeof(passive_scan), true); ++ if (unlikely(err)) { ++ WL_SCAN(("WLC_SET_PASSIVE_SCAN error (%d)\n", err)); ++ goto scan_out; ++ } ++ err = wldev_ioctl(ndev, WLC_SCAN, &sr->ssid, ++ sizeof(sr->ssid), false); ++ if (err) { ++ if (err == -EBUSY) { ++ WL_ERR(("system busy : scan for \"%s\" " ++ "canceled\n", sr->ssid.SSID)); ++ } else { ++ WL_ERR(("WLC_SCAN error (%d)\n", err)); ++ } ++ goto scan_out; ++ } ++ } ++ ++scan_success: ++ ++ busy_count = 0; ++ ++ return 0; ++ ++scan_out: ++ ++ if (err == BCME_BUSY || err == BCME_NOTREADY) { ++ WL_ERR(("Scan err = (%d), busy?%d", err, -EBUSY)); ++ err = -EBUSY; ++ } ++ ++#define SCAN_EBUSY_RETRY_LIMIT 10 ++ if (err == -EBUSY) { ++ if (busy_count++ > SCAN_EBUSY_RETRY_LIMIT) { ++ struct ether_addr bssid; ++ s32 ret = 0; ++ busy_count = 0; ++ WL_ERR(("Unusual continuous EBUSY error, %d %d %d %d %d %d %d %d %d\n", ++ wl_get_drv_status(wl, SCANNING, ndev), ++ wl_get_drv_status(wl, SCAN_ABORTING, ndev), ++ wl_get_drv_status(wl, CONNECTING, ndev), ++ wl_get_drv_status(wl, CONNECTED, ndev), ++ wl_get_drv_status(wl, DISCONNECTING, ndev), ++ wl_get_drv_status(wl, AP_CREATING, ndev), ++ wl_get_drv_status(wl, AP_CREATED, ndev), ++ wl_get_drv_status(wl, SENDING_ACT_FRM, ndev), ++ wl_get_drv_status(wl, SENDING_ACT_FRM, ndev))); ++ ++ bzero(&bssid, sizeof(bssid)); ++ if ((ret = wldev_ioctl(ndev, WLC_GET_BSSID, ++ &bssid, ETHER_ADDR_LEN, false)) == 0) ++ WL_ERR(("FW is connected with " MACDBG "/n", ++ MAC2STRDBG(bssid.octet))); ++ else ++ WL_ERR(("GET BSSID failed with %d\n", ret)); ++ ++ wl_cfg80211_disconnect(wiphy, ndev, DOT11_RC_DISASSOC_LEAVING); ++ } ++ } else { ++ busy_count = 0; ++ } ++ wl_clr_drv_status(wl, SCANNING, ndev); ++ if (timer_pending(&wl->scan_timeout)) ++ del_timer_sync(&wl->scan_timeout); ++ spin_lock_irqsave(&wl->cfgdrv_lock, flags); ++ wl->scan_request = NULL; ++ spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); ++ return err; ++} ++ ++static s32 ++wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, ++ struct cfg80211_scan_request *request) ++{ ++ s32 err = 0; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ ++ WL_DBG(("Enter \n")); ++ CHECK_SYS_UP(wl); ++ ++ err = __wl_cfg80211_scan(wiphy, ndev, request, NULL); ++ if (unlikely(err)) { ++ if ((err == BCME_EPERM) && wl->scan_suppressed) ++ WL_DBG(("scan not permitted at this time (%d)\n", err)); ++ else ++ WL_ERR(("scan error (%d)\n", err)); ++ return err; ++ } ++ ++ return err; ++} ++ ++static s32 wl_set_rts(struct net_device *dev, u32 rts_threshold) ++{ ++ s32 err = 0; ++ ++ err = wldev_iovar_setint(dev, "rtsthresh", rts_threshold); ++ if (unlikely(err)) { ++ WL_ERR(("Error (%d)\n", err)); ++ return err; ++ } ++ return err; ++} ++ ++static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold) ++{ ++ s32 err = 0; ++ ++ err = wldev_iovar_setint_bsscfg(dev, "fragthresh", frag_threshold, 0); ++ if (unlikely(err)) { ++ WL_ERR(("Error (%d)\n", err)); ++ return err; ++ } ++ return err; ++} ++ ++static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l) ++{ ++ s32 err = 0; ++ u32 cmd = (l ? WLC_SET_LRL : WLC_SET_SRL); ++ ++ retry = htod32(retry); ++ err = wldev_ioctl(dev, cmd, &retry, sizeof(retry), true); ++ if (unlikely(err)) { ++ WL_ERR(("cmd (%d) , error (%d)\n", cmd, err)); ++ return err; ++ } ++ return err; ++} ++ ++static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) ++{ ++ struct wl_priv *wl = (struct wl_priv *)wiphy_priv(wiphy); ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ s32 err = 0; ++ ++ CHECK_SYS_UP(wl); ++ WL_DBG(("Enter\n")); ++ if (changed & WIPHY_PARAM_RTS_THRESHOLD && ++ (wl->conf->rts_threshold != wiphy->rts_threshold)) { ++ wl->conf->rts_threshold = wiphy->rts_threshold; ++ err = wl_set_rts(ndev, wl->conf->rts_threshold); ++ if (!err) ++ return err; ++ } ++ if (changed & WIPHY_PARAM_FRAG_THRESHOLD && ++ (wl->conf->frag_threshold != wiphy->frag_threshold)) { ++ wl->conf->frag_threshold = wiphy->frag_threshold; ++ err = wl_set_frag(ndev, wl->conf->frag_threshold); ++ if (!err) ++ return err; ++ } ++ if (changed & WIPHY_PARAM_RETRY_LONG && ++ (wl->conf->retry_long != wiphy->retry_long)) { ++ wl->conf->retry_long = wiphy->retry_long; ++ err = wl_set_retry(ndev, wl->conf->retry_long, true); ++ if (!err) ++ return err; ++ } ++ if (changed & WIPHY_PARAM_RETRY_SHORT && ++ (wl->conf->retry_short != wiphy->retry_short)) { ++ wl->conf->retry_short = wiphy->retry_short; ++ err = wl_set_retry(ndev, wl->conf->retry_short, false); ++ if (!err) { ++ return err; ++ } ++ } ++ ++ return err; ++} ++ ++static s32 ++wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, ++ struct cfg80211_ibss_params *params) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct cfg80211_bss *bss; ++ struct ieee80211_channel *chan; ++ struct wl_join_params join_params; ++ struct cfg80211_ssid ssid; ++ s32 scan_retry = 0; ++ s32 err = 0; ++ bool rollback_lock = false; ++ ++ WL_TRACE(("In\n")); ++ CHECK_SYS_UP(wl); ++ if (params->bssid) { ++ WL_ERR(("Invalid bssid\n")); ++ return -EOPNOTSUPP; ++ } ++ bss = cfg80211_get_ibss(wiphy, NULL, params->ssid, params->ssid_len); ++ if (!bss) { ++ memcpy(ssid.ssid, params->ssid, params->ssid_len); ++ ssid.ssid_len = params->ssid_len; ++ do { ++ if (unlikely ++ (__wl_cfg80211_scan(wiphy, dev, NULL, &ssid) == ++ -EBUSY)) { ++ wl_delay(150); ++ } else { ++ break; ++ } ++ } while (++scan_retry < WL_SCAN_RETRY_MAX); ++ /* to allow scan_inform to propagate to cfg80211 plane */ ++ if (rtnl_is_locked()) { ++ rtnl_unlock(); ++ rollback_lock = true; ++ } ++ ++ /* wait 4 secons till scan done.... */ ++ schedule_timeout_interruptible(msecs_to_jiffies(4000)); ++ if (rollback_lock) ++ rtnl_lock(); ++ bss = cfg80211_get_ibss(wiphy, NULL, ++ params->ssid, params->ssid_len); ++ } ++ if (bss) { ++ wl->ibss_starter = false; ++ WL_DBG(("Found IBSS\n")); ++ } else { ++ wl->ibss_starter = true; ++ } ++ chan = params->channel; ++ if (chan) ++ wl->channel = ieee80211_frequency_to_channel(chan->center_freq); ++ /* ++ * Join with specific BSSID and cached SSID ++ * If SSID is zero join based on BSSID only ++ */ ++ memset(&join_params, 0, sizeof(join_params)); ++ memcpy((void *)join_params.ssid.SSID, (void *)params->ssid, ++ params->ssid_len); ++ join_params.ssid.SSID_len = htod32(params->ssid_len); ++ if (params->bssid) ++ memcpy(&join_params.params.bssid, params->bssid, ++ ETHER_ADDR_LEN); ++ else ++ memset(&join_params.params.bssid, 0, ETHER_ADDR_LEN); ++ ++ err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, ++ sizeof(join_params), true); ++ if (unlikely(err)) { ++ WL_ERR(("Error (%d)\n", err)); ++ return err; ++ } ++ return err; ++} ++ ++static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ s32 err = 0; ++ ++ CHECK_SYS_UP(wl); ++ wl_link_down(wl); ++ ++ return err; ++} ++ ++static s32 ++wl_set_wpa_version(struct net_device *dev, struct cfg80211_connect_params *sme) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ struct wl_security *sec; ++ s32 val = 0; ++ s32 err = 0; ++ s32 bssidx = wl_cfgp2p_find_idx(wl, dev); ++ ++ if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) ++ val = WPA_AUTH_PSK | ++ WPA_AUTH_UNSPECIFIED; ++ else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) ++ val = WPA2_AUTH_PSK| ++ WPA2_AUTH_UNSPECIFIED; ++ else ++ val = WPA_AUTH_DISABLED; ++ ++ if (is_wps_conn(sme)) ++ val = WPA_AUTH_DISABLED; ++ ++ WL_DBG(("setting wpa_auth to 0x%0x\n", val)); ++ err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", val, bssidx); ++ if (unlikely(err)) { ++ WL_ERR(("set wpa_auth failed (%d)\n", err)); ++ return err; ++ } ++ sec = wl_read_prof(wl, dev, WL_PROF_SEC); ++ sec->wpa_versions = sme->crypto.wpa_versions; ++ return err; ++} ++ ++ ++static s32 ++wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ struct wl_security *sec; ++ s32 val = 0; ++ s32 err = 0; ++ s32 bssidx = wl_cfgp2p_find_idx(wl, dev); ++ switch (sme->auth_type) { ++ case NL80211_AUTHTYPE_OPEN_SYSTEM: ++ val = WL_AUTH_OPEN_SYSTEM; ++ WL_DBG(("open system\n")); ++ break; ++ case NL80211_AUTHTYPE_SHARED_KEY: ++ val = WL_AUTH_SHARED_KEY; ++ WL_DBG(("shared key\n")); ++ break; ++ case NL80211_AUTHTYPE_AUTOMATIC: ++ val = WL_AUTH_OPEN_SHARED; ++ WL_DBG(("automatic\n")); ++ break; ++ default: ++ val = WL_AUTH_OPEN_SHARED; ++ WL_ERR(("invalid auth type (%d)\n", sme->auth_type)); ++ break; ++ } ++ ++ err = wldev_iovar_setint_bsscfg(dev, "auth", val, bssidx); ++ if (unlikely(err)) { ++ WL_ERR(("set auth failed (%d)\n", err)); ++ return err; ++ } ++ sec = wl_read_prof(wl, dev, WL_PROF_SEC); ++ sec->auth_type = sme->auth_type; ++ return err; ++} ++ ++static s32 ++wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ struct wl_security *sec; ++ s32 pval = 0; ++ s32 gval = 0; ++ s32 err = 0; ++ s32 bssidx = wl_cfgp2p_find_idx(wl, dev); ++ ++ if (sme->crypto.n_ciphers_pairwise) { ++ switch (sme->crypto.ciphers_pairwise[0]) { ++ case WLAN_CIPHER_SUITE_WEP40: ++ case WLAN_CIPHER_SUITE_WEP104: ++ pval = WEP_ENABLED; ++ break; ++ case WLAN_CIPHER_SUITE_TKIP: ++ pval = TKIP_ENABLED; ++ break; ++ case WLAN_CIPHER_SUITE_CCMP: ++ pval = AES_ENABLED; ++ break; ++ case WLAN_CIPHER_SUITE_AES_CMAC: ++ pval = AES_ENABLED; ++ break; ++ default: ++ WL_ERR(("invalid cipher pairwise (%d)\n", ++ sme->crypto.ciphers_pairwise[0])); ++ return -EINVAL; ++ } ++ } ++ if (sme->crypto.cipher_group) { ++ switch (sme->crypto.cipher_group) { ++ case WLAN_CIPHER_SUITE_WEP40: ++ case WLAN_CIPHER_SUITE_WEP104: ++ gval = WEP_ENABLED; ++ break; ++ case WLAN_CIPHER_SUITE_TKIP: ++ gval = TKIP_ENABLED; ++ break; ++ case WLAN_CIPHER_SUITE_CCMP: ++ gval = AES_ENABLED; ++ break; ++ case WLAN_CIPHER_SUITE_AES_CMAC: ++ gval = AES_ENABLED; ++ break; ++ default: ++ WL_ERR(("invalid cipher group (%d)\n", ++ sme->crypto.cipher_group)); ++ return -EINVAL; ++ } ++ } ++ ++ WL_DBG(("pval (%d) gval (%d)\n", pval, gval)); ++ ++ if (is_wps_conn(sme)) { ++ if (sme->privacy) ++ err = wldev_iovar_setint_bsscfg(dev, "wsec", 4, bssidx); ++ else ++ /* WPS-2.0 allows no security */ ++ err = wldev_iovar_setint_bsscfg(dev, "wsec", 0, bssidx); ++ } else { ++ WL_DBG((" NO, is_wps_conn, Set pval | gval to WSEC")); ++ err = wldev_iovar_setint_bsscfg(dev, "wsec", ++ pval | gval, bssidx); ++ } ++ if (unlikely(err)) { ++ WL_ERR(("error (%d)\n", err)); ++ return err; ++ } ++ ++ sec = wl_read_prof(wl, dev, WL_PROF_SEC); ++ sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; ++ sec->cipher_group = sme->crypto.cipher_group; ++ ++ return err; ++} ++ ++static s32 ++wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ struct wl_security *sec; ++ s32 val = 0; ++ s32 err = 0; ++ s32 bssidx = wl_cfgp2p_find_idx(wl, dev); ++ ++ if (sme->crypto.n_akm_suites) { ++ err = wldev_iovar_getint(dev, "wpa_auth", &val); ++ if (unlikely(err)) { ++ WL_ERR(("could not get wpa_auth (%d)\n", err)); ++ return err; ++ } ++ if (val & (WPA_AUTH_PSK | ++ WPA_AUTH_UNSPECIFIED)) { ++ switch (sme->crypto.akm_suites[0]) { ++ case WLAN_AKM_SUITE_8021X: ++ val = WPA_AUTH_UNSPECIFIED; ++ break; ++ case WLAN_AKM_SUITE_PSK: ++ val = WPA_AUTH_PSK; ++ break; ++ default: ++ WL_ERR(("invalid cipher group (%d)\n", ++ sme->crypto.cipher_group)); ++ return -EINVAL; ++ } ++ } else if (val & (WPA2_AUTH_PSK | ++ WPA2_AUTH_UNSPECIFIED)) { ++ switch (sme->crypto.akm_suites[0]) { ++ case WLAN_AKM_SUITE_8021X: ++ val = WPA2_AUTH_UNSPECIFIED; ++ break; ++ case WLAN_AKM_SUITE_PSK: ++ val = WPA2_AUTH_PSK; ++ break; ++ default: ++ WL_ERR(("invalid cipher group (%d)\n", ++ sme->crypto.cipher_group)); ++ return -EINVAL; ++ } ++ } ++ WL_DBG(("setting wpa_auth to %d\n", val)); ++ ++ err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", val, bssidx); ++ if (unlikely(err)) { ++ WL_ERR(("could not set wpa_auth (%d)\n", err)); ++ return err; ++ } ++ } ++ sec = wl_read_prof(wl, dev, WL_PROF_SEC); ++ sec->wpa_auth = sme->crypto.akm_suites[0]; ++ ++ return err; ++} ++ ++static s32 ++wl_set_set_sharedkey(struct net_device *dev, ++ struct cfg80211_connect_params *sme) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ struct wl_security *sec; ++ struct wl_wsec_key key; ++ s32 val; ++ s32 err = 0; ++ s32 bssidx = wl_cfgp2p_find_idx(wl, dev); ++ ++ WL_DBG(("key len (%d)\n", sme->key_len)); ++ if (sme->key_len) { ++ sec = wl_read_prof(wl, dev, WL_PROF_SEC); ++ WL_DBG(("wpa_versions 0x%x cipher_pairwise 0x%x\n", ++ sec->wpa_versions, sec->cipher_pairwise)); ++ if (!(sec->wpa_versions & (NL80211_WPA_VERSION_1 | ++ NL80211_WPA_VERSION_2)) && ++ (sec->cipher_pairwise & (WLAN_CIPHER_SUITE_WEP40 | ++ WLAN_CIPHER_SUITE_WEP104))) ++ { ++ memset(&key, 0, sizeof(key)); ++ key.len = (u32) sme->key_len; ++ key.index = (u32) sme->key_idx; ++ if (unlikely(key.len > sizeof(key.data))) { ++ WL_ERR(("Too long key length (%u)\n", key.len)); ++ return -EINVAL; ++ } ++ memcpy(key.data, sme->key, key.len); ++ key.flags = WL_PRIMARY_KEY; ++ switch (sec->cipher_pairwise) { ++ case WLAN_CIPHER_SUITE_WEP40: ++ key.algo = CRYPTO_ALGO_WEP1; ++ break; ++ case WLAN_CIPHER_SUITE_WEP104: ++ key.algo = CRYPTO_ALGO_WEP128; ++ break; ++ default: ++ WL_ERR(("Invalid algorithm (%d)\n", ++ sme->crypto.ciphers_pairwise[0])); ++ return -EINVAL; ++ } ++ /* Set the new key/index */ ++ WL_DBG(("key length (%d) key index (%d) algo (%d)\n", ++ key.len, key.index, key.algo)); ++ WL_DBG(("key \"%s\"\n", key.data)); ++ swap_key_from_BE(&key); ++ err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); ++ if (unlikely(err)) { ++ WL_ERR(("WLC_SET_KEY error (%d)\n", err)); ++ return err; ++ } ++ if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) { ++ WL_DBG(("set auth_type to shared key\n")); ++ val = WL_AUTH_SHARED_KEY; /* shared key */ ++ err = wldev_iovar_setint_bsscfg(dev, "auth", val, bssidx); ++ if (unlikely(err)) { ++ WL_ERR(("set auth failed (%d)\n", err)); ++ return err; ++ } ++ } ++ } ++ } ++ return err; ++} ++ ++#ifdef ESCAN_RESULT_PATCH ++static u8 connect_req_bssid[6]; ++static u8 broad_bssid[6]; ++#endif ++ ++ ++static s32 ++wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, ++ struct cfg80211_connect_params *sme) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct ieee80211_channel *chan = sme->channel; ++ wl_extjoin_params_t *ext_join_params; ++ struct wl_join_params join_params; ++ size_t join_params_size; ++ s32 err = 0; ++ wpa_ie_fixed_t *wpa_ie; ++ bcm_tlv_t *wpa2_ie; ++ u8* wpaie = 0; ++ u32 wpaie_len = 0; ++ u32 chan_cnt = 0; ++ struct ether_addr bssid; ++ int ret; ++ ++ WL_DBG(("In\n")); ++ ++ if (unlikely(!sme->ssid)) { ++ WL_ERR(("Invalid ssid\n")); ++ return -EOPNOTSUPP; ++ } ++ ++ CHECK_SYS_UP(wl); ++ ++ /* ++ * Cancel ongoing scan to sync up with sme state machine of cfg80211. ++ */ ++#if !defined(ESCAN_RESULT_PATCH) ++ if (wl->scan_request) { ++ wl_notify_escan_complete(wl, dev, true, true); ++ } ++#endif ++#ifdef ESCAN_RESULT_PATCH ++ if (sme->bssid) { ++ memcpy(connect_req_bssid, sme->bssid, ETHER_ADDR_LEN); ++ } ++ else { ++ bzero(connect_req_bssid, ETHER_ADDR_LEN); ++ } ++ bzero(broad_bssid, ETHER_ADDR_LEN); ++#endif ++ ++ bzero(&bssid, sizeof(bssid)); ++ if (!wl_get_drv_status(wl, CONNECTED, dev)&& ++ (ret = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false)) == 0) { ++ if (!ETHER_ISNULLADDR(&bssid)) { ++ scb_val_t scbval; ++ wl_set_drv_status(wl, DISCONNECTING, dev); ++ scbval.val = DOT11_RC_DISASSOC_LEAVING; ++ memcpy(&scbval.ea, &bssid, ETHER_ADDR_LEN); ++ scbval.val = htod32(scbval.val); ++ ++ WL_DBG(("drv status CONNECTED is not set, but connected in FW!" MACDBG "/n", ++ MAC2STRDBG(bssid.octet))); ++ err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, ++ sizeof(scb_val_t), true); ++ if (unlikely(err)) { ++ wl_clr_drv_status(wl, DISCONNECTING, dev); ++ WL_ERR(("error (%d)\n", err)); ++ return err; ++ } ++ while (wl_get_drv_status(wl, DISCONNECTING, dev)) { ++ WL_ERR(("Waiting for disconnection terminated.\n")); ++ msleep(20); ++ } ++ } else ++ WL_DBG(("Currently not associated!\n")); ++ } ++ ++ /* Clean BSSID */ ++ bzero(&bssid, sizeof(bssid)); ++ if (!wl_get_drv_status(wl, DISCONNECTING, dev)) ++ wl_update_prof(wl, dev, NULL, (void *)&bssid, WL_PROF_BSSID); ++ ++ if (p2p_is_on(wl) && (dev != wl_to_prmry_ndev(wl))) { ++ /* we only allow to connect using virtual interface in case of P2P */ ++ wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), ++ VNDR_IE_ASSOCREQ_FLAG, sme->ie, sme->ie_len); ++ } else if (dev == wl_to_prmry_ndev(wl)) { ++ /* find the RSN_IE */ ++ if ((wpa2_ie = bcm_parse_tlvs((u8 *)sme->ie, sme->ie_len, ++ DOT11_MNG_RSN_ID)) != NULL) { ++ WL_DBG((" WPA2 IE is found\n")); ++ } ++ /* find the WPA_IE */ ++ if ((wpa_ie = wl_cfgp2p_find_wpaie((u8 *)sme->ie, ++ sme->ie_len)) != NULL) { ++ WL_DBG((" WPA IE is found\n")); ++ } ++ if (wpa_ie != NULL || wpa2_ie != NULL) { ++ wpaie = (wpa_ie != NULL) ? (u8 *)wpa_ie : (u8 *)wpa2_ie; ++ wpaie_len = (wpa_ie != NULL) ? wpa_ie->length : wpa2_ie->len; ++ wpaie_len += WPA_RSN_IE_TAG_FIXED_LEN; ++ wldev_iovar_setbuf(dev, "wpaie", wpaie, wpaie_len, ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); ++ } else { ++ wldev_iovar_setbuf(dev, "wpaie", NULL, 0, ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); ++ } ++ ++ err = wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), ++ VNDR_IE_ASSOCREQ_FLAG, (u8 *)sme->ie, sme->ie_len); ++ if (unlikely(err)) { ++ return err; ++ } ++ } ++ ++ if (chan) { ++ wl->channel = ieee80211_frequency_to_channel(chan->center_freq); ++ chan_cnt = 1; ++ WL_DBG(("channel (%d), center_req (%d), %d channels\n", wl->channel, ++ chan->center_freq, chan_cnt)); ++ } else ++ wl->channel = 0; ++ WL_DBG(("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len)); ++ WL_DBG(("3. set wapi version \n")); ++ err = wl_set_wpa_version(dev, sme); ++ if (unlikely(err)) { ++ WL_ERR(("Invalid wpa_version\n")); ++ return err; ++ } ++ err = wl_set_auth_type(dev, sme); ++ if (unlikely(err)) { ++ WL_ERR(("Invalid auth type\n")); ++ return err; ++ } ++ ++ err = wl_set_set_cipher(dev, sme); ++ if (unlikely(err)) { ++ WL_ERR(("Invalid ciper\n")); ++ return err; ++ } ++ ++ err = wl_set_key_mgmt(dev, sme); ++ if (unlikely(err)) { ++ WL_ERR(("Invalid key mgmt\n")); ++ return err; ++ } ++ ++ err = wl_set_set_sharedkey(dev, sme); ++ if (unlikely(err)) { ++ WL_ERR(("Invalid shared key\n")); ++ return err; ++ } ++ ++ /* ++ * Join with specific BSSID and cached SSID ++ * If SSID is zero join based on BSSID only ++ */ ++ join_params_size = WL_EXTJOIN_PARAMS_FIXED_SIZE + ++ chan_cnt * sizeof(chanspec_t); ++ ext_join_params = (wl_extjoin_params_t*)kzalloc(join_params_size, GFP_KERNEL); ++ if (ext_join_params == NULL) { ++ err = -ENOMEM; ++ wl_clr_drv_status(wl, CONNECTING, dev); ++ goto exit; ++ } ++ ext_join_params->ssid.SSID_len = min(sizeof(ext_join_params->ssid.SSID), sme->ssid_len); ++ memcpy(&ext_join_params->ssid.SSID, sme->ssid, ext_join_params->ssid.SSID_len); ++ wl_update_prof(wl, dev, NULL, &ext_join_params->ssid, WL_PROF_SSID); ++ ext_join_params->ssid.SSID_len = htod32(ext_join_params->ssid.SSID_len); ++ /* increate dwell time to receive probe response or detect Beacon ++ * from target AP at a noisy air only during connect command ++ */ ++ ext_join_params->scan.active_time = WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS; ++ ext_join_params->scan.passive_time = WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS; ++ /* Set up join scan parameters */ ++ ext_join_params->scan.scan_type = -1; ++ ext_join_params->scan.nprobes ++ = (ext_join_params->scan.active_time/WL_SCAN_JOIN_PROBE_INTERVAL_MS); ++ ext_join_params->scan.home_time = -1; ++ ++ if (sme->bssid) ++ memcpy(&ext_join_params->assoc.bssid, sme->bssid, ETH_ALEN); ++ else ++ memcpy(&ext_join_params->assoc.bssid, ðer_bcast, ETH_ALEN); ++ ext_join_params->assoc.chanspec_num = chan_cnt; ++ if (chan_cnt) { ++ u16 channel, band, bw, ctl_sb; ++ chanspec_t chspec; ++ channel = wl->channel; ++ band = (channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G ++ : WL_CHANSPEC_BAND_5G; ++ bw = WL_CHANSPEC_BW_20; ++ ctl_sb = WL_CHANSPEC_CTL_SB_NONE; ++ chspec = (channel | band | bw | ctl_sb); ++ ext_join_params->assoc.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK; ++ ext_join_params->assoc.chanspec_list[0] |= chspec; ++ ext_join_params->assoc.chanspec_list[0] = ++ wl_chspec_host_to_driver(ext_join_params->assoc.chanspec_list[0]); ++ } ++ ext_join_params->assoc.chanspec_num = htod32(ext_join_params->assoc.chanspec_num); ++ if (ext_join_params->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { ++ WL_INFO(("ssid \"%s\", len (%d)\n", ext_join_params->ssid.SSID, ++ ext_join_params->ssid.SSID_len)); ++ } ++ wl_set_drv_status(wl, CONNECTING, dev); ++ err = wldev_iovar_setbuf_bsscfg(dev, "join", ext_join_params, join_params_size, ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, wl_cfgp2p_find_idx(wl, dev), &wl->ioctl_buf_sync); ++ kfree(ext_join_params); ++ if (err) { ++ wl_clr_drv_status(wl, CONNECTING, dev); ++ if (err == BCME_UNSUPPORTED) { ++ WL_DBG(("join iovar is not supported\n")); ++ goto set_ssid; ++ } else ++ WL_ERR(("error (%d)\n", err)); ++ } else ++ goto exit; ++ ++set_ssid: ++ memset(&join_params, 0, sizeof(join_params)); ++ join_params_size = sizeof(join_params.ssid); ++ ++ join_params.ssid.SSID_len = min(sizeof(join_params.ssid.SSID), sme->ssid_len); ++ memcpy(&join_params.ssid.SSID, sme->ssid, join_params.ssid.SSID_len); ++ join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len); ++ wl_update_prof(wl, dev, NULL, &join_params.ssid, WL_PROF_SSID); ++ if (sme->bssid) ++ memcpy(&join_params.params.bssid, sme->bssid, ETH_ALEN); ++ else ++ memcpy(&join_params.params.bssid, ðer_bcast, ETH_ALEN); ++ ++ wl_ch_to_chanspec(wl->channel, &join_params, &join_params_size); ++ WL_DBG(("join_param_size %d\n", join_params_size)); ++ ++ if (join_params.ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { ++ WL_INFO(("ssid \"%s\", len (%d)\n", join_params.ssid.SSID, ++ join_params.ssid.SSID_len)); ++ } ++ wl_set_drv_status(wl, CONNECTING, dev); ++ err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, true); ++ if (err) { ++ WL_ERR(("error (%d)\n", err)); ++ wl_clr_drv_status(wl, CONNECTING, dev); ++ } ++exit: ++ return err; ++} ++ ++static s32 ++wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, ++ u16 reason_code) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ scb_val_t scbval; ++ bool act = false; ++ s32 err = 0; ++ u8 *curbssid; ++ WL_ERR(("Reason %d\n", reason_code)); ++ CHECK_SYS_UP(wl); ++ act = *(bool *) wl_read_prof(wl, dev, WL_PROF_ACT); ++ curbssid = wl_read_prof(wl, dev, WL_PROF_BSSID); ++ if (act) { ++ /* ++ * Cancel ongoing scan to sync up with sme state machine of cfg80211. ++ */ ++#if !defined(ESCAN_RESULT_PATCH) ++ /* Let scan aborted by F/W */ ++ if (wl->scan_request) { ++ wl_notify_escan_complete(wl, dev, true, true); ++ } ++#endif /* ESCAN_RESULT_PATCH */ ++ wl_set_drv_status(wl, DISCONNECTING, dev); ++ scbval.val = reason_code; ++ memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); ++ scbval.val = htod32(scbval.val); ++ err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, ++ sizeof(scb_val_t), true); ++ if (unlikely(err)) { ++ wl_clr_drv_status(wl, DISCONNECTING, dev); ++ WL_ERR(("error (%d)\n", err)); ++ return err; ++ } ++ } ++ ++ return err; ++} ++ ++static s32 ++wl_cfg80211_set_tx_power(struct wiphy *wiphy, ++ enum nl80211_tx_power_setting type, s32 dbm) ++{ ++ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ u16 txpwrmw; ++ s32 err = 0; ++ s32 disable = 0; ++ ++ CHECK_SYS_UP(wl); ++ switch (type) { ++ case NL80211_TX_POWER_AUTOMATIC: ++ break; ++ case NL80211_TX_POWER_LIMITED: ++ if (dbm < 0) { ++ WL_ERR(("TX_POWER_LIMITTED - dbm is negative\n")); ++ return -EINVAL; ++ } ++ break; ++ case NL80211_TX_POWER_FIXED: ++ if (dbm < 0) { ++ WL_ERR(("TX_POWER_FIXED - dbm is negative..\n")); ++ return -EINVAL; ++ } ++ break; ++ } ++ /* Make sure radio is off or on as far as software is concerned */ ++ disable = WL_RADIO_SW_DISABLE << 16; ++ disable = htod32(disable); ++ err = wldev_ioctl(ndev, WLC_SET_RADIO, &disable, sizeof(disable), true); ++ if (unlikely(err)) { ++ WL_ERR(("WLC_SET_RADIO error (%d)\n", err)); ++ return err; ++ } ++ ++ if (dbm > 0xffff) ++ txpwrmw = 0xffff; ++ else ++ txpwrmw = (u16) dbm; ++ err = wldev_iovar_setint(ndev, "qtxpower", ++ (s32) (bcm_mw_to_qdbm(txpwrmw))); ++ if (unlikely(err)) { ++ WL_ERR(("qtxpower error (%d)\n", err)); ++ return err; ++ } ++ wl->conf->tx_power = dbm; ++ ++ return err; ++} ++ ++static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ s32 txpwrdbm; ++ u8 result; ++ s32 err = 0; ++ ++ CHECK_SYS_UP(wl); ++ err = wldev_iovar_getint(ndev, "qtxpower", &txpwrdbm); ++ if (unlikely(err)) { ++ WL_ERR(("error (%d)\n", err)); ++ return err; ++ } ++ result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE); ++ *dbm = (s32) bcm_qdbm_to_mw(result); ++ ++ return err; ++} ++ ++static s32 ++wl_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *dev, ++ u8 key_idx, bool unicast, bool multicast) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ u32 index; ++ s32 wsec; ++ s32 err = 0; ++ s32 bssidx = wl_cfgp2p_find_idx(wl, dev); ++ ++ WL_DBG(("key index (%d)\n", key_idx)); ++ CHECK_SYS_UP(wl); ++ err = wldev_iovar_getint_bsscfg(dev, "wsec", &wsec, bssidx); ++ if (unlikely(err)) { ++ WL_ERR(("WLC_GET_WSEC error (%d)\n", err)); ++ return err; ++ } ++ if (wsec & WEP_ENABLED) { ++ /* Just select a new current key */ ++ index = (u32) key_idx; ++ index = htod32(index); ++ err = wldev_ioctl(dev, WLC_SET_KEY_PRIMARY, &index, ++ sizeof(index), true); ++ if (unlikely(err)) { ++ WL_ERR(("error (%d)\n", err)); ++ } ++ } ++ return err; ++} ++ ++static s32 ++wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, ++ u8 key_idx, const u8 *mac_addr, struct key_params *params) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct wl_wsec_key key; ++ s32 err = 0; ++ s32 bssidx = wl_cfgp2p_find_idx(wl, dev); ++ s32 mode = wl_get_mode_by_netdev(wl, dev); ++ memset(&key, 0, sizeof(key)); ++ key.index = (u32) key_idx; ++ ++ if (!ETHER_ISMULTI(mac_addr)) ++ memcpy((char *)&key.ea, (void *)mac_addr, ETHER_ADDR_LEN); ++ key.len = (u32) params->key_len; ++ ++ /* check for key index change */ ++ if (key.len == 0) { ++ /* key delete */ ++ swap_key_from_BE(&key); ++ err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); ++ if (unlikely(err)) { ++ WL_ERR(("key delete error (%d)\n", err)); ++ return err; ++ } ++ } else { ++ if (key.len > sizeof(key.data)) { ++ WL_ERR(("Invalid key length (%d)\n", key.len)); ++ return -EINVAL; ++ } ++ WL_DBG(("Setting the key index %d\n", key.index)); ++ memcpy(key.data, params->key, key.len); ++ ++ if ((mode == WL_MODE_BSS) && ++ (params->cipher == WLAN_CIPHER_SUITE_TKIP)) { ++ u8 keybuf[8]; ++ memcpy(keybuf, &key.data[24], sizeof(keybuf)); ++ memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); ++ memcpy(&key.data[16], keybuf, sizeof(keybuf)); ++ } ++ ++ /* if IW_ENCODE_EXT_RX_SEQ_VALID set */ ++ if (params->seq && params->seq_len == 6) { ++ /* rx iv */ ++ u8 *ivptr; ++ ivptr = (u8 *) params->seq; ++ key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | ++ (ivptr[3] << 8) | ivptr[2]; ++ key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; ++ key.iv_initialized = true; ++ } ++ ++ switch (params->cipher) { ++ case WLAN_CIPHER_SUITE_WEP40: ++ key.algo = CRYPTO_ALGO_WEP1; ++ WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); ++ break; ++ case WLAN_CIPHER_SUITE_WEP104: ++ key.algo = CRYPTO_ALGO_WEP128; ++ WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n")); ++ break; ++ case WLAN_CIPHER_SUITE_TKIP: ++ key.algo = CRYPTO_ALGO_TKIP; ++ WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n")); ++ break; ++ case WLAN_CIPHER_SUITE_AES_CMAC: ++ key.algo = CRYPTO_ALGO_AES_CCM; ++ WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n")); ++ break; ++ case WLAN_CIPHER_SUITE_CCMP: ++ key.algo = CRYPTO_ALGO_AES_CCM; ++ WL_DBG(("WLAN_CIPHER_SUITE_CCMP\n")); ++ break; ++ default: ++ WL_ERR(("Invalid cipher (0x%x)\n", params->cipher)); ++ return -EINVAL; ++ } ++ swap_key_from_BE(&key); ++ /* need to guarantee EAPOL 4/4 send out before set key */ ++ dhd_wait_pend8021x(dev); ++ err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); ++ if (unlikely(err)) { ++ WL_ERR(("WLC_SET_KEY error (%d)\n", err)); ++ return err; ++ } ++ } ++ return err; ++} ++ ++static s32 ++wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, ++ u8 key_idx, bool pairwise, const u8 *mac_addr, ++ struct key_params *params) ++{ ++ struct wl_wsec_key key; ++ s32 val = 0; ++ s32 wsec = 0; ++ s32 err = 0; ++ u8 keybuf[8]; ++ s32 bssidx = 0; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ s32 mode = wl_get_mode_by_netdev(wl, dev); ++ WL_DBG(("key index (%d)\n", key_idx)); ++ CHECK_SYS_UP(wl); ++ ++ bssidx = wl_cfgp2p_find_idx(wl, dev); ++ ++ if (mac_addr && ++ ((params->cipher != WLAN_CIPHER_SUITE_WEP40) && ++ (params->cipher != WLAN_CIPHER_SUITE_WEP104))) { ++ wl_add_keyext(wiphy, dev, key_idx, mac_addr, params); ++ goto exit; ++ } ++ memset(&key, 0, sizeof(key)); ++ ++ key.len = (u32) params->key_len; ++ key.index = (u32) key_idx; ++ ++ if (unlikely(key.len > sizeof(key.data))) { ++ WL_ERR(("Too long key length (%u)\n", key.len)); ++ return -EINVAL; ++ } ++ memcpy(key.data, params->key, key.len); ++ ++ key.flags = WL_PRIMARY_KEY; ++ switch (params->cipher) { ++ case WLAN_CIPHER_SUITE_WEP40: ++ key.algo = CRYPTO_ALGO_WEP1; ++ val = WEP_ENABLED; ++ WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); ++ break; ++ case WLAN_CIPHER_SUITE_WEP104: ++ key.algo = CRYPTO_ALGO_WEP128; ++ val = WEP_ENABLED; ++ WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n")); ++ break; ++ case WLAN_CIPHER_SUITE_TKIP: ++ key.algo = CRYPTO_ALGO_TKIP; ++ val = TKIP_ENABLED; ++ /* wpa_supplicant switches the third and fourth quarters of the TKIP key */ ++ if (mode == WL_MODE_BSS) { ++ bcopy(&key.data[24], keybuf, sizeof(keybuf)); ++ bcopy(&key.data[16], &key.data[24], sizeof(keybuf)); ++ bcopy(keybuf, &key.data[16], sizeof(keybuf)); ++ } ++ WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n")); ++ break; ++ case WLAN_CIPHER_SUITE_AES_CMAC: ++ key.algo = CRYPTO_ALGO_AES_CCM; ++ val = AES_ENABLED; ++ WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n")); ++ break; ++ case WLAN_CIPHER_SUITE_CCMP: ++ key.algo = CRYPTO_ALGO_AES_CCM; ++ val = AES_ENABLED; ++ WL_DBG(("WLAN_CIPHER_SUITE_CCMP\n")); ++ break; ++ default: ++ WL_ERR(("Invalid cipher (0x%x)\n", params->cipher)); ++ return -EINVAL; ++ } ++ ++ /* Set the new key/index */ ++ swap_key_from_BE(&key); ++ err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), wl->ioctl_buf, ++ WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); ++ if (unlikely(err)) { ++ WL_ERR(("WLC_SET_KEY error (%d)\n", err)); ++ return err; ++ } ++ ++exit: ++ err = wldev_iovar_getint_bsscfg(dev, "wsec", &wsec, bssidx); ++ if (unlikely(err)) { ++ WL_ERR(("get wsec error (%d)\n", err)); ++ return err; ++ } ++ ++ wsec |= val; ++ err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx); ++ if (unlikely(err)) { ++ WL_ERR(("set wsec error (%d)\n", err)); ++ return err; ++ } ++ ++ return err; ++} ++ ++static s32 ++wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, ++ u8 key_idx, bool pairwise, const u8 *mac_addr) ++{ ++ struct wl_wsec_key key; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ s32 err = 0; ++ s32 bssidx = wl_cfgp2p_find_idx(wl, dev); ++ ++ WL_DBG(("Enter\n")); ++#ifndef IEEE80211W ++ if ((key_idx >= DOT11_MAX_DEFAULT_KEYS) && (key_idx < DOT11_MAX_DEFAULT_KEYS+2)) ++ return -EINVAL; ++#endif ++ CHECK_SYS_UP(wl); ++ memset(&key, 0, sizeof(key)); ++ ++ key.flags = WL_PRIMARY_KEY; ++ key.algo = CRYPTO_ALGO_OFF; ++ key.index = (u32) key_idx; ++ ++ WL_DBG(("key index (%d)\n", key_idx)); ++ /* Set the new key/index */ ++ swap_key_from_BE(&key); ++ err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), wl->ioctl_buf, ++ WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); ++ if (unlikely(err)) { ++ if (err == -EINVAL) { ++ if (key.index >= DOT11_MAX_DEFAULT_KEYS) { ++ /* we ignore this key index in this case */ ++ WL_DBG(("invalid key index (%d)\n", key_idx)); ++ } ++ } else { ++ WL_ERR(("WLC_SET_KEY error (%d)\n", err)); ++ } ++ return err; ++ } ++ return err; ++} ++ ++static s32 ++wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, ++ u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, ++ void (*callback) (void *cookie, struct key_params * params)) ++{ ++ struct key_params params; ++ struct wl_wsec_key key; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct wl_security *sec; ++ s32 wsec; ++ s32 err = 0; ++ s32 bssidx = wl_cfgp2p_find_idx(wl, dev); ++ ++ WL_DBG(("key index (%d)\n", key_idx)); ++ CHECK_SYS_UP(wl); ++ memset(&key, 0, sizeof(key)); ++ key.index = key_idx; ++ swap_key_to_BE(&key); ++ memset(¶ms, 0, sizeof(params)); ++ params.key_len = (u8) min_t(u8, DOT11_MAX_KEY_SIZE, key.len); ++ memcpy(params.key, key.data, params.key_len); ++ ++ wldev_iovar_getint_bsscfg(dev, "wsec", &wsec, bssidx); ++ if (unlikely(err)) { ++ WL_ERR(("WLC_GET_WSEC error (%d)\n", err)); ++ return err; ++ } ++ switch (wsec & ~SES_OW_ENABLED) { ++ case WEP_ENABLED: ++ sec = wl_read_prof(wl, dev, WL_PROF_SEC); ++ if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { ++ params.cipher = WLAN_CIPHER_SUITE_WEP40; ++ WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); ++ } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) { ++ params.cipher = WLAN_CIPHER_SUITE_WEP104; ++ WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n")); ++ } ++ break; ++ case TKIP_ENABLED: ++ params.cipher = WLAN_CIPHER_SUITE_TKIP; ++ WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n")); ++ break; ++ case AES_ENABLED: ++ params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; ++ WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n")); ++ break; ++ default: ++ WL_ERR(("Invalid algo (0x%x)\n", wsec)); ++ return -EINVAL; ++ } ++ ++ callback(cookie, ¶ms); ++ return err; ++} ++ ++static s32 ++wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, ++ struct net_device *dev, u8 key_idx) ++{ ++ WL_INFO(("Not supported\n")); ++ return -EOPNOTSUPP; ++} ++ ++static s32 ++wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, ++ u8 *mac, struct station_info *sinfo) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ scb_val_t scb_val; ++ s32 rssi; ++ s32 rate; ++ s32 err = 0; ++ sta_info_t *sta; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) ++ s8 eabuf[ETHER_ADDR_STR_LEN]; ++#endif ++ dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); ++ CHECK_SYS_UP(wl); ++ if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP) { ++ err = wldev_iovar_getbuf(dev, "sta_info", (struct ether_addr *)mac, ++ ETHER_ADDR_LEN, wl->ioctl_buf, WLC_IOCTL_SMLEN, &wl->ioctl_buf_sync); ++ if (err < 0) { ++ WL_ERR(("GET STA INFO failed, %d\n", err)); ++ return err; ++ } ++ sinfo->filled = STATION_INFO_INACTIVE_TIME; ++ sta = (sta_info_t *)wl->ioctl_buf; ++ sta->len = dtoh16(sta->len); ++ sta->cap = dtoh16(sta->cap); ++ sta->flags = dtoh32(sta->flags); ++ sta->idle = dtoh32(sta->idle); ++ sta->in = dtoh32(sta->in); ++ sinfo->inactive_time = sta->idle * 1000; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) ++ if (sta->flags & WL_STA_ASSOC) { ++ sinfo->filled |= STATION_INFO_CONNECTED_TIME; ++ sinfo->connected_time = sta->in; ++ } ++ WL_INFO(("STA %s : idle time : %d sec, connected time :%d ms\n", ++ bcm_ether_ntoa((const struct ether_addr *)mac, eabuf), sinfo->inactive_time, ++ sta->idle * 1000)); ++#endif ++ } else if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_BSS) { ++ get_pktcnt_t pktcnt; ++ u8 *curmacp = wl_read_prof(wl, dev, WL_PROF_BSSID); ++ if (!wl_get_drv_status(wl, CONNECTED, dev) || ++ (dhd_is_associated(dhd, NULL, &err) == FALSE)) { ++ WL_ERR(("NOT assoc\n")); ++ if (err == -ERESTARTSYS) ++ return err; ++ err = -ENODEV; ++ return err; ++ } ++ if (memcmp(mac, curmacp, ETHER_ADDR_LEN)) { ++ WL_ERR(("Wrong Mac address: "MACDBG" != "MACDBG"\n", ++ MAC2STRDBG(mac), MAC2STRDBG(curmacp))); ++ } ++ ++ /* Report the current tx rate */ ++ err = wldev_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate), false); ++ if (err) { ++ WL_ERR(("Could not get rate (%d)\n", err)); ++ } else { ++ rate = dtoh32(rate); ++ sinfo->filled |= STATION_INFO_TX_BITRATE; ++ sinfo->txrate.legacy = rate * 5; ++ WL_DBG(("Rate %d Mbps\n", (rate / 2))); ++ } ++ ++ memset(&scb_val, 0, sizeof(scb_val)); ++ scb_val.val = 0; ++ err = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, ++ sizeof(scb_val_t), false); ++ if (err) { ++ WL_ERR(("Could not get rssi (%d)\n", err)); ++ goto get_station_err; ++ } ++ rssi = dtoh32(scb_val.val) + RSSI_OFFSET; ++ sinfo->filled |= STATION_INFO_SIGNAL; ++ sinfo->signal = rssi; ++ WL_DBG(("RSSI %d dBm\n", rssi)); ++ err = wldev_ioctl(dev, WLC_GET_PKTCNTS, &pktcnt, ++ sizeof(pktcnt), false); ++ if (!err) { ++ sinfo->filled |= (STATION_INFO_RX_PACKETS | ++ STATION_INFO_RX_DROP_MISC | ++ STATION_INFO_TX_PACKETS | ++ STATION_INFO_TX_FAILED); ++ sinfo->rx_packets = pktcnt.rx_good_pkt; ++ sinfo->rx_dropped_misc = pktcnt.rx_bad_pkt; ++ sinfo->tx_packets = pktcnt.tx_good_pkt; ++ sinfo->tx_failed = pktcnt.tx_bad_pkt; ++ } ++get_station_err: ++ if (err && (err != -ERESTARTSYS)) { ++ /* Disconnect due to zero BSSID or error to get RSSI */ ++ WL_ERR(("force cfg80211_disconnected\n")); ++ wl_clr_drv_status(wl, CONNECTED, dev); ++ cfg80211_disconnected(dev, 0, NULL, 0, GFP_KERNEL); ++ wl_link_down(wl); ++ } ++ } ++ ++ return err; ++} ++ ++/* Function to update sta power save mode for Kernel wifi stack */ ++int wl_cfg80211_update_power_mode(struct net_device *dev) ++{ ++ int pm = -1; ++ int err; ++ ++ err = wldev_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), false); ++ if (err || (pm == -1)) { ++ WL_ERR(("error (%d)\n", err)); ++ } else { ++ pm = (pm == PM_OFF) ? false : true; ++ WL_DBG(("%s: %d\n", __func__, pm)); ++ if (dev->ieee80211_ptr) ++ dev->ieee80211_ptr->ps = pm; ++ } ++ return err; ++} ++ ++static s32 ++wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, ++ bool enabled, s32 timeout) ++{ ++ s32 pm; ++ s32 err = 0; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct net_info *_net_info = wl_get_netinfo_by_netdev(wl, dev); ++#if !defined(SUPPORT_PM2_ONLY) ++ dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); ++#endif ++ ++ CHECK_SYS_UP(wl); ++ ++ if (wl->p2p_net == dev || _net_info == NULL) { ++ return err; ++ } ++ WL_DBG(("%s: Enter power save enabled %d\n", dev->name, enabled)); ++ ++#if !defined(SUPPORT_PM2_ONLY) ++ /* android has special hooks to change pm when kernel suspended */ ++ pm = enabled ? ((dhd->in_suspend) ? PM_MAX : PM_FAST) : PM_OFF; ++#else ++ pm = enabled ? PM_FAST : PM_OFF; ++#endif /* SUPPORT_PM2_ONLY */ ++ ++ if (_net_info->pm_block || wl->vsdb_mode) { ++ /* Do not enable the power save if it is p2p interface or vsdb mode is set */ ++ WL_DBG(("%s:Do not enable the power save for pm_block %d or vsdb_mode %d\n", ++ dev->name, _net_info->pm_block, wl->vsdb_mode)); ++ pm = PM_OFF; ++ } ++ pm = htod32(pm); ++ WL_DBG(("%s:power save %s\n", dev->name, (pm ? "enabled" : "disabled"))); ++ err = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), true); ++ if (unlikely(err)) { ++ if (err == -ENODEV) ++ WL_DBG(("net_device is not ready yet\n")); ++ else ++ WL_ERR(("error (%d)\n", err)); ++ return err; ++ } ++ return err; ++} ++ ++static __used u32 wl_find_msb(u16 bit16) ++{ ++ u32 ret = 0; ++ ++ if (bit16 & 0xff00) { ++ ret += 8; ++ bit16 >>= 8; ++ } ++ ++ if (bit16 & 0xf0) { ++ ret += 4; ++ bit16 >>= 4; ++ } ++ ++ if (bit16 & 0xc) { ++ ret += 2; ++ bit16 >>= 2; ++ } ++ ++ if (bit16 & 2) ++ ret += bit16 & 2; ++ else if (bit16) ++ ret += bit16; ++ ++ return ret; ++} ++ ++static s32 wl_cfg80211_resume(struct wiphy *wiphy) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ s32 err = 0; ++ ++ if (unlikely(!wl_get_drv_status(wl, READY, ndev))) { ++ WL_INFO(("device is not ready\n")); ++ return 0; ++ } ++ ++ wl_invoke_iscan(wl); ++ ++ return err; ++} ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) ++static s32 wl_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow) ++#else ++static s32 wl_cfg80211_suspend(struct wiphy *wiphy) ++#endif ++{ ++#ifdef DHD_CLEAR_ON_SUSPEND ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct net_info *iter, *next; ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ unsigned long flags; ++ if (unlikely(!wl_get_drv_status(wl, READY, ndev))) { ++ WL_INFO(("device is not ready : status (%d)\n", ++ (int)wl->status)); ++ return 0; ++ } ++ for_each_ndev(wl, iter, next) ++ wl_set_drv_status(wl, SCAN_ABORTING, iter->ndev); ++ wl_term_iscan(wl); ++ spin_lock_irqsave(&wl->cfgdrv_lock, flags); ++ if (wl->scan_request) { ++ cfg80211_scan_done(wl->scan_request, true); ++ wl->scan_request = NULL; ++ } ++ for_each_ndev(wl, iter, next) { ++ wl_clr_drv_status(wl, SCANNING, iter->ndev); ++ wl_clr_drv_status(wl, SCAN_ABORTING, iter->ndev); ++ } ++ spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); ++ for_each_ndev(wl, iter, next) { ++ if (wl_get_drv_status(wl, CONNECTING, iter->ndev)) { ++ wl_bss_connect_done(wl, iter->ndev, NULL, NULL, false); ++ } ++ } ++#endif /* DHD_CLEAR_ON_SUSPEND */ ++ return 0; ++} ++ ++static s32 ++wl_update_pmklist(struct net_device *dev, struct wl_pmk_list *pmk_list, ++ s32 err) ++{ ++ int i, j; ++ struct wl_priv *wl = wlcfg_drv_priv; ++ struct net_device *primary_dev = wl_to_prmry_ndev(wl); ++ ++ if (!pmk_list) { ++ printk("pmk_list is NULL\n"); ++ return -EINVAL; ++ } ++ /* pmk list is supported only for STA interface i.e. primary interface ++ * Refer code wlc_bsscfg.c->wlc_bsscfg_sta_init ++ */ ++ if (primary_dev != dev) { ++ WL_INFO(("Not supporting Flushing pmklist on virtual" ++ " interfaces than primary interface\n")); ++ return err; ++ } ++ ++ WL_DBG(("No of elements %d\n", pmk_list->pmkids.npmkid)); ++ for (i = 0; i < pmk_list->pmkids.npmkid; i++) { ++ WL_DBG(("PMKID[%d]: %pM =\n", i, ++ &pmk_list->pmkids.pmkid[i].BSSID)); ++ for (j = 0; j < WPA2_PMKID_LEN; j++) { ++ WL_DBG(("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j])); ++ } ++ } ++ if (likely(!err)) { ++ err = wldev_iovar_setbuf(dev, "pmkid_info", (char *)pmk_list, ++ sizeof(*pmk_list), wl->ioctl_buf, WLC_IOCTL_MAXLEN, NULL); ++ } ++ ++ return err; ++} ++ ++static s32 ++wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, ++ struct cfg80211_pmksa *pmksa) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ s32 err = 0; ++ int i; ++ ++ CHECK_SYS_UP(wl); ++ for (i = 0; i < wl->pmk_list->pmkids.npmkid; i++) ++ if (!memcmp(pmksa->bssid, &wl->pmk_list->pmkids.pmkid[i].BSSID, ++ ETHER_ADDR_LEN)) ++ break; ++ if (i < WL_NUM_PMKIDS_MAX) { ++ memcpy(&wl->pmk_list->pmkids.pmkid[i].BSSID, pmksa->bssid, ++ ETHER_ADDR_LEN); ++ memcpy(&wl->pmk_list->pmkids.pmkid[i].PMKID, pmksa->pmkid, ++ WPA2_PMKID_LEN); ++ if (i == wl->pmk_list->pmkids.npmkid) ++ wl->pmk_list->pmkids.npmkid++; ++ } else { ++ err = -EINVAL; ++ } ++ WL_DBG(("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n", ++ &wl->pmk_list->pmkids.pmkid[wl->pmk_list->pmkids.npmkid - 1].BSSID)); ++ for (i = 0; i < WPA2_PMKID_LEN; i++) { ++ WL_DBG(("%02x\n", ++ wl->pmk_list->pmkids.pmkid[wl->pmk_list->pmkids.npmkid - 1]. ++ PMKID[i])); ++ } ++ ++ err = wl_update_pmklist(dev, wl->pmk_list, err); ++ ++ return err; ++} ++ ++static s32 ++wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, ++ struct cfg80211_pmksa *pmksa) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct _pmkid_list pmkid; ++ s32 err = 0; ++ int i; ++ ++ CHECK_SYS_UP(wl); ++ memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETHER_ADDR_LEN); ++ memcpy(pmkid.pmkid[0].PMKID, pmksa->pmkid, WPA2_PMKID_LEN); ++ ++ WL_DBG(("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n", ++ &pmkid.pmkid[0].BSSID)); ++ for (i = 0; i < WPA2_PMKID_LEN; i++) { ++ WL_DBG(("%02x\n", pmkid.pmkid[0].PMKID[i])); ++ } ++ ++ for (i = 0; i < wl->pmk_list->pmkids.npmkid; i++) ++ if (!memcmp ++ (pmksa->bssid, &wl->pmk_list->pmkids.pmkid[i].BSSID, ++ ETHER_ADDR_LEN)) ++ break; ++ ++ if ((wl->pmk_list->pmkids.npmkid > 0) && ++ (i < wl->pmk_list->pmkids.npmkid)) { ++ memset(&wl->pmk_list->pmkids.pmkid[i], 0, sizeof(pmkid_t)); ++ for (; i < (wl->pmk_list->pmkids.npmkid - 1); i++) { ++ memcpy(&wl->pmk_list->pmkids.pmkid[i].BSSID, ++ &wl->pmk_list->pmkids.pmkid[i + 1].BSSID, ++ ETHER_ADDR_LEN); ++ memcpy(&wl->pmk_list->pmkids.pmkid[i].PMKID, ++ &wl->pmk_list->pmkids.pmkid[i + 1].PMKID, ++ WPA2_PMKID_LEN); ++ } ++ wl->pmk_list->pmkids.npmkid--; ++ } else { ++ err = -EINVAL; ++ } ++ ++ err = wl_update_pmklist(dev, wl->pmk_list, err); ++ ++ return err; ++ ++} ++ ++static s32 ++wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ s32 err = 0; ++ CHECK_SYS_UP(wl); ++ memset(wl->pmk_list, 0, sizeof(*wl->pmk_list)); ++ err = wl_update_pmklist(dev, wl->pmk_list, err); ++ return err; ++ ++} ++ ++static wl_scan_params_t * ++wl_cfg80211_scan_alloc_params(int channel, int nprobes, int *out_params_size) ++{ ++ wl_scan_params_t *params; ++ int params_size; ++ int num_chans; ++ ++ *out_params_size = 0; ++ ++ /* Our scan params only need space for 1 channel and 0 ssids */ ++ params_size = WL_SCAN_PARAMS_FIXED_SIZE + 1 * sizeof(uint16); ++ params = (wl_scan_params_t*) kzalloc(params_size, GFP_KERNEL); ++ if (params == NULL) { ++ WL_ERR(("%s: mem alloc failed (%d bytes)\n", __func__, params_size)); ++ return params; ++ } ++ memset(params, 0, params_size); ++ params->nprobes = nprobes; ++ ++ num_chans = (channel == 0) ? 0 : 1; ++ ++ memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN); ++ params->bss_type = DOT11_BSSTYPE_ANY; ++ params->scan_type = DOT11_SCANTYPE_ACTIVE; ++ params->nprobes = htod32(1); ++ params->active_time = htod32(-1); ++ params->passive_time = htod32(-1); ++ params->home_time = htod32(10); ++ if (channel == -1) ++ params->channel_list[0] = htodchanspec(channel); ++ else ++ params->channel_list[0] = wl_ch_host_to_driver(channel); ++ ++ /* Our scan params have 1 channel and 0 ssids */ ++ params->channel_num = htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) | ++ (num_chans & WL_SCAN_PARAMS_COUNT_MASK)); ++ ++ *out_params_size = params_size; /* rtn size to the caller */ ++ return params; ++} ++ ++static s32 ++wl_cfg80211_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, ++ struct ieee80211_channel * channel, ++ enum nl80211_channel_type channel_type, ++ unsigned int duration, u64 *cookie) ++{ ++ s32 target_channel; ++ u32 id; ++ struct ether_addr primary_mac; ++ struct net_device *ndev = NULL; ++ ++ s32 err = BCME_OK; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ ++ WL_DBG(("Enter, ifindex: %d, channel: %d, duration ms (%d) SCANNING ?? %s \n", ++ dev->ifindex, ieee80211_frequency_to_channel(channel->center_freq), ++ duration, (wl_get_drv_status(wl, SCANNING, ndev)) ? "YES":"NO")); ++ ++ if (wl->p2p_net == dev) { ++ ndev = wl_to_prmry_ndev(wl); ++ } else { ++ ndev = dev; ++ } ++ ++ if (!wl->p2p) { ++ WL_ERR(("wl->p2p is not initialized\n")); ++ err = BCME_ERROR; ++ goto exit; ++ } ++ ++#ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++ if (wl_get_drv_status(wl, SCANNING, ndev)) { ++ wl_notify_escan_complete(wl, ndev, true, true); ++ } ++#endif /* not WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ ++ ++ target_channel = ieee80211_frequency_to_channel(channel->center_freq); ++ memcpy(&wl->remain_on_chan, channel, sizeof(struct ieee80211_channel)); ++ wl->remain_on_chan_type = channel_type; ++ id = ++wl->last_roc_id; ++ if (id == 0) ++ id = ++wl->last_roc_id; ++ *cookie = id; ++ ++#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++ if (wl_get_drv_status(wl, SCANNING, ndev)) { ++ struct timer_list *_timer; ++ WL_DBG(("scan is running. go to fake listen state\n")); ++ ++ wl_set_drv_status(wl, FAKE_REMAINING_ON_CHANNEL, ndev); ++ ++ if (timer_pending(&wl->p2p->listen_timer)) { ++ WL_DBG(("cancel current listen timer \n")); ++ del_timer_sync(&wl->p2p->listen_timer); ++ } ++ ++ _timer = &wl->p2p->listen_timer; ++ wl_clr_p2p_status(wl, LISTEN_EXPIRED); ++ ++ INIT_TIMER(_timer, wl_cfgp2p_listen_expired, duration, 0); ++ ++ err = BCME_OK; ++ goto exit; ++ } ++#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ ++ ++#ifdef WL_CFG80211_SYNC_GON ++ if (wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM_LISTEN)) { ++ /* do not enter listen mode again if we are in listen mode already for next af. ++ * remain on channel completion will be returned by waiting next af completion. ++ */ ++#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++ wl_set_drv_status(wl, FAKE_REMAINING_ON_CHANNEL, ndev); ++#else ++ wl_set_drv_status(wl, REMAINING_ON_CHANNEL, ndev); ++#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ ++ goto exit; ++ } ++#endif /* WL_CFG80211_SYNC_GON */ ++ if (wl->p2p && !wl->p2p->on) { ++ /* In case of p2p_listen command, supplicant send remain_on_channel ++ * without turning on P2P ++ */ ++ get_primary_mac(wl, &primary_mac); ++ wl_cfgp2p_generate_bss_mac(&primary_mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); ++ p2p_on(wl) = true; ++ } ++ ++ if (p2p_is_on(wl)) { ++ err = wl_cfgp2p_enable_discovery(wl, ndev, NULL, 0); ++ if (unlikely(err)) { ++ goto exit; ++ } ++#ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++ wl_set_drv_status(wl, REMAINING_ON_CHANNEL, ndev); ++#endif /* not WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ ++ err = wl_cfgp2p_discover_listen(wl, target_channel, duration); ++ ++#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++ if (err == BCME_OK) { ++ wl_set_drv_status(wl, REMAINING_ON_CHANNEL, ndev); ++ } else { ++ /* if failed, firmware may be internal scanning state. ++ * so other scan request shall not abort it ++ */ ++ wl_set_drv_status(wl, FAKE_REMAINING_ON_CHANNEL, ndev); ++ } ++#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ ++ /* WAR: set err = ok to prevent cookie mismatch in wpa_supplicant ++ * and expire timer will send a completion to the upper layer ++ */ ++ err = BCME_OK; ++ } ++ ++exit: ++ if (err == BCME_OK) { ++ WL_INFO(("Success\n")); ++ cfg80211_ready_on_channel(dev, *cookie, channel, ++ channel_type, duration, GFP_KERNEL); ++ } else { ++ WL_ERR(("Fail to Set (err=%d cookie:%llu)\n", err, *cookie)); ++ } ++ return err; ++} ++ ++static s32 ++wl_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, ++ u64 cookie) ++{ ++ s32 err = 0; ++ WL_DBG((" enter ) netdev_ifidx: %d \n", dev->ifindex)); ++ return err; ++} ++ ++static void ++wl_cfg80211_afx_handler(struct work_struct *work) ++{ ++ struct afx_hdl *afx_instance; ++ struct wl_priv *wl = wlcfg_drv_priv; ++ s32 ret = BCME_OK; ++ ++ afx_instance = container_of(work, struct afx_hdl, work); ++ if (afx_instance != NULL && wl->afx_hdl->is_active) { ++ if (wl->afx_hdl->is_listen && wl->afx_hdl->my_listen_chan) { ++ ret = wl_cfgp2p_discover_listen(wl, wl->afx_hdl->my_listen_chan, ++ (100 * (1 + (random32() % 3)))); /* 100ms ~ 300ms */ ++ } else { ++ ret = wl_cfgp2p_act_frm_search(wl, wl->afx_hdl->dev, ++ wl->afx_hdl->bssidx, wl->afx_hdl->peer_listen_chan, ++ &wl->afx_hdl->tx_dst_addr); ++ } ++ if (unlikely(ret != BCME_OK)) { ++ WL_ERR(("ERROR occurred! returned value is (%d)\n", ret)); ++ if (wl_get_drv_status_all(wl, FINDING_COMMON_CHANNEL)) ++ complete(&wl->act_frm_scan); ++ } ++ } ++} ++ ++static s32 ++wl_cfg80211_af_searching_channel(struct wl_priv *wl, struct net_device *dev) ++{ ++ u32 max_retry = WL_CHANNEL_SYNC_RETRY; ++ ++ if (dev == NULL) ++ return -1; ++ ++ WL_DBG((" enter ) \n")); ++ ++ wl_set_drv_status(wl, FINDING_COMMON_CHANNEL, dev); ++ wl->afx_hdl->is_active = TRUE; ++ ++ /* Loop to wait until we find a peer's channel or the ++ * pending action frame tx is cancelled. ++ */ ++ while ((wl->afx_hdl->retry < max_retry) && ++ (wl->afx_hdl->peer_chan == WL_INVALID)) { ++ wl->afx_hdl->is_listen = FALSE; ++ wl_set_drv_status(wl, SCANNING, dev); ++ WL_DBG(("Scheduling the action frame for sending.. retry %d\n", ++ wl->afx_hdl->retry)); ++ /* search peer on peer's listen channel */ ++ schedule_work(&wl->afx_hdl->work); ++ wait_for_completion_timeout(&wl->act_frm_scan, ++ msecs_to_jiffies(MAX_WAIT_TIME)); ++ ++ if ((wl->afx_hdl->peer_chan != WL_INVALID) || ++ !(wl_get_drv_status(wl, FINDING_COMMON_CHANNEL, dev))) ++ break; ++ ++ if (wl->afx_hdl->my_listen_chan) { ++ WL_DBG(("Scheduling Listen peer in my listen channel = %d\n", ++ wl->afx_hdl->my_listen_chan)); ++ /* listen on my listen channel */ ++ wl->afx_hdl->is_listen = TRUE; ++ schedule_work(&wl->afx_hdl->work); ++ wait_for_completion_timeout(&wl->act_frm_scan, ++ msecs_to_jiffies(MAX_WAIT_TIME)); ++ } ++ if ((wl->afx_hdl->peer_chan != WL_INVALID) || ++ !(wl_get_drv_status(wl, FINDING_COMMON_CHANNEL, dev))) ++ break; ++ ++ wl->afx_hdl->retry++; ++ ++ WL_AF_TX_KEEP_PRI_CONNECTION_VSDB(wl); ++ } ++ ++ wl->afx_hdl->is_active = FALSE; ++ ++ wl_clr_drv_status(wl, SCANNING, dev); ++ wl_clr_drv_status(wl, FINDING_COMMON_CHANNEL, dev); ++ ++ return (wl->afx_hdl->peer_chan); ++} ++ ++struct p2p_config_af_params { ++ s32 max_tx_retry; /* max tx retry count if tx no ack */ ++ /* To make sure to send successfully action frame, we have to turn off mpc ++ * 0: off, 1: on, (-1): do nothing ++ */ ++ s32 mpc_onoff; ++#ifdef WL_CFG80211_SYNC_GON ++ bool extra_listen; ++#endif ++ bool search_channel; /* 1: search peer's channel to send af */ ++}; ++ ++static s32 ++wl_cfg80211_config_p2p_pub_af_tx(struct wiphy *wiphy, ++ wl_action_frame_t *action_frame, wl_af_params_t *af_params, ++ struct p2p_config_af_params *config_af_params) ++{ ++ s32 err = BCME_OK; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ wifi_p2p_pub_act_frame_t *act_frm = ++ (wifi_p2p_pub_act_frame_t *) (action_frame->data); ++ ++ /* initialize default value */ ++#ifdef WL_CFG80211_SYNC_GON ++ config_af_params->extra_listen = true; ++#endif ++ config_af_params->search_channel = false; ++ config_af_params->max_tx_retry = WL_AF_TX_MAX_RETRY; ++ config_af_params->mpc_onoff = -1; ++ ++ switch (act_frm->subtype) { ++ case P2P_PAF_GON_REQ: { ++ WL_DBG(("P2P: GO_NEG_PHASE status set \n")); ++ wl_set_p2p_status(wl, GO_NEG_PHASE); ++ ++ config_af_params->mpc_onoff = 0; ++ config_af_params->search_channel = true; ++ wl->next_af_subtype = act_frm->subtype + 1; ++ ++ /* increase dwell time to wait for RESP frame */ ++ af_params->dwell_time = WL_MED_DWELL_TIME; ++ ++ break; ++ } ++ case P2P_PAF_GON_RSP: { ++ wl->next_af_subtype = act_frm->subtype + 1; ++ /* increase dwell time to wait for CONF frame */ ++ af_params->dwell_time = WL_MED_DWELL_TIME; ++ break; ++ } ++ case P2P_PAF_GON_CONF: { ++ /* If we reached till GO Neg confirmation reset the filter */ ++ WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); ++ wl_clr_p2p_status(wl, GO_NEG_PHASE); ++ ++ /* turn on mpc again if go nego is done */ ++ config_af_params->mpc_onoff = 1; ++ ++ /* minimize dwell time */ ++ af_params->dwell_time = WL_MIN_DWELL_TIME; ++ ++#ifdef WL_CFG80211_SYNC_GON ++ config_af_params->extra_listen = false; ++#endif /* WL_CFG80211_SYNC_GON */ ++ break; ++ } ++ case P2P_PAF_INVITE_REQ: { ++ config_af_params->search_channel = true; ++ wl->next_af_subtype = act_frm->subtype + 1; ++ ++ /* increase dwell time */ ++ af_params->dwell_time = WL_MED_DWELL_TIME; ++ break; ++ } ++ case P2P_PAF_INVITE_RSP: ++ /* minimize dwell time */ ++ af_params->dwell_time = WL_MIN_DWELL_TIME; ++#ifdef WL_CFG80211_SYNC_GON ++ config_af_params->extra_listen = false; ++#endif /* WL_CFG80211_SYNC_GON */ ++ break; ++ case P2P_PAF_DEVDIS_REQ: { ++ config_af_params->search_channel = true; ++ ++ wl->next_af_subtype = act_frm->subtype + 1; ++ /* maximize dwell time to wait for RESP frame */ ++ af_params->dwell_time = WL_LONG_DWELL_TIME; ++ break; ++ } ++ case P2P_PAF_DEVDIS_RSP: ++ /* minimize dwell time */ ++ af_params->dwell_time = WL_MIN_DWELL_TIME; ++#ifdef WL_CFG80211_SYNC_GON ++ config_af_params->extra_listen = false; ++#endif /* WL_CFG80211_SYNC_GON */ ++ break; ++ case P2P_PAF_PROVDIS_REQ: { ++ if (IS_PROV_DISC_WITHOUT_GROUP_ID(&act_frm->elts[0], ++ action_frame->len)) { ++ config_af_params->search_channel = true; ++ } ++ ++ config_af_params->mpc_onoff = 0; ++ wl->next_af_subtype = act_frm->subtype + 1; ++ /* increase dwell time to wait for RESP frame */ ++ af_params->dwell_time = WL_MED_DWELL_TIME; ++ break; ++ } ++ case P2P_PAF_PROVDIS_RSP: { ++ wl->next_af_subtype = P2P_PAF_GON_REQ; ++ /* increase dwell time to MED level */ ++ af_params->dwell_time = WL_MED_DWELL_TIME; ++#ifdef WL_CFG80211_SYNC_GON ++ config_af_params->extra_listen = false; ++#endif /* WL_CFG80211_SYNC_GON */ ++ break; ++ } ++ default: ++ WL_DBG(("Unknown p2p pub act frame subtype: %d\n", ++ act_frm->subtype)); ++ err = BCME_BADARG; ++ } ++ return err; ++} ++ ++ ++static bool ++wl_cfg80211_send_action_frame(struct wiphy *wiphy, struct net_device *dev, ++ struct net_device *ndev, wl_af_params_t *af_params, ++ wl_action_frame_t *action_frame, u16 action_frame_len, s32 bssidx) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ bool ack = false; ++ u8 category, action; ++ s32 tx_retry; ++ struct p2p_config_af_params config_af_params; ++#ifdef VSDB ++ ulong off_chan_started_jiffies = 0; ++#endif ++ dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); ++ ++ wl_cfgp2p_print_actframe(true, action_frame->data, action_frame->len); ++ ++ category = action_frame->data[DOT11_ACTION_CAT_OFF]; ++ action = action_frame->data[DOT11_ACTION_ACT_OFF]; ++ ++ /* initialize variables */ ++ tx_retry = 0; ++ wl->next_af_subtype = P2P_PAF_SUBTYPE_INVALID; ++ config_af_params.max_tx_retry = WL_AF_TX_MAX_RETRY; ++ config_af_params.mpc_onoff = -1; ++ config_af_params.search_channel = false; ++#ifdef WL_CFG80211_SYNC_GON ++ config_af_params.extra_listen = false; ++#endif ++ ++ /* config parameters */ ++ /* Public Action Frame Process - DOT11_ACTION_CAT_PUBLIC */ ++ if (category == DOT11_ACTION_CAT_PUBLIC) { ++ if ((action == P2P_PUB_AF_ACTION) && ++ (action_frame_len >= sizeof(wifi_p2p_pub_act_frame_t))) { ++ /* p2p public action frame process */ ++ if (BCME_OK != wl_cfg80211_config_p2p_pub_af_tx(wiphy, ++ action_frame, af_params, &config_af_params)) { ++ WL_DBG(("Unknown subtype.\n")); ++ } ++ ++ } else if (action_frame_len >= sizeof(wifi_p2psd_gas_pub_act_frame_t)) { ++ /* service discovery process */ ++ if (action == P2PSD_ACTION_ID_GAS_IREQ || ++ action == P2PSD_ACTION_ID_GAS_CREQ) { ++ /* configure service discovery query frame */ ++ ++ config_af_params.search_channel = true; ++ ++ /* save next af suptype to cancel remained dwell time */ ++ wl->next_af_subtype = action + 1; ++ ++ af_params->dwell_time = WL_MED_DWELL_TIME; ++ } else if (action == P2PSD_ACTION_ID_GAS_IRESP || ++ action == P2PSD_ACTION_ID_GAS_CRESP) { ++ /* configure service discovery response frame */ ++ af_params->dwell_time = WL_MIN_DWELL_TIME; ++ } else { ++ WL_DBG(("Unknown action type: %d\n", action)); ++ } ++ } else { ++ WL_DBG(("Unknown Frame: category 0x%x, action 0x%x, length %d\n", ++ category, action, action_frame_len)); ++ } ++ } else if (category == P2P_AF_CATEGORY) { ++ /* do not configure anything. it will be sent with a default configuration */ ++ } else { ++ WL_DBG(("Unknown Frame: category 0x%x, action 0x%x\n", ++ category, action)); ++ if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) { ++ wl_clr_drv_status(wl, SENDING_ACT_FRM, dev); ++ return false; ++ } ++ } ++ ++ /* To make sure to send successfully action frame, we have to turn off mpc */ ++ if (config_af_params.mpc_onoff == 0) { ++ wldev_iovar_setint(dev, "mpc", 0); ++ } ++ ++ /* validate channel and p2p ies */ ++ if (config_af_params.search_channel && IS_P2P_SOCIAL(af_params->channel) && ++ wl_to_p2p_bss_saved_ie(wl, P2PAPI_BSSCFG_DEVICE).p2p_probe_req_ie_len) { ++ config_af_params.search_channel = true; ++ } else { ++ config_af_params.search_channel = false; ++ } ++ ++#ifdef VSDB ++ /* if connecting on primary iface, sleep for a while before sending af tx for VSDB */ ++ if (wl_get_drv_status(wl, CONNECTING, wl_to_prmry_ndev(wl))) { ++ msleep(50); ++ } ++#endif ++ ++ /* if scan is ongoing, abort current scan. */ ++ if (wl_get_drv_status_all(wl, SCANNING)) { ++ wl_notify_escan_complete(wl, ndev, true, true); ++ } ++ ++ /* set status and destination address before sending af */ ++ if (wl->next_af_subtype != P2P_PAF_SUBTYPE_INVALID) { ++ /* set this status to cancel the remained dwell time in rx process */ ++ wl_set_drv_status(wl, WAITING_NEXT_ACT_FRM, dev); ++ } ++ wl_set_drv_status(wl, SENDING_ACT_FRM, dev); ++ memcpy(wl->afx_hdl->tx_dst_addr.octet, ++ af_params->action_frame.da.octet, ++ sizeof(wl->afx_hdl->tx_dst_addr.octet)); ++ ++ /* save af_params for rx process */ ++ wl->afx_hdl->pending_tx_act_frm = af_params; ++ ++ /* search peer's channel */ ++ if (config_af_params.search_channel) { ++ /* initialize afx_hdl */ ++ wl->afx_hdl->bssidx = wl_cfgp2p_find_idx(wl, dev); ++ wl->afx_hdl->dev = dev; ++ wl->afx_hdl->retry = 0; ++ wl->afx_hdl->peer_chan = WL_INVALID; ++ ++ if (wl_cfg80211_af_searching_channel(wl, dev) == WL_INVALID) { ++ WL_ERR(("couldn't find peer's channel.\n")); ++ goto exit; ++ } ++ ++ wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); ++ /* ++ * Abort scan even for VSDB scenarios. Scan gets aborted in firmware ++ * but after the check of piggyback algorithm. ++ * To take care of current piggback algo, lets abort the scan here itself. ++ */ ++ wl_notify_escan_complete(wl, dev, true, true); ++ /* Suspend P2P discovery's search-listen to prevent it from ++ * starting a scan or changing the channel. ++ */ ++ wl_cfgp2p_discover_enable_search(wl, false); ++ ++ /* update channel */ ++ af_params->channel = wl->afx_hdl->peer_chan; ++ } ++ ++#ifdef VSDB ++ off_chan_started_jiffies = jiffies; ++#endif /* VSDB */ ++ ++ /* Now send a tx action frame */ ++ ack = wl_cfgp2p_tx_action_frame(wl, dev, af_params, bssidx) ? false : true; ++ ++ /* if failed, retry it. tx_retry_max value is configure by .... */ ++ while ((ack == false) && (tx_retry++ < config_af_params.max_tx_retry)) { ++#ifdef VSDB ++ if (af_params->channel) { ++ if (jiffies_to_msecs(jiffies - off_chan_started_jiffies) > ++ OFF_CHAN_TIME_THRESHOLD_MS) { ++ WL_AF_TX_KEEP_PRI_CONNECTION_VSDB(wl); ++ off_chan_started_jiffies = jiffies; ++ } ++ } ++#endif /* VSDB */ ++ ack = wl_cfgp2p_tx_action_frame(wl, dev, af_params, bssidx) ? ++ false : true; ++ } ++ if (ack == false) { ++ WL_ERR(("Failed to send Action Frame(retry %d)\n", tx_retry)); ++ } ++exit: ++ /* Clear SENDING_ACT_FRM after all sending af is done */ ++ wl_clr_drv_status(wl, SENDING_ACT_FRM, dev); ++ ++#ifdef WL_CFG80211_SYNC_GON ++ /* WAR: sometimes dongle does not keep the dwell time of 'actframe'. ++ * if we coundn't get the next action response frame and dongle does not keep ++ * the dwell time, go to listen state again to get next action response frame. ++ */ ++ if (ack && config_af_params.extra_listen && ++ wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM) && ++ wl->af_sent_channel == wl->afx_hdl->my_listen_chan) { ++ s32 extar_listen_time; ++ ++ extar_listen_time = af_params->dwell_time - ++ jiffies_to_msecs(jiffies - wl->af_tx_sent_jiffies); ++ ++ if (extar_listen_time > 50) { ++ wl_set_drv_status(wl, WAITING_NEXT_ACT_FRM_LISTEN, dev); ++ WL_DBG(("Wait more time! actual af time:%d," ++ "calculated extar listen:%d\n", ++ af_params->dwell_time, extar_listen_time)); ++ if (wl_cfgp2p_discover_listen(wl, wl->af_sent_channel, ++ extar_listen_time + 100) == BCME_OK) { ++ wait_for_completion_timeout(&wl->wait_next_af, ++ msecs_to_jiffies(extar_listen_time + 100 + 300)); ++ } ++ wl_clr_drv_status(wl, WAITING_NEXT_ACT_FRM_LISTEN, dev); ++ } ++ } ++#endif /* WL_CFG80211_SYNC_GON */ ++ wl_clr_drv_status(wl, WAITING_NEXT_ACT_FRM, dev); ++ ++ if (wl->afx_hdl->pending_tx_act_frm) ++ wl->afx_hdl->pending_tx_act_frm = NULL; ++ ++ WL_INFO(("-- sending Action Frame is %s, listen chan: %d\n", ++ (ack) ? "Succeeded!!":"Failed!!", wl->afx_hdl->my_listen_chan)); ++ ++ ++ /* if all done, turn mpc on again */ ++ if (config_af_params.mpc_onoff == 1) { ++ wldev_iovar_setint(dev, "mpc", 1); ++ } ++ ++ return ack; ++} ++ ++#define MAX_NUM_OF_ASSOCIATED_DEV 64 ++static s32 ++wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, ++ struct ieee80211_channel *channel, bool offchan, ++ enum nl80211_channel_type channel_type, ++ bool channel_type_valid, unsigned int wait, ++ const u8* buf, size_t len, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) ++ bool no_cck, ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) ++ bool dont_wait_for_ack, ++#endif ++ u64 *cookie) ++{ ++ wl_action_frame_t *action_frame; ++ wl_af_params_t *af_params; ++ scb_val_t scb_val; ++ const struct ieee80211_mgmt *mgmt; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct net_device *dev = NULL; ++ s32 err = BCME_OK; ++ s32 bssidx = 0; ++ u32 id; ++ bool ack = false; ++ s8 eabuf[ETHER_ADDR_STR_LEN]; ++ ++ WL_DBG(("Enter \n")); ++ ++ if (ndev == wl->p2p_net) { ++ dev = wl_to_prmry_ndev(wl); ++ } else { ++ /* If TX req is for any valid ifidx. Use as is */ ++ dev = ndev; ++ } ++ ++ /* find bssidx based on ndev */ ++ bssidx = wl_cfgp2p_find_idx(wl, dev); ++ if (bssidx == -1) { ++ ++ WL_ERR(("Can not find the bssidx for dev( %p )\n", dev)); ++ return -ENODEV; ++ } ++ if (p2p_is_on(wl)) { ++ /* Suspend P2P discovery search-listen to prevent it from changing the ++ * channel. ++ */ ++ if ((err = wl_cfgp2p_discover_enable_search(wl, false)) < 0) { ++ WL_ERR(("Can not disable discovery mode\n")); ++ return -EFAULT; ++ } ++ } ++ *cookie = 0; ++ id = wl->send_action_id++; ++ if (id == 0) ++ id = wl->send_action_id++; ++ *cookie = id; ++ mgmt = (const struct ieee80211_mgmt *)buf; ++ if (ieee80211_is_mgmt(mgmt->frame_control)) { ++ if (ieee80211_is_probe_resp(mgmt->frame_control)) { ++ s32 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; ++ s32 ie_len = len - ie_offset; ++ if (dev == wl_to_prmry_ndev(wl)) ++ bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); ++ wl_cfgp2p_set_management_ie(wl, dev, bssidx, ++ VNDR_IE_PRBRSP_FLAG, (u8 *)(buf + ie_offset), ie_len); ++ cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); ++ goto exit; ++ } else if (ieee80211_is_disassoc(mgmt->frame_control) || ++ ieee80211_is_deauth(mgmt->frame_control)) { ++ char mac_buf[MAX_NUM_OF_ASSOCIATED_DEV * ++ sizeof(struct ether_addr) + sizeof(uint)] = {0}; ++ int num_associated = 0; ++ struct maclist *assoc_maclist = (struct maclist *)mac_buf; ++ if (!bcmp((const uint8 *)BSSID_BROADCAST, ++ (const struct ether_addr *)mgmt->da, ETHER_ADDR_LEN)) { ++ assoc_maclist->count = MAX_NUM_OF_ASSOCIATED_DEV; ++ err = wldev_ioctl(ndev, WLC_GET_ASSOCLIST, ++ assoc_maclist, sizeof(mac_buf), false); ++ if (err < 0) ++ WL_ERR(("WLC_GET_ASSOCLIST error %d\n", err)); ++ else ++ num_associated = assoc_maclist->count; ++ } ++ memcpy(scb_val.ea.octet, mgmt->da, ETH_ALEN); ++ scb_val.val = mgmt->u.disassoc.reason_code; ++ err = wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, ++ sizeof(scb_val_t), true); ++ if (err < 0) ++ WL_ERR(("WLC_SCB_DEAUTHENTICATE_FOR_REASON error %d\n", err)); ++ WL_DBG(("Disconnect STA : %s scb_val.val %d\n", ++ bcm_ether_ntoa((const struct ether_addr *)mgmt->da, eabuf), ++ scb_val.val)); ++ if (num_associated) { ++ wl_delay(400); ++ } ++ cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); ++ goto exit; ++ ++ } else if (ieee80211_is_action(mgmt->frame_control)) { ++ /* Abort the dwell time of any previous off-channel ++ * action frame that may be still in effect. Sending ++ * off-channel action frames relies on the driver's ++ * scan engine. If a previous off-channel action frame ++ * tx is still in progress (including the dwell time), ++ * then this new action frame will not be sent out. ++ */ ++/* Do not abort scan for VSDB. Scan will be aborted in firmware if necessary. ++ * And previous off-channel action frame must be ended before new af tx. ++ */ ++#ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++ wl_notify_escan_complete(wl, dev, true, true); ++#endif /* not WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ ++ } ++ ++ } else { ++ WL_ERR(("Driver only allows MGMT packet type\n")); ++ goto exit; ++ } ++ ++ af_params = (wl_af_params_t *) kzalloc(WL_WIFI_AF_PARAMS_SIZE, GFP_KERNEL); ++ ++ if (af_params == NULL) ++ { ++ WL_ERR(("unable to allocate frame\n")); ++ return -ENOMEM; ++ } ++ ++ action_frame = &af_params->action_frame; ++ ++ /* Add the packet Id */ ++ action_frame->packetId = *cookie; ++ WL_DBG(("action frame %d\n", action_frame->packetId)); ++ /* Add BSSID */ ++ memcpy(&action_frame->da, &mgmt->da[0], ETHER_ADDR_LEN); ++ memcpy(&af_params->BSSID, &mgmt->bssid[0], ETHER_ADDR_LEN); ++ ++ /* Add the length exepted for 802.11 header */ ++ action_frame->len = len - DOT11_MGMT_HDR_LEN; ++ WL_DBG(("action_frame->len: %d\n", action_frame->len)); ++ ++ /* Add the channel */ ++ af_params->channel = ++ ieee80211_frequency_to_channel(channel->center_freq); ++ ++ /* Save listen_chan for searching common channel */ ++ wl->afx_hdl->peer_listen_chan = af_params->channel; ++ WL_DBG(("channel from upper layer %d\n", wl->afx_hdl->peer_listen_chan)); ++ ++ /* Add the default dwell time ++ * Dwell time to stay off-channel to wait for a response action frame ++ * after transmitting an GO Negotiation action frame ++ */ ++ af_params->dwell_time = WL_DWELL_TIME; ++ ++ memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], action_frame->len); ++ ++ ack = wl_cfg80211_send_action_frame(wiphy, dev, ndev, af_params, ++ action_frame, action_frame->len, bssidx); ++ ++ cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, ack, GFP_KERNEL); ++ ++ kfree(af_params); ++exit: ++ return err; ++} ++ ++ ++static void ++wl_cfg80211_mgmt_frame_register(struct wiphy *wiphy, struct net_device *dev, ++ u16 frame_type, bool reg) ++{ ++ ++ WL_DBG(("%s: frame_type: %x, reg: %d\n", __func__, frame_type, reg)); ++ ++ if (frame_type != (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ)) ++ return; ++ ++ return; ++} ++ ++ ++static s32 ++wl_cfg80211_change_bss(struct wiphy *wiphy, ++ struct net_device *dev, ++ struct bss_parameters *params) ++{ ++ if (params->use_cts_prot >= 0) { ++ } ++ ++ if (params->use_short_preamble >= 0) { ++ } ++ ++ if (params->use_short_slot_time >= 0) { ++ } ++ ++ if (params->basic_rates) { ++ } ++ ++ if (params->ap_isolate >= 0) { ++ } ++ ++ if (params->ht_opmode >= 0) { ++ } ++ ++ return 0; ++} ++ ++static s32 ++wl_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, ++ struct ieee80211_channel *chan, ++ enum nl80211_channel_type channel_type) ++{ ++ s32 _chan; ++ chanspec_t chspec = 0; ++ chanspec_t fw_chspec = 0; ++ u32 bw = WL_CHANSPEC_BW_20; ++ ++ s32 err = BCME_OK; ++ s32 bw_cap = 0; ++ struct { ++ u32 band; ++ u32 bw_cap; ++ } param = {0, 0}; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ ++ if (wl->p2p_net == dev) { ++ dev = wl_to_prmry_ndev(wl); ++ } ++ _chan = ieee80211_frequency_to_channel(chan->center_freq); ++ WL_ERR(("netdev_ifidx(%d), chan_type(%d) target channel(%d) \n", ++ dev->ifindex, channel_type, _chan)); ++ ++ ++ if (chan->band == IEEE80211_BAND_5GHZ) { ++ param.band = WLC_BAND_5G; ++ err = wldev_iovar_getbuf(dev, "bw_cap", ¶m, sizeof(param), ++ wl->ioctl_buf, WLC_IOCTL_SMLEN, &wl->ioctl_buf_sync); ++ if (err) { ++ if (err != BCME_UNSUPPORTED) { ++ WL_ERR(("bw_cap failed, %d\n", err)); ++ return err; ++ } else { ++ err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap); ++ if (err) { ++ WL_ERR(("error get mimo_bw_cap (%d)\n", err)); ++ } ++ if (bw_cap != WLC_N_BW_20ALL) ++ bw = WL_CHANSPEC_BW_40; ++ } ++ } else { ++ if (WL_BW_CAP_80MHZ(wl->ioctl_buf[0])) ++ bw = WL_CHANSPEC_BW_80; ++ else if (WL_BW_CAP_40MHZ(wl->ioctl_buf[0])) ++ bw = WL_CHANSPEC_BW_40; ++ else ++ bw = WL_CHANSPEC_BW_20; ++ ++ } ++ ++ } else if (chan->band == IEEE80211_BAND_2GHZ) ++ bw = WL_CHANSPEC_BW_20; ++set_channel: ++ chspec = wf_channel2chspec(_chan, bw); ++ if (wf_chspec_valid(chspec)) { ++ fw_chspec = wl_chspec_host_to_driver(chspec); ++ if (fw_chspec != INVCHANSPEC) { ++ if ((err = wldev_iovar_setint(dev, "chanspec", ++ fw_chspec)) == BCME_BADCHAN) { ++ if (bw == WL_CHANSPEC_BW_80) ++ goto change_bw; ++ err = wldev_ioctl(dev, WLC_SET_CHANNEL, ++ &_chan, sizeof(_chan), true); ++ if (err < 0) { ++ WL_ERR(("WLC_SET_CHANNEL error %d" ++ "chip may not be supporting this channel\n", err)); ++ } ++ } else if (err) { ++ WL_ERR(("failed to set chanspec error %d\n", err)); ++ } ++ } else { ++ WL_ERR(("failed to convert host chanspec to fw chanspec\n")); ++ err = BCME_ERROR; ++ } ++ } else { ++change_bw: ++ if (bw == WL_CHANSPEC_BW_80) ++ bw = WL_CHANSPEC_BW_40; ++ else if (bw == WL_CHANSPEC_BW_40) ++ bw = WL_CHANSPEC_BW_20; ++ else ++ bw = 0; ++ if (bw) ++ goto set_channel; ++ WL_ERR(("Invalid chanspec 0x%x\n", chspec)); ++ err = BCME_ERROR; ++ } ++ return err; ++} ++ ++static s32 ++wl_validate_opensecurity(struct net_device *dev, s32 bssidx) ++{ ++ s32 err = BCME_OK; ++ ++ /* set auth */ ++ err = wldev_iovar_setint_bsscfg(dev, "auth", 0, bssidx); ++ if (err < 0) { ++ WL_ERR(("auth error %d\n", err)); ++ return BCME_ERROR; ++ } ++ /* set wsec */ ++ err = wldev_iovar_setint_bsscfg(dev, "wsec", 0, bssidx); ++ if (err < 0) { ++ WL_ERR(("wsec error %d\n", err)); ++ return BCME_ERROR; ++ } ++ /* set upper-layer auth */ ++ err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", WPA_AUTH_NONE, bssidx); ++ if (err < 0) { ++ WL_ERR(("wpa_auth error %d\n", err)); ++ return BCME_ERROR; ++ } ++ ++ return 0; ++} ++ ++static s32 ++wl_validate_wpa2ie(struct net_device *dev, bcm_tlv_t *wpa2ie, s32 bssidx) ++{ ++ s32 len = 0; ++ s32 err = BCME_OK; ++ u16 auth = 0; /* d11 open authentication */ ++ u32 wsec; ++ u32 pval = 0; ++ u32 gval = 0; ++ u32 wpa_auth = 0; ++ wpa_suite_mcast_t *mcast; ++ wpa_suite_ucast_t *ucast; ++ wpa_suite_auth_key_mgmt_t *mgmt; ++ ++ u16 suite_count; ++ u8 rsn_cap[2]; ++ u32 wme_bss_disable; ++ ++ if (wpa2ie == NULL) ++ goto exit; ++ ++ WL_DBG(("Enter \n")); ++ len = wpa2ie->len; ++ /* check the mcast cipher */ ++ mcast = (wpa_suite_mcast_t *)&wpa2ie->data[WPA2_VERSION_LEN]; ++ switch (mcast->type) { ++ case WPA_CIPHER_NONE: ++ gval = 0; ++ break; ++ case WPA_CIPHER_WEP_40: ++ case WPA_CIPHER_WEP_104: ++ gval = WEP_ENABLED; ++ break; ++ case WPA_CIPHER_TKIP: ++ gval = TKIP_ENABLED; ++ break; ++ case WPA_CIPHER_AES_CCM: ++ gval = AES_ENABLED; ++ break; ++ default: ++ WL_ERR(("No Security Info\n")); ++ break; ++ } ++ if ((len -= WPA_SUITE_LEN) <= 0) ++ return BCME_BADLEN; ++ ++ /* check the unicast cipher */ ++ ucast = (wpa_suite_ucast_t *)&mcast[1]; ++ suite_count = ltoh16_ua(&ucast->count); ++ switch (ucast->list[0].type) { ++ case WPA_CIPHER_NONE: ++ pval = 0; ++ break; ++ case WPA_CIPHER_WEP_40: ++ case WPA_CIPHER_WEP_104: ++ pval = WEP_ENABLED; ++ break; ++ case WPA_CIPHER_TKIP: ++ pval = TKIP_ENABLED; ++ break; ++ case WPA_CIPHER_AES_CCM: ++ pval = AES_ENABLED; ++ break; ++ default: ++ WL_ERR(("No Security Info\n")); ++ } ++ if ((len -= (WPA_IE_SUITE_COUNT_LEN + (WPA_SUITE_LEN * suite_count))) <= 0) ++ return BCME_BADLEN; ++ ++ /* FOR WPS , set SEC_OW_ENABLED */ ++ wsec = (pval | gval | SES_OW_ENABLED); ++ /* check the AKM */ ++ mgmt = (wpa_suite_auth_key_mgmt_t *)&ucast->list[suite_count]; ++ suite_count = ltoh16_ua(&mgmt->count); ++ switch (mgmt->list[0].type) { ++ case RSN_AKM_NONE: ++ wpa_auth = WPA_AUTH_NONE; ++ break; ++ case RSN_AKM_UNSPECIFIED: ++ wpa_auth = WPA2_AUTH_UNSPECIFIED; ++ break; ++ case RSN_AKM_PSK: ++ wpa_auth = WPA2_AUTH_PSK; ++ break; ++ default: ++ WL_ERR(("No Key Mgmt Info\n")); ++ } ++ ++ if ((len -= (WPA_IE_SUITE_COUNT_LEN + (WPA_SUITE_LEN * suite_count))) >= RSN_CAP_LEN) { ++ rsn_cap[0] = *(u8 *)&mgmt->list[suite_count]; ++ rsn_cap[1] = *((u8 *)&mgmt->list[suite_count] + 1); ++ ++ if (rsn_cap[0] & (RSN_CAP_16_REPLAY_CNTRS << RSN_CAP_PTK_REPLAY_CNTR_SHIFT)) { ++ wme_bss_disable = 0; ++ } else { ++ wme_bss_disable = 1; ++ } ++ ++ /* set wme_bss_disable to sync RSN Capabilities */ ++ err = wldev_iovar_setint_bsscfg(dev, "wme_bss_disable", wme_bss_disable, bssidx); ++ if (err < 0) { ++ WL_ERR(("wme_bss_disable error %d\n", err)); ++ return BCME_ERROR; ++ } ++ } else { ++ WL_DBG(("There is no RSN Capabilities. remained len %d\n", len)); ++ } ++ ++ /* set auth */ ++ err = wldev_iovar_setint_bsscfg(dev, "auth", auth, bssidx); ++ if (err < 0) { ++ WL_ERR(("auth error %d\n", err)); ++ return BCME_ERROR; ++ } ++ /* set wsec */ ++ err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx); ++ if (err < 0) { ++ WL_ERR(("wsec error %d\n", err)); ++ return BCME_ERROR; ++ } ++ /* set upper-layer auth */ ++ err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", wpa_auth, bssidx); ++ if (err < 0) { ++ WL_ERR(("wpa_auth error %d\n", err)); ++ return BCME_ERROR; ++ } ++exit: ++ return 0; ++} ++ ++static s32 ++wl_validate_wpaie(struct net_device *dev, wpa_ie_fixed_t *wpaie, s32 bssidx) ++{ ++ wpa_suite_mcast_t *mcast; ++ wpa_suite_ucast_t *ucast; ++ wpa_suite_auth_key_mgmt_t *mgmt; ++ u16 auth = 0; /* d11 open authentication */ ++ u16 count; ++ s32 err = BCME_OK; ++ s32 len = 0; ++ u32 i; ++ u32 wsec; ++ u32 pval = 0; ++ u32 gval = 0; ++ u32 wpa_auth = 0; ++ u32 tmp = 0; ++ ++ if (wpaie == NULL) ++ goto exit; ++ WL_DBG(("Enter \n")); ++ len = wpaie->length; /* value length */ ++ len -= WPA_IE_TAG_FIXED_LEN; ++ /* check for multicast cipher suite */ ++ if (len < WPA_SUITE_LEN) { ++ WL_INFO(("no multicast cipher suite\n")); ++ goto exit; ++ } ++ ++ /* pick up multicast cipher */ ++ mcast = (wpa_suite_mcast_t *)&wpaie[1]; ++ len -= WPA_SUITE_LEN; ++ if (!bcmp(mcast->oui, WPA_OUI, WPA_OUI_LEN)) { ++ if (IS_WPA_CIPHER(mcast->type)) { ++ tmp = 0; ++ switch (mcast->type) { ++ case WPA_CIPHER_NONE: ++ tmp = 0; ++ break; ++ case WPA_CIPHER_WEP_40: ++ case WPA_CIPHER_WEP_104: ++ tmp = WEP_ENABLED; ++ break; ++ case WPA_CIPHER_TKIP: ++ tmp = TKIP_ENABLED; ++ break; ++ case WPA_CIPHER_AES_CCM: ++ tmp = AES_ENABLED; ++ break; ++ default: ++ WL_ERR(("No Security Info\n")); ++ } ++ gval |= tmp; ++ } ++ } ++ /* Check for unicast suite(s) */ ++ if (len < WPA_IE_SUITE_COUNT_LEN) { ++ WL_INFO(("no unicast suite\n")); ++ goto exit; ++ } ++ /* walk thru unicast cipher list and pick up what we recognize */ ++ ucast = (wpa_suite_ucast_t *)&mcast[1]; ++ count = ltoh16_ua(&ucast->count); ++ len -= WPA_IE_SUITE_COUNT_LEN; ++ for (i = 0; i < count && len >= WPA_SUITE_LEN; ++ i++, len -= WPA_SUITE_LEN) { ++ if (!bcmp(ucast->list[i].oui, WPA_OUI, WPA_OUI_LEN)) { ++ if (IS_WPA_CIPHER(ucast->list[i].type)) { ++ tmp = 0; ++ switch (ucast->list[i].type) { ++ case WPA_CIPHER_NONE: ++ tmp = 0; ++ break; ++ case WPA_CIPHER_WEP_40: ++ case WPA_CIPHER_WEP_104: ++ tmp = WEP_ENABLED; ++ break; ++ case WPA_CIPHER_TKIP: ++ tmp = TKIP_ENABLED; ++ break; ++ case WPA_CIPHER_AES_CCM: ++ tmp = AES_ENABLED; ++ break; ++ default: ++ WL_ERR(("No Security Info\n")); ++ } ++ pval |= tmp; ++ } ++ } ++ } ++ len -= (count - i) * WPA_SUITE_LEN; ++ /* Check for auth key management suite(s) */ ++ if (len < WPA_IE_SUITE_COUNT_LEN) { ++ WL_INFO((" no auth key mgmt suite\n")); ++ goto exit; ++ } ++ /* walk thru auth management suite list and pick up what we recognize */ ++ mgmt = (wpa_suite_auth_key_mgmt_t *)&ucast->list[count]; ++ count = ltoh16_ua(&mgmt->count); ++ len -= WPA_IE_SUITE_COUNT_LEN; ++ for (i = 0; i < count && len >= WPA_SUITE_LEN; ++ i++, len -= WPA_SUITE_LEN) { ++ if (!bcmp(mgmt->list[i].oui, WPA_OUI, WPA_OUI_LEN)) { ++ if (IS_WPA_AKM(mgmt->list[i].type)) { ++ tmp = 0; ++ switch (mgmt->list[i].type) { ++ case RSN_AKM_NONE: ++ tmp = WPA_AUTH_NONE; ++ break; ++ case RSN_AKM_UNSPECIFIED: ++ tmp = WPA_AUTH_UNSPECIFIED; ++ break; ++ case RSN_AKM_PSK: ++ tmp = WPA_AUTH_PSK; ++ break; ++ default: ++ WL_ERR(("No Key Mgmt Info\n")); ++ } ++ wpa_auth |= tmp; ++ } ++ } ++ ++ } ++ /* FOR WPS , set SEC_OW_ENABLED */ ++ wsec = (pval | gval | SES_OW_ENABLED); ++ /* set auth */ ++ err = wldev_iovar_setint_bsscfg(dev, "auth", auth, bssidx); ++ if (err < 0) { ++ WL_ERR(("auth error %d\n", err)); ++ return BCME_ERROR; ++ } ++ /* set wsec */ ++ err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx); ++ if (err < 0) { ++ WL_ERR(("wsec error %d\n", err)); ++ return BCME_ERROR; ++ } ++ /* set upper-layer auth */ ++ err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", wpa_auth, bssidx); ++ if (err < 0) { ++ WL_ERR(("wpa_auth error %d\n", err)); ++ return BCME_ERROR; ++ } ++exit: ++ return 0; ++} ++ ++static s32 ++wl_cfg80211_bcn_validate_sec( ++ struct net_device *dev, ++ struct parsed_ies *ies, ++ u32 dev_role, ++ s32 bssidx) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ ++ if (dev_role == NL80211_IFTYPE_P2P_GO && (ies->wpa2_ie)) { ++ /* For P2P GO, the sec type is WPA2-PSK */ ++ WL_DBG(("P2P GO: validating wpa2_ie")); ++ if (wl_validate_wpa2ie(dev, ies->wpa2_ie, bssidx) < 0) ++ return BCME_ERROR; ++ ++ } else if (dev_role == NL80211_IFTYPE_AP) { ++ ++ WL_DBG(("SoftAP: validating security")); ++ /* If wpa2_ie or wpa_ie is present validate it */ ++ if ((ies->wpa2_ie || ies->wpa_ie) && ++ ((wl_validate_wpa2ie(dev, ies->wpa2_ie, bssidx) < 0 || ++ wl_validate_wpaie(dev, ies->wpa_ie, bssidx) < 0))) { ++ wl->ap_info->security_mode = false; ++ return BCME_ERROR; ++ } ++ ++ wl->ap_info->security_mode = true; ++ if (wl->ap_info->rsn_ie) { ++ kfree(wl->ap_info->rsn_ie); ++ wl->ap_info->rsn_ie = NULL; ++ } ++ if (wl->ap_info->wpa_ie) { ++ kfree(wl->ap_info->wpa_ie); ++ wl->ap_info->wpa_ie = NULL; ++ } ++ if (wl->ap_info->wps_ie) { ++ kfree(wl->ap_info->wps_ie); ++ wl->ap_info->wps_ie = NULL; ++ } ++ if (ies->wpa_ie != NULL) { ++ /* WPAIE */ ++ wl->ap_info->rsn_ie = NULL; ++ wl->ap_info->wpa_ie = kmemdup(ies->wpa_ie, ++ ies->wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN, ++ GFP_KERNEL); ++ } else if (ies->wpa2_ie != NULL) { ++ /* RSNIE */ ++ wl->ap_info->wpa_ie = NULL; ++ wl->ap_info->rsn_ie = kmemdup(ies->wpa2_ie, ++ ies->wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN, ++ GFP_KERNEL); ++ } ++ ++ if (!ies->wpa2_ie && !ies->wpa_ie) { ++ wl_validate_opensecurity(dev, bssidx); ++ wl->ap_info->security_mode = false; ++ } ++ ++ if (ies->wps_ie) { ++ wl->ap_info->wps_ie = kmemdup(ies->wps_ie, ies->wps_ie_len, GFP_KERNEL); ++ } ++ } ++ ++ return 0; ++ ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) ++static s32 wl_cfg80211_bcn_set_params( ++ struct cfg80211_ap_settings *info, ++ struct net_device *dev, ++ u32 dev_role, s32 bssidx) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ s32 err = BCME_OK; ++ ++ WL_DBG(("interval (%d) \ndtim_period (%d) \n", ++ info->beacon_interval, info->dtim_period)); ++ ++ if (info->beacon_interval) { ++ if ((err = wldev_ioctl(dev, WLC_SET_BCNPRD, ++ &info->beacon_interval, sizeof(s32), true)) < 0) { ++ WL_ERR(("Beacon Interval Set Error, %d\n", err)); ++ return err; ++ } ++ } ++ ++ if (info->dtim_period) { ++ if ((err = wldev_ioctl(dev, WLC_SET_DTIMPRD, ++ &info->dtim_period, sizeof(s32), true)) < 0) { ++ WL_ERR(("DTIM Interval Set Error, %d\n", err)); ++ return err; ++ } ++ } ++ ++ if ((info->ssid) && (info->ssid_len > 0) && ++ (info->ssid_len <= 32)) { ++ WL_DBG(("SSID (%s) len:%d \n", info->ssid, info->ssid_len)); ++ if (dev_role == NL80211_IFTYPE_AP) { ++ /* Store the hostapd SSID */ ++ memset(wl->hostapd_ssid.SSID, 0x00, 32); ++ memcpy(wl->hostapd_ssid.SSID, info->ssid, info->ssid_len); ++ wl->hostapd_ssid.SSID_len = info->ssid_len; ++ } else { ++ /* P2P GO */ ++ memset(wl->p2p->ssid.SSID, 0x00, 32); ++ memcpy(wl->p2p->ssid.SSID, info->ssid, info->ssid_len); ++ wl->p2p->ssid.SSID_len = info->ssid_len; ++ } ++ } ++ ++ if (info->hidden_ssid) { ++ if ((err = wldev_iovar_setint(dev, "closednet", 1)) < 0) ++ WL_ERR(("failed to set hidden : %d\n", err)); ++ WL_DBG(("hidden_ssid_enum_val: %d \n", info->hidden_ssid)); ++ } ++ ++ return err; ++} ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) */ ++ ++static s32 ++wl_cfg80211_parse_ies(u8 *ptr, u32 len, struct parsed_ies *ies) ++{ ++ s32 err = BCME_OK; ++ ++ memset(ies, 0, sizeof(struct parsed_ies)); ++ ++ /* find the WPSIE */ ++ if ((ies->wps_ie = wl_cfgp2p_find_wpsie(ptr, len)) != NULL) { ++ WL_DBG(("WPSIE in beacon \n")); ++ ies->wps_ie_len = ies->wps_ie->length + WPA_RSN_IE_TAG_FIXED_LEN; ++ } else { ++ WL_ERR(("No WPSIE in beacon \n")); ++ } ++ ++ /* find the RSN_IE */ ++ if ((ies->wpa2_ie = bcm_parse_tlvs(ptr, len, ++ DOT11_MNG_RSN_ID)) != NULL) { ++ WL_DBG((" WPA2 IE found\n")); ++ ies->wpa2_ie_len = ies->wpa2_ie->len; ++ } ++ ++ /* find the WPA_IE */ ++ if ((ies->wpa_ie = wl_cfgp2p_find_wpaie(ptr, len)) != NULL) { ++ WL_DBG((" WPA found\n")); ++ ies->wpa_ie_len = ies->wpa_ie->length; ++ } ++ ++ return err; ++ ++} ++ ++static s32 ++wl_cfg80211_bcn_bringup_ap( ++ struct net_device *dev, ++ struct parsed_ies *ies, ++ u32 dev_role, s32 bssidx) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ struct wl_join_params join_params; ++ bool is_bssup = false; ++ s32 infra = 1; ++ s32 join_params_size = 0; ++ s32 ap = 1; ++ s32 err = BCME_OK; ++ ++ WL_DBG(("Enter dev_role: %d\n", dev_role)); ++ ++ /* Common code for SoftAP and P2P GO */ ++ wldev_iovar_setint(dev, "mpc", 0); ++ ++ if (dev_role == NL80211_IFTYPE_P2P_GO) { ++ is_bssup = wl_cfgp2p_bss_isup(dev, bssidx); ++ if (!is_bssup && (ies->wpa2_ie != NULL)) { ++ ++ err = wldev_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(s32), true); ++ if (err < 0) { ++ WL_ERR(("SET INFRA error %d\n", err)); ++ goto exit; ++ } ++ ++ err = wldev_iovar_setbuf_bsscfg(dev, "ssid", &wl->p2p->ssid, ++ sizeof(wl->p2p->ssid), wl->ioctl_buf, WLC_IOCTL_MAXLEN, ++ bssidx, &wl->ioctl_buf_sync); ++ if (err < 0) { ++ WL_ERR(("GO SSID setting error %d\n", err)); ++ goto exit; ++ } ++ ++ if ((err = wl_cfgp2p_bss(wl, dev, bssidx, 1)) < 0) { ++ WL_ERR(("GO Bring up error %d\n", err)); ++ goto exit; ++ } ++ } else ++ WL_DBG(("Bss is already up\n")); ++ } else if ((dev_role == NL80211_IFTYPE_AP) && ++ (wl_get_drv_status(wl, AP_CREATING, dev))) { ++ /* Device role SoftAP */ ++ err = wldev_ioctl(dev, WLC_DOWN, &ap, sizeof(s32), true); ++ if (err < 0) { ++ WL_ERR(("WLC_DOWN error %d\n", err)); ++ goto exit; ++ } ++ err = wldev_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(s32), true); ++ if (err < 0) { ++ WL_ERR(("SET INFRA error %d\n", err)); ++ goto exit; ++ } ++ if ((err = wldev_ioctl(dev, WLC_SET_AP, &ap, sizeof(s32), true)) < 0) { ++ WL_ERR(("setting AP mode failed %d \n", err)); ++ goto exit; ++ } ++ ++ err = wldev_ioctl(dev, WLC_UP, &ap, sizeof(s32), true); ++ if (unlikely(err)) { ++ WL_ERR(("WLC_UP error (%d)\n", err)); ++ goto exit; ++ } ++ ++ memset(&join_params, 0, sizeof(join_params)); ++ /* join parameters starts with ssid */ ++ join_params_size = sizeof(join_params.ssid); ++ memcpy(join_params.ssid.SSID, wl->hostapd_ssid.SSID, ++ wl->hostapd_ssid.SSID_len); ++ join_params.ssid.SSID_len = htod32(wl->hostapd_ssid.SSID_len); ++ ++ /* create softap */ ++ if ((err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, ++ join_params_size, true)) == 0) { ++ WL_DBG(("SoftAP set SSID (%s) success\n", join_params.ssid.SSID)); ++ wl_clr_drv_status(wl, AP_CREATING, dev); ++ wl_set_drv_status(wl, AP_CREATED, dev); ++ } ++ } ++ ++ ++exit: ++ return err; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) ++s32 ++wl_cfg80211_parse_ap_ies( ++ struct net_device *dev, ++ struct cfg80211_beacon_data *info, ++ struct parsed_ies *ies) ++{ ++ struct parsed_ies prb_ies; ++ s32 err = BCME_OK; ++ ++ /* Parse Beacon IEs */ ++ if (wl_cfg80211_parse_ies((u8 *)info->tail, ++ info->tail_len, ies) < 0) { ++ WL_ERR(("Beacon get IEs failed \n")); ++ err = -EINVAL; ++ goto fail; ++ } ++ ++ /* Parse Probe Response IEs */ ++ if (wl_cfg80211_parse_ies((u8 *)info->proberesp_ies, ++ info->proberesp_ies_len, &prb_ies) < 0) { ++ WL_ERR(("PROBE RESP get IEs failed \n")); ++ err = -EINVAL; ++ } ++ ++fail: ++ ++ return err; ++} ++ ++s32 ++wl_cfg80211_set_ies( ++ struct net_device *dev, ++ struct cfg80211_beacon_data *info, ++ s32 bssidx) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ s32 err = BCME_OK; ++ ++ /* Set Beacon IEs to FW */ ++ if ((err = wl_cfgp2p_set_management_ie(wl, dev, bssidx, ++ VNDR_IE_BEACON_FLAG, (u8 *)info->tail, ++ info->tail_len)) < 0) { ++ WL_ERR(("Set Beacon IE Failed \n")); ++ } else { ++ WL_DBG(("Applied Vndr IEs for Beacon \n")); ++ } ++ ++ /* Set Probe Response IEs to FW */ ++ if ((err = wl_cfgp2p_set_management_ie(wl, dev, bssidx, ++ VNDR_IE_PRBRSP_FLAG, (u8 *)info->proberesp_ies, ++ info->proberesp_ies_len)) < 0) { ++ WL_ERR(("Set Probe Resp IE Failed \n")); ++ } else { ++ WL_DBG(("Applied Vndr IEs for Probe Resp \n")); ++ } ++ ++ return err; ++} ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) */ ++ ++static s32 wl_cfg80211_hostapd_sec( ++ struct net_device *dev, ++ struct parsed_ies *ies, ++ s32 bssidx) ++{ ++ bool update_bss = 0; ++ struct wl_priv *wl = wlcfg_drv_priv; ++ ++ ++ if (ies->wps_ie) { ++ if (wl->ap_info->wps_ie && ++ memcmp(wl->ap_info->wps_ie, ies->wps_ie, ies->wps_ie_len)) { ++ WL_DBG((" WPS IE is changed\n")); ++ kfree(wl->ap_info->wps_ie); ++ wl->ap_info->wps_ie = kmemdup(ies->wps_ie, ies->wps_ie_len, GFP_KERNEL); ++ } else if (wl->ap_info->wps_ie == NULL) { ++ WL_DBG((" WPS IE is added\n")); ++ wl->ap_info->wps_ie = kmemdup(ies->wps_ie, ies->wps_ie_len, GFP_KERNEL); ++ } ++ if ((ies->wpa_ie != NULL || ies->wpa2_ie != NULL)) { ++ if (!wl->ap_info->security_mode) { ++ /* change from open mode to security mode */ ++ update_bss = true; ++ if (ies->wpa_ie != NULL) { ++ wl->ap_info->wpa_ie = kmemdup(ies->wpa_ie, ++ ies->wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN, ++ GFP_KERNEL); ++ } else { ++ wl->ap_info->rsn_ie = kmemdup(ies->wpa2_ie, ++ ies->wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN, ++ GFP_KERNEL); ++ } ++ } else if (wl->ap_info->wpa_ie) { ++ /* change from WPA2 mode to WPA mode */ ++ if (ies->wpa_ie != NULL) { ++ update_bss = true; ++ kfree(wl->ap_info->rsn_ie); ++ wl->ap_info->rsn_ie = NULL; ++ wl->ap_info->wpa_ie = kmemdup(ies->wpa_ie, ++ ies->wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN, ++ GFP_KERNEL); ++ } else if (memcmp(wl->ap_info->rsn_ie, ++ ies->wpa2_ie, ies->wpa2_ie->len ++ + WPA_RSN_IE_TAG_FIXED_LEN)) { ++ update_bss = true; ++ kfree(wl->ap_info->rsn_ie); ++ wl->ap_info->rsn_ie = kmemdup(ies->wpa2_ie, ++ ies->wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN, ++ GFP_KERNEL); ++ wl->ap_info->wpa_ie = NULL; ++ } ++ } ++ if (update_bss) { ++ wl->ap_info->security_mode = true; ++ wl_cfgp2p_bss(wl, dev, bssidx, 0); ++ if (wl_validate_wpa2ie(dev, ies->wpa2_ie, bssidx) < 0 || ++ wl_validate_wpaie(dev, ies->wpa_ie, bssidx) < 0) { ++ return BCME_ERROR; ++ } ++ wl_cfgp2p_bss(wl, dev, bssidx, 1); ++ } ++ } ++ } else { ++ WL_ERR(("No WPSIE in beacon \n")); ++ } ++ return 0; ++} ++ ++static s32 ++wl_cfg80211_del_station( ++ struct wiphy *wiphy, ++ struct net_device *ndev, ++ u8* mac_addr) ++{ ++ struct net_device *dev; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ scb_val_t scb_val; ++ s8 eabuf[ETHER_ADDR_STR_LEN]; ++ char mac_buf[MAX_NUM_OF_ASSOCIATED_DEV * ++ sizeof(struct ether_addr) + sizeof(uint)] = {0}; ++ struct maclist *assoc_maclist = (struct maclist *)mac_buf; ++ int num_associated = 0, err; ++ ++ WL_DBG(("Entry\n")); ++ if (mac_addr == NULL) { ++ WL_DBG(("mac_addr is NULL ignore it\n")); ++ return 0; ++ } ++ ++ if (ndev == wl->p2p_net) { ++ dev = wl_to_prmry_ndev(wl); ++ } else { ++ dev = ndev; ++ } ++ ++ if (p2p_is_on(wl)) { ++ /* Suspend P2P discovery search-listen to prevent it from changing the ++ * channel. ++ */ ++ if ((wl_cfgp2p_discover_enable_search(wl, false)) < 0) { ++ WL_ERR(("Can not disable discovery mode\n")); ++ return -EFAULT; ++ } ++ } ++ ++ assoc_maclist->count = MAX_NUM_OF_ASSOCIATED_DEV; ++ err = wldev_ioctl(ndev, WLC_GET_ASSOCLIST, ++ assoc_maclist, sizeof(mac_buf), false); ++ if (err < 0) ++ WL_ERR(("WLC_GET_ASSOCLIST error %d\n", err)); ++ else ++ num_associated = assoc_maclist->count; ++ ++ memcpy(scb_val.ea.octet, mac_addr, ETHER_ADDR_LEN); ++ scb_val.val = DOT11_RC_DEAUTH_LEAVING; ++ err = wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, ++ sizeof(scb_val_t), true); ++ if (err < 0) ++ WL_ERR(("WLC_SCB_DEAUTHENTICATE_FOR_REASON err %d\n", err)); ++ WL_DBG(("Disconnect STA : %s scb_val.val %d\n", ++ bcm_ether_ntoa((const struct ether_addr *)mac_addr, eabuf), ++ scb_val.val)); ++ if (num_associated) ++ wl_delay(400); ++ return 0; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) ++static s32 ++wl_cfg80211_start_ap( ++ struct wiphy *wiphy, ++ struct net_device *dev, ++ struct cfg80211_ap_settings *info) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ s32 err = BCME_OK; ++ struct parsed_ies ies; ++ s32 bssidx = 0; ++ u32 dev_role = 0; ++ ++ WL_DBG(("Enter \n")); ++ if (dev == wl_to_prmry_ndev(wl)) { ++ WL_DBG(("Start AP req on primary iface: Softap\n")); ++ dev_role = NL80211_IFTYPE_AP; ++ } else if (dev == wl->p2p_net) { ++ /* Group Add request on p2p0 */ ++ WL_DBG(("Start AP req on P2P iface: GO\n")); ++ dev = wl_to_prmry_ndev(wl); ++ dev_role = NL80211_IFTYPE_P2P_GO; ++ } ++ ++ bssidx = wl_cfgp2p_find_idx(wl, dev); ++ if (p2p_is_on(wl) && ++ (bssidx == wl_to_p2p_bss_bssidx(wl, ++ P2PAPI_BSSCFG_CONNECTION))) { ++ dev_role = NL80211_IFTYPE_P2P_GO; ++ WL_DBG(("Start AP req on P2P connection iface\n")); ++ } ++ ++ if ((err = wl_cfg80211_bcn_set_params(info, dev, ++ dev_role, bssidx)) < 0) { ++ WL_ERR(("Beacon params set failed \n")); ++ goto fail; ++ } ++ ++ /* Parse IEs */ ++ if ((err = wl_cfg80211_parse_ap_ies(dev, &info->beacon, &ies) < 0)) { ++ WL_ERR(("Set IEs failed \n")); ++ goto fail; ++ } ++ ++ if ((wl_cfg80211_bcn_validate_sec(dev, &ies, ++ dev_role, bssidx)) < 0) ++ { ++ WL_ERR(("Beacon set security failed \n")); ++ goto fail; ++ } ++ ++ if ((err = wl_cfg80211_bcn_bringup_ap(dev, &ies, ++ dev_role, bssidx)) < 0) { ++ WL_ERR(("Beacon bring up AP/GO failed \n")); ++ goto fail; ++ } ++ ++ WL_DBG(("** AP/GO Created **\n")); ++ ++ /* Set IEs to FW */ ++ if ((err = wl_cfg80211_set_ies(dev, &info->beacon, bssidx) < 0)) ++ WL_ERR(("Set IEs failed \n")); ++ ++fail: ++ if (err) { ++ WL_ERR(("ADD/SET beacon failed\n")); ++ wldev_iovar_setint(dev, "mpc", 1); ++ } ++ ++ return err; ++} ++ ++static s32 ++wl_cfg80211_stop_ap( ++ struct wiphy *wiphy, ++ struct net_device *dev) ++{ ++ int err = 0; ++ u32 dev_role = 0; ++ int infra = 0; ++ int ap = 0; ++ s32 bssidx = 0; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ ++ WL_DBG(("Enter \n")); ++ if (dev == wl_to_prmry_ndev(wl)) { ++ dev_role = NL80211_IFTYPE_AP; ++ } else if (dev == wl->p2p_net) { ++ /* Group Add request on p2p0 */ ++ dev = wl_to_prmry_ndev(wl); ++ dev_role = NL80211_IFTYPE_P2P_GO; ++ } ++ bssidx = wl_cfgp2p_find_idx(wl, dev); ++ if (p2p_is_on(wl) && ++ (bssidx == wl_to_p2p_bss_bssidx(wl, ++ P2PAPI_BSSCFG_CONNECTION))) { ++ dev_role = NL80211_IFTYPE_P2P_GO; ++ } ++ ++ if (dev_role == NL80211_IFTYPE_AP) { ++ /* SoftAp on primary Interface. ++ * Shut down AP and turn on MPC ++ */ ++ err = wldev_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(s32), true); ++ if (err < 0) { ++ WL_ERR(("SET INFRA error %d\n", err)); ++ err = -ENOTSUPP; ++ goto exit; ++ } ++ if ((err = wldev_ioctl(dev, WLC_SET_AP, &ap, sizeof(s32), true)) < 0) { ++ WL_ERR(("setting AP mode failed %d \n", err)); ++ err = -ENOTSUPP; ++ goto exit; ++ } ++ ++ err = wldev_ioctl(dev, WLC_UP, &ap, sizeof(s32), true); ++ if (unlikely(err)) { ++ WL_ERR(("WLC_UP error (%d)\n", err)); ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ wl_clr_drv_status(wl, AP_CREATED, dev); ++ /* Turn on the MPC */ ++ wldev_iovar_setint(dev, "mpc", 1); ++ } else { ++ WL_DBG(("Stopping P2P GO \n")); ++ } ++ ++exit: ++ return err; ++} ++ ++static s32 ++wl_cfg80211_change_beacon( ++ struct wiphy *wiphy, ++ struct net_device *dev, ++ struct cfg80211_beacon_data *info) ++{ ++ s32 err = BCME_OK; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct parsed_ies ies; ++ u32 dev_role = 0; ++ s32 bssidx = 0; ++ ++ WL_DBG(("Enter \n")); ++ ++ if (dev == wl_to_prmry_ndev(wl)) { ++ dev_role = NL80211_IFTYPE_AP; ++ } else if (dev == wl->p2p_net) { ++ /* Group Add request on p2p0 */ ++ dev = wl_to_prmry_ndev(wl); ++ dev_role = NL80211_IFTYPE_P2P_GO; ++ } ++ ++ bssidx = wl_cfgp2p_find_idx(wl, dev); ++ if (p2p_is_on(wl) && ++ (bssidx == wl_to_p2p_bss_bssidx(wl, ++ P2PAPI_BSSCFG_CONNECTION))) { ++ dev_role = NL80211_IFTYPE_P2P_GO; ++ } ++ ++ /* Parse IEs */ ++ if ((err = wl_cfg80211_parse_ap_ies(dev, info, &ies) < 0)) { ++ WL_ERR(("Parse IEs failed \n")); ++ goto fail; ++ } ++ ++ /* Set IEs to FW */ ++ if ((err = wl_cfg80211_set_ies(dev, info, bssidx) < 0)) { ++ WL_ERR(("Set IEs failed \n")); ++ goto fail; ++ } ++ ++ if (dev_role == NL80211_IFTYPE_AP) { ++ if (wl_cfg80211_hostapd_sec(dev, &ies, bssidx) < 0) { ++ WL_ERR(("Hostapd update sec failed \n")); ++ err = -EINVAL; ++ goto fail; ++ } ++ } ++ ++fail: ++ return err; ++} ++#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ ++static s32 ++wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, ++ struct beacon_parameters *info) ++{ ++ s32 err = BCME_OK; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ s32 ie_offset = 0; ++ s32 bssidx = 0; ++ u32 dev_role = NL80211_IFTYPE_AP; ++ struct parsed_ies ies; ++ bcm_tlv_t *ssid_ie; ++ bool pbc = 0; ++ ++ WL_DBG(("interval (%d) dtim_period (%d) head_len (%d) tail_len (%d)\n", ++ info->interval, info->dtim_period, info->head_len, info->tail_len)); ++ ++ if (dev == wl_to_prmry_ndev(wl)) { ++ dev_role = NL80211_IFTYPE_AP; ++ } else if (dev == wl->p2p_net) { ++ /* Group Add request on p2p0 */ ++ dev = wl_to_prmry_ndev(wl); ++ dev_role = NL80211_IFTYPE_P2P_GO; ++ } ++ ++ bssidx = wl_cfgp2p_find_idx(wl, dev); ++ if (p2p_is_on(wl) && ++ (bssidx == wl_to_p2p_bss_bssidx(wl, ++ P2PAPI_BSSCFG_CONNECTION))) { ++ dev_role = NL80211_IFTYPE_P2P_GO; ++ } ++ ++ ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; ++ /* find the SSID */ ++ if ((ssid_ie = bcm_parse_tlvs((u8 *)&info->head[ie_offset], ++ info->head_len - ie_offset, ++ DOT11_MNG_SSID_ID)) != NULL) { ++ if (dev_role == NL80211_IFTYPE_AP) { ++ /* Store the hostapd SSID */ ++ memset(&wl->hostapd_ssid.SSID[0], 0x00, 32); ++ memcpy(&wl->hostapd_ssid.SSID[0], ssid_ie->data, ssid_ie->len); ++ wl->hostapd_ssid.SSID_len = ssid_ie->len; ++ } else { ++ /* P2P GO */ ++ memset(&wl->p2p->ssid.SSID[0], 0x00, 32); ++ memcpy(wl->p2p->ssid.SSID, ssid_ie->data, ssid_ie->len); ++ wl->p2p->ssid.SSID_len = ssid_ie->len; ++ } ++ } ++ ++ if (wl_cfg80211_parse_ies((u8 *)info->tail, ++ info->tail_len, &ies) < 0) { ++ WL_ERR(("Beacon get IEs failed \n")); ++ err = -EINVAL; ++ goto fail; ++ } ++ ++ if (wl_cfgp2p_set_management_ie(wl, dev, bssidx, ++ VNDR_IE_BEACON_FLAG, (u8 *)info->tail, ++ info->tail_len) < 0) { ++ WL_ERR(("Beacon set IEs failed \n")); ++ goto fail; ++ } else { ++ WL_DBG(("Applied Vndr IEs for Beacon \n")); ++ } ++ if (!wl_cfgp2p_bss_isup(dev, bssidx) && ++ (wl_cfg80211_bcn_validate_sec(dev, &ies, dev_role, bssidx) < 0)) ++ { ++ WL_ERR(("Beacon set security failed \n")); ++ goto fail; ++ } ++ ++ /* Set BI and DTIM period */ ++ if (info->interval) { ++ if ((err = wldev_ioctl(dev, WLC_SET_BCNPRD, ++ &info->interval, sizeof(s32), true)) < 0) { ++ WL_ERR(("Beacon Interval Set Error, %d\n", err)); ++ return err; ++ } ++ } ++ if (info->dtim_period) { ++ if ((err = wldev_ioctl(dev, WLC_SET_DTIMPRD, ++ &info->dtim_period, sizeof(s32), true)) < 0) { ++ WL_ERR(("DTIM Interval Set Error, %d\n", err)); ++ return err; ++ } ++ } ++ ++ if (wl_cfg80211_bcn_bringup_ap(dev, &ies, dev_role, bssidx) < 0) { ++ WL_ERR(("Beacon bring up AP/GO failed \n")); ++ goto fail; ++ } ++ ++ if (wl_get_drv_status(wl, AP_CREATED, dev)) { ++ /* Soft AP already running. Update changed params */ ++ if (wl_cfg80211_hostapd_sec(dev, &ies, bssidx) < 0) { ++ WL_ERR(("Hostapd update sec failed \n")); ++ err = -EINVAL; ++ goto fail; ++ } ++ } ++ ++ /* Enable Probe Req filter */ ++ if (((dev_role == NL80211_IFTYPE_P2P_GO) || ++ (dev_role == NL80211_IFTYPE_AP)) && (ies.wps_ie != NULL)) { ++ wl_validate_wps_ie((char *) ies.wps_ie, ies.wps_ie_len, &pbc); ++ if (pbc) ++ wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, true); ++ } ++ ++ WL_DBG(("** ADD/SET beacon done **\n")); ++ ++fail: ++ if (err) { ++ WL_ERR(("ADD/SET beacon failed\n")); ++ wldev_iovar_setint(dev, "mpc", 1); ++ } ++ return err; ++ ++} ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ ++ ++#ifdef WL_SCHED_SCAN ++#define PNO_TIME 30 ++#define PNO_REPEAT 4 ++#define PNO_FREQ_EXPO_MAX 2 ++int wl_cfg80211_sched_scan_start(struct wiphy *wiphy, ++ struct net_device *dev, ++ struct cfg80211_sched_scan_request *request) ++{ ++ ushort pno_time = PNO_TIME; ++ int pno_repeat = PNO_REPEAT; ++ int pno_freq_expo_max = PNO_FREQ_EXPO_MAX; ++ wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ struct cfg80211_ssid *ssid = NULL; ++ int ssid_count = 0; ++ int i; ++ int ret = 0; ++ ++ WL_DBG(("Enter \n")); ++ WL_PNO((">>> SCHED SCAN START\n")); ++ WL_PNO(("Enter n_match_sets:%d n_ssids:%d \n", ++ request->n_match_sets, request->n_ssids)); ++ WL_PNO(("ssids:%d pno_time:%d pno_repeat:%d pno_freq:%d \n", ++ request->n_ssids, pno_time, pno_repeat, pno_freq_expo_max)); ++ ++ ++ if (!request || !request->n_ssids || !request->n_match_sets) { ++ WL_ERR(("Invalid sched scan req!! n_ssids:%d \n", request->n_ssids)); ++ return -EINVAL; ++ } ++ ++ memset(&ssids_local, 0, sizeof(ssids_local)); ++ ++ if (request->n_match_sets > 0) { ++ for (i = 0; i < request->n_match_sets; i++) { ++ ssid = &request->match_sets[i].ssid; ++ memcpy(ssids_local[i].SSID, ssid->ssid, ssid->ssid_len); ++ ssids_local[i].SSID_len = ssid->ssid_len; ++ WL_PNO((">>> PNO filter set for ssid (%s) \n", ssid->ssid)); ++ ssid_count++; ++ } ++ } ++ ++ if (request->n_ssids > 0) { ++ for (i = 0; i < request->n_ssids; i++) { ++ /* Active scan req for ssids */ ++ WL_PNO((">>> Active scan req for ssid (%s) \n", request->ssids[i].ssid)); ++ ++ /* match_set ssids is a supert set of n_ssid list, so we need ++ * not add these set seperately ++ */ ++ } ++ } ++ ++ if (ssid_count) { ++ if ((ret = dhd_dev_pno_set(dev, ssids_local, request->n_match_sets, ++ pno_time, pno_repeat, pno_freq_expo_max)) < 0) { ++ WL_ERR(("PNO setup failed!! ret=%d \n", ret)); ++ return -EINVAL; ++ } ++ ++ /* Enable the PNO */ ++ if (dhd_dev_pno_enable(dev, 1) < 0) { ++ WL_ERR(("PNO enable failed!! ret=%d \n", ret)); ++ return -EINVAL; ++ } ++ wl->sched_scan_req = request; ++ } else { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) ++{ ++ struct wl_priv *wl = wiphy_priv(wiphy); ++ ++ WL_DBG(("Enter \n")); ++ WL_PNO((">>> SCHED SCAN STOP\n")); ++ ++ if (dhd_dev_pno_enable(dev, 0) < 0) ++ WL_ERR(("PNO disable failed")); ++ ++ if (dhd_dev_pno_reset(dev) < 0) ++ WL_ERR(("PNO reset failed")); ++ ++ if (wl->scan_request && wl->sched_scan_running) { ++ WL_PNO((">>> Sched scan running. Aborting it..\n")); ++ wl_notify_escan_complete(wl, dev, true, true); ++ } ++ ++ wl->sched_scan_req = NULL; ++ wl->sched_scan_running = FALSE; ++ ++ return 0; ++} ++#endif /* WL_SCHED_SCAN */ ++ ++static struct cfg80211_ops wl_cfg80211_ops = { ++ .add_virtual_intf = wl_cfg80211_add_virtual_iface, ++ .del_virtual_intf = wl_cfg80211_del_virtual_iface, ++ .change_virtual_intf = wl_cfg80211_change_virtual_iface, ++ .scan = wl_cfg80211_scan, ++ .set_wiphy_params = wl_cfg80211_set_wiphy_params, ++ .join_ibss = wl_cfg80211_join_ibss, ++ .leave_ibss = wl_cfg80211_leave_ibss, ++ .get_station = wl_cfg80211_get_station, ++ .set_tx_power = wl_cfg80211_set_tx_power, ++ .get_tx_power = wl_cfg80211_get_tx_power, ++ .add_key = wl_cfg80211_add_key, ++ .del_key = wl_cfg80211_del_key, ++ .get_key = wl_cfg80211_get_key, ++ .set_default_key = wl_cfg80211_config_default_key, ++ .set_default_mgmt_key = wl_cfg80211_config_default_mgmt_key, ++ .set_power_mgmt = wl_cfg80211_set_power_mgmt, ++ .connect = wl_cfg80211_connect, ++ .disconnect = wl_cfg80211_disconnect, ++ .suspend = wl_cfg80211_suspend, ++ .resume = wl_cfg80211_resume, ++ .set_pmksa = wl_cfg80211_set_pmksa, ++ .del_pmksa = wl_cfg80211_del_pmksa, ++ .flush_pmksa = wl_cfg80211_flush_pmksa, ++ .remain_on_channel = wl_cfg80211_remain_on_channel, ++ .cancel_remain_on_channel = wl_cfg80211_cancel_remain_on_channel, ++ .mgmt_tx = wl_cfg80211_mgmt_tx, ++ .mgmt_frame_register = wl_cfg80211_mgmt_frame_register, ++ .change_bss = wl_cfg80211_change_bss, ++ .set_channel = wl_cfg80211_set_channel, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) ++ .set_beacon = wl_cfg80211_add_set_beacon, ++ .add_beacon = wl_cfg80211_add_set_beacon, ++#else ++ .change_beacon = wl_cfg80211_change_beacon, ++ .start_ap = wl_cfg80211_start_ap, ++ .stop_ap = wl_cfg80211_stop_ap, ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ ++#ifdef WL_SCHED_SCAN ++ .sched_scan_start = wl_cfg80211_sched_scan_start, ++ .sched_scan_stop = wl_cfg80211_sched_scan_stop, ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) */ ++ .del_station = wl_cfg80211_del_station, ++ .mgmt_tx_cancel_wait = wl_cfg80211_mgmt_tx_cancel_wait, ++}; ++ ++s32 wl_mode_to_nl80211_iftype(s32 mode) ++{ ++ s32 err = 0; ++ ++ switch (mode) { ++ case WL_MODE_BSS: ++ return NL80211_IFTYPE_STATION; ++ case WL_MODE_IBSS: ++ return NL80211_IFTYPE_ADHOC; ++ case WL_MODE_AP: ++ return NL80211_IFTYPE_AP; ++ default: ++ return NL80211_IFTYPE_UNSPECIFIED; ++ } ++ ++ return err; ++} ++ ++/* Kernel Network Support->Wireless->Regulatory rules database ++ options should be enabled and regulatory CRDA regdb table populated in Kernel ++ for proper country reg notification ++*/ ++#ifdef CONFIG_CFG80211_INTERNAL_REGDB ++static int ++wl_cfg80211_reg_notifier( ++ struct wiphy *wiphy, ++ struct regulatory_request *request) ++{ ++ struct wl_priv *wl = (struct wl_priv *)wiphy_priv(wiphy); ++ int ret = 0; ++ ++ if (!request || !wl) { ++ WL_ERR(("Invalid arg\n")); ++ return -EINVAL; ++ } ++ ++ WL_DBG(("ccode: %c%c Initiator: %d\n", ++ request->alpha2[0], request->alpha2[1], request->initiator)); ++ ++ /* We support only REGDOM_SET_BY_USER and by ++ NL80211_REGDOM_SET_BY_COUNTRY_IE (11d) right now ++ */ ++ if ((request->initiator != NL80211_REGDOM_SET_BY_USER) && ++ (request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE)) { ++ WL_ERR(("reg_notifier for intiator:%d not supported : set default\n", ++ request->initiator)); ++ /* in case of no supported country by regdb ++ lets driver setup platform default Locale ++ */ ++ } ++ ++ WL_ERR(("Set country code %c%c from %s\n", ++ request->alpha2[0], request->alpha2[1], ++ ((request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) ? " 11d AP" : "User"))); ++ ++ if ((ret = wldev_set_country(wl_to_prmry_ndev(wl), request->alpha2, ++ false, (request->initiator == NL80211_REGDOM_SET_BY_USER ? true : false))) < 0) { ++ WL_ERR(("set country Failed :%d\n", ret)); ++ } ++ ++ return ret; ++} ++#endif /* CONFIG_CFG80211_INTERNAL_REGDB */ ++ ++static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev) ++{ ++ s32 err = 0; ++ wdev->wiphy = ++ wiphy_new(&wl_cfg80211_ops, sizeof(struct wl_priv)); ++ if (unlikely(!wdev->wiphy)) { ++ WL_ERR(("Couldn not allocate wiphy device\n")); ++ err = -ENOMEM; ++ return err; ++ } ++ set_wiphy_dev(wdev->wiphy, sdiofunc_dev); ++ wdev->wiphy->max_scan_ie_len = WL_SCAN_IE_LEN_MAX; ++ /* Report how many SSIDs Driver can support per Scan request */ ++ wdev->wiphy->max_scan_ssids = WL_SCAN_PARAMS_SSID_MAX; ++ wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; ++#ifdef WL_SCHED_SCAN ++ wdev->wiphy->max_sched_scan_ssids = MAX_PFN_LIST_COUNT; ++ wdev->wiphy->max_match_sets = MAX_PFN_LIST_COUNT; ++ wdev->wiphy->max_sched_scan_ie_len = WL_SCAN_IE_LEN_MAX; ++ wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; ++#endif /* WL_SCHED_SCAN */ ++ wdev->wiphy->interface_modes = ++ BIT(NL80211_IFTYPE_STATION) ++#if !(defined(WLP2P) && defined(WL_ENABLE_P2P_IF)) ++ | BIT(NL80211_IFTYPE_MONITOR) ++#endif ++ | BIT(NL80211_IFTYPE_AP); ++ ++ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; ++ ++ wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; ++ wdev->wiphy->cipher_suites = __wl_cipher_suites; ++ wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); ++ wdev->wiphy->max_remain_on_channel_duration = 5000; ++ wdev->wiphy->mgmt_stypes = wl_cfg80211_default_mgmt_stypes; ++#ifndef WL_POWERSAVE_DISABLED ++ wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; ++#else ++ wdev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; ++#endif /* !WL_POWERSAVE_DISABLED */ ++ wdev->wiphy->flags |= WIPHY_FLAG_NETNS_OK | ++ WIPHY_FLAG_4ADDR_AP | ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39) ++ WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS | ++#endif ++ WIPHY_FLAG_4ADDR_STATION; ++ /* If driver advertises FW_ROAM, the supplicant wouldn't ++ * send the BSSID & Freq in the connect command allowing the ++ * the driver to choose the AP to connect to. But unless we ++ * support ROAM_CACHE in firware this will delay the ASSOC as ++ * as the FW need to do a full scan before attempting to connect ++ * So that feature will just increase assoc. The better approach ++ * to let Supplicant to provide channel info and FW letter may roam ++ * if needed so DON'T advertise that featur eto Supplicant. ++ */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) ++ /* wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; */ ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) ++ wdev->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | ++ WIPHY_FLAG_OFFCHAN_TX; ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) ++ /* From 3.4 kernel ownards AP_SME flag can be advertised ++ * to remove the patch from supplicant ++ */ ++ wdev->wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME; ++#endif ++ ++#ifdef CONFIG_CFG80211_INTERNAL_REGDB ++ wdev->wiphy->reg_notifier = wl_cfg80211_reg_notifier; ++#endif /* CONFIG_CFG80211_INTERNAL_REGDB */ ++ ++ WL_DBG(("Registering custom regulatory)\n")); ++ wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; ++ wiphy_apply_custom_regulatory(wdev->wiphy, &brcm_regdom); ++ /* Now we can register wiphy with cfg80211 module */ ++ err = wiphy_register(wdev->wiphy); ++ if (unlikely(err < 0)) { ++ WL_ERR(("Couldn not register wiphy device (%d)\n", err)); ++ wiphy_free(wdev->wiphy); ++ } ++ return err; ++} ++ ++static void wl_free_wdev(struct wl_priv *wl) ++{ ++ struct wireless_dev *wdev = wl->wdev; ++ struct wiphy *wiphy; ++ if (!wdev) { ++ WL_ERR(("wdev is invalid\n")); ++ return; ++ } ++ wiphy = wdev->wiphy; ++ wiphy_unregister(wdev->wiphy); ++ wdev->wiphy->dev.parent = NULL; ++ ++ wl_delete_all_netinfo(wl); ++ wiphy_free(wiphy); ++ /* PLEASE do NOT call any function after wiphy_free, the driver's private structure "wl", ++ * which is the private part of wiphy, has been freed in wiphy_free !!!!!!!!!!! ++ */ ++} ++ ++static s32 wl_inform_bss(struct wl_priv *wl) ++{ ++ struct wl_scan_results *bss_list; ++ struct wl_bss_info *bi = NULL; /* must be initialized */ ++ s32 err = 0; ++ s32 i; ++ ++ bss_list = wl->bss_list; ++ WL_DBG(("scanned AP count (%d)\n", bss_list->count)); ++ bi = next_bss(bss_list, bi); ++ for_each_bss(bss_list, bi, i) { ++ err = wl_inform_single_bss(wl, bi, 0); ++ if (unlikely(err)) ++ break; ++ } ++ return err; ++} ++ ++static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi, u8 is_roam_done) ++{ ++ struct wiphy *wiphy = wl_to_wiphy(wl); ++ struct ieee80211_mgmt *mgmt; ++ struct ieee80211_channel *channel; ++ struct ieee80211_supported_band *band; ++ struct wl_cfg80211_bss_info *notif_bss_info; ++ struct wl_scan_req *sr = wl_to_sr(wl); ++ struct beacon_proberesp *beacon_proberesp; ++ struct cfg80211_bss *cbss = NULL; ++ s32 mgmt_type; ++ s32 signal; ++ u32 freq; ++ s32 err = 0; ++ gfp_t aflags; ++ u8 *ie_offset = NULL; ++ ++ if (unlikely(dtoh32(bi->length) > WL_BSS_INFO_MAX)) { ++ WL_DBG(("Beacon is larger than buffer. Discarding\n")); ++ return err; ++ } ++ aflags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; ++ notif_bss_info = kzalloc(sizeof(*notif_bss_info) + sizeof(*mgmt) ++ - sizeof(u8) + WL_BSS_INFO_MAX, aflags); ++ if (unlikely(!notif_bss_info)) { ++ WL_ERR(("notif_bss_info alloc failed\n")); ++ return -ENOMEM; ++ } ++ mgmt = (struct ieee80211_mgmt *)notif_bss_info->frame_buf; ++ notif_bss_info->channel = ++ bi->ctl_ch ? bi->ctl_ch : CHSPEC_CHANNEL(wl_chspec_driver_to_host(bi->chanspec)); ++ ++ if (notif_bss_info->channel <= CH_MAX_2G_CHANNEL) ++ band = wiphy->bands[IEEE80211_BAND_2GHZ]; ++ else ++ band = wiphy->bands[IEEE80211_BAND_5GHZ]; ++ if (!band) { ++ WL_ERR(("No valid band")); ++ kfree(notif_bss_info); ++ return -EINVAL; ++ } ++ notif_bss_info->rssi = dtoh16(bi->RSSI) + RSSI_OFFSET; ++ memcpy(mgmt->bssid, &bi->BSSID, ETHER_ADDR_LEN); ++ mgmt_type = wl->active_scan ? ++ IEEE80211_STYPE_PROBE_RESP : IEEE80211_STYPE_BEACON; ++ if (!memcmp(bi->SSID, sr->ssid.SSID, bi->SSID_len)) { ++ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | mgmt_type); ++ } ++ beacon_proberesp = wl->active_scan ? ++ (struct beacon_proberesp *)&mgmt->u.probe_resp : ++ (struct beacon_proberesp *)&mgmt->u.beacon; ++ beacon_proberesp->timestamp = 0; ++ beacon_proberesp->beacon_int = cpu_to_le16(bi->beacon_period); ++ beacon_proberesp->capab_info = cpu_to_le16(bi->capability); ++ wl_rst_ie(wl); ++ ++ ie_offset = ((u8 *) bi) + bi->ie_offset; ++ ++ if (is_roam_done && ((int)(*(ie_offset)) == WLAN_EID_SSID && ++ ((int)(*(ie_offset+1)) == 0 || (int)(*(ie_offset+2)) == 0))) { ++ u8 *ie_new_offset = NULL; ++ uint8 ie_new_length; ++ ++ WL_ERR(("WAR trace: Changing the SSID Info, from beacon %d\n", ++ bi->flags & WL_BSS_FLAGS_FROM_BEACON)); ++ ++ ie_new_offset = (u8 *)kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); ++ if (ie_new_offset) { ++ *(ie_new_offset) = WLAN_EID_SSID; ++ *(ie_new_offset+1) = bi->SSID_len; ++ memcpy(ie_new_offset+2, bi->SSID, bi->SSID_len); ++ ie_new_length = bi->ie_length - *(ie_offset+1) + bi->SSID_len; ++ ++ /* Copy the remaining IE apart from SSID IE from bi */ ++ memcpy(ie_new_offset+2 + bi->SSID_len, ++ ie_offset+2 + *(ie_offset+1), ++ bi->ie_length - 2 - *(ie_offset+1)); ++ wl_mrg_ie(wl, ie_new_offset, ie_new_length); ++ kfree(ie_new_offset); ++ } else { ++ wl_mrg_ie(wl, ((u8 *) bi) + bi->ie_offset, bi->ie_length); ++ } ++ } else { ++ wl_mrg_ie(wl, ((u8 *) bi) + bi->ie_offset, bi->ie_length); ++ } ++ ++ wl_cp_ie(wl, beacon_proberesp->variable, WL_BSS_INFO_MAX - ++ offsetof(struct wl_cfg80211_bss_info, frame_buf)); ++ notif_bss_info->frame_len = offsetof(struct ieee80211_mgmt, ++ u.beacon.variable) + wl_get_ielen(wl); ++#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) ++ freq = ieee80211_channel_to_frequency(notif_bss_info->channel); ++ (void)band->band; ++#else ++ freq = ieee80211_channel_to_frequency(notif_bss_info->channel, band->band); ++#endif ++ if (freq == 0) { ++ WL_ERR(("Invalid channel, fail to chcnage channel to freq\n")); ++ kfree(notif_bss_info); ++ return -EINVAL; ++ } ++ channel = ieee80211_get_channel(wiphy, freq); ++ if (unlikely(!channel)) { ++ WL_ERR(("ieee80211_get_channel error\n")); ++ kfree(notif_bss_info); ++ return -EINVAL; ++ } ++ WL_DBG(("SSID : \"%s\", rssi %d, channel %d, capability : 0x04%x, bssid %pM" ++ "mgmt_type %d frame_len %d\n", bi->SSID, ++ notif_bss_info->rssi, notif_bss_info->channel, ++ mgmt->u.beacon.capab_info, &bi->BSSID, mgmt_type, ++ notif_bss_info->frame_len)); ++ ++ signal = notif_bss_info->rssi * 100; ++ if (!mgmt->u.probe_resp.timestamp) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) ++ struct timespec ts; ++ get_monotonic_boottime(&ts); ++ mgmt->u.probe_resp.timestamp = ((u64)ts.tv_sec*1000000) ++ + ts.tv_nsec / 1000; ++#else ++ struct timeval tv; ++ do_gettimeofday(&tv); ++ mgmt->u.probe_resp.timestamp = ((u64)tv.tv_sec*1000000) ++ + tv.tv_usec; ++#endif ++ } ++ ++ cbss = cfg80211_inform_bss_frame(wiphy, channel, mgmt, ++ le16_to_cpu(notif_bss_info->frame_len), signal, aflags); ++ if (unlikely(!cbss)) { ++ WL_ERR(("cfg80211_inform_bss_frame error\n")); ++ kfree(notif_bss_info); ++ return -EINVAL; ++ } ++ ++ cfg80211_put_bss(cbss); ++ kfree(notif_bss_info); ++ return err; ++} ++ ++static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net_device *ndev) ++{ ++ u32 event = ntoh32(e->event_type); ++ u32 status = ntoh32(e->status); ++ u16 flags = ntoh16(e->flags); ++ ++ WL_DBG(("event %d, status %d flags %x\n", event, status, flags)); ++ if (event == WLC_E_SET_SSID) { ++ if (status == WLC_E_STATUS_SUCCESS) { ++ if (!wl_is_ibssmode(wl, ndev)) ++ return true; ++ } ++ } else if (event == WLC_E_LINK) { ++ if (flags & WLC_EVENT_MSG_LINK) ++ return true; ++ } ++ ++ WL_DBG(("wl_is_linkup false\n")); ++ return false; ++} ++ ++static bool wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e) ++{ ++ u32 event = ntoh32(e->event_type); ++ u16 flags = ntoh16(e->flags); ++ ++ if (event == WLC_E_DEAUTH_IND || ++ event == WLC_E_DISASSOC_IND || ++ event == WLC_E_DISASSOC || ++ event == WLC_E_DEAUTH) { ++#if (WL_DBG_LEVEL > 0) ++ WL_ERR(("Link down Reason : WLC_E_%s\n", wl_dbg_estr[event])); ++#endif /* (WL_DBG_LEVEL > 0) */ ++ return true; ++ } else if (event == WLC_E_LINK) { ++ if (!(flags & WLC_EVENT_MSG_LINK)) { ++#if (WL_DBG_LEVEL > 0) ++ WL_ERR(("Link down Reason : WLC_E_%s\n", wl_dbg_estr[event])); ++#endif /* (WL_DBG_LEVEL > 0) */ ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++static bool wl_is_nonetwork(struct wl_priv *wl, const wl_event_msg_t *e) ++{ ++ u32 event = ntoh32(e->event_type); ++ u32 status = ntoh32(e->status); ++ ++ if (event == WLC_E_LINK && status == WLC_E_STATUS_NO_NETWORKS) ++ return true; ++ if (event == WLC_E_SET_SSID && status != WLC_E_STATUS_SUCCESS) ++ return true; ++ ++ return false; ++} ++ ++/* The mainline kernel >= 3.2.0 has support for indicating new/del station ++ * to AP/P2P GO via events. If this change is backported to kernel for which ++ * this driver is being built, then define WL_CFG80211_STA_EVENT. You ++ * should use this new/del sta event mechanism for BRCM supplicant >= 22. ++ */ ++static s32 ++wl_notify_connect_status_ap(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data) ++{ ++ s32 err = 0; ++ u32 event = ntoh32(e->event_type); ++ u32 reason = ntoh32(e->reason); ++ u32 len = ntoh32(e->datalen); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !defined(WL_CFG80211_STA_EVENT) ++ bool isfree = false; ++ u8 *mgmt_frame; ++ u8 bsscfgidx = e->bsscfgidx; ++ s32 freq; ++ s32 channel; ++ u8 *body = NULL; ++ u16 fc = 0; ++ ++ struct ieee80211_supported_band *band; ++ struct ether_addr da; ++ struct ether_addr bssid; ++ struct wiphy *wiphy = wl_to_wiphy(wl); ++ channel_info_t ci; ++#else ++ struct station_info sinfo; ++#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !WL_CFG80211_STA_EVENT */ ++ ++ WL_DBG(("event %d status %d reason %d\n", event, ntoh32(e->status), reason)); ++ /* if link down, bsscfg is disabled. */ ++ if (event == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS && ++ wl_get_p2p_status(wl, IF_DELETING) && (ndev != wl_to_prmry_ndev(wl))) { ++ wl_add_remove_eventmsg(ndev, WLC_E_PROBREQ_MSG, false); ++ WL_INFO(("AP mode link down !! \n")); ++ complete(&wl->iface_disable); ++ return 0; ++ } ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !defined(WL_CFG80211_STA_EVENT) ++ WL_DBG(("Enter \n")); ++ if (!len && (event == WLC_E_DEAUTH)) { ++ len = 2; /* reason code field */ ++ data = &reason; ++ } ++ if (len) { ++ body = kzalloc(len, GFP_KERNEL); ++ ++ if (body == NULL) { ++ WL_ERR(("wl_notify_connect_status: Failed to allocate body\n")); ++ return WL_INVALID; ++ } ++ } ++ memset(&bssid, 0, ETHER_ADDR_LEN); ++ WL_DBG(("Enter event %d ndev %p\n", event, ndev)); ++ if (wl_get_mode_by_netdev(wl, ndev) == WL_INVALID) { ++ kfree(body); ++ return WL_INVALID; ++ } ++ if (len) ++ memcpy(body, data, len); ++ ++ wldev_iovar_getbuf_bsscfg(ndev, "cur_etheraddr", ++ NULL, 0, wl->ioctl_buf, WLC_IOCTL_SMLEN, bsscfgidx, &wl->ioctl_buf_sync); ++ memcpy(da.octet, wl->ioctl_buf, ETHER_ADDR_LEN); ++ err = wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); ++ switch (event) { ++ case WLC_E_ASSOC_IND: ++ fc = FC_ASSOC_REQ; ++ break; ++ case WLC_E_REASSOC_IND: ++ fc = FC_REASSOC_REQ; ++ break; ++ case WLC_E_DISASSOC_IND: ++ fc = FC_DISASSOC; ++ break; ++ case WLC_E_DEAUTH_IND: ++ fc = FC_DISASSOC; ++ break; ++ case WLC_E_DEAUTH: ++ fc = FC_DISASSOC; ++ break; ++ default: ++ fc = 0; ++ goto exit; ++ } ++ if ((err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &ci, sizeof(ci), false))) { ++ kfree(body); ++ return err; ++ } ++ ++ channel = dtoh32(ci.hw_channel); ++ if (channel <= CH_MAX_2G_CHANNEL) ++ band = wiphy->bands[IEEE80211_BAND_2GHZ]; ++ else ++ band = wiphy->bands[IEEE80211_BAND_5GHZ]; ++ if (!band) { ++ WL_ERR(("No valid band")); ++ if (body) ++ kfree(body); ++ return -EINVAL; ++ } ++#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) ++ freq = ieee80211_channel_to_frequency(channel); ++ (void)band->band; ++#else ++ freq = ieee80211_channel_to_frequency(channel, band->band); ++#endif ++ ++ err = wl_frame_get_mgmt(fc, &da, &e->addr, &bssid, ++ &mgmt_frame, &len, body); ++ if (err < 0) ++ goto exit; ++ isfree = true; ++ ++ if (event == WLC_E_ASSOC_IND && reason == DOT11_SC_SUCCESS) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) ++ cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, GFP_ATOMIC); ++#else ++ cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) */ ++ } else if (event == WLC_E_DISASSOC_IND) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) ++ cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, GFP_ATOMIC); ++#else ++ cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) */ ++ } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) ++ cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, GFP_ATOMIC); ++#else ++ cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) */ ++ } ++ ++exit: ++ if (isfree) ++ kfree(mgmt_frame); ++ if (body) ++ kfree(body); ++ return err; ++#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) && !WL_CFG80211_STA_EVENT */ ++ sinfo.filled = 0; ++ if (((event == WLC_E_ASSOC_IND) || (event == WLC_E_REASSOC_IND)) && ++ reason == DOT11_SC_SUCCESS) { ++ sinfo.filled = STATION_INFO_ASSOC_REQ_IES; ++ if (!data) { ++ WL_ERR(("No IEs present in ASSOC/REASSOC_IND")); ++ return -EINVAL; ++ } ++ sinfo.assoc_req_ies = data; ++ sinfo.assoc_req_ies_len = len; ++ cfg80211_new_sta(ndev, e->addr.octet, &sinfo, GFP_ATOMIC); ++ } else if (event == WLC_E_DISASSOC_IND) { ++ cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); ++ } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { ++ cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); ++ } ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) && !WL_CFG80211_STA_EVENT */ ++ return err; ++} ++ ++static s32 ++wl_get_auth_assoc_status(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e) ++{ ++ u32 reason = ntoh32(e->reason); ++ u32 event = ntoh32(e->event_type); ++ struct wl_security *sec = wl_read_prof(wl, ndev, WL_PROF_SEC); ++ WL_DBG(("event type : %d, reason : %d\n", event, reason)); ++ if (sec) { ++ switch (event) { ++ case WLC_E_ASSOC: ++ case WLC_E_AUTH: ++ sec->auth_assoc_res_status = reason; ++ default: ++ break; ++ } ++ } else ++ WL_ERR(("sec is NULL\n")); ++ return 0; ++} ++ ++static s32 ++wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data) ++{ ++ bool act; ++ s32 err = 0; ++ u32 event = ntoh32(e->event_type); ++ ++ if (wl_get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { ++ wl_notify_connect_status_ap(wl, ndev, e, data); ++ } else { ++ WL_DBG(("wl_notify_connect_status : event %d status : %d ndev %p\n", ++ ntoh32(e->event_type), ntoh32(e->status), ndev)); ++ if (event == WLC_E_ASSOC || event == WLC_E_AUTH) { ++ wl_get_auth_assoc_status(wl, ndev, e); ++ return err; ++ } ++ if (wl_is_linkup(wl, e, ndev)) { ++ wl_link_up(wl); ++ act = true; ++ if (wl_is_ibssmode(wl, ndev)) { ++ printk("cfg80211_ibss_joined\n"); ++ cfg80211_ibss_joined(ndev, (s8 *)&e->addr, ++ GFP_KERNEL); ++ WL_DBG(("joined in IBSS network\n")); ++ } else { ++ if (!wl_get_drv_status(wl, DISCONNECTING, ndev)) { ++ printk("wl_bss_connect_done succeeded with " MACDBG "\n", ++ MAC2STRDBG((u8*)(&e->addr))); ++ wl_bss_connect_done(wl, ndev, e, data, true); ++ WL_DBG(("joined in BSS network \"%s\"\n", ++ ((struct wlc_ssid *) ++ wl_read_prof(wl, ndev, WL_PROF_SSID))->SSID)); ++ } ++ } ++ wl_update_prof(wl, ndev, e, &act, WL_PROF_ACT); ++ wl_update_prof(wl, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); ++ ++ } else if (wl_is_linkdown(wl, e)) { ++ if (wl->scan_request) { ++ if (wl->escan_on) { ++ wl_notify_escan_complete(wl, ndev, true, true); ++ } else { ++ del_timer_sync(&wl->scan_timeout); ++ wl_iscan_aborted(wl); ++ } ++ } ++ if (wl_get_drv_status(wl, CONNECTED, ndev)) { ++ scb_val_t scbval; ++ u8 *curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); ++ s32 reason = 0; ++ if (event == WLC_E_DEAUTH_IND || event == WLC_E_DISASSOC_IND) ++ reason = ntoh32(e->reason); ++ /* WLAN_REASON_UNSPECIFIED is used for hang up event in Android */ ++ reason = (reason == WLAN_REASON_UNSPECIFIED)? 0 : reason; ++ ++ printk("link down if %s may call cfg80211_disconnected. " ++ "event : %d, reason=%d from " MACDBG "\n", ++ ndev->name, event, ntoh32(e->reason), ++ MAC2STRDBG((u8*)(&e->addr))); ++ if (memcmp(curbssid, &e->addr, ETHER_ADDR_LEN) != 0) { ++ WL_ERR(("BSSID of event is not the connected BSSID" ++ "(ignore it) cur: " MACDBG " event: " MACDBG"\n", ++ MAC2STRDBG(curbssid), MAC2STRDBG((u8*)(&e->addr)))); ++ return 0; ++ } ++ wl_clr_drv_status(wl, CONNECTED, ndev); ++ if (! wl_get_drv_status(wl, DISCONNECTING, ndev)) { ++ /* To make sure disconnect, explictly send dissassoc ++ * for BSSID 00:00:00:00:00:00 issue ++ */ ++ scbval.val = WLAN_REASON_DEAUTH_LEAVING; ++ ++ memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); ++ scbval.val = htod32(scbval.val); ++ err = wldev_ioctl(ndev, WLC_DISASSOC, &scbval, ++ sizeof(scb_val_t), true); ++ if (err < 0) { ++ WL_ERR(("WLC_DISASSOC error %d\n", err)); ++ err = 0; ++ } ++ cfg80211_disconnected(ndev, reason, NULL, 0, GFP_KERNEL); ++ wl_link_down(wl); ++ wl_init_prof(wl, ndev); ++ } ++ } ++ else if (wl_get_drv_status(wl, CONNECTING, ndev)) { ++ printk("link down, during connecting\n"); ++#ifdef ESCAN_RESULT_PATCH ++ if ((memcmp(connect_req_bssid, broad_bssid, ETHER_ADDR_LEN) == 0) || ++ (memcmp(&e->addr, broad_bssid, ETHER_ADDR_LEN) == 0) || ++ (memcmp(&e->addr, connect_req_bssid, ETHER_ADDR_LEN) == 0)) ++ /* In case this event comes while associating another AP */ ++#endif /* ESCAN_RESULT_PATCH */ ++ wl_bss_connect_done(wl, ndev, e, data, false); ++ } ++ wl_clr_drv_status(wl, DISCONNECTING, ndev); ++ ++ /* if link down, bsscfg is diabled */ ++ if (ndev != wl_to_prmry_ndev(wl)) ++ complete(&wl->iface_disable); ++ ++ } else if (wl_is_nonetwork(wl, e)) { ++ printk("connect failed event=%d e->status %d e->reason %d \n", ++ event, (int)ntoh32(e->status), (int)ntoh32(e->reason)); ++ /* Clean up any pending scan request */ ++ if (wl->scan_request) { ++ if (wl->escan_on) { ++ wl_notify_escan_complete(wl, ndev, true, true); ++ } else { ++ del_timer_sync(&wl->scan_timeout); ++ wl_iscan_aborted(wl); ++ } ++ } ++ if (wl_get_drv_status(wl, CONNECTING, ndev)) ++ wl_bss_connect_done(wl, ndev, e, data, false); ++ } else { ++ printk("%s nothing\n", __FUNCTION__); ++ } ++ } ++ return err; ++} ++ ++static s32 ++wl_notify_roaming_status(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data) ++{ ++ bool act; ++ s32 err = 0; ++ u32 event = be32_to_cpu(e->event_type); ++ u32 status = be32_to_cpu(e->status); ++ WL_DBG(("Enter \n")); ++ if (event == WLC_E_ROAM && status == WLC_E_STATUS_SUCCESS) { ++ if (wl_get_drv_status(wl, CONNECTED, ndev)) ++ wl_bss_roaming_done(wl, ndev, e, data); ++ else ++ wl_bss_connect_done(wl, ndev, e, data, true); ++ act = true; ++ wl_update_prof(wl, ndev, e, &act, WL_PROF_ACT); ++ wl_update_prof(wl, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); ++ } ++ return err; ++} ++ ++static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) ++{ ++ wl_assoc_info_t assoc_info; ++ struct wl_connect_info *conn_info = wl_to_conn(wl); ++ s32 err = 0; ++ ++ WL_DBG(("Enter \n")); ++ err = wldev_iovar_getbuf(ndev, "assoc_info", NULL, 0, wl->extra_buf, ++ WL_ASSOC_INFO_MAX, NULL); ++ if (unlikely(err)) { ++ WL_ERR(("could not get assoc info (%d)\n", err)); ++ return err; ++ } ++ memcpy(&assoc_info, wl->extra_buf, sizeof(wl_assoc_info_t)); ++ assoc_info.req_len = htod32(assoc_info.req_len); ++ assoc_info.resp_len = htod32(assoc_info.resp_len); ++ assoc_info.flags = htod32(assoc_info.flags); ++ if (conn_info->req_ie_len) { ++ conn_info->req_ie_len = 0; ++ bzero(conn_info->req_ie, sizeof(conn_info->req_ie)); ++ } ++ if (conn_info->resp_ie_len) { ++ conn_info->resp_ie_len = 0; ++ bzero(conn_info->resp_ie, sizeof(conn_info->resp_ie)); ++ } ++ if (assoc_info.req_len) { ++ err = wldev_iovar_getbuf(ndev, "assoc_req_ies", NULL, 0, wl->extra_buf, ++ WL_ASSOC_INFO_MAX, NULL); ++ if (unlikely(err)) { ++ WL_ERR(("could not get assoc req (%d)\n", err)); ++ return err; ++ } ++ conn_info->req_ie_len = assoc_info.req_len - sizeof(struct dot11_assoc_req); ++ if (assoc_info.flags & WLC_ASSOC_REQ_IS_REASSOC) { ++ conn_info->req_ie_len -= ETHER_ADDR_LEN; ++ } ++ if (conn_info->req_ie_len <= MAX_REQ_LINE) ++ memcpy(conn_info->req_ie, wl->extra_buf, conn_info->req_ie_len); ++ else { ++ WL_ERR(("%s IE size %d above max %d size \n", ++ __FUNCTION__, conn_info->req_ie_len, MAX_REQ_LINE)); ++ return err; ++ } ++ } else { ++ conn_info->req_ie_len = 0; ++ } ++ if (assoc_info.resp_len) { ++ err = wldev_iovar_getbuf(ndev, "assoc_resp_ies", NULL, 0, wl->extra_buf, ++ WL_ASSOC_INFO_MAX, NULL); ++ if (unlikely(err)) { ++ WL_ERR(("could not get assoc resp (%d)\n", err)); ++ return err; ++ } ++ conn_info->resp_ie_len = assoc_info.resp_len -sizeof(struct dot11_assoc_resp); ++ if (conn_info->resp_ie_len <= MAX_REQ_LINE) ++ memcpy(conn_info->resp_ie, wl->extra_buf, conn_info->resp_ie_len); ++ else { ++ WL_ERR(("%s IE size %d above max %d size \n", ++ __FUNCTION__, conn_info->resp_ie_len, MAX_REQ_LINE)); ++ return err; ++ } ++ } else { ++ conn_info->resp_ie_len = 0; ++ } ++ WL_DBG(("req len (%d) resp len (%d)\n", conn_info->req_ie_len, ++ conn_info->resp_ie_len)); ++ ++ return err; ++} ++ ++static void wl_ch_to_chanspec(int ch, struct wl_join_params *join_params, ++ size_t *join_params_size) ++{ ++ chanspec_t chanspec = 0; ++ if (ch != 0) { ++ join_params->params.chanspec_num = 1; ++ join_params->params.chanspec_list[0] = ch; ++ ++ if (join_params->params.chanspec_list[0] <= CH_MAX_2G_CHANNEL) ++ chanspec |= WL_CHANSPEC_BAND_2G; ++ else ++ chanspec |= WL_CHANSPEC_BAND_5G; ++ ++ chanspec |= WL_CHANSPEC_BW_20; ++ chanspec |= WL_CHANSPEC_CTL_SB_NONE; ++ ++ *join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE + ++ join_params->params.chanspec_num * sizeof(chanspec_t); ++ ++ join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK; ++ join_params->params.chanspec_list[0] |= chanspec; ++ join_params->params.chanspec_list[0] = ++ wl_chspec_host_to_driver(join_params->params.chanspec_list[0]); ++ ++ join_params->params.chanspec_num = ++ htod32(join_params->params.chanspec_num); ++ WL_DBG(("join_params->params.chanspec_list[0]= %X, %d channels\n", ++ join_params->params.chanspec_list[0], ++ join_params->params.chanspec_num)); ++ } ++} ++ ++static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev, u8 is_roam_done) ++{ ++ struct cfg80211_bss *bss; ++ struct wl_bss_info *bi; ++ struct wlc_ssid *ssid; ++ struct bcm_tlv *tim; ++ s32 beacon_interval; ++ s32 dtim_period; ++ size_t ie_len; ++ u8 *ie; ++ u8 *ssidie; ++ u8 *curbssid; ++ s32 err = 0; ++ struct wiphy *wiphy; ++ ++ wiphy = wl_to_wiphy(wl); ++ ++ if (wl_is_ibssmode(wl, ndev)) ++ return err; ++ ++ ssid = (struct wlc_ssid *)wl_read_prof(wl, ndev, WL_PROF_SSID); ++ curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); ++ bss = cfg80211_get_bss(wiphy, NULL, curbssid, ++ ssid->SSID, ssid->SSID_len, WLAN_CAPABILITY_ESS, ++ WLAN_CAPABILITY_ESS); ++ ++ mutex_lock(&wl->usr_sync); ++ if (!bss) { ++ WL_DBG(("Could not find the AP\n")); ++ *(u32 *) wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); ++ err = wldev_ioctl(ndev, WLC_GET_BSS_INFO, ++ wl->extra_buf, WL_EXTRA_BUF_MAX, false); ++ if (unlikely(err)) { ++ WL_ERR(("Could not get bss info %d\n", err)); ++ goto update_bss_info_out; ++ } ++ bi = (struct wl_bss_info *)(wl->extra_buf + 4); ++ if (memcmp(bi->BSSID.octet, curbssid, ETHER_ADDR_LEN)) { ++ err = -EIO; ++ goto update_bss_info_out; ++ } ++ ++ ie = ((u8 *)bi) + bi->ie_offset; ++ ie_len = bi->ie_length; ++ ssidie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ie, ie_len); ++ if (ssidie && ssidie[1] == bi->SSID_len && !ssidie[2] && bi->SSID[0]) ++ memcpy(ssidie + 2, bi->SSID, bi->SSID_len); ++ ++ err = wl_inform_single_bss(wl, bi, is_roam_done); ++ if (unlikely(err)) ++ goto update_bss_info_out; ++ ++ ie = ((u8 *)bi) + bi->ie_offset; ++ ie_len = bi->ie_length; ++ beacon_interval = cpu_to_le16(bi->beacon_period); ++ } else { ++ WL_DBG(("Found the AP in the list - BSSID %pM\n", bss->bssid)); ++ ie = bss->information_elements; ++ ie_len = bss->len_information_elements; ++ beacon_interval = bss->beacon_interval; ++ cfg80211_put_bss(bss); ++ } ++ ++ tim = bcm_parse_tlvs(ie, ie_len, WLAN_EID_TIM); ++ if (tim) { ++ dtim_period = tim->data[1]; ++ } else { ++ /* ++ * active scan was done so we could not get dtim ++ * information out of probe response. ++ * so we speficially query dtim information. ++ */ ++ err = wldev_ioctl(ndev, WLC_GET_DTIMPRD, ++ &dtim_period, sizeof(dtim_period), false); ++ if (unlikely(err)) { ++ WL_ERR(("WLC_GET_DTIMPRD error (%d)\n", err)); ++ goto update_bss_info_out; ++ } ++ } ++ ++ wl_update_prof(wl, ndev, NULL, &beacon_interval, WL_PROF_BEACONINT); ++ wl_update_prof(wl, ndev, NULL, &dtim_period, WL_PROF_DTIMPERIOD); ++ ++update_bss_info_out: ++ mutex_unlock(&wl->usr_sync); ++ return err; ++} ++ ++static s32 ++wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data) ++{ ++ struct wl_connect_info *conn_info = wl_to_conn(wl); ++ s32 err = 0; ++ u8 *curbssid; ++ ++ wl_get_assoc_ies(wl, ndev); ++ wl_update_prof(wl, ndev, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); ++ curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); ++ wl_update_bss_info(wl, ndev, 1); ++ wl_update_pmklist(ndev, wl->pmk_list, err); ++ printk("wl_bss_roaming_done succeeded to " MACDBG "\n", ++ MAC2STRDBG((u8*)(&e->addr))); ++ ++ cfg80211_roamed(ndev, ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) ++ NULL, /* struct cfg80211_bss *bss */ ++#elif LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) ++ NULL, ++#endif ++ curbssid, ++ conn_info->req_ie, conn_info->req_ie_len, ++ conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); ++ WL_DBG(("Report roaming result\n")); ++ ++ wl_set_drv_status(wl, CONNECTED, ndev); ++ ++ return err; ++} ++ ++static s32 ++wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data, bool completed) ++{ ++ struct wl_connect_info *conn_info = wl_to_conn(wl); ++ struct wl_security *sec = wl_read_prof(wl, ndev, WL_PROF_SEC); ++ s32 err = 0; ++ u8 *curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); ++ if (!sec) { ++ WL_ERR(("sec is NULL\n")); ++ return -ENODEV; ++ } ++ WL_DBG((" enter\n")); ++#ifdef ESCAN_RESULT_PATCH ++ if (wl_get_drv_status(wl, CONNECTED, ndev)) { ++ if (memcmp(curbssid, connect_req_bssid, ETHER_ADDR_LEN) == 0) { ++ WL_DBG((" Connected event of connected device e=%d s=%d, ignore it\n", ++ ntoh32(e->event_type), ntoh32(e->status))); ++ return err; ++ } ++ } ++ if (memcmp(curbssid, broad_bssid, ETHER_ADDR_LEN) == 0 && ++ memcmp(broad_bssid, connect_req_bssid, ETHER_ADDR_LEN) != 0) { ++ WL_DBG(("copy bssid\n")); ++ memcpy(curbssid, connect_req_bssid, ETHER_ADDR_LEN); ++ } ++ ++#else ++ if (wl->scan_request) { ++ wl_notify_escan_complete(wl, ndev, true, true); ++ } ++#endif /* ESCAN_RESULT_PATCH */ ++ if (wl_get_drv_status(wl, CONNECTING, ndev)) { ++ wl_clr_drv_status(wl, CONNECTING, ndev); ++ if (completed) { ++ wl_get_assoc_ies(wl, ndev); ++ wl_update_prof(wl, ndev, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); ++ curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); ++ wl_update_bss_info(wl, ndev, 0); ++ wl_update_pmklist(ndev, wl->pmk_list, err); ++ wl_set_drv_status(wl, CONNECTED, ndev); ++ } ++ cfg80211_connect_result(ndev, ++ curbssid, ++ conn_info->req_ie, ++ conn_info->req_ie_len, ++ conn_info->resp_ie, ++ conn_info->resp_ie_len, ++ completed ? WLAN_STATUS_SUCCESS : ++ (sec->auth_assoc_res_status) ? ++ sec->auth_assoc_res_status : ++ WLAN_STATUS_UNSPECIFIED_FAILURE, ++ GFP_KERNEL); ++ if (completed) ++ WL_INFO(("Report connect result - connection succeeded\n")); ++ else ++ WL_ERR(("Report connect result - connection failed\n")); ++ } ++ return err; ++} ++ ++static s32 ++wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data) ++{ ++ u16 flags = ntoh16(e->flags); ++ enum nl80211_key_type key_type; ++ ++ mutex_lock(&wl->usr_sync); ++ if (flags & WLC_EVENT_MSG_GROUP) ++ key_type = NL80211_KEYTYPE_GROUP; ++ else ++ key_type = NL80211_KEYTYPE_PAIRWISE; ++ ++ cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1, ++ NULL, GFP_KERNEL); ++ mutex_unlock(&wl->usr_sync); ++ ++ return 0; ++} ++ ++#ifdef PNO_SUPPORT ++static s32 ++wl_notify_pfn_status(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data) ++{ ++ WL_ERR((">>> PNO Event\n")); ++ ++#ifndef WL_SCHED_SCAN ++ mutex_lock(&wl->usr_sync); ++ /* TODO: Use cfg80211_sched_scan_results(wiphy); */ ++ cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); ++ mutex_unlock(&wl->usr_sync); ++#else ++ /* If cfg80211 scheduled scan is supported, report the pno results via sched ++ * scan results ++ */ ++ wl_notify_sched_scan_results(wl, ndev, e, data); ++#endif /* WL_SCHED_SCAN */ ++ return 0; ++} ++#endif /* PNO_SUPPORT */ ++ ++static s32 ++wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data) ++{ ++ struct channel_info channel_inform; ++ struct wl_scan_results *bss_list; ++ u32 len = WL_SCAN_BUF_MAX; ++ s32 err = 0; ++ unsigned long flags; ++ ++ WL_DBG(("Enter \n")); ++ if (!wl_get_drv_status(wl, SCANNING, ndev)) { ++ WL_ERR(("scan is not ready \n")); ++ return err; ++ } ++ if (wl->iscan_on && wl->iscan_kickstart) ++ return wl_wakeup_iscan(wl_to_iscan(wl)); ++ ++ mutex_lock(&wl->usr_sync); ++ wl_clr_drv_status(wl, SCANNING, ndev); ++ err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &channel_inform, ++ sizeof(channel_inform), false); ++ if (unlikely(err)) { ++ WL_ERR(("scan busy (%d)\n", err)); ++ goto scan_done_out; ++ } ++ channel_inform.scan_channel = dtoh32(channel_inform.scan_channel); ++ if (unlikely(channel_inform.scan_channel)) { ++ ++ WL_DBG(("channel_inform.scan_channel (%d)\n", ++ channel_inform.scan_channel)); ++ } ++ wl->bss_list = wl->scan_results; ++ bss_list = wl->bss_list; ++ memset(bss_list, 0, len); ++ bss_list->buflen = htod32(len); ++ err = wldev_ioctl(ndev, WLC_SCAN_RESULTS, bss_list, len, false); ++ if (unlikely(err) && unlikely(!wl->scan_suppressed)) { ++ WL_ERR(("%s Scan_results error (%d)\n", ndev->name, err)); ++ err = -EINVAL; ++ goto scan_done_out; ++ } ++ bss_list->buflen = dtoh32(bss_list->buflen); ++ bss_list->version = dtoh32(bss_list->version); ++ bss_list->count = dtoh32(bss_list->count); ++ ++ err = wl_inform_bss(wl); ++ ++scan_done_out: ++ del_timer_sync(&wl->scan_timeout); ++ spin_lock_irqsave(&wl->cfgdrv_lock, flags); ++ if (wl->scan_request) { ++ cfg80211_scan_done(wl->scan_request, false); ++ wl->scan_request = NULL; ++ } ++ spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); ++ WL_DBG(("cfg80211_scan_done\n")); ++ mutex_unlock(&wl->usr_sync); ++ return err; ++} ++static s32 ++wl_frame_get_mgmt(u16 fc, const struct ether_addr *da, ++ const struct ether_addr *sa, const struct ether_addr *bssid, ++ u8 **pheader, u32 *body_len, u8 *pbody) ++{ ++ struct dot11_management_header *hdr; ++ u32 totlen = 0; ++ s32 err = 0; ++ u8 *offset; ++ u32 prebody_len = *body_len; ++ switch (fc) { ++ case FC_ASSOC_REQ: ++ /* capability , listen interval */ ++ totlen = DOT11_ASSOC_REQ_FIXED_LEN; ++ *body_len += DOT11_ASSOC_REQ_FIXED_LEN; ++ break; ++ ++ case FC_REASSOC_REQ: ++ /* capability, listen inteval, ap address */ ++ totlen = DOT11_REASSOC_REQ_FIXED_LEN; ++ *body_len += DOT11_REASSOC_REQ_FIXED_LEN; ++ break; ++ } ++ totlen += DOT11_MGMT_HDR_LEN + prebody_len; ++ *pheader = kzalloc(totlen, GFP_KERNEL); ++ if (*pheader == NULL) { ++ WL_ERR(("memory alloc failed \n")); ++ return -ENOMEM; ++ } ++ hdr = (struct dot11_management_header *) (*pheader); ++ hdr->fc = htol16(fc); ++ hdr->durid = 0; ++ hdr->seq = 0; ++ offset = (u8*)(hdr + 1) + (totlen - DOT11_MGMT_HDR_LEN - prebody_len); ++ bcopy((const char*)da, (u8*)&hdr->da, ETHER_ADDR_LEN); ++ bcopy((const char*)sa, (u8*)&hdr->sa, ETHER_ADDR_LEN); ++ bcopy((const char*)bssid, (u8*)&hdr->bssid, ETHER_ADDR_LEN); ++ if ((pbody != NULL) && prebody_len) ++ bcopy((const char*)pbody, offset, prebody_len); ++ *body_len = totlen; ++ return err; ++} ++ ++ ++void ++wl_stop_wait_next_action_frame(struct wl_priv *wl, struct net_device *ndev) ++{ ++ if (wl_get_drv_status_all(wl, SENDING_ACT_FRM) && ++ (wl_get_p2p_status(wl, ACTION_TX_COMPLETED) || ++ wl_get_p2p_status(wl, ACTION_TX_NOACK))) { ++ WL_DBG(("*** Wake UP ** abort actframe iovar\n")); ++ /* if channel is not zero, "actfame" uses off channel scan. ++ * So abort scan for off channel completion. ++ */ ++ if (wl->af_sent_channel) ++ /* wl_cfg80211_scan_abort(wl, ndev); */ ++ wl_notify_escan_complete(wl, ++ (ndev == wl->p2p_net) ? wl_to_prmry_ndev(wl) : ndev, true, true); ++ } ++#ifdef WL_CFG80211_SYNC_GON ++ else if (wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM_LISTEN)) { ++ WL_DBG(("*** Wake UP ** abort listen for next af frame\n")); ++ /* So abort scan to cancel listen */ ++ wl_notify_escan_complete(wl, ++ (ndev == wl->p2p_net) ? wl_to_prmry_ndev(wl) : ndev, true, true); ++ } ++#endif /* WL_CFG80211_SYNC_GON */ ++} ++ ++static s32 ++wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data) ++{ ++ struct ieee80211_supported_band *band; ++ struct wiphy *wiphy = wl_to_wiphy(wl); ++ struct ether_addr da; ++ struct ether_addr bssid; ++ bool isfree = false; ++ s32 err = 0; ++ s32 freq; ++ struct net_device *dev = NULL; ++ wifi_p2p_pub_act_frame_t *act_frm = NULL; ++ wifi_p2p_action_frame_t *p2p_act_frm = NULL; ++ wifi_p2psd_gas_pub_act_frame_t *sd_act_frm = NULL; ++ wl_event_rx_frame_data_t *rxframe = ++ (wl_event_rx_frame_data_t*)data; ++ u32 event = ntoh32(e->event_type); ++ u8 *mgmt_frame; ++ u8 bsscfgidx = e->bsscfgidx; ++ u32 mgmt_frame_len = ntoh32(e->datalen) - sizeof(wl_event_rx_frame_data_t); ++ u16 channel = ((ntoh16(rxframe->channel) & WL_CHANSPEC_CHAN_MASK)); ++ ++ memset(&bssid, 0, ETHER_ADDR_LEN); ++ ++ if (wl->p2p_net == ndev) { ++ dev = wl_to_prmry_ndev(wl); ++ } else { ++ dev = ndev; ++ } ++ ++ if (channel <= CH_MAX_2G_CHANNEL) ++ band = wiphy->bands[IEEE80211_BAND_2GHZ]; ++ else ++ band = wiphy->bands[IEEE80211_BAND_5GHZ]; ++ if (!band) { ++ WL_ERR(("No valid band")); ++ return -EINVAL; ++ } ++#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) ++ freq = ieee80211_channel_to_frequency(channel); ++ (void)band->band; ++#else ++ freq = ieee80211_channel_to_frequency(channel, band->band); ++#endif ++ if (event == WLC_E_ACTION_FRAME_RX) { ++ wldev_iovar_getbuf_bsscfg(dev, "cur_etheraddr", ++ NULL, 0, wl->ioctl_buf, WLC_IOCTL_SMLEN, bsscfgidx, &wl->ioctl_buf_sync); ++ ++ err = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); ++ if (err < 0) ++ WL_ERR(("WLC_GET_BSSID error %d\n", err)); ++ memcpy(da.octet, wl->ioctl_buf, ETHER_ADDR_LEN); ++ err = wl_frame_get_mgmt(FC_ACTION, &da, &e->addr, &bssid, ++ &mgmt_frame, &mgmt_frame_len, ++ (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1)); ++ if (err < 0) { ++ WL_ERR(("%s: Error in receiving action frame len %d channel %d freq %d\n", ++ __func__, mgmt_frame_len, channel, freq)); ++ goto exit; ++ } ++ isfree = true; ++ if (wl_cfgp2p_is_pub_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], ++ mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { ++ act_frm = (wifi_p2p_pub_act_frame_t *) ++ (&mgmt_frame[DOT11_MGMT_HDR_LEN]); ++ } else if (wl_cfgp2p_is_p2p_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], ++ mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { ++ p2p_act_frm = (wifi_p2p_action_frame_t *) ++ (&mgmt_frame[DOT11_MGMT_HDR_LEN]); ++ (void) p2p_act_frm; ++ } else if (wl_cfgp2p_is_gas_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], ++ mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { ++ sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *) ++ (&mgmt_frame[DOT11_MGMT_HDR_LEN]); ++ if (sd_act_frm && wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM)) { ++ if (wl->next_af_subtype == sd_act_frm->action) { ++ WL_DBG(("We got a right next frame of SD!(%d)\n", ++ sd_act_frm->action)); ++ wl_clr_drv_status(wl, WAITING_NEXT_ACT_FRM, ++ (ndev == wl->p2p_net) ? ++ wl_to_prmry_ndev(wl) : ndev); ++ ++ /* Stop waiting for next AF. */ ++ wl_stop_wait_next_action_frame(wl, ndev); ++ } ++ } ++ (void) sd_act_frm; ++ } else { ++ /* ++ * if we got normal action frame and ndev is p2p0, ++ * we have to change ndev from p2p0 to wlan0 ++ */ ++ if (wl->p2p_net == ndev) ++ ndev = wl_to_prmry_ndev(wl); ++ } ++ ++ if (act_frm) { ++ ++ if (wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM)) { ++ if (wl->next_af_subtype == act_frm->subtype) { ++ WL_DBG(("We got a right next frame!(%d)\n", ++ act_frm->subtype)); ++ wl_clr_drv_status(wl, WAITING_NEXT_ACT_FRM, ++ (ndev == wl->p2p_net) ? ++ wl_to_prmry_ndev(wl) : ndev); ++ ++ /* Stop waiting for next AF. */ ++ wl_stop_wait_next_action_frame(wl, ndev); ++ } ++ } ++ } ++ ++ wl_cfgp2p_print_actframe(false, &mgmt_frame[DOT11_MGMT_HDR_LEN], ++ mgmt_frame_len - DOT11_MGMT_HDR_LEN); ++ /* ++ * After complete GO Negotiation, roll back to mpc mode ++ */ ++ if (act_frm && ((act_frm->subtype == P2P_PAF_GON_CONF) || ++ (act_frm->subtype == P2P_PAF_PROVDIS_RSP))) { ++ wldev_iovar_setint(dev, "mpc", 1); ++ } ++ if (act_frm && (act_frm->subtype == P2P_PAF_GON_CONF)) { ++ WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); ++ wl_clr_p2p_status(wl, GO_NEG_PHASE); ++ } ++ } else { ++ mgmt_frame = (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1); ++ ++ /* wpa supplicant use probe request event for restarting another GON Req. ++ * but it makes GON Req repetition. ++ * so if src addr of prb req is same as my target device, ++ * do not send probe request event during sending action frame. ++ */ ++ if (event == WLC_E_P2P_PROBREQ_MSG) { ++ WL_DBG((" Event %s\n", (event == WLC_E_P2P_PROBREQ_MSG) ? ++ "WLC_E_P2P_PROBREQ_MSG":"WLC_E_PROBREQ_MSG")); ++ ++ ++ /* Filter any P2P probe reqs arriving during the ++ * GO-NEG Phase ++ */ ++ if (wl->p2p && ++ wl_get_p2p_status(wl, GO_NEG_PHASE)) { ++ WL_DBG(("Filtering P2P probe_req while " ++ "being in GO-Neg state\n")); ++ return 0; ++ } ++ } ++ } ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) ++ cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, mgmt_frame_len, GFP_ATOMIC); ++#else ++ cfg80211_rx_mgmt(ndev, freq, mgmt_frame, mgmt_frame_len, GFP_ATOMIC); ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) */ ++ ++ WL_DBG(("%s: mgmt_frame_len (%d) , e->datalen (%d), channel (%d), freq (%d)\n", __func__, ++ mgmt_frame_len, ntoh32(e->datalen), channel, freq)); ++exit: ++ if (isfree) ++ kfree(mgmt_frame); ++ return 0; ++} ++ ++#ifdef WL_SCHED_SCAN ++/* If target scan is not reliable, set the below define to "1" to do a ++ * full escan ++ */ ++#define FULL_ESCAN_ON_PFN_NET_FOUND 0 ++static s32 ++wl_notify_sched_scan_results(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data) ++{ ++ wl_pfn_net_info_t *netinfo, *pnetinfo; ++ struct cfg80211_scan_request request; ++ struct wiphy *wiphy = wl_to_wiphy(wl); ++ int err = 0; ++ struct cfg80211_ssid ssid[MAX_PFN_LIST_COUNT]; ++ struct ieee80211_channel *channel = NULL; ++ int channel_req = 0; ++ int band = 0; ++ struct wl_pfn_scanresults *pfn_result = (struct wl_pfn_scanresults *)data; ++ ++ WL_DBG(("Enter\n")); ++ ++ if (e->event_type == WLC_E_PFN_NET_LOST) { ++ WL_PNO(("PFN NET LOST event. Do Nothing \n")); ++ return 0; ++ } ++ WL_PNO((">>> PFN NET FOUND event. count:%d \n", pfn_result->count)); ++ if (pfn_result->count > 0) { ++ int i; ++ ++ memset(&request, 0x00, sizeof(struct cfg80211_scan_request)); ++ memset(&ssid, 0x00, sizeof(ssid)); ++ request.wiphy = wiphy; ++ ++ pnetinfo = (wl_pfn_net_info_t *)(data + sizeof(wl_pfn_scanresults_t) ++ - sizeof(wl_pfn_net_info_t)); ++ channel = (struct ieee80211_channel *)kzalloc( ++ (sizeof(struct ieee80211_channel) * MAX_PFN_LIST_COUNT), ++ GFP_KERNEL); ++ if (!channel) { ++ WL_ERR(("No memory")); ++ err = -ENOMEM; ++ goto out_err; ++ } ++ ++ for (i = 0; i < pfn_result->count; i++) { ++ netinfo = &pnetinfo[i]; ++ if (!netinfo) { ++ WL_ERR(("Invalid netinfo ptr. index:%d", i)); ++ err = -EINVAL; ++ goto out_err; ++ } ++ WL_PNO((">>> SSID:%s Channel:%d \n", ++ netinfo->pfnsubnet.SSID, netinfo->pfnsubnet.channel)); ++ /* PFN result doesn't have all the info which are required by the supplicant ++ * (For e.g IEs) Do a target Escan so that sched scan results are reported ++ * via wl_inform_single_bss in the required format. Escan does require the ++ * scan request in the form of cfg80211_scan_request. For timebeing, create ++ * cfg80211_scan_request one out of the received PNO event. ++ */ ++ memcpy(ssid[i].ssid, netinfo->pfnsubnet.SSID, ++ netinfo->pfnsubnet.SSID_len); ++ ssid[i].ssid_len = netinfo->pfnsubnet.SSID_len; ++ request.n_ssids++; ++ ++ channel_req = netinfo->pfnsubnet.channel; ++ band = (channel_req <= CH_MAX_2G_CHANNEL) ? NL80211_BAND_2GHZ ++ : NL80211_BAND_5GHZ; ++ channel[i].center_freq = ieee80211_channel_to_frequency(channel_req, band); ++ channel[i].band = band; ++ channel[i].flags |= IEEE80211_CHAN_NO_HT40; ++ request.channels[i] = &channel[i]; ++ request.n_channels++; ++ } ++ ++ /* assign parsed ssid array */ ++ if (request.n_ssids) ++ request.ssids = &ssid[0]; ++ ++ if (wl_get_drv_status_all(wl, SCANNING)) { ++ /* Abort any on-going scan */ ++ wl_notify_escan_complete(wl, ndev, true, true); ++ } ++ ++ if (wl_get_p2p_status(wl, DISCOVERY_ON)) { ++ WL_PNO((">>> P2P discovery was ON. Disabling it\n")); ++ err = wl_cfgp2p_discover_enable_search(wl, false); ++ if (unlikely(err)) { ++ wl_clr_drv_status(wl, SCANNING, ndev); ++ goto out_err; ++ } ++ } ++ ++ wl_set_drv_status(wl, SCANNING, ndev); ++#if FULL_ESCAN_ON_PFN_NET_FOUND ++ WL_PNO((">>> Doing Full ESCAN on PNO event\n")); ++ err = wl_do_escan(wl, wiphy, ndev, NULL); ++#else ++ WL_PNO((">>> Doing targeted ESCAN on PNO event\n")); ++ err = wl_do_escan(wl, wiphy, ndev, &request); ++#endif ++ if (err) { ++ wl_clr_drv_status(wl, SCANNING, ndev); ++ goto out_err; ++ } ++ wl->sched_scan_running = TRUE; ++ } ++ else { ++ WL_ERR(("FALSE PNO Event. (pfn_count == 0) \n")); ++ } ++out_err: ++ if (channel) ++ kfree(channel); ++ return err; ++} ++#endif /* WL_SCHED_SCAN */ ++ ++static void wl_init_conf(struct wl_conf *conf) ++{ ++ WL_DBG(("Enter \n")); ++ conf->frag_threshold = (u32)-1; ++ conf->rts_threshold = (u32)-1; ++ conf->retry_short = (u32)-1; ++ conf->retry_long = (u32)-1; ++ conf->tx_power = -1; ++} ++ ++static void wl_init_prof(struct wl_priv *wl, struct net_device *ndev) ++{ ++ unsigned long flags; ++ struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); ++ ++ spin_lock_irqsave(&wl->cfgdrv_lock, flags); ++ memset(profile, 0, sizeof(struct wl_profile)); ++ spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); ++} ++ ++static void wl_init_event_handler(struct wl_priv *wl) ++{ ++ memset(wl->evt_handler, 0, sizeof(wl->evt_handler)); ++ ++ wl->evt_handler[WLC_E_SCAN_COMPLETE] = wl_notify_scan_status; ++ wl->evt_handler[WLC_E_AUTH] = wl_notify_connect_status; ++ wl->evt_handler[WLC_E_ASSOC] = wl_notify_connect_status; ++ wl->evt_handler[WLC_E_LINK] = wl_notify_connect_status; ++ wl->evt_handler[WLC_E_DEAUTH_IND] = wl_notify_connect_status; ++ wl->evt_handler[WLC_E_DEAUTH] = wl_notify_connect_status; ++ wl->evt_handler[WLC_E_DISASSOC_IND] = wl_notify_connect_status; ++ wl->evt_handler[WLC_E_ASSOC_IND] = wl_notify_connect_status; ++ wl->evt_handler[WLC_E_REASSOC_IND] = wl_notify_connect_status; ++ wl->evt_handler[WLC_E_ROAM] = wl_notify_roaming_status; ++ wl->evt_handler[WLC_E_MIC_ERROR] = wl_notify_mic_status; ++ wl->evt_handler[WLC_E_SET_SSID] = wl_notify_connect_status; ++ wl->evt_handler[WLC_E_ACTION_FRAME_RX] = wl_notify_rx_mgmt_frame; ++ wl->evt_handler[WLC_E_PROBREQ_MSG] = wl_notify_rx_mgmt_frame; ++ wl->evt_handler[WLC_E_P2P_PROBREQ_MSG] = wl_notify_rx_mgmt_frame; ++ wl->evt_handler[WLC_E_P2P_DISC_LISTEN_COMPLETE] = wl_cfgp2p_listen_complete; ++ wl->evt_handler[WLC_E_ACTION_FRAME_COMPLETE] = wl_cfgp2p_action_tx_complete; ++ wl->evt_handler[WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE] = wl_cfgp2p_action_tx_complete; ++#ifdef PNO_SUPPORT ++ wl->evt_handler[WLC_E_PFN_NET_FOUND] = wl_notify_pfn_status; ++#endif /* PNO_SUPPORT */ ++} ++ ++static s32 wl_init_priv_mem(struct wl_priv *wl) ++{ ++ WL_DBG(("Enter \n")); ++ wl->scan_results = (void *)kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL); ++ if (unlikely(!wl->scan_results)) { ++ WL_ERR(("Scan results alloc failed\n")); ++ goto init_priv_mem_out; ++ } ++ wl->conf = (void *)kzalloc(sizeof(*wl->conf), GFP_KERNEL); ++ if (unlikely(!wl->conf)) { ++ WL_ERR(("wl_conf alloc failed\n")); ++ goto init_priv_mem_out; ++ } ++ wl->scan_req_int = ++ (void *)kzalloc(sizeof(*wl->scan_req_int), GFP_KERNEL); ++ if (unlikely(!wl->scan_req_int)) { ++ WL_ERR(("Scan req alloc failed\n")); ++ goto init_priv_mem_out; ++ } ++ wl->ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL); ++ if (unlikely(!wl->ioctl_buf)) { ++ WL_ERR(("Ioctl buf alloc failed\n")); ++ goto init_priv_mem_out; ++ } ++ wl->escan_ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL); ++ if (unlikely(!wl->escan_ioctl_buf)) { ++ WL_ERR(("Ioctl buf alloc failed\n")); ++ goto init_priv_mem_out; ++ } ++ wl->extra_buf = (void *)kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); ++ if (unlikely(!wl->extra_buf)) { ++ WL_ERR(("Extra buf alloc failed\n")); ++ goto init_priv_mem_out; ++ } ++ wl->iscan = (void *)kzalloc(sizeof(*wl->iscan), GFP_KERNEL); ++ if (unlikely(!wl->iscan)) { ++ WL_ERR(("Iscan buf alloc failed\n")); ++ goto init_priv_mem_out; ++ } ++ wl->pmk_list = (void *)kzalloc(sizeof(*wl->pmk_list), GFP_KERNEL); ++ if (unlikely(!wl->pmk_list)) { ++ WL_ERR(("pmk list alloc failed\n")); ++ goto init_priv_mem_out; ++ } ++ wl->sta_info = (void *)kzalloc(sizeof(*wl->sta_info), GFP_KERNEL); ++ if (unlikely(!wl->sta_info)) { ++ WL_ERR(("sta info alloc failed\n")); ++ goto init_priv_mem_out; ++ } ++ ++#if defined(STATIC_WL_PRIV_STRUCT) ++ wl->conn_info = (void *)kzalloc(sizeof(*wl->conn_info), GFP_KERNEL); ++ if (unlikely(!wl->conn_info)) { ++ WL_ERR(("wl->conn_info alloc failed\n")); ++ goto init_priv_mem_out; ++ } ++ wl->ie = (void *)kzalloc(sizeof(*wl->ie), GFP_KERNEL); ++ if (unlikely(!wl->ie)) { ++ WL_ERR(("wl->ie alloc failed\n")); ++ goto init_priv_mem_out; ++ } ++ wl->escan_info.escan_buf = dhd_os_prealloc(NULL, DHD_PREALLOC_WIPHY_ESCAN0, 0); ++ bzero(wl->escan_info.escan_buf, ESCAN_BUF_SIZE); ++#endif /* STATIC_WL_PRIV_STRUCT */ ++ wl->afx_hdl = (void *)kzalloc(sizeof(*wl->afx_hdl), GFP_KERNEL); ++ if (unlikely(!wl->afx_hdl)) { ++ WL_ERR(("afx hdl alloc failed\n")); ++ goto init_priv_mem_out; ++ } else { ++ init_completion(&wl->act_frm_scan); ++ init_completion(&wl->wait_next_af); ++ ++ INIT_WORK(&wl->afx_hdl->work, wl_cfg80211_afx_handler); ++ } ++ return 0; ++ ++init_priv_mem_out: ++ wl_deinit_priv_mem(wl); ++ ++ return -ENOMEM; ++} ++ ++static void wl_deinit_priv_mem(struct wl_priv *wl) ++{ ++ kfree(wl->scan_results); ++ wl->scan_results = NULL; ++ kfree(wl->conf); ++ wl->conf = NULL; ++ kfree(wl->scan_req_int); ++ wl->scan_req_int = NULL; ++ kfree(wl->ioctl_buf); ++ wl->ioctl_buf = NULL; ++ kfree(wl->escan_ioctl_buf); ++ wl->escan_ioctl_buf = NULL; ++ kfree(wl->extra_buf); ++ wl->extra_buf = NULL; ++ kfree(wl->iscan); ++ wl->iscan = NULL; ++ kfree(wl->pmk_list); ++ wl->pmk_list = NULL; ++ kfree(wl->sta_info); ++ wl->sta_info = NULL; ++#if defined(STATIC_WL_PRIV_STRUCT) ++ kfree(wl->conn_info); ++ wl->conn_info = NULL; ++ kfree(wl->ie); ++ wl->ie = NULL; ++ wl->escan_info.escan_buf = NULL; ++#endif /* STATIC_WL_PRIV_STRUCT */ ++ if (wl->afx_hdl) { ++ cancel_work_sync(&wl->afx_hdl->work); ++ kfree(wl->afx_hdl); ++ wl->afx_hdl = NULL; ++ } ++ ++ if (wl->ap_info) { ++ kfree(wl->ap_info->wpa_ie); ++ kfree(wl->ap_info->rsn_ie); ++ kfree(wl->ap_info->wps_ie); ++ kfree(wl->ap_info); ++ wl->ap_info = NULL; ++ } ++} ++ ++static s32 wl_create_event_handler(struct wl_priv *wl) ++{ ++ int ret = 0; ++ WL_DBG(("Enter \n")); ++ ++ /* Do not use DHD in cfg driver */ ++ wl->event_tsk.thr_pid = -1; ++ PROC_START(wl_event_handler, wl, &wl->event_tsk, 0); ++ if (wl->event_tsk.thr_pid < 0) ++ ret = -ENOMEM; ++ return ret; ++} ++ ++static void wl_destroy_event_handler(struct wl_priv *wl) ++{ ++ if (wl->event_tsk.thr_pid >= 0) ++ PROC_STOP(&wl->event_tsk); ++} ++ ++static void wl_term_iscan(struct wl_priv *wl) ++{ ++ struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); ++ WL_TRACE(("In\n")); ++ if (wl->iscan_on && iscan->tsk) { ++ iscan->state = WL_ISCAN_STATE_IDLE; ++ WL_INFO(("SIGTERM\n")); ++ send_sig(SIGTERM, iscan->tsk, 1); ++ WL_DBG(("kthread_stop\n")); ++ kthread_stop(iscan->tsk); ++ iscan->tsk = NULL; ++ } ++} ++ ++static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) ++{ ++ struct wl_priv *wl = iscan_to_wl(iscan); ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ unsigned long flags; ++ ++ WL_DBG(("Enter \n")); ++ if (!wl_get_drv_status(wl, SCANNING, ndev)) { ++ wl_clr_drv_status(wl, SCANNING, ndev); ++ WL_ERR(("Scan complete while device not scanning\n")); ++ return; ++ } ++ spin_lock_irqsave(&wl->cfgdrv_lock, flags); ++ wl_clr_drv_status(wl, SCANNING, ndev); ++ if (likely(wl->scan_request)) { ++ cfg80211_scan_done(wl->scan_request, aborted); ++ wl->scan_request = NULL; ++ } ++ spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); ++ wl->iscan_kickstart = false; ++} ++ ++static s32 wl_wakeup_iscan(struct wl_iscan_ctrl *iscan) ++{ ++ if (likely(iscan->state != WL_ISCAN_STATE_IDLE)) { ++ WL_DBG(("wake up iscan\n")); ++ up(&iscan->sync); ++ return 0; ++ } ++ ++ return -EIO; ++} ++ ++static s32 ++wl_get_iscan_results(struct wl_iscan_ctrl *iscan, u32 *status, ++ struct wl_scan_results **bss_list) ++{ ++ struct wl_iscan_results list; ++ struct wl_scan_results *results; ++ struct wl_iscan_results *list_buf; ++ s32 err = 0; ++ ++ WL_DBG(("Enter \n")); ++ memset(iscan->scan_buf, 0, WL_ISCAN_BUF_MAX); ++ list_buf = (struct wl_iscan_results *)iscan->scan_buf; ++ results = &list_buf->results; ++ results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE; ++ results->version = 0; ++ results->count = 0; ++ ++ memset(&list, 0, sizeof(list)); ++ list.results.buflen = htod32(WL_ISCAN_BUF_MAX); ++ err = wldev_iovar_getbuf(iscan->dev, "iscanresults", &list, ++ WL_ISCAN_RESULTS_FIXED_SIZE, iscan->scan_buf, ++ WL_ISCAN_BUF_MAX, NULL); ++ if (unlikely(err)) { ++ WL_ERR(("error (%d)\n", err)); ++ return err; ++ } ++ results->buflen = dtoh32(results->buflen); ++ results->version = dtoh32(results->version); ++ results->count = dtoh32(results->count); ++ WL_DBG(("results->count = %d\n", results->count)); ++ WL_DBG(("results->buflen = %d\n", results->buflen)); ++ *status = dtoh32(list_buf->status); ++ *bss_list = results; ++ ++ return err; ++} ++ ++static s32 wl_iscan_done(struct wl_priv *wl) ++{ ++ struct wl_iscan_ctrl *iscan = wl->iscan; ++ s32 err = 0; ++ ++ iscan->state = WL_ISCAN_STATE_IDLE; ++ mutex_lock(&wl->usr_sync); ++ wl_inform_bss(wl); ++ wl_notify_iscan_complete(iscan, false); ++ mutex_unlock(&wl->usr_sync); ++ ++ return err; ++} ++ ++static s32 wl_iscan_pending(struct wl_priv *wl) ++{ ++ struct wl_iscan_ctrl *iscan = wl->iscan; ++ s32 err = 0; ++ ++ /* Reschedule the timer */ ++ mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); ++ iscan->timer_on = 1; ++ ++ return err; ++} ++ ++static s32 wl_iscan_inprogress(struct wl_priv *wl) ++{ ++ struct wl_iscan_ctrl *iscan = wl->iscan; ++ s32 err = 0; ++ ++ mutex_lock(&wl->usr_sync); ++ wl_inform_bss(wl); ++ wl_run_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE); ++ mutex_unlock(&wl->usr_sync); ++ /* Reschedule the timer */ ++ mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); ++ iscan->timer_on = 1; ++ ++ return err; ++} ++ ++static s32 wl_iscan_aborted(struct wl_priv *wl) ++{ ++ struct wl_iscan_ctrl *iscan = wl->iscan; ++ s32 err = 0; ++ ++ iscan->state = WL_ISCAN_STATE_IDLE; ++ mutex_lock(&wl->usr_sync); ++ wl_notify_iscan_complete(iscan, true); ++ mutex_unlock(&wl->usr_sync); ++ ++ return err; ++} ++ ++static s32 wl_iscan_thread(void *data) ++{ ++ struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; ++ struct wl_priv *wl = iscan_to_wl(iscan); ++ u32 status; ++ int err = 0; ++ ++ allow_signal(SIGTERM); ++ status = WL_SCAN_RESULTS_PARTIAL; ++ while (likely(!down_interruptible(&iscan->sync))) { ++ if (kthread_should_stop()) ++ break; ++ if (iscan->timer_on) { ++ del_timer_sync(&iscan->timer); ++ iscan->timer_on = 0; ++ } ++ mutex_lock(&wl->usr_sync); ++ err = wl_get_iscan_results(iscan, &status, &wl->bss_list); ++ if (unlikely(err)) { ++ status = WL_SCAN_RESULTS_ABORTED; ++ WL_ERR(("Abort iscan\n")); ++ } ++ mutex_unlock(&wl->usr_sync); ++ iscan->iscan_handler[status] (wl); ++ } ++ if (iscan->timer_on) { ++ del_timer_sync(&iscan->timer); ++ iscan->timer_on = 0; ++ } ++ WL_DBG(("%s was terminated\n", __func__)); ++ ++ return 0; ++} ++ ++static void wl_scan_timeout(unsigned long data) ++{ ++ struct wl_priv *wl = (struct wl_priv *)data; ++ ++ if (wl->scan_request) { ++ WL_ERR(("timer expired\n")); ++ if (wl->escan_on) ++ wl_notify_escan_complete(wl, wl->escan_info.ndev, true, true); ++ else ++ wl_notify_iscan_complete(wl_to_iscan(wl), true); ++ } ++} ++static void wl_iscan_timer(unsigned long data) ++{ ++ struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; ++ ++ if (iscan) { ++ iscan->timer_on = 0; ++ WL_DBG(("timer expired\n")); ++ wl_wakeup_iscan(iscan); ++ } ++} ++ ++static s32 wl_invoke_iscan(struct wl_priv *wl) ++{ ++ struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); ++ int err = 0; ++ ++ if (wl->iscan_on && !iscan->tsk) { ++ iscan->state = WL_ISCAN_STATE_IDLE; ++ sema_init(&iscan->sync, 0); ++ iscan->tsk = kthread_run(wl_iscan_thread, iscan, "wl_iscan"); ++ if (IS_ERR(iscan->tsk)) { ++ WL_ERR(("Could not create iscan thread\n")); ++ iscan->tsk = NULL; ++ return -ENOMEM; ++ } ++ } ++ ++ return err; ++} ++ ++static void wl_init_iscan_handler(struct wl_iscan_ctrl *iscan) ++{ ++ memset(iscan->iscan_handler, 0, sizeof(iscan->iscan_handler)); ++ iscan->iscan_handler[WL_SCAN_RESULTS_SUCCESS] = wl_iscan_done; ++ iscan->iscan_handler[WL_SCAN_RESULTS_PARTIAL] = wl_iscan_inprogress; ++ iscan->iscan_handler[WL_SCAN_RESULTS_PENDING] = wl_iscan_pending; ++ iscan->iscan_handler[WL_SCAN_RESULTS_ABORTED] = wl_iscan_aborted; ++ iscan->iscan_handler[WL_SCAN_RESULTS_NO_MEM] = wl_iscan_aborted; ++} ++ ++static s32 ++wl_cfg80211_netdev_notifier_call(struct notifier_block * nb, ++ unsigned long state, ++ void *ndev) ++{ ++ struct net_device *dev = ndev; ++ struct wireless_dev *wdev = dev->ieee80211_ptr; ++ struct wl_priv *wl = wlcfg_drv_priv; ++ int refcnt = 0; ++ ++ WL_DBG(("Enter \n")); ++ if (!wdev || !wl || dev == wl_to_prmry_ndev(wl)) ++ return NOTIFY_DONE; ++ switch (state) { ++ case NETDEV_DOWN: ++ while (work_pending(&wdev->cleanup_work) && refcnt < 100) { ++ if (refcnt%5 == 0) ++ WL_ERR(("%s : [NETDEV_DOWN] work_pending (%d th)\n", ++ __FUNCTION__, refcnt)); ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(100); ++ set_current_state(TASK_RUNNING); ++ refcnt++; ++ } ++ break; ++ ++ case NETDEV_UNREGISTER: ++ /* after calling list_del_rcu(&wdev->list) */ ++ wl_dealloc_netinfo(wl, ndev); ++ break; ++ case NETDEV_GOING_DOWN: ++ /* At NETDEV_DOWN state, wdev_cleanup_work work will be called. ++ * In front of door, the function checks ++ * whether current scan is working or not. ++ * If the scanning is still working, wdev_cleanup_work call WARN_ON and ++ * make the scan done forcibly. ++ */ ++ if (wl_get_drv_status(wl, SCANNING, dev)) { ++ if (wl->escan_on) { ++ wl_notify_escan_complete(wl, dev, true, true); ++ } ++ } ++ break; ++ } ++ return NOTIFY_DONE; ++} ++static struct notifier_block wl_cfg80211_netdev_notifier = { ++ .notifier_call = wl_cfg80211_netdev_notifier_call, ++}; ++ ++static s32 wl_notify_escan_complete(struct wl_priv *wl, ++ struct net_device *ndev, ++ bool aborted, bool fw_abort) ++{ ++ wl_scan_params_t *params = NULL; ++ s32 params_size = 0; ++ s32 err = BCME_OK; ++ unsigned long flags; ++ struct net_device *dev; ++ ++ WL_DBG(("Enter \n")); ++ ++ if (wl->escan_info.ndev != ndev) ++ { ++ WL_ERR(("ndev is different %p %p\n", wl->escan_info.ndev, ndev)); ++ return err; ++ } ++ ++ if (wl->scan_request) { ++ if (wl->scan_request->dev == wl->p2p_net) ++ dev = wl_to_prmry_ndev(wl); ++ else ++ dev = wl->scan_request->dev; ++ } ++ else { ++ WL_DBG(("wl->scan_request is NULL may be internal scan." ++ "doing scan_abort for ndev %p primary %p p2p_net %p", ++ ndev, wl_to_prmry_ndev(wl), wl->p2p_net)); ++ dev = ndev; ++ } ++ if (fw_abort && !in_atomic()) { ++ /* Our scan params only need space for 1 channel and 0 ssids */ ++ params = wl_cfg80211_scan_alloc_params(-1, 0, ¶ms_size); ++ if (params == NULL) { ++ WL_ERR(("scan params allocation failed \n")); ++ err = -ENOMEM; ++ } else { ++ /* Do a scan abort to stop the driver's scan engine */ ++ err = wldev_ioctl(dev, WLC_SCAN, params, params_size, true); ++ if (err < 0) { ++ WL_ERR(("scan abort failed \n")); ++ } ++ } ++ } ++ if (timer_pending(&wl->scan_timeout)) ++ del_timer_sync(&wl->scan_timeout); ++#if defined(ESCAN_RESULT_PATCH) ++ if (likely(wl->scan_request)) { ++ wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; ++ wl_inform_bss(wl); ++ } ++#endif /* ESCAN_RESULT_PATCH */ ++ spin_lock_irqsave(&wl->cfgdrv_lock, flags); ++#ifdef WL_SCHED_SCAN ++ if (wl->sched_scan_req && !wl->scan_request) { ++ WL_PNO((">>> REPORTING SCHED SCAN RESULTS \n")); ++ if (aborted) ++ cfg80211_sched_scan_stopped(wl->sched_scan_req->wiphy); ++ else ++ cfg80211_sched_scan_results(wl->sched_scan_req->wiphy); ++ wl->sched_scan_running = FALSE; ++ wl->sched_scan_req = NULL; ++ } ++#endif /* WL_SCHED_SCAN */ ++ if (likely(wl->scan_request)) { ++ cfg80211_scan_done(wl->scan_request, aborted); ++ wl->scan_request = NULL; ++ } ++ if (p2p_is_on(wl)) ++ wl_clr_p2p_status(wl, SCANNING); ++ wl_clr_drv_status(wl, SCANNING, dev); ++ spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); ++ if (params) ++ kfree(params); ++ ++ return err; ++} ++ ++static s32 wl_escan_handler(struct wl_priv *wl, ++ struct net_device *ndev, ++ const wl_event_msg_t *e, void *data) ++{ ++ s32 err = BCME_OK; ++ s32 status = ntoh32(e->status); ++ wl_bss_info_t *bi; ++ wl_escan_result_t *escan_result; ++ wl_bss_info_t *bss = NULL; ++ wl_scan_results_t *list; ++ wifi_p2p_ie_t * p2p_ie; ++ u32 bi_length; ++ u32 i; ++ u8 *p2p_dev_addr = NULL; ++ ++ WL_DBG((" enter event type : %d, status : %d \n", ++ ntoh32(e->event_type), ntoh32(e->status))); ++ ++ mutex_lock(&wl->usr_sync); ++ /* P2P SCAN is coming from primary interface */ ++ if (wl_get_p2p_status(wl, SCANNING)) { ++ if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) ++ ndev = wl->afx_hdl->dev; ++ else ++ ndev = wl->escan_info.ndev; ++ ++ } ++ if (!ndev || !wl->escan_on || ++ (!wl_get_drv_status(wl, SCANNING, ndev) && ++ !wl->sched_scan_running)) { ++ WL_ERR(("escan is not ready ndev %p wl->escan_on %d drv_status 0x%x\n", ++ ndev, wl->escan_on, wl_get_drv_status(wl, SCANNING, ndev))); ++ goto exit; ++ } ++ if (status == WLC_E_STATUS_PARTIAL) { ++ WL_INFO(("WLC_E_STATUS_PARTIAL \n")); ++ escan_result = (wl_escan_result_t *) data; ++ if (!escan_result) { ++ WL_ERR(("Invalid escan result (NULL pointer)\n")); ++ goto exit; ++ } ++ if (dtoh16(escan_result->bss_count) != 1) { ++ WL_ERR(("Invalid bss_count %d: ignoring\n", escan_result->bss_count)); ++ goto exit; ++ } ++ bi = escan_result->bss_info; ++ if (!bi) { ++ WL_ERR(("Invalid escan bss info (NULL pointer)\n")); ++ goto exit; ++ } ++ bi_length = dtoh32(bi->length); ++ if (bi_length != (dtoh32(escan_result->buflen) - WL_ESCAN_RESULTS_FIXED_SIZE)) { ++ WL_ERR(("Invalid bss_info length %d: ignoring\n", bi_length)); ++ goto exit; ++ } ++ ++ if (!(wl_to_wiphy(wl)->interface_modes & BIT(NL80211_IFTYPE_ADHOC))) { ++ if (dtoh16(bi->capability) & DOT11_CAP_IBSS) { ++ WL_DBG(("Ignoring IBSS result\n")); ++ goto exit; ++ } ++ } ++ ++ if (wl_get_drv_status_all(wl, FINDING_COMMON_CHANNEL)) { ++ p2p_dev_addr = wl_cfgp2p_retreive_p2p_dev_addr(bi, bi_length); ++ if (p2p_dev_addr && !memcmp(p2p_dev_addr, ++ wl->afx_hdl->tx_dst_addr.octet, ETHER_ADDR_LEN)) { ++ s32 channel = wf_chspec_ctlchan( ++ wl_chspec_driver_to_host(bi->chanspec)); ++ WL_DBG(("ACTION FRAME SCAN : Peer " MACDBG " found, channel : %d\n", ++ MAC2STRDBG(wl->afx_hdl->tx_dst_addr.octet), channel)); ++ wl_clr_p2p_status(wl, SCANNING); ++ wl->afx_hdl->peer_chan = channel; ++ complete(&wl->act_frm_scan); ++ goto exit; ++ } ++ ++ } else { ++ int cur_len = 0; ++ list = (wl_scan_results_t *)wl->escan_info.escan_buf; ++#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) ++ if (wl->p2p_net && wl->scan_request && ++ wl->scan_request->dev == wl->p2p_net) { ++#else ++ if (p2p_is_on(wl) && p2p_scan(wl)) { ++#endif ++#ifdef WL_HOST_BAND_MGMT ++ s32 channel = 0; ++ s32 channel_band = 0; ++#endif /* WL_HOST_BAND_MGMT */ ++ /* p2p scan && allow only probe response */ ++ if (bi->flags & WL_BSS_FLAGS_FROM_BEACON) ++ goto exit; ++ if ((p2p_ie = wl_cfgp2p_find_p2pie(((u8 *) bi) + bi->ie_offset, ++ bi->ie_length)) == NULL) { ++ WL_ERR(("Couldn't find P2PIE in probe" ++ " response/beacon\n")); ++ goto exit; ++ } ++#ifdef WL_HOST_BAND_MGMT ++ channel = CHSPEC_CHANNEL(wl_chspec_driver_to_host(bi->chanspec)); ++ channel_band = (channel > CH_MAX_2G_CHANNEL) ? ++ WLC_BAND_5G : WLC_BAND_2G; ++ ++ ++ if ((wl->curr_band == WLC_BAND_5G) && ++ (channel_band == WLC_BAND_2G)) { ++ /* Avoid sending the GO results in band conflict */ ++ if (wl_cfgp2p_retreive_p2pattrib(p2p_ie, ++ P2P_SEID_GROUP_ID) != NULL) ++ goto exit; ++ } ++#endif /* WL_HOST_BAND_MGMT */ ++ } ++ for (i = 0; i < list->count; i++) { ++ bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) ++ : list->bss_info; ++ ++ if (!bcmp(&bi->BSSID, &bss->BSSID, ETHER_ADDR_LEN) && ++ (CHSPEC_BAND(wl_chspec_driver_to_host(bi->chanspec)) ++ == CHSPEC_BAND(wl_chspec_driver_to_host(bss->chanspec))) && ++ bi->SSID_len == bss->SSID_len && ++ !bcmp(bi->SSID, bss->SSID, bi->SSID_len)) { ++ ++ /* do not allow beacon data to update ++ *the data recd from a probe response ++ */ ++ if (!(bss->flags & WL_BSS_FLAGS_FROM_BEACON) && ++ (bi->flags & WL_BSS_FLAGS_FROM_BEACON)) ++ goto exit; ++ ++ WL_DBG(("%s("MACDBG"), i=%d prev: RSSI %d" ++ " flags 0x%x, new: RSSI %d flags 0x%x\n", ++ bss->SSID, MAC2STRDBG(bi->BSSID.octet), i, ++ bss->RSSI, bss->flags, bi->RSSI, bi->flags)); ++ ++ if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) == ++ (bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL)) { ++ /* preserve max RSSI if the measurements are ++ * both on-channel or both off-channel ++ */ ++ WL_SCAN(("%s("MACDBG"), same onchan" ++ ", RSSI: prev %d new %d\n", ++ bss->SSID, MAC2STRDBG(bi->BSSID.octet), ++ bss->RSSI, bi->RSSI)); ++ bi->RSSI = MAX(bss->RSSI, bi->RSSI); ++ } else if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) && ++ (bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) == 0) { ++ /* preserve the on-channel rssi measurement ++ * if the new measurement is off channel ++ */ ++ WL_SCAN(("%s("MACDBG"), prev onchan" ++ ", RSSI: prev %d new %d\n", ++ bss->SSID, MAC2STRDBG(bi->BSSID.octet), ++ bss->RSSI, bi->RSSI)); ++ bi->RSSI = bss->RSSI; ++ bi->flags |= WL_BSS_FLAGS_RSSI_ONCHANNEL; ++ } ++ if (dtoh32(bss->length) != bi_length) { ++ u32 prev_len = dtoh32(bss->length); ++ ++ WL_SCAN(("bss info replacement" ++ " is occured(bcast:%d->probresp%d)\n", ++ bss->ie_length, bi->ie_length)); ++ WL_DBG(("%s("MACDBG"), replacement!(%d -> %d)\n", ++ bss->SSID, MAC2STRDBG(bi->BSSID.octet), ++ prev_len, bi_length)); ++ ++ if (list->buflen - prev_len + bi_length ++ > ESCAN_BUF_SIZE) { ++ WL_ERR(("Buffer is too small: keep the" ++ " previous result of this AP\n")); ++ /* Only update RSSI */ ++ bss->RSSI = bi->RSSI; ++ bss->flags |= (bi->flags ++ & WL_BSS_FLAGS_RSSI_ONCHANNEL); ++ goto exit; ++ } ++ ++ if (i < list->count - 1) { ++ /* memory copy required by this case only */ ++ memmove((u8 *)bss + bi_length, ++ (u8 *)bss + prev_len, ++ list->buflen - cur_len - prev_len); ++ } ++ list->buflen -= prev_len; ++ list->buflen += bi_length; ++ } ++ list->version = dtoh32(bi->version); ++ memcpy((u8 *)bss, (u8 *)bi, bi_length); ++ goto exit; ++ } ++ cur_len += dtoh32(bss->length); ++ } ++ if (bi_length > ESCAN_BUF_SIZE - list->buflen) { ++ WL_ERR(("Buffer is too small: ignoring\n")); ++ goto exit; ++ } ++ memcpy(&(wl->escan_info.escan_buf[list->buflen]), bi, bi_length); ++ list->version = dtoh32(bi->version); ++ list->buflen += bi_length; ++ list->count++; ++ } ++ ++ } ++ else if (status == WLC_E_STATUS_SUCCESS) { ++ wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; ++ if (wl_get_drv_status_all(wl, FINDING_COMMON_CHANNEL)) { ++ WL_INFO(("ACTION FRAME SCAN DONE\n")); ++ wl_clr_p2p_status(wl, SCANNING); ++ wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); ++ if (wl->afx_hdl->peer_chan == WL_INVALID) ++ complete(&wl->act_frm_scan); ++ } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { ++ WL_INFO(("ESCAN COMPLETED\n")); ++ wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; ++ wl_inform_bss(wl); ++ wl_notify_escan_complete(wl, ndev, false, false); ++ } ++ } ++ else if (status == WLC_E_STATUS_ABORT) { ++ wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; ++ if (wl_get_drv_status_all(wl, FINDING_COMMON_CHANNEL)) { ++ WL_INFO(("ACTION FRAME SCAN DONE\n")); ++ wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); ++ wl_clr_p2p_status(wl, SCANNING); ++ if (wl->afx_hdl->peer_chan == WL_INVALID) ++ complete(&wl->act_frm_scan); ++ } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { ++ WL_INFO(("ESCAN ABORTED\n")); ++ wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; ++ wl_inform_bss(wl); ++ wl_notify_escan_complete(wl, ndev, true, false); ++ } ++ } ++ else if (status == WLC_E_STATUS_NEWSCAN) ++ { ++ escan_result = (wl_escan_result_t *) data; ++ WL_ERR(("WLC_E_STATUS_NEWSCAN : scan_request[%p]\n", wl->scan_request)); ++ WL_ERR(("sync_id[%d], bss_count[%d]\n", escan_result->sync_id, ++ escan_result->bss_count)); ++ } else { ++ WL_ERR(("unexpected Escan Event %d : abort\n", status)); ++ wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; ++ if (wl_get_drv_status_all(wl, FINDING_COMMON_CHANNEL)) { ++ WL_INFO(("ACTION FRAME SCAN DONE\n")); ++ wl_clr_p2p_status(wl, SCANNING); ++ wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); ++ if (wl->afx_hdl->peer_chan == WL_INVALID) ++ complete(&wl->act_frm_scan); ++ } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { ++ wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; ++ wl_inform_bss(wl); ++ wl_notify_escan_complete(wl, ndev, true, false); ++ } ++ } ++exit: ++ mutex_unlock(&wl->usr_sync); ++ return err; ++} ++static void wl_cfg80211_concurrent_roam(struct wl_priv *wl, int enable) ++{ ++ u32 connected_cnt = wl_get_drv_status_all(wl, CONNECTED); ++ struct net_info *iter, *next; ++ int err; ++ ++ if (!wl->roamoff_on_concurrent) ++ return; ++ if (enable && connected_cnt > 1) { ++ for_each_ndev(wl, iter, next) { ++ /* Save the current roam setting */ ++ if ((err = wldev_iovar_getint(iter->ndev, "roam_off", ++ (s32 *)&iter->roam_off)) != BCME_OK) { ++ WL_ERR(("%s:Failed to get current roam setting err %d\n", ++ iter->ndev->name, err)); ++ continue; ++ } ++ if ((err = wldev_iovar_setint(iter->ndev, "roam_off", 1)) != BCME_OK) { ++ WL_ERR((" %s:failed to set roam_off : %d\n", ++ iter->ndev->name, err)); ++ } ++ } ++ } ++ else if (!enable) { ++ for_each_ndev(wl, iter, next) { ++ if (iter->roam_off != WL_INVALID) { ++ if ((err = wldev_iovar_setint(iter->ndev, "roam_off", ++ iter->roam_off)) == BCME_OK) ++ iter->roam_off = WL_INVALID; ++ else { ++ WL_ERR((" %s:failed to set roam_off : %d\n", ++ iter->ndev->name, err)); ++ } ++ } ++ } ++ } ++ return; ++} ++ ++static void wl_cfg80211_determine_vsdb_mode(struct wl_priv *wl) ++{ ++ struct net_info *iter, *next; ++ u32 chan = 0; ++ u32 chanspec = 0; ++ u32 prev_chan = 0; ++ u32 connected_cnt = wl_get_drv_status_all(wl, CONNECTED); ++ wl->vsdb_mode = false; ++ ++ if (connected_cnt <= 1) { ++ return; ++ } ++ for_each_ndev(wl, iter, next) { ++ chanspec = 0; ++ chan = 0; ++ if (wl_get_drv_status(wl, CONNECTED, iter->ndev)) { ++ if (wldev_iovar_getint(iter->ndev, "chanspec", ++ (s32 *)&chanspec) == BCME_OK) { ++ chan = CHSPEC_CHANNEL(chanspec); ++ if (CHSPEC_IS40(chanspec)) { ++ if (CHSPEC_SB_UPPER(chanspec)) ++ chan += CH_10MHZ_APART; ++ else ++ chan -= CH_10MHZ_APART; ++ } ++ wl_update_prof(wl, iter->ndev, NULL, ++ &chan, WL_PROF_CHAN); ++ } ++ if (!prev_chan && chan) ++ prev_chan = chan; ++ else if (prev_chan && (prev_chan != chan)) ++ wl->vsdb_mode = true; ++ } ++ } ++ return; ++} ++static s32 wl_notifier_change_state(struct wl_priv *wl, struct net_info *_net_info, ++ enum wl_status state, bool set) ++{ ++ s32 pm = PM_FAST; ++ s32 err = BCME_OK; ++ u32 chan = 0; ++ struct net_info *iter, *next; ++ struct net_device *primary_dev = wl_to_prmry_ndev(wl); ++ WL_DBG(("Enter state %d set %d _net_info->pm_restore %d iface %s\n", ++ state, set, _net_info->pm_restore, _net_info->ndev->name)); ++ ++ if (state != WL_STATUS_CONNECTED) ++ return 0; ++ ++ if (set) { ++ wl_cfg80211_concurrent_roam(wl, 1); ++ ++ if (wl_get_mode_by_netdev(wl, _net_info->ndev) == WL_MODE_AP) { ++ pm = PM_OFF; ++ WL_DBG(("%s:AP power save %s\n", _net_info->ndev->name, ++ pm ? "enabled" : "disabled")); ++ if ((err = wldev_ioctl(_net_info->ndev, WLC_SET_PM, ++ &pm, sizeof(pm), true)) != 0) { ++ if (err == -ENODEV) ++ WL_DBG(("%s:net_device is not ready\n", ++ _net_info->ndev->name)); ++ else ++ WL_ERR(("%s:error (%d)\n", _net_info->ndev->name, err)); ++ } ++ if (wl_add_remove_eventmsg(primary_dev, WLC_E_P2P_PROBREQ_MSG, false)) ++ WL_ERR((" failed to unset WLC_E_P2P_PROPREQ_MSG\n")); ++ return 0; ++ } ++ wl_cfg80211_determine_vsdb_mode(wl); ++ pm = PM_OFF; ++ for_each_ndev(wl, iter, next) { ++ if ((!wl->vsdb_mode) && (iter->ndev != _net_info->ndev)) { ++ /* Do not touch the other interfaces power save ++ * if we are not in vsdb mode ++ */ ++ continue; ++ } ++ /* Save the current power mode */ ++ iter->pm_restore = true; ++ err = wldev_ioctl(iter->ndev, WLC_GET_PM, &iter->pm, ++ sizeof(iter->pm), false); ++ WL_DBG(("%s:power save %s\n", iter->ndev->name, ++ iter->pm ? "enabled" : "disabled")); ++ if ((err = wldev_ioctl(iter->ndev, WLC_SET_PM, &pm, ++ sizeof(pm), true)) != 0) { ++ if (err == -ENODEV) ++ WL_DBG(("%s:netdev not ready\n", iter->ndev->name)); ++ else ++ WL_ERR(("%s:error (%d)\n", iter->ndev->name, err)); ++ iter->ndev->ieee80211_ptr->ps = pm ? true: false; ++ } ++ } ++ } ++ else { /* clear */ ++ chan = 0; ++ /* clear chan information when the net device is disconnected */ ++ wl_update_prof(wl, _net_info->ndev, NULL, &chan, WL_PROF_CHAN); ++ wl_cfg80211_determine_vsdb_mode(wl); ++ for_each_ndev(wl, iter, next) { ++ if (iter->pm_restore) { ++ WL_DBG(("%s:restoring power save %s\n", ++ iter->ndev->name, (iter->pm ? "enabled" : "disabled"))); ++ err = wldev_ioctl(iter->ndev, ++ WLC_SET_PM, &iter->pm, sizeof(iter->pm), true); ++ if (unlikely(err)) { ++ if (err == -ENODEV) ++ WL_DBG(("%s:netdev not ready\n", iter->ndev->name)); ++ else ++ WL_ERR(("%s:error(%d)\n", iter->ndev->name, err)); ++ break; ++ } ++ iter->pm_restore = 0; ++ } ++ } ++ wl_cfg80211_concurrent_roam(wl, 0); ++ } ++ return err; ++} ++ ++static s32 wl_init_scan(struct wl_priv *wl) ++{ ++ struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); ++ int err = 0; ++ ++ if (wl->iscan_on) { ++ iscan->dev = wl_to_prmry_ndev(wl); ++ iscan->state = WL_ISCAN_STATE_IDLE; ++ wl_init_iscan_handler(iscan); ++ iscan->timer_ms = WL_ISCAN_TIMER_INTERVAL_MS; ++ init_timer(&iscan->timer); ++ iscan->timer.data = (unsigned long) iscan; ++ iscan->timer.function = wl_iscan_timer; ++ sema_init(&iscan->sync, 0); ++ iscan->tsk = kthread_run(wl_iscan_thread, iscan, "wl_iscan"); ++ if (IS_ERR(iscan->tsk)) { ++ WL_ERR(("Could not create iscan thread\n")); ++ iscan->tsk = NULL; ++ return -ENOMEM; ++ } ++ iscan->data = wl; ++ } else if (wl->escan_on) { ++ wl->evt_handler[WLC_E_ESCAN_RESULT] = wl_escan_handler; ++ wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; ++ } ++ /* Init scan_timeout timer */ ++ init_timer(&wl->scan_timeout); ++ wl->scan_timeout.data = (unsigned long) wl; ++ wl->scan_timeout.function = wl_scan_timeout; ++ ++ return err; ++} ++ ++static s32 wl_init_priv(struct wl_priv *wl) ++{ ++ struct wiphy *wiphy = wl_to_wiphy(wl); ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ s32 err = 0; ++ ++ wl->scan_request = NULL; ++ wl->pwr_save = !!(wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT); ++ wl->iscan_on = false; ++ wl->escan_on = true; ++ wl->roam_on = false; ++ wl->iscan_kickstart = false; ++ wl->active_scan = true; ++ wl->rf_blocked = false; ++ wl->vsdb_mode = false; ++ wl->wlfc_on = false; ++ wl->roamoff_on_concurrent = true; ++ /* register interested state */ ++ set_bit(WL_STATUS_CONNECTED, &wl->interrested_state); ++ spin_lock_init(&wl->cfgdrv_lock); ++ mutex_init(&wl->ioctl_buf_sync); ++ init_waitqueue_head(&wl->netif_change_event); ++ init_completion(&wl->send_af_done); ++ init_completion(&wl->iface_disable); ++ wl_init_eq(wl); ++ err = wl_init_priv_mem(wl); ++ if (err) ++ return err; ++ if (wl_create_event_handler(wl)) ++ return -ENOMEM; ++ wl_init_event_handler(wl); ++ mutex_init(&wl->usr_sync); ++ mutex_init(&wl->event_sync); ++ err = wl_init_scan(wl); ++ if (err) ++ return err; ++ wl_init_conf(wl->conf); ++ wl_init_prof(wl, ndev); ++ wl_link_down(wl); ++ DNGL_FUNC(dhd_cfg80211_init, (wl)); ++ ++ return err; ++} ++ ++static void wl_deinit_priv(struct wl_priv *wl) ++{ ++ DNGL_FUNC(dhd_cfg80211_deinit, (wl)); ++ wl_destroy_event_handler(wl); ++ wl_flush_eq(wl); ++ wl_link_down(wl); ++ del_timer_sync(&wl->scan_timeout); ++ wl_term_iscan(wl); ++ wl_deinit_priv_mem(wl); ++ unregister_netdevice_notifier(&wl_cfg80211_netdev_notifier); ++} ++ ++#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) ++static s32 wl_cfg80211_attach_p2p(void) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ ++ WL_TRACE(("Enter \n")); ++ ++ if (wl_cfgp2p_register_ndev(wl) < 0) { ++ WL_ERR(("%s: P2P attach failed. \n", __func__)); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static s32 wl_cfg80211_detach_p2p(void) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ struct wireless_dev *wdev = wl->p2p_wdev; ++ ++ WL_DBG(("Enter \n")); ++ if (!wdev || !wl) { ++ WL_ERR(("Invalid Ptr\n")); ++ return -EINVAL; ++ } ++ ++ wl_cfgp2p_unregister_ndev(wl); ++ ++ wl->p2p_wdev = NULL; ++ wl->p2p_net = NULL; ++ WL_DBG(("Freeing 0x%08x \n", (unsigned int)wdev)); ++ kfree(wdev); ++ ++ return 0; ++} ++#endif /* defined(WLP2P) && defined(WL_ENABLE_P2P_IF) */ ++ ++s32 wl_cfg80211_attach_post(struct net_device *ndev) ++{ ++ struct wl_priv * wl = NULL; ++ s32 err = 0; ++ WL_TRACE(("In\n")); ++ if (unlikely(!ndev)) { ++ WL_ERR(("ndev is invaild\n")); ++ return -ENODEV; ++ } ++ wl = wlcfg_drv_priv; ++ if (unlikely(!wl)) { ++ WL_ERR(("wl is invaild\n")); ++ return -EINVAL; ++ } ++ if (!wl_get_drv_status(wl, READY, ndev)) { ++ if (wl->wdev && ++ wl_cfgp2p_supported(wl, ndev)) { ++#if !defined(WL_ENABLE_P2P_IF) ++ wl->wdev->wiphy->interface_modes |= ++ (BIT(NL80211_IFTYPE_P2P_CLIENT)| ++ BIT(NL80211_IFTYPE_P2P_GO)); ++#endif ++ if ((err = wl_cfgp2p_init_priv(wl)) != 0) ++ goto fail; ++ ++#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) ++ if (wl->p2p_net) { ++ /* Update MAC addr for p2p0 interface here. */ ++ memcpy(wl->p2p_net->dev_addr, ndev->dev_addr, ETH_ALEN); ++ wl->p2p_net->dev_addr[0] |= 0x02; ++ WL_ERR(("%s: p2p_dev_addr="MACDBG "\n", ++ wl->p2p_net->name, ++ MAC2STRDBG(wl->p2p_net->dev_addr))); ++ } else { ++ WL_ERR(("p2p_net not yet populated." ++ " Couldn't update the MAC Address for p2p0 \n")); ++ return -ENODEV; ++ } ++#endif /* defined(WLP2P) && (WL_ENABLE_P2P_IF) */ ++ ++ wl->p2p_supported = true; ++ } ++ } ++ wl_set_drv_status(wl, READY, ndev); ++fail: ++ return err; ++} ++ ++s32 wl_cfg80211_attach(struct net_device *ndev, void *data) ++{ ++ struct wireless_dev *wdev; ++ struct wl_priv *wl; ++ s32 err = 0; ++ struct device *dev; ++ ++ WL_TRACE(("In\n")); ++ if (!ndev) { ++ WL_ERR(("ndev is invaild\n")); ++ return -ENODEV; ++ } ++ WL_DBG(("func %p\n", wl_cfg80211_get_parent_dev())); ++ dev = wl_cfg80211_get_parent_dev(); ++ ++ wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); ++ if (unlikely(!wdev)) { ++ WL_ERR(("Could not allocate wireless device\n")); ++ return -ENOMEM; ++ } ++ err = wl_setup_wiphy(wdev, dev); ++ if (unlikely(err)) { ++ kfree(wdev); ++ return -ENOMEM; ++ } ++ wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); ++ wl = (struct wl_priv *)wiphy_priv(wdev->wiphy); ++ wl->wdev = wdev; ++ wl->pub = data; ++ INIT_LIST_HEAD(&wl->net_list); ++ ndev->ieee80211_ptr = wdev; ++ SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); ++ wdev->netdev = ndev; ++ wl->state_notifier = wl_notifier_change_state; ++ err = wl_alloc_netinfo(wl, ndev, wdev, WL_MODE_BSS, PM_ENABLE); ++ if (err) { ++ WL_ERR(("Failed to alloc net_info (%d)\n", err)); ++ goto cfg80211_attach_out; ++ } ++ err = wl_init_priv(wl); ++ if (err) { ++ WL_ERR(("Failed to init iwm_priv (%d)\n", err)); ++ goto cfg80211_attach_out; ++ } ++ ++ err = wl_setup_rfkill(wl, TRUE); ++ if (err) { ++ WL_ERR(("Failed to setup rfkill %d\n", err)); ++ goto cfg80211_attach_out; ++ } ++ err = register_netdevice_notifier(&wl_cfg80211_netdev_notifier); ++ if (err) { ++ WL_ERR(("Failed to register notifierl %d\n", err)); ++ goto cfg80211_attach_out; ++ } ++#if defined(COEX_DHCP) ++ err = wl_cfg80211_btcoex_init(wl); ++ if (err) ++ goto cfg80211_attach_out; ++#endif ++ ++ wlcfg_drv_priv = wl; ++ ++#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) ++ err = wl_cfg80211_attach_p2p(); ++ if (err) ++ goto cfg80211_attach_out; ++#endif ++ ++ return err; ++ ++cfg80211_attach_out: ++ wl_setup_rfkill(wl, FALSE); ++ wl_free_wdev(wl); ++ return err; ++} ++ ++void wl_cfg80211_detach(void *para) ++{ ++ struct wl_priv *wl; ++ ++ (void)para; ++ wl = wlcfg_drv_priv; ++ ++ WL_TRACE(("In\n")); ++ ++#if defined(COEX_DHCP) ++ wl_cfg80211_btcoex_deinit(wl); ++#endif ++ ++ wl_setup_rfkill(wl, FALSE); ++ if (wl->p2p_supported) { ++ if (timer_pending(&wl->p2p->listen_timer)) ++ del_timer_sync(&wl->p2p->listen_timer); ++ wl_cfgp2p_deinit_priv(wl); ++ } ++ ++#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) ++ wl_cfg80211_detach_p2p(); ++#endif ++ wl_deinit_priv(wl); ++ wlcfg_drv_priv = NULL; ++ wl_cfg80211_clear_parent_dev(); ++ wl_free_wdev(wl); ++ /* PLEASE do NOT call any function after wl_free_wdev, the driver's private structure "wl", ++ * which is the private part of wiphy, has been freed in wl_free_wdev !!!!!!!!!!! ++ */ ++} ++ ++static void wl_wakeup_event(struct wl_priv *wl) ++{ ++ if (wl->event_tsk.thr_pid >= 0) { ++ DHD_OS_WAKE_LOCK(wl->pub); ++ up(&wl->event_tsk.sema); ++ } ++} ++ ++static int wl_is_p2p_event(struct wl_event_q *e) ++{ ++ switch (e->etype) { ++ /* We have to seperate out the P2P events received ++ * on primary interface so that it can be send up ++ * via p2p0 interface. ++ */ ++ case WLC_E_P2P_PROBREQ_MSG: ++ case WLC_E_P2P_DISC_LISTEN_COMPLETE: ++ case WLC_E_ACTION_FRAME_RX: ++ case WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE: ++ case WLC_E_ACTION_FRAME_COMPLETE: ++ ++ if (e->emsg.ifidx != 0) { ++ WL_TRACE(("P2P Event on Virtual I/F (ifidx:%d) \n", ++ e->emsg.ifidx)); ++ /* We are only bothered about the P2P events received ++ * on primary interface. For rest of them return false ++ * so that it is sent over the interface corresponding ++ * to the ifidx. ++ */ ++ return FALSE; ++ } else { ++ WL_TRACE(("P2P Event on Primary I/F (ifidx:%d)." ++ " Sent it to p2p0 \n", e->emsg.ifidx)); ++ return TRUE; ++ } ++ break; ++ ++ default: ++ WL_TRACE(("NON-P2P Event %d on ifidx (ifidx:%d) \n", ++ e->etype, e->emsg.ifidx)); ++ return FALSE; ++ } ++} ++ ++static s32 wl_event_handler(void *data) ++{ ++ struct net_device *netdev; ++ struct wl_priv *wl = NULL; ++ struct wl_event_q *e; ++ tsk_ctl_t *tsk = (tsk_ctl_t *)data; ++ ++ wl = (struct wl_priv *)tsk->parent; ++ DAEMONIZE("dhd_cfg80211_event"); ++ complete(&tsk->completed); ++ ++ while (down_interruptible (&tsk->sema) == 0) { ++ SMP_RD_BARRIER_DEPENDS(); ++ if (tsk->terminated) ++ break; ++ while ((e = wl_deq_event(wl))) { ++ WL_DBG(("event type (%d), if idx: %d\n", e->etype, e->emsg.ifidx)); ++ /* All P2P device address related events comes on primary interface since ++ * there is no corresponding bsscfg for P2P interface. Map it to p2p0 ++ * interface. ++ */ ++ if ((wl_is_p2p_event(e) == TRUE) && (wl->p2p_net)) { ++ netdev = wl->p2p_net; ++ } else { ++ netdev = dhd_idx2net((struct dhd_pub *)(wl->pub), e->emsg.ifidx); ++ } ++ if (!netdev) ++ netdev = wl_to_prmry_ndev(wl); ++ if (e->etype < WLC_E_LAST && wl->evt_handler[e->etype]) { ++ wl->evt_handler[e->etype] (wl, netdev, &e->emsg, e->edata); ++ } else { ++ WL_DBG(("Unknown Event (%d): ignoring\n", e->etype)); ++ } ++ wl_put_event(e); ++ } ++ DHD_OS_WAKE_UNLOCK(wl->pub); ++ } ++ WL_ERR(("%s was terminated\n", __func__)); ++ complete_and_exit(&tsk->completed, 0); ++ return 0; ++} ++ ++void ++wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t * e, void *data) ++{ ++ u32 event_type = ntoh32(e->event_type); ++ struct wl_priv *wl = wlcfg_drv_priv; ++ ++#if (WL_DBG_LEVEL > 0) ++ s8 *estr = (event_type <= sizeof(wl_dbg_estr) / WL_DBG_ESTR_MAX - 1) ? ++ wl_dbg_estr[event_type] : (s8 *) "Unknown"; ++ WL_DBG(("event_type (%d):" "WLC_E_" "%s\n", event_type, estr)); ++#endif /* (WL_DBG_LEVEL > 0) */ ++ ++ if (event_type == WLC_E_PFN_NET_FOUND) { ++ WL_DBG((" PNOEVENT: PNO_NET_FOUND\n")); ++ } ++ else if (event_type == WLC_E_PFN_NET_LOST) { ++ WL_DBG((" PNOEVENT: PNO_NET_LOST\n")); ++ } ++ ++ if (likely(!wl_enq_event(wl, ndev, event_type, e, data))) ++ wl_wakeup_event(wl); ++} ++ ++static void wl_init_eq(struct wl_priv *wl) ++{ ++ wl_init_eq_lock(wl); ++ INIT_LIST_HEAD(&wl->eq_list); ++} ++ ++static void wl_flush_eq(struct wl_priv *wl) ++{ ++ struct wl_event_q *e; ++ unsigned long flags; ++ ++ flags = wl_lock_eq(wl); ++ while (!list_empty(&wl->eq_list)) { ++ e = list_first_entry(&wl->eq_list, struct wl_event_q, eq_list); ++ list_del(&e->eq_list); ++ kfree(e); ++ } ++ wl_unlock_eq(wl, flags); ++} ++ ++/* ++* retrieve first queued event from head ++*/ ++ ++static struct wl_event_q *wl_deq_event(struct wl_priv *wl) ++{ ++ struct wl_event_q *e = NULL; ++ unsigned long flags; ++ ++ flags = wl_lock_eq(wl); ++ if (likely(!list_empty(&wl->eq_list))) { ++ e = list_first_entry(&wl->eq_list, struct wl_event_q, eq_list); ++ list_del(&e->eq_list); ++ } ++ wl_unlock_eq(wl, flags); ++ ++ return e; ++} ++ ++/* ++ * push event to tail of the queue ++ */ ++ ++static s32 ++wl_enq_event(struct wl_priv *wl, struct net_device *ndev, u32 event, const wl_event_msg_t *msg, ++ void *data) ++{ ++ struct wl_event_q *e; ++ s32 err = 0; ++ uint32 evtq_size; ++ uint32 data_len; ++ unsigned long flags; ++ gfp_t aflags; ++ ++ data_len = 0; ++ if (data) ++ data_len = ntoh32(msg->datalen); ++ evtq_size = sizeof(struct wl_event_q) + data_len; ++ aflags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; ++ e = kzalloc(evtq_size, aflags); ++ if (unlikely(!e)) { ++ WL_ERR(("event alloc failed\n")); ++ return -ENOMEM; ++ } ++ e->etype = event; ++ memcpy(&e->emsg, msg, sizeof(wl_event_msg_t)); ++ if (data) ++ memcpy(e->edata, data, data_len); ++ flags = wl_lock_eq(wl); ++ list_add_tail(&e->eq_list, &wl->eq_list); ++ wl_unlock_eq(wl, flags); ++ ++ return err; ++} ++ ++static void wl_put_event(struct wl_event_q *e) ++{ ++ kfree(e); ++} ++ ++static s32 wl_config_ifmode(struct wl_priv *wl, struct net_device *ndev, s32 iftype) ++{ ++ s32 infra = 0; ++ s32 err = 0; ++ s32 mode = 0; ++ switch (iftype) { ++ case NL80211_IFTYPE_MONITOR: ++ case NL80211_IFTYPE_WDS: ++ WL_ERR(("type (%d) : currently we do not support this mode\n", ++ iftype)); ++ err = -EINVAL; ++ return err; ++ case NL80211_IFTYPE_ADHOC: ++ mode = WL_MODE_IBSS; ++ break; ++ case NL80211_IFTYPE_STATION: ++ case NL80211_IFTYPE_P2P_CLIENT: ++ mode = WL_MODE_BSS; ++ infra = 1; ++ break; ++ case NL80211_IFTYPE_AP: ++ case NL80211_IFTYPE_P2P_GO: ++ mode = WL_MODE_AP; ++ infra = 1; ++ break; ++ default: ++ err = -EINVAL; ++ WL_ERR(("invalid type (%d)\n", iftype)); ++ return err; ++ } ++ infra = htod32(infra); ++ err = wldev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra), true); ++ if (unlikely(err)) { ++ WL_ERR(("WLC_SET_INFRA error (%d)\n", err)); ++ return err; ++ } ++ ++ wl_set_mode_by_netdev(wl, ndev, mode); ++ ++ return 0; ++} ++ ++void wl_cfg80211_add_to_eventbuffer(struct wl_eventmsg_buf *ev, u16 event, bool set) ++{ ++ if (!ev || (event > WLC_E_LAST)) ++ return; ++ ++ if (ev->num < MAX_EVENT_BUF_NUM) { ++ ev->event[ev->num].type = event; ++ ev->event[ev->num].set = set; ++ ev->num++; ++ } else { ++ WL_ERR(("evenbuffer doesn't support > %u events. Update" ++ " the define MAX_EVENT_BUF_NUM \n", MAX_EVENT_BUF_NUM)); ++ ASSERT(0); ++ } ++} ++ ++s32 wl_cfg80211_apply_eventbuffer( ++ struct net_device *ndev, ++ struct wl_priv *wl, ++ wl_eventmsg_buf_t *ev) ++{ ++ char eventmask[WL_EVENTING_MASK_LEN]; ++ int i, ret = 0; ++ s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; ++ ++ if (!ev || (!ev->num)) ++ return -EINVAL; ++ ++ mutex_lock(&wl->event_sync); ++ ++ /* Read event_msgs mask */ ++ bcm_mkiovar("event_msgs", NULL, 0, iovbuf, ++ sizeof(iovbuf)); ++ ret = wldev_ioctl(ndev, WLC_GET_VAR, iovbuf, sizeof(iovbuf), false); ++ if (unlikely(ret)) { ++ WL_ERR(("Get event_msgs error (%d)\n", ret)); ++ goto exit; ++ } ++ memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN); ++ ++ /* apply the set bits */ ++ for (i = 0; i < ev->num; i++) { ++ if (ev->event[i].set) ++ setbit(eventmask, ev->event[i].type); ++ else ++ clrbit(eventmask, ev->event[i].type); ++ } ++ ++ /* Write updated Event mask */ ++ bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, ++ sizeof(iovbuf)); ++ ret = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); ++ if (unlikely(ret)) { ++ WL_ERR(("Set event_msgs error (%d)\n", ret)); ++ } ++ ++exit: ++ mutex_unlock(&wl->event_sync); ++ return ret; ++} ++ ++s32 wl_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add) ++{ ++ s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; ++ s8 eventmask[WL_EVENTING_MASK_LEN]; ++ s32 err = 0; ++ struct wl_priv *wl = wlcfg_drv_priv; ++ ++ if (!ndev || !wl) ++ return -ENODEV; ++ ++ mutex_lock(&wl->event_sync); ++ ++ /* Setup event_msgs */ ++ bcm_mkiovar("event_msgs", NULL, 0, iovbuf, ++ sizeof(iovbuf)); ++ err = wldev_ioctl(ndev, WLC_GET_VAR, iovbuf, sizeof(iovbuf), false); ++ if (unlikely(err)) { ++ WL_ERR(("Get event_msgs error (%d)\n", err)); ++ goto eventmsg_out; ++ } ++ memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN); ++ if (add) { ++ setbit(eventmask, event); ++ } else { ++ clrbit(eventmask, event); ++ } ++ bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, ++ sizeof(iovbuf)); ++ err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); ++ if (unlikely(err)) { ++ WL_ERR(("Set event_msgs error (%d)\n", err)); ++ goto eventmsg_out; ++ } ++ ++eventmsg_out: ++ mutex_unlock(&wl->event_sync); ++ return err; ++} ++ ++static int wl_construct_reginfo(struct wl_priv *wl, s32 bw_cap) ++{ ++ struct net_device *dev = wl_to_prmry_ndev(wl); ++ struct ieee80211_channel *band_chan_arr = NULL; ++ wl_uint32_list_t *list; ++ u32 i, j, index, n_2g, n_5g, band, channel, array_size; ++ u32 *n_cnt = NULL; ++ chanspec_t c = 0; ++ s32 err = BCME_OK; ++ bool update; ++ bool ht40_allowed; ++ u8 *pbuf = NULL; ++ ++#define LOCAL_BUF_LEN 1024 ++ pbuf = kzalloc(LOCAL_BUF_LEN, GFP_KERNEL); ++ ++ if (pbuf == NULL) { ++ WL_ERR(("failed to allocate local buf\n")); ++ return -ENOMEM; ++ } ++ list = (wl_uint32_list_t *)(void *) pbuf; ++ list->count = htod32(WL_NUMCHANSPECS); ++ ++ ++ err = wldev_iovar_getbuf_bsscfg(dev, "chanspecs", NULL, ++ 0, pbuf, LOCAL_BUF_LEN, 0, &wl->ioctl_buf_sync); ++ if (err != 0) { ++ WL_ERR(("get chanspecs failed with %d\n", err)); ++ kfree(pbuf); ++ return err; ++ } ++#undef LOCAL_BUF_LEN ++ ++ list = (wl_uint32_list_t *)(void *)pbuf; ++ band = array_size = n_2g = n_5g = 0; ++ for (i = 0; i < dtoh32(list->count); i++) { ++ index = 0; ++ update = false; ++ ht40_allowed = false; ++ c = (chanspec_t)dtoh32(list->element[i]); ++ c = wl_chspec_driver_to_host(c); ++ channel = CHSPEC_CHANNEL(c); ++ if (CHSPEC_IS40(c)) { ++ if (CHSPEC_SB_UPPER(c)) ++ channel += CH_10MHZ_APART; ++ else ++ channel -= CH_10MHZ_APART; ++ } else if (CHSPEC_IS80(c)) { ++ WL_DBG(("HT80 center channel : %d\n", channel)); ++ continue; ++ } ++ if (CHSPEC_IS2G(c) && (channel >= CH_MIN_2G_CHANNEL) && ++ (channel <= CH_MAX_2G_CHANNEL)) { ++ band_chan_arr = __wl_2ghz_channels; ++ array_size = ARRAYSIZE(__wl_2ghz_channels); ++ n_cnt = &n_2g; ++ band = IEEE80211_BAND_2GHZ; ++ ht40_allowed = (bw_cap == WLC_N_BW_40ALL)? true : false; ++ } else if (CHSPEC_IS5G(c) && channel >= CH_MIN_5G_CHANNEL) { ++ band_chan_arr = __wl_5ghz_a_channels; ++ array_size = ARRAYSIZE(__wl_5ghz_a_channels); ++ n_cnt = &n_5g; ++ band = IEEE80211_BAND_5GHZ; ++ ht40_allowed = (bw_cap == WLC_N_BW_20ALL)? false : true; ++ } else { ++ WL_ERR(("Invalid channel Sepc. 0x%x.\n", c)); ++ continue; ++ } ++ if (!ht40_allowed && CHSPEC_IS40(c)) ++ continue; ++ for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { ++ if (band_chan_arr[j].hw_value == channel) { ++ update = true; ++ break; ++ } ++ } ++ if (update) ++ index = j; ++ else ++ index = *n_cnt; ++ if (index < array_size) { ++#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) ++ band_chan_arr[index].center_freq = ++ ieee80211_channel_to_frequency(channel); ++#else ++ band_chan_arr[index].center_freq = ++ ieee80211_channel_to_frequency(channel, band); ++#endif ++ band_chan_arr[index].hw_value = channel; ++ ++ if (CHSPEC_IS40(c) && ht40_allowed) { ++ /* assuming the order is HT20, HT40 Upper, ++ HT40 lower from chanspecs ++ */ ++ u32 ht40_flag = band_chan_arr[index].flags & IEEE80211_CHAN_NO_HT40; ++ if (CHSPEC_SB_UPPER(c)) { ++ if (ht40_flag == IEEE80211_CHAN_NO_HT40) ++ band_chan_arr[index].flags &= ++ ~IEEE80211_CHAN_NO_HT40; ++ band_chan_arr[index].flags |= IEEE80211_CHAN_NO_HT40PLUS; ++ } else { ++ /* It should be one of ++ IEEE80211_CHAN_NO_HT40 or IEEE80211_CHAN_NO_HT40PLUS ++ */ ++ band_chan_arr[index].flags &= ~IEEE80211_CHAN_NO_HT40; ++ if (ht40_flag == IEEE80211_CHAN_NO_HT40) ++ band_chan_arr[index].flags |= ++ IEEE80211_CHAN_NO_HT40MINUS; ++ } ++ } else { ++ band_chan_arr[index].flags = IEEE80211_CHAN_NO_HT40; ++ if (band == IEEE80211_BAND_2GHZ) ++ channel |= WL_CHANSPEC_BAND_2G; ++ else ++ channel |= WL_CHANSPEC_BAND_5G; ++ channel |= WL_CHANSPEC_BW_20; ++ channel = wl_chspec_host_to_driver(channel); ++ err = wldev_iovar_getint(dev, "per_chan_info", &channel); ++ if (!err) { ++ if (channel & WL_CHAN_RADAR) ++ band_chan_arr[index].flags |= ++ (IEEE80211_CHAN_RADAR | ++ IEEE80211_CHAN_NO_IBSS); ++ if (channel & WL_CHAN_PASSIVE) ++ band_chan_arr[index].flags |= ++ IEEE80211_CHAN_PASSIVE_SCAN; ++ } ++ } ++ if (!update) ++ (*n_cnt)++; ++ } ++ ++ } ++ __wl_band_2ghz.n_channels = n_2g; ++ __wl_band_5ghz_a.n_channels = n_5g; ++ kfree(pbuf); ++ return err; ++} ++ ++s32 wl_update_wiphybands(struct wl_priv *wl, bool notify) ++{ ++ struct wiphy *wiphy; ++ struct net_device *dev; ++ u32 bandlist[3]; ++ u32 nband = 0; ++ u32 i = 0; ++ s32 err = 0; ++ s32 index = 0; ++ s32 nmode = 0; ++ bool rollback_lock = false; ++ s32 bw_cap = 0; ++ s32 cur_band = -1; ++ struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS] = {NULL, }; ++ ++ if (wl == NULL) { ++ wl = wlcfg_drv_priv; ++ mutex_lock(&wl->usr_sync); ++ rollback_lock = true; ++ } ++ dev = wl_to_prmry_ndev(wl); ++ ++ memset(bandlist, 0, sizeof(bandlist)); ++ err = wldev_ioctl(dev, WLC_GET_BANDLIST, bandlist, ++ sizeof(bandlist), false); ++ if (unlikely(err)) { ++ WL_ERR(("error read bandlist (%d)\n", err)); ++ goto end_bands; ++ } ++ ++ wiphy = wl_to_wiphy(wl); ++ wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; ++ wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; ++ ++ err = wldev_ioctl(dev, WLC_GET_BAND, &cur_band, ++ sizeof(s32), false); ++ if (unlikely(err)) { ++ WL_ERR(("error (%d)\n", err)); ++ goto end_bands; ++ } ++ ++ err = wldev_iovar_getint(dev, "nmode", &nmode); ++ if (unlikely(err)) { ++ WL_ERR(("error reading nmode (%d)\n", err)); ++ } else { ++ /* For nmodeonly check bw cap */ ++ err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap); ++ if (unlikely(err)) { ++ WL_ERR(("error get mimo_bw_cap (%d)\n", err)); ++ } ++ } ++ ++ err = wl_construct_reginfo(wl, bw_cap); ++ if (err) { ++ WL_ERR(("wl_construct_reginfo() fails err=%d\n", err)); ++ if (err != BCME_UNSUPPORTED) ++ goto end_bands; ++ /* Ignore error if "chanspecs" command is not supported */ ++ err = 0; ++ } ++ ++ nband = bandlist[0]; ++ ++ for (i = 1; i <= nband && i < ARRAYSIZE(bandlist); i++) { ++ index = -1; ++ if (bandlist[i] == WLC_BAND_5G && __wl_band_5ghz_a.n_channels > 0) { ++ bands[IEEE80211_BAND_5GHZ] = ++ &__wl_band_5ghz_a; ++ index = IEEE80211_BAND_5GHZ; ++ if (bw_cap == WLC_N_BW_40ALL || bw_cap == WLC_N_BW_20IN2G_40IN5G) ++ bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; ++ } ++ else if (bandlist[i] == WLC_BAND_2G && __wl_band_2ghz.n_channels > 0) { ++ bands[IEEE80211_BAND_2GHZ] = ++ &__wl_band_2ghz; ++ index = IEEE80211_BAND_2GHZ; ++ if (bw_cap == WLC_N_BW_40ALL) ++ bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; ++ } ++ ++ if ((index >= 0) && nmode) { ++ bands[index]->ht_cap.cap |= ++ (IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_DSSSCCK40); ++ bands[index]->ht_cap.ht_supported = TRUE; ++ bands[index]->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; ++ bands[index]->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; ++ /* An HT shall support all EQM rates for one spatial stream */ ++ bands[index]->ht_cap.mcs.rx_mask[0] = 0xff; ++ } ++ ++ } ++ ++ wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ]; ++ wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ]; ++ ++ if (notify) ++ wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); ++ ++end_bands: ++ if (rollback_lock) ++ mutex_unlock(&wl->usr_sync); ++ return err; ++} ++ ++static s32 __wl_cfg80211_up(struct wl_priv *wl) ++{ ++ s32 err = 0; ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ struct wireless_dev *wdev = ndev->ieee80211_ptr; ++ ++ WL_DBG(("In\n")); ++ ++ err = dhd_config_dongle(wl, false); ++ if (unlikely(err)) ++ return err; ++ ++ err = wl_config_ifmode(wl, ndev, wdev->iftype); ++ if (unlikely(err && err != -EINPROGRESS)) { ++ WL_ERR(("wl_config_ifmode failed\n")); ++ } ++ err = wl_update_wiphybands(wl, true); ++ if (unlikely(err)) { ++ WL_ERR(("wl_update_wiphybands failed\n")); ++ } ++ ++ err = dhd_monitor_init(wl->pub); ++ err = wl_invoke_iscan(wl); ++ ++#ifdef WL_HOST_BAND_MGMT ++ /* By default the curr_band is initialized to BAND_AUTO */ ++ if (wl_cfg80211_set_band(ndev, WLC_BAND_AUTO) < 0) { ++ WL_ERR(("roam_band set failed\n")); ++ err = -1; ++ } ++#endif /* WL_HOST_BAND_MGMT */ ++ ++#if defined(DHCP_SCAN_SUPPRESS) ++ /* wlan scan_supp timer and work thread info */ ++ init_timer(&wl->scan_supp_timer); ++ wl->scan_supp_timer.data = (ulong)wl; ++ wl->scan_supp_timer.function = wl_cfg80211_scan_supp_timerfunc; ++ INIT_WORK(&wl->wlan_work, wl_cfg80211_work_handler); ++#endif /* DHCP_SCAN_SUPPRESS */ ++ ++ wl_set_drv_status(wl, READY, ndev); ++ return err; ++} ++ ++static s32 __wl_cfg80211_down(struct wl_priv *wl) ++{ ++ s32 err = 0; ++ unsigned long flags; ++ struct net_info *iter, *next; ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ struct net_device *p2p_net = wl->p2p_net; ++ u32 bssidx = wl_cfgp2p_find_idx(wl, ndev); ++ WL_DBG(("In\n")); ++ ++#if defined(DHCP_SCAN_SUPPRESS) ++ /* Force clear of scan_suppress */ ++ if (wl->scan_suppressed) ++ wl_cfg80211_scan_suppress(ndev, 0); ++ if (timer_pending(&wl->scan_supp_timer)) ++ del_timer_sync(&wl->scan_supp_timer); ++ cancel_work_sync(&wl->wlan_work); ++#endif /* DHCP_SCAN_SUPPRESS */ ++ ++ /* If BSS is operational (e.g SoftAp), bring it down */ ++ if (wl_cfgp2p_bss_isup(ndev, bssidx)) { ++ if (wl_cfgp2p_bss(wl, ndev, bssidx, 0) < 0) ++ WL_ERR(("BSS down failed \n")); ++ } ++ ++ /* Check if cfg80211 interface is already down */ ++ if (!wl_get_drv_status(wl, READY, ndev)) ++ return err; /* it is even not ready */ ++ ++ for_each_ndev(wl, iter, next) ++ wl_set_drv_status(wl, SCAN_ABORTING, iter->ndev); ++ ++ wl_term_iscan(wl); ++ spin_lock_irqsave(&wl->cfgdrv_lock, flags); ++ if (wl->scan_request) { ++ cfg80211_scan_done(wl->scan_request, true); ++ wl->scan_request = NULL; ++ } ++ spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); ++ ++ for_each_ndev(wl, iter, next) { ++ wl_clr_drv_status(wl, READY, iter->ndev); ++ wl_clr_drv_status(wl, SCANNING, iter->ndev); ++ wl_clr_drv_status(wl, SCAN_ABORTING, iter->ndev); ++ wl_clr_drv_status(wl, CONNECTING, iter->ndev); ++ wl_clr_drv_status(wl, CONNECTED, iter->ndev); ++ wl_clr_drv_status(wl, DISCONNECTING, iter->ndev); ++ wl_clr_drv_status(wl, AP_CREATED, iter->ndev); ++ wl_clr_drv_status(wl, AP_CREATING, iter->ndev); ++ } ++ wl_to_prmry_ndev(wl)->ieee80211_ptr->iftype = ++ NL80211_IFTYPE_STATION; ++ if (p2p_net) ++ dev_close(p2p_net); ++ DNGL_FUNC(dhd_cfg80211_down, (wl)); ++ wl_flush_eq(wl); ++ wl_link_down(wl); ++ if (wl->p2p_supported) ++ wl_cfgp2p_down(wl); ++ dhd_monitor_uninit(); ++ ++ return err; ++} ++ ++s32 wl_cfg80211_up(void *para) ++{ ++ struct wl_priv *wl; ++ s32 err = 0; ++ int val = 1; ++ dhd_pub_t *dhd; ++ ++ (void)para; ++ WL_DBG(("In\n")); ++ wl = wlcfg_drv_priv; ++ ++ if ((err = wldev_ioctl(wl_to_prmry_ndev(wl), WLC_GET_VERSION, &val, ++ sizeof(int), false) < 0)) { ++ WL_ERR(("WLC_GET_VERSION failed, err=%d\n", err)); ++ return err; ++ } ++ val = dtoh32(val); ++ if (val != WLC_IOCTL_VERSION && val != 1) { ++ WL_ERR(("Version mismatch, please upgrade. Got %d, expected %d or 1\n", ++ val, WLC_IOCTL_VERSION)); ++ return BCME_VERSION; ++ } ++ ioctl_version = val; ++ WL_TRACE(("WLC_GET_VERSION=%d\n", ioctl_version)); ++ ++ mutex_lock(&wl->usr_sync); ++ dhd = (dhd_pub_t *)(wl->pub); ++ if (!(dhd->op_mode & DHD_FLAG_HOSTAP_MODE)) { ++ err = wl_cfg80211_attach_post(wl_to_prmry_ndev(wl)); ++ if (unlikely(err)) ++ return err; ++ } ++ err = __wl_cfg80211_up(wl); ++ if (unlikely(err)) ++ WL_ERR(("__wl_cfg80211_up failed\n")); ++ mutex_unlock(&wl->usr_sync); ++ return err; ++} ++ ++/* Private Event to Supplicant with indication that chip hangs */ ++int wl_cfg80211_hang(struct net_device *dev, u16 reason) ++{ ++ struct wl_priv *wl; ++ wl = wlcfg_drv_priv; ++ ++ WL_ERR(("In : chip crash eventing\n")); ++ cfg80211_disconnected(dev, reason, NULL, 0, GFP_KERNEL); ++ if (wl != NULL) { ++ wl_link_down(wl); ++ } ++ return 0; ++} ++ ++s32 wl_cfg80211_down(void *para) ++{ ++ struct wl_priv *wl; ++ s32 err = 0; ++ ++ (void)para; ++ WL_DBG(("In\n")); ++ wl = wlcfg_drv_priv; ++ mutex_lock(&wl->usr_sync); ++ err = __wl_cfg80211_down(wl); ++ mutex_unlock(&wl->usr_sync); ++ ++ return err; ++} ++ ++static void *wl_read_prof(struct wl_priv *wl, struct net_device *ndev, s32 item) ++{ ++ unsigned long flags; ++ void *rptr = NULL; ++ struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); ++ ++ if (!profile) ++ return NULL; ++ spin_lock_irqsave(&wl->cfgdrv_lock, flags); ++ switch (item) { ++ case WL_PROF_SEC: ++ rptr = &profile->sec; ++ break; ++ case WL_PROF_ACT: ++ rptr = &profile->active; ++ break; ++ case WL_PROF_BSSID: ++ rptr = profile->bssid; ++ break; ++ case WL_PROF_SSID: ++ rptr = &profile->ssid; ++ break; ++ case WL_PROF_CHAN: ++ rptr = &profile->channel; ++ break; ++ } ++ spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); ++ if (!rptr) ++ WL_ERR(("invalid item (%d)\n", item)); ++ return rptr; ++} ++ ++static s32 ++wl_update_prof(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data, s32 item) ++{ ++ s32 err = 0; ++ struct wlc_ssid *ssid; ++ unsigned long flags; ++ struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); ++ ++ if (!profile) ++ return WL_INVALID; ++ spin_lock_irqsave(&wl->cfgdrv_lock, flags); ++ switch (item) { ++ case WL_PROF_SSID: ++ ssid = (wlc_ssid_t *) data; ++ memset(profile->ssid.SSID, 0, ++ sizeof(profile->ssid.SSID)); ++ memcpy(profile->ssid.SSID, ssid->SSID, ssid->SSID_len); ++ profile->ssid.SSID_len = ssid->SSID_len; ++ break; ++ case WL_PROF_BSSID: ++ if (data) ++ memcpy(profile->bssid, data, ETHER_ADDR_LEN); ++ else ++ memset(profile->bssid, 0, ETHER_ADDR_LEN); ++ break; ++ case WL_PROF_SEC: ++ memcpy(&profile->sec, data, sizeof(profile->sec)); ++ break; ++ case WL_PROF_ACT: ++ profile->active = *(bool *)data; ++ break; ++ case WL_PROF_BEACONINT: ++ profile->beacon_interval = *(u16 *)data; ++ break; ++ case WL_PROF_DTIMPERIOD: ++ profile->dtim_period = *(u8 *)data; ++ break; ++ case WL_PROF_CHAN: ++ profile->channel = *(u32*)data; ++ break; ++ default: ++ err = -EOPNOTSUPP; ++ break; ++ } ++ spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); ++ ++ if (err == EOPNOTSUPP) ++ WL_ERR(("unsupported item (%d)\n", item)); ++ ++ return err; ++} ++ ++void wl_cfg80211_dbg_level(u32 level) ++{ ++ /* ++ * prohibit to change debug level ++ * by insmod parameter. ++ * eventually debug level will be configured ++ * in compile time by using CONFIG_XXX ++ */ ++ /* wl_dbg_level = level; */ ++} ++ ++static bool wl_is_ibssmode(struct wl_priv *wl, struct net_device *ndev) ++{ ++ return wl_get_mode_by_netdev(wl, ndev) == WL_MODE_IBSS; ++} ++ ++static __used bool wl_is_ibssstarter(struct wl_priv *wl) ++{ ++ return wl->ibss_starter; ++} ++ ++static void wl_rst_ie(struct wl_priv *wl) ++{ ++ struct wl_ie *ie = wl_to_ie(wl); ++ ++ ie->offset = 0; ++} ++ ++static __used s32 wl_add_ie(struct wl_priv *wl, u8 t, u8 l, u8 *v) ++{ ++ struct wl_ie *ie = wl_to_ie(wl); ++ s32 err = 0; ++ ++ if (unlikely(ie->offset + l + 2 > WL_TLV_INFO_MAX)) { ++ WL_ERR(("ei crosses buffer boundary\n")); ++ return -ENOSPC; ++ } ++ ie->buf[ie->offset] = t; ++ ie->buf[ie->offset + 1] = l; ++ memcpy(&ie->buf[ie->offset + 2], v, l); ++ ie->offset += l + 2; ++ ++ return err; ++} ++ ++static s32 wl_mrg_ie(struct wl_priv *wl, u8 *ie_stream, u16 ie_size) ++{ ++ struct wl_ie *ie = wl_to_ie(wl); ++ s32 err = 0; ++ ++ if (unlikely(ie->offset + ie_size > WL_TLV_INFO_MAX)) { ++ WL_ERR(("ei_stream crosses buffer boundary\n")); ++ return -ENOSPC; ++ } ++ memcpy(&ie->buf[ie->offset], ie_stream, ie_size); ++ ie->offset += ie_size; ++ ++ return err; ++} ++ ++static s32 wl_cp_ie(struct wl_priv *wl, u8 *dst, u16 dst_size) ++{ ++ struct wl_ie *ie = wl_to_ie(wl); ++ s32 err = 0; ++ ++ if (unlikely(ie->offset > dst_size)) { ++ WL_ERR(("dst_size is not enough\n")); ++ return -ENOSPC; ++ } ++ memcpy(dst, &ie->buf[0], ie->offset); ++ ++ return err; ++} ++ ++static u32 wl_get_ielen(struct wl_priv *wl) ++{ ++ struct wl_ie *ie = wl_to_ie(wl); ++ ++ return ie->offset; ++} ++ ++static void wl_link_up(struct wl_priv *wl) ++{ ++ wl->link_up = true; ++} ++ ++static void wl_link_down(struct wl_priv *wl) ++{ ++ struct wl_connect_info *conn_info = wl_to_conn(wl); ++ ++ WL_DBG(("In\n")); ++ wl->link_up = false; ++ conn_info->req_ie_len = 0; ++ conn_info->resp_ie_len = 0; ++} ++ ++static unsigned long wl_lock_eq(struct wl_priv *wl) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&wl->eq_lock, flags); ++ return flags; ++} ++ ++static void wl_unlock_eq(struct wl_priv *wl, unsigned long flags) ++{ ++ spin_unlock_irqrestore(&wl->eq_lock, flags); ++} ++ ++static void wl_init_eq_lock(struct wl_priv *wl) ++{ ++ spin_lock_init(&wl->eq_lock); ++} ++ ++static void wl_delay(u32 ms) ++{ ++ if (in_atomic() || (ms < jiffies_to_msecs(1))) { ++ mdelay(ms); ++ } else { ++ msleep(ms); ++ } ++} ++ ++s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ struct ether_addr p2pif_addr; ++ struct ether_addr primary_mac; ++ if (!wl->p2p) ++ return -1; ++ if (!p2p_is_on(wl)) { ++ get_primary_mac(wl, &primary_mac); ++ wl_cfgp2p_generate_bss_mac(&primary_mac, p2pdev_addr, &p2pif_addr); ++ } else { ++ memcpy(p2pdev_addr->octet, ++ wl->p2p->dev_addr.octet, ETHER_ADDR_LEN); ++ } ++ ++ ++ return 0; ++} ++s32 wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len) ++{ ++ struct wl_priv *wl; ++ ++ wl = wlcfg_drv_priv; ++ ++ return wl_cfgp2p_set_p2p_noa(wl, net, buf, len); ++} ++ ++s32 wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len) ++{ ++ struct wl_priv *wl; ++ wl = wlcfg_drv_priv; ++ ++ return wl_cfgp2p_get_p2p_noa(wl, net, buf, len); ++} ++ ++s32 wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len) ++{ ++ struct wl_priv *wl; ++ wl = wlcfg_drv_priv; ++ ++ return wl_cfgp2p_set_p2p_ps(wl, net, buf, len); ++} ++ ++s32 wl_cfg80211_channel_to_freq(u32 channel) ++{ ++ int freq = 0; ++ ++#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) ++ freq = ieee80211_channel_to_frequency(channel); ++#else ++ { ++ u16 band = 0; ++ if (channel <= CH_MAX_2G_CHANNEL) ++ band = IEEE80211_BAND_2GHZ; ++ else ++ band = IEEE80211_BAND_5GHZ; ++ freq = ieee80211_channel_to_frequency(channel, band); ++ } ++#endif ++ return freq; ++} ++ ++s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, ++ enum wl_management_type type) ++{ ++ struct wl_priv *wl; ++ struct net_device *ndev = NULL; ++ struct ether_addr primary_mac; ++ s32 ret = 0; ++ s32 bssidx = 0; ++ s32 pktflag = 0; ++ wl = wlcfg_drv_priv; ++ ++ if (wl_get_drv_status(wl, AP_CREATING, net) || ++ wl_get_drv_status(wl, AP_CREATED, net)) { ++ ndev = net; ++ bssidx = 0; ++ } else if (wl->p2p) { ++ if (net == wl->p2p_net) { ++ net = wl_to_prmry_ndev(wl); ++ } ++ if (!wl->p2p->on) { ++ get_primary_mac(wl, &primary_mac); ++ wl_cfgp2p_generate_bss_mac(&primary_mac, &wl->p2p->dev_addr, ++ &wl->p2p->int_addr); ++ /* In case of p2p_listen command, supplicant send remain_on_channel ++ * without turning on P2P ++ */ ++ ++ p2p_on(wl) = true; ++ ret = wl_cfgp2p_enable_discovery(wl, net, NULL, 0); ++ ++ if (unlikely(ret)) { ++ goto exit; ++ } ++ } ++ if (net != wl_to_prmry_ndev(wl)) { ++ if (wl_get_mode_by_netdev(wl, net) == WL_MODE_AP) { ++ ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); ++ bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION); ++ } ++ } else { ++ ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); ++ bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); ++ } ++ } ++ if (ndev != NULL) { ++ switch (type) { ++ case WL_BEACON: ++ pktflag = VNDR_IE_BEACON_FLAG; ++ break; ++ case WL_PROBE_RESP: ++ pktflag = VNDR_IE_PRBRSP_FLAG; ++ break; ++ case WL_ASSOC_RESP: ++ pktflag = VNDR_IE_ASSOCRSP_FLAG; ++ break; ++ } ++ if (pktflag) ++ ret = wl_cfgp2p_set_management_ie(wl, ndev, bssidx, pktflag, buf, len); ++ } ++exit: ++ return ret; ++} ++ ++static const struct rfkill_ops wl_rfkill_ops = { ++ .set_block = wl_rfkill_set ++}; ++ ++static int wl_rfkill_set(void *data, bool blocked) ++{ ++ struct wl_priv *wl = (struct wl_priv *)data; ++ ++ WL_DBG(("Enter \n")); ++ WL_DBG(("RF %s\n", blocked ? "blocked" : "unblocked")); ++ ++ if (!wl) ++ return -EINVAL; ++ ++ wl->rf_blocked = blocked; ++ ++ return 0; ++} ++ ++static int wl_setup_rfkill(struct wl_priv *wl, bool setup) ++{ ++ s32 err = 0; ++ ++ WL_DBG(("Enter \n")); ++ if (!wl) ++ return -EINVAL; ++ if (setup) { ++ wl->rfkill = rfkill_alloc("brcmfmac-wifi", ++ wl_cfg80211_get_parent_dev(), ++ RFKILL_TYPE_WLAN, &wl_rfkill_ops, (void *)wl); ++ ++ if (!wl->rfkill) { ++ err = -ENOMEM; ++ goto err_out; ++ } ++ ++ err = rfkill_register(wl->rfkill); ++ ++ if (err) ++ rfkill_destroy(wl->rfkill); ++ } else { ++ if (!wl->rfkill) { ++ err = -ENOMEM; ++ goto err_out; ++ } ++ ++ rfkill_unregister(wl->rfkill); ++ rfkill_destroy(wl->rfkill); ++ } ++ ++err_out: ++ return err; ++} ++ ++struct device *wl_cfg80211_get_parent_dev(void) ++{ ++ return cfg80211_parent_dev; ++} ++ ++void wl_cfg80211_set_parent_dev(void *dev) ++{ ++ cfg80211_parent_dev = dev; ++} ++ ++static void wl_cfg80211_clear_parent_dev(void) ++{ ++ cfg80211_parent_dev = NULL; ++} ++ ++static void get_primary_mac(struct wl_priv *wl, struct ether_addr *mac) ++{ ++ wldev_iovar_getbuf_bsscfg(wl_to_prmry_ndev(wl), "cur_etheraddr", NULL, ++ 0, wl->ioctl_buf, WLC_IOCTL_SMLEN, 0, &wl->ioctl_buf_sync); ++ memcpy(mac->octet, wl->ioctl_buf, ETHER_ADDR_LEN); ++} ++ ++int wl_cfg80211_do_driver_init(struct net_device *net) ++{ ++ struct wl_priv *wl = *(struct wl_priv **)netdev_priv(net); ++ ++ if (!wl || !wl->wdev) ++ return -EINVAL; ++ ++ if (dhd_do_driver_init(wl->wdev->netdev) < 0) ++ return -1; ++ ++ return 0; ++} ++ ++void wl_cfg80211_enable_trace(bool set, u32 level) ++{ ++ if (set) ++ wl_dbg_level = level & WL_DBG_LEVEL; ++ else ++ wl_dbg_level |= (WL_DBG_LEVEL & level); ++} ++ ++static s32 ++wl_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, ++ struct net_device *dev, u64 cookie) ++{ ++ /* CFG80211 checks for tx_cancel_wait callback when ATTR_DURATION ++ * is passed with CMD_FRAME. This callback is supposed to cancel ++ * the OFFCHANNEL Wait. Since we are already taking care of that ++ * with the tx_mgmt logic, do nothing here. ++ */ ++ ++ return 0; ++} ++ ++#ifdef WL_HOST_BAND_MGMT ++s32 ++wl_cfg80211_set_band(struct net_device *ndev, int band) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ int ret = 0; ++ char ioctl_buf[50]; ++ ++ if ((band < WLC_BAND_AUTO) || (band > WLC_BAND_2G)) { ++ WL_ERR(("Invalid band\n")); ++ return -EINVAL; ++ } ++ ++ if ((ret = wldev_iovar_setbuf(ndev, "roam_band", &band, ++ sizeof(int), ioctl_buf, sizeof(ioctl_buf), NULL)) < 0) { ++ WL_ERR(("seting roam_band failed code=%d\n", ret)); ++ return ret; ++ } ++ ++ WL_DBG(("Setting band to %d\n", band)); ++ wl->curr_band = band; ++ ++ return 0; ++} ++#endif /* WL_HOST_BAND_MGMT */ ++ ++#if defined(DHCP_SCAN_SUPPRESS) ++static void wl_cfg80211_scan_supp_timerfunc(ulong data) ++{ ++ struct wl_priv *wl = (struct wl_priv *)data; ++ ++ WL_DBG(("Enter \n")); ++ schedule_work(&wl->wlan_work); ++} ++ ++static void wl_cfg80211_work_handler(struct work_struct *work) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ ++ wl = container_of(work, struct wl_priv, wlan_work); ++ ++ if (!wl) { ++ WL_ERR(("wl_priv ptr NULL\n")); ++ return; ++ } ++ ++ if (wl->scan_suppressed) { ++ /* There is pending scan_suppress. Clean it */ ++ WL_ERR(("Clean up from timer after %d msec\n", WL_SCAN_SUPPRESS_TIMEOUT)); ++ wl_cfg80211_scan_suppress(wl_to_prmry_ndev(wl), 0); ++ } ++} ++ ++int wl_cfg80211_scan_suppress(struct net_device *dev, int suppress) ++{ ++ struct wl_priv *wl = wlcfg_drv_priv; ++ int ret = 0; ++ ++ if (!dev || !wl || ((suppress != 0) && (suppress != 1))) ++ return -EINVAL; ++ ++ if (suppress == wl->scan_suppressed) { ++ WL_DBG(("No change in scan_suppress state. Ignoring cmd..\n")); ++ return 0; ++ } ++ ++ if (timer_pending(&wl->scan_supp_timer)) ++ del_timer_sync(&wl->scan_supp_timer); ++ ++ if ((ret = wldev_ioctl(dev, WLC_SET_SCANSUPPRESS, ++ &suppress, sizeof(int), true)) < 0) { ++ WL_ERR(("Scan suppress setting failed ret:%d \n", ret)); ++ } else { ++ WL_DBG(("Scan suppress %s \n", suppress ? "Enabled" : "Disabled")); ++ wl->scan_suppressed = suppress; ++ } ++ ++ /* If scan_suppress is set, Start a timer to monitor it (just incase) */ ++ if (wl->scan_suppressed) { ++ if (ret) { ++ WL_ERR(("Retry scan_suppress reset at a later time \n")); ++ mod_timer(&wl->scan_supp_timer, ++ jiffies + msecs_to_jiffies(WL_SCAN_SUPPRESS_RETRY)); ++ } else { ++ WL_DBG(("Start wlan_timer to clear of scan_suppress \n")); ++ mod_timer(&wl->scan_supp_timer, ++ jiffies + msecs_to_jiffies(WL_SCAN_SUPPRESS_TIMEOUT)); ++ } ++ } ++ ++ return ret; ++} ++#endif /* DHCP_SCAN_SUPPRESS */ +diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h +new file mode 100644 +index 00000000..fc300899 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h +@@ -0,0 +1,850 @@ ++/* ++ * Linux cfg80211 driver ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wl_cfg80211.h 378667 2013-01-14 10:11:50Z $ ++ */ ++ ++#ifndef _wl_cfg80211_h_ ++#define _wl_cfg80211_h_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++struct wl_conf; ++struct wl_iface; ++struct wl_priv; ++struct wl_security; ++struct wl_ibss; ++ ++ ++#define htod32(i) i ++#define htod16(i) i ++#define dtoh32(i) i ++#define dtoh16(i) i ++#define htodchanspec(i) i ++#define dtohchanspec(i) i ++ ++#define WL_DBG_NONE 0 ++#define WL_DBG_P2P_ACTION (1 << 5) ++#define WL_DBG_TRACE (1 << 4) ++#define WL_DBG_SCAN (1 << 3) ++#define WL_DBG_DBG (1 << 2) ++#define WL_DBG_INFO (1 << 1) ++#define WL_DBG_ERR (1 << 0) ++ ++/* 0 invalidates all debug messages. default is 1 */ ++#define WL_DBG_LEVEL 0xFF ++ ++#define CFG80211_ERROR_TEXT "CFG80211-ERROR) " ++ ++#if defined(DHD_DEBUG) ++#define WL_ERR(args) \ ++do { \ ++ if (wl_dbg_level & WL_DBG_ERR) { \ ++ printk(KERN_INFO CFG80211_ERROR_TEXT "%s : ", __func__); \ ++ printk args; \ ++ } \ ++} while (0) ++#else /* defined(DHD_DEBUG) */ ++#define WL_ERR(args) \ ++do { \ ++ if ((wl_dbg_level & WL_DBG_ERR) && net_ratelimit()) { \ ++ printk(KERN_INFO CFG80211_ERROR_TEXT "%s : ", __func__); \ ++ printk args; \ ++ } \ ++} while (0) ++#endif /* defined(DHD_DEBUG) */ ++ ++#ifdef WL_INFO ++#undef WL_INFO ++#endif ++#define WL_INFO(args) \ ++do { \ ++ if (wl_dbg_level & WL_DBG_INFO) { \ ++ printk(KERN_INFO "CFG80211-INFO) %s : ", __func__); \ ++ printk args; \ ++ } \ ++} while (0) ++#ifdef WL_SCAN ++#undef WL_SCAN ++#endif ++#define WL_SCAN(args) \ ++do { \ ++ if (wl_dbg_level & WL_DBG_SCAN) { \ ++ printk(KERN_INFO "CFG80211-SCAN) %s :", __func__); \ ++ printk args; \ ++ } \ ++} while (0) ++#ifdef WL_TRACE ++#undef WL_TRACE ++#endif ++#define WL_TRACE(args) \ ++do { \ ++ if (wl_dbg_level & WL_DBG_TRACE) { \ ++ printk(KERN_INFO "CFG80211-TRACE) %s :", __func__); \ ++ printk args; \ ++ } \ ++} while (0) ++#ifdef WL_TRACE_HW4 ++#undef WL_TRACE_HW4 ++#endif ++#define WL_TRACE_HW4 WL_TRACE ++#if (WL_DBG_LEVEL > 0) ++#define WL_DBG(args) \ ++do { \ ++ if (wl_dbg_level & WL_DBG_DBG) { \ ++ printk(KERN_DEBUG "CFG80211-DEBUG) %s :", __func__); \ ++ printk args; \ ++ } \ ++} while (0) ++#else /* !(WL_DBG_LEVEL > 0) */ ++#define WL_DBG(args) ++#endif /* (WL_DBG_LEVEL > 0) */ ++#define WL_PNO(x) ++#define WL_SD(x) ++ ++ ++#define WL_SCAN_RETRY_MAX 3 ++#define WL_NUM_PMKIDS_MAX MAXPMKID ++#define WL_SCAN_BUF_MAX (1024 * 8) ++#define WL_TLV_INFO_MAX 1500 ++#define WL_SCAN_IE_LEN_MAX 2048 ++#define WL_BSS_INFO_MAX 2048 ++#define WL_ASSOC_INFO_MAX 512 ++#define WL_IOCTL_LEN_MAX 1024 ++#define WL_EXTRA_BUF_MAX 2048 ++#define WL_ISCAN_BUF_MAX 2048 ++#define WL_ISCAN_TIMER_INTERVAL_MS 3000 ++#define WL_SCAN_ERSULTS_LAST (WL_SCAN_RESULTS_NO_MEM+1) ++#define WL_AP_MAX 256 ++#define WL_FILE_NAME_MAX 256 ++#define WL_DWELL_TIME 200 ++#define WL_MED_DWELL_TIME 400 ++#define WL_MIN_DWELL_TIME 100 ++#define WL_LONG_DWELL_TIME 1000 ++#define IFACE_MAX_CNT 2 ++#define WL_SCAN_CONNECT_DWELL_TIME_MS 200 ++#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20 ++#define WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 ++#define WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 ++#define WL_AF_TX_MAX_RETRY 5 ++ ++#define WL_SCAN_TIMER_INTERVAL_MS 8000 /* Scan timeout */ ++#define WL_CHANNEL_SYNC_RETRY 5 ++#define WL_INVALID -1 ++ ++/* Bring down SCB Timeout to 20secs from 60secs default */ ++#ifndef WL_SCB_TIMEOUT ++#define WL_SCB_TIMEOUT 20 ++#endif ++ ++/* SCAN_SUPPRESS timer values in ms */ ++#define WL_SCAN_SUPPRESS_TIMEOUT 31000 /* default Framwork DHCP timeout is 30 sec */ ++#define WL_SCAN_SUPPRESS_RETRY 3000 ++ ++/* driver status */ ++enum wl_status { ++ WL_STATUS_READY = 0, ++ WL_STATUS_SCANNING, ++ WL_STATUS_SCAN_ABORTING, ++ WL_STATUS_CONNECTING, ++ WL_STATUS_CONNECTED, ++ WL_STATUS_DISCONNECTING, ++ WL_STATUS_AP_CREATING, ++ WL_STATUS_AP_CREATED, ++ /* whole sending action frame procedure: ++ * includes a) 'finding common channel' for public action request frame ++ * and b) 'sending af via 'actframe' iovar' ++ */ ++ WL_STATUS_SENDING_ACT_FRM, ++ /* find a peer to go to a common channel before sending public action req frame */ ++ WL_STATUS_FINDING_COMMON_CHANNEL, ++ /* waiting for next af to sync time of supplicant. ++ * it includes SENDING_ACT_FRM and WAITING_NEXT_ACT_FRM_LISTEN ++ */ ++ WL_STATUS_WAITING_NEXT_ACT_FRM, ++#ifdef WL_CFG80211_SYNC_GON ++ /* go to listen state to wait for next af after SENDING_ACT_FRM */ ++ WL_STATUS_WAITING_NEXT_ACT_FRM_LISTEN, ++#endif /* WL_CFG80211_SYNC_GON */ ++ /* it will be set when upper layer requests listen and succeed in setting listen mode. ++ * if set, other scan request can abort current listen state ++ */ ++ WL_STATUS_REMAINING_ON_CHANNEL, ++#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++ /* it's fake listen state to keep current scan state. ++ * it will be set when upper layer requests listen but scan is running. then just run ++ * a expire timer without actual listen state. ++ * if set, other scan request does not need to abort scan. ++ */ ++ WL_STATUS_FAKE_REMAINING_ON_CHANNEL ++#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ ++}; ++ ++/* wi-fi mode */ ++enum wl_mode { ++ WL_MODE_BSS, ++ WL_MODE_IBSS, ++ WL_MODE_AP ++}; ++ ++/* driver profile list */ ++enum wl_prof_list { ++ WL_PROF_MODE, ++ WL_PROF_SSID, ++ WL_PROF_SEC, ++ WL_PROF_IBSS, ++ WL_PROF_BAND, ++ WL_PROF_CHAN, ++ WL_PROF_BSSID, ++ WL_PROF_ACT, ++ WL_PROF_BEACONINT, ++ WL_PROF_DTIMPERIOD ++}; ++ ++/* driver iscan state */ ++enum wl_iscan_state { ++ WL_ISCAN_STATE_IDLE, ++ WL_ISCAN_STATE_SCANING ++}; ++ ++/* donlge escan state */ ++enum wl_escan_state { ++ WL_ESCAN_STATE_IDLE, ++ WL_ESCAN_STATE_SCANING ++}; ++/* fw downloading status */ ++enum wl_fw_status { ++ WL_FW_LOADING_DONE, ++ WL_NVRAM_LOADING_DONE ++}; ++ ++enum wl_management_type { ++ WL_BEACON = 0x1, ++ WL_PROBE_RESP = 0x2, ++ WL_ASSOC_RESP = 0x4 ++}; ++/* beacon / probe_response */ ++struct beacon_proberesp { ++ __le64 timestamp; ++ __le16 beacon_int; ++ __le16 capab_info; ++ u8 variable[0]; ++} __attribute__ ((packed)); ++ ++/* driver configuration */ ++struct wl_conf { ++ u32 frag_threshold; ++ u32 rts_threshold; ++ u32 retry_short; ++ u32 retry_long; ++ s32 tx_power; ++ struct ieee80211_channel channel; ++}; ++ ++typedef s32(*EVENT_HANDLER) (struct wl_priv *wl, ++ struct net_device *ndev, const wl_event_msg_t *e, void *data); ++ ++/* bss inform structure for cfg80211 interface */ ++struct wl_cfg80211_bss_info { ++ u16 band; ++ u16 channel; ++ s16 rssi; ++ u16 frame_len; ++ u8 frame_buf[1]; ++}; ++ ++/* basic structure of scan request */ ++struct wl_scan_req { ++ struct wlc_ssid ssid; ++}; ++ ++/* basic structure of information element */ ++struct wl_ie { ++ u16 offset; ++ u8 buf[WL_TLV_INFO_MAX]; ++}; ++ ++/* event queue for cfg80211 main event */ ++struct wl_event_q { ++ struct list_head eq_list; ++ u32 etype; ++ wl_event_msg_t emsg; ++ s8 edata[1]; ++}; ++ ++/* security information with currently associated ap */ ++struct wl_security { ++ u32 wpa_versions; ++ u32 auth_type; ++ u32 cipher_pairwise; ++ u32 cipher_group; ++ u32 wpa_auth; ++ u32 auth_assoc_res_status; ++}; ++ ++/* ibss information for currently joined ibss network */ ++struct wl_ibss { ++ u8 beacon_interval; /* in millisecond */ ++ u8 atim; /* in millisecond */ ++ s8 join_only; ++ u8 band; ++ u8 channel; ++}; ++ ++/* wl driver profile */ ++struct wl_profile { ++ u32 mode; ++ s32 band; ++ u32 channel; ++ struct wlc_ssid ssid; ++ struct wl_security sec; ++ struct wl_ibss ibss; ++ u8 bssid[ETHER_ADDR_LEN]; ++ u16 beacon_interval; ++ u8 dtim_period; ++ bool active; ++}; ++ ++struct net_info { ++ struct net_device *ndev; ++ struct wireless_dev *wdev; ++ struct wl_profile profile; ++ s32 mode; ++ s32 roam_off; ++ unsigned long sme_state; ++ bool pm_restore; ++ bool pm_block; ++ s32 pm; ++ struct list_head list; /* list of all net_info structure */ ++}; ++typedef s32(*ISCAN_HANDLER) (struct wl_priv *wl); ++ ++/* iscan controller */ ++struct wl_iscan_ctrl { ++ struct net_device *dev; ++ struct timer_list timer; ++ u32 timer_ms; ++ u32 timer_on; ++ s32 state; ++ struct task_struct *tsk; ++ struct semaphore sync; ++ ISCAN_HANDLER iscan_handler[WL_SCAN_ERSULTS_LAST]; ++ void *data; ++ s8 ioctl_buf[WLC_IOCTL_SMLEN]; ++ s8 scan_buf[WL_ISCAN_BUF_MAX]; ++}; ++ ++/* association inform */ ++#define MAX_REQ_LINE 1024 ++struct wl_connect_info { ++ u8 req_ie[MAX_REQ_LINE]; ++ s32 req_ie_len; ++ u8 resp_ie[MAX_REQ_LINE]; ++ s32 resp_ie_len; ++}; ++ ++/* firmware /nvram downloading controller */ ++struct wl_fw_ctrl { ++ const struct firmware *fw_entry; ++ unsigned long status; ++ u32 ptr; ++ s8 fw_name[WL_FILE_NAME_MAX]; ++ s8 nvram_name[WL_FILE_NAME_MAX]; ++}; ++ ++/* assoc ie length */ ++struct wl_assoc_ielen { ++ u32 req_len; ++ u32 resp_len; ++}; ++ ++/* wpa2 pmk list */ ++struct wl_pmk_list { ++ pmkid_list_t pmkids; ++ pmkid_t foo[MAXPMKID - 1]; ++}; ++ ++ ++#define ESCAN_BUF_SIZE (64 * 1024) ++ ++struct escan_info { ++ u32 escan_state; ++#if defined(STATIC_WL_PRIV_STRUCT) ++#ifndef CONFIG_DHD_USE_STATIC_BUF ++#error STATIC_WL_PRIV_STRUCT should be used with CONFIG_DHD_USE_STATIC_BUF ++#endif ++ u8 *escan_buf; ++#else ++ u8 escan_buf[ESCAN_BUF_SIZE]; ++#endif /* STATIC_WL_PRIV_STRUCT */ ++ struct wiphy *wiphy; ++ struct net_device *ndev; ++}; ++ ++struct ap_info { ++/* Structure to hold WPS, WPA IEs for a AP */ ++ u8 probe_res_ie[VNDR_IES_MAX_BUF_LEN]; ++ u8 beacon_ie[VNDR_IES_MAX_BUF_LEN]; ++ u32 probe_res_ie_len; ++ u32 beacon_ie_len; ++ u8 *wpa_ie; ++ u8 *rsn_ie; ++ u8 *wps_ie; ++ bool security_mode; ++}; ++struct btcoex_info { ++ struct timer_list timer; ++ u32 timer_ms; ++ u32 timer_on; ++ u32 ts_dhcp_start; /* ms ts ecord time stats */ ++ u32 ts_dhcp_ok; /* ms ts ecord time stats */ ++ bool dhcp_done; /* flag, indicates that host done with ++ * dhcp before t1/t2 expiration ++ */ ++ s32 bt_state; ++ struct work_struct work; ++ struct net_device *dev; ++}; ++ ++struct sta_info { ++ /* Structure to hold WPS IE for a STA */ ++ u8 probe_req_ie[VNDR_IES_BUF_LEN]; ++ u8 assoc_req_ie[VNDR_IES_BUF_LEN]; ++ u32 probe_req_ie_len; ++ u32 assoc_req_ie_len; ++}; ++ ++struct afx_hdl { ++ wl_af_params_t *pending_tx_act_frm; ++ struct ether_addr tx_dst_addr; ++ struct net_device *dev; ++ struct work_struct work; ++ u32 bssidx; ++ u32 retry; ++ s32 peer_chan; ++ s32 peer_listen_chan; /* search channel: configured by upper layer */ ++ s32 my_listen_chan; /* listen chanel: extract it from prb req or gon req */ ++ bool is_listen; ++ bool ack_recv; ++ bool is_active; ++}; ++ ++struct parsed_ies { ++ wpa_ie_fixed_t *wps_ie; ++ u32 wps_ie_len; ++ wpa_ie_fixed_t *wpa_ie; ++ u32 wpa_ie_len; ++ bcm_tlv_t *wpa2_ie; ++ u32 wpa2_ie_len; ++}; ++ ++ ++#define MAX_EVENT_BUF_NUM 16 ++typedef struct wl_eventmsg_buf { ++ u16 num; ++ struct { ++ u16 type; ++ bool set; ++ } event [MAX_EVENT_BUF_NUM]; ++} wl_eventmsg_buf_t; ++ ++/* private data of cfg80211 interface */ ++struct wl_priv { ++ struct wireless_dev *wdev; /* representing wl cfg80211 device */ ++ ++ struct wireless_dev *p2p_wdev; /* representing wl cfg80211 device for P2P */ ++ struct net_device *p2p_net; /* reference to p2p0 interface */ ++ ++ struct wl_conf *conf; ++ struct cfg80211_scan_request *scan_request; /* scan request object */ ++ EVENT_HANDLER evt_handler[WLC_E_LAST]; ++ struct list_head eq_list; /* used for event queue */ ++ struct list_head net_list; /* used for struct net_info */ ++ spinlock_t eq_lock; /* for event queue synchronization */ ++ spinlock_t cfgdrv_lock; /* to protect scan status (and others if needed) */ ++ struct completion act_frm_scan; ++ struct completion iface_disable; ++ struct completion wait_next_af; ++ struct mutex usr_sync; /* maily for up/down synchronization */ ++ struct wl_scan_results *bss_list; ++ struct wl_scan_results *scan_results; ++ ++ /* scan request object for internal purpose */ ++ struct wl_scan_req *scan_req_int; ++ /* information element object for internal purpose */ ++#if defined(STATIC_WL_PRIV_STRUCT) ++ struct wl_ie *ie; ++#else ++ struct wl_ie ie; ++#endif ++ struct wl_iscan_ctrl *iscan; /* iscan controller */ ++ ++ /* association information container */ ++#if defined(STATIC_WL_PRIV_STRUCT) ++ struct wl_connect_info *conn_info; ++#else ++ struct wl_connect_info conn_info; ++#endif ++ ++ struct wl_pmk_list *pmk_list; /* wpa2 pmk list */ ++ tsk_ctl_t event_tsk; /* task of main event handler thread */ ++ void *pub; ++ u32 iface_cnt; ++ u32 channel; /* current channel */ ++ u32 af_sent_channel; /* channel action frame is sent */ ++ /* next af subtype to cancel the remained dwell time in rx process */ ++ u8 next_af_subtype; ++#ifdef WL_CFG80211_SYNC_GON ++ ulong af_tx_sent_jiffies; ++#endif /* WL_CFG80211_SYNC_GON */ ++ bool iscan_on; /* iscan on/off switch */ ++ bool iscan_kickstart; /* indicate iscan already started */ ++ bool escan_on; /* escan on/off switch */ ++ struct escan_info escan_info; /* escan information */ ++ bool active_scan; /* current scan mode */ ++ bool ibss_starter; /* indicates this sta is ibss starter */ ++ bool link_up; /* link/connection up flag */ ++ ++ /* indicate whether chip to support power save mode */ ++ bool pwr_save; ++ bool roam_on; /* on/off switch for self-roaming */ ++ bool scan_tried; /* indicates if first scan attempted */ ++ bool wlfc_on; ++ bool vsdb_mode; ++ bool roamoff_on_concurrent; ++ u8 *ioctl_buf; /* ioctl buffer */ ++ struct mutex ioctl_buf_sync; ++ u8 *escan_ioctl_buf; ++ u8 *extra_buf; /* maily to grab assoc information */ ++ struct dentry *debugfsdir; ++ struct rfkill *rfkill; ++ bool rf_blocked; ++ struct ieee80211_channel remain_on_chan; ++ enum nl80211_channel_type remain_on_chan_type; ++ u64 send_action_id; ++ u64 last_roc_id; ++ wait_queue_head_t netif_change_event; ++ struct completion send_af_done; ++ struct afx_hdl *afx_hdl; ++ struct ap_info *ap_info; ++ struct sta_info *sta_info; ++ struct p2p_info *p2p; ++ bool p2p_supported; ++ struct btcoex_info *btcoex_info; ++ struct timer_list scan_timeout; /* Timer for catch scan event timeout */ ++ s32(*state_notifier) (struct wl_priv *wl, ++ struct net_info *_net_info, enum wl_status state, bool set); ++ unsigned long interrested_state; ++ wlc_ssid_t hostapd_ssid; ++ bool sched_scan_running; /* scheduled scan req status */ ++#ifdef WL_SCHED_SCAN ++ struct cfg80211_sched_scan_request *sched_scan_req; /* scheduled scan req */ ++#endif /* WL_SCHED_SCAN */ ++#ifdef WL_HOST_BAND_MGMT ++ u8 curr_band; ++#endif /* WL_HOST_BAND_MGMT */ ++ bool scan_suppressed; ++ struct timer_list scan_supp_timer; ++ struct work_struct wlan_work; ++ struct mutex event_sync; /* maily for up/down synchronization */ ++}; ++ ++ ++static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss) ++{ ++ return bss = bss ? ++ (struct wl_bss_info *)((uintptr) bss + dtoh32(bss->length)) : list->bss_info; ++} ++static inline s32 ++wl_alloc_netinfo(struct wl_priv *wl, struct net_device *ndev, ++ struct wireless_dev * wdev, s32 mode, bool pm_block) ++{ ++ struct net_info *_net_info; ++ s32 err = 0; ++ if (wl->iface_cnt == IFACE_MAX_CNT) ++ return -ENOMEM; ++ _net_info = kzalloc(sizeof(struct net_info), GFP_KERNEL); ++ if (!_net_info) ++ err = -ENOMEM; ++ else { ++ _net_info->mode = mode; ++ _net_info->ndev = ndev; ++ _net_info->wdev = wdev; ++ _net_info->pm_restore = 0; ++ _net_info->pm = 0; ++ _net_info->pm_block = pm_block; ++ _net_info->roam_off = WL_INVALID; ++ wl->iface_cnt++; ++ list_add(&_net_info->list, &wl->net_list); ++ } ++ return err; ++} ++static inline void ++wl_dealloc_netinfo(struct wl_priv *wl, struct net_device *ndev) ++{ ++ struct net_info *_net_info, *next; ++ ++ list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { ++ if (ndev && (_net_info->ndev == ndev)) { ++ list_del(&_net_info->list); ++ wl->iface_cnt--; ++ if (_net_info->wdev) { ++ kfree(_net_info->wdev); ++ ndev->ieee80211_ptr = NULL; ++ } ++ kfree(_net_info); ++ } ++ } ++ ++} ++static inline void ++wl_delete_all_netinfo(struct wl_priv *wl) ++{ ++ struct net_info *_net_info, *next; ++ ++ list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { ++ list_del(&_net_info->list); ++ if (_net_info->wdev) ++ kfree(_net_info->wdev); ++ kfree(_net_info); ++ } ++ wl->iface_cnt = 0; ++} ++static inline u32 ++wl_get_status_all(struct wl_priv *wl, s32 status) ++ ++{ ++ struct net_info *_net_info, *next; ++ u32 cnt = 0; ++ list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { ++ if (_net_info->ndev && ++ test_bit(status, &_net_info->sme_state)) ++ cnt++; ++ } ++ return cnt; ++} ++static inline void ++wl_set_status_all(struct wl_priv *wl, s32 status, u32 op) ++{ ++ struct net_info *_net_info, *next; ++ list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { ++ switch (op) { ++ case 1: ++ return; /* set all status is not allowed */ ++ case 2: ++ clear_bit(status, &_net_info->sme_state); ++ if (wl->state_notifier && ++ test_bit(status, &(wl->interrested_state))) ++ wl->state_notifier(wl, _net_info, status, false); ++ break; ++ case 4: ++ return; /* change all status is not allowed */ ++ default: ++ return; /* unknown operation */ ++ } ++ } ++} ++static inline void ++wl_set_status_by_netdev(struct wl_priv *wl, s32 status, ++ struct net_device *ndev, u32 op) ++{ ++ ++ struct net_info *_net_info, *next; ++ ++ list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { ++ if (ndev && (_net_info->ndev == ndev)) { ++ switch (op) { ++ case 1: ++ set_bit(status, &_net_info->sme_state); ++ if (wl->state_notifier && ++ test_bit(status, &(wl->interrested_state))) ++ wl->state_notifier(wl, _net_info, status, true); ++ break; ++ case 2: ++ clear_bit(status, &_net_info->sme_state); ++ if (wl->state_notifier && ++ test_bit(status, &(wl->interrested_state))) ++ wl->state_notifier(wl, _net_info, status, false); ++ break; ++ case 4: ++ change_bit(status, &_net_info->sme_state); ++ break; ++ } ++ } ++ ++ } ++ ++} ++ ++static inline u32 ++wl_get_status_by_netdev(struct wl_priv *wl, s32 status, ++ struct net_device *ndev) ++{ ++ struct net_info *_net_info, *next; ++ ++ list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { ++ if (ndev && (_net_info->ndev == ndev)) ++ return test_bit(status, &_net_info->sme_state); ++ } ++ return 0; ++} ++ ++static inline s32 ++wl_get_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev) ++{ ++ struct net_info *_net_info, *next; ++ ++ list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { ++ if (ndev && (_net_info->ndev == ndev)) ++ return _net_info->mode; ++ } ++ return -1; ++} ++ ++ ++static inline void ++wl_set_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev, ++ s32 mode) ++{ ++ struct net_info *_net_info, *next; ++ ++ list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { ++ if (ndev && (_net_info->ndev == ndev)) ++ _net_info->mode = mode; ++ } ++} ++static inline struct wl_profile * ++wl_get_profile_by_netdev(struct wl_priv *wl, struct net_device *ndev) ++{ ++ struct net_info *_net_info, *next; ++ ++ list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { ++ if (ndev && (_net_info->ndev == ndev)) ++ return &_net_info->profile; ++ } ++ return NULL; ++} ++static inline struct net_info * ++wl_get_netinfo_by_netdev(struct wl_priv *wl, struct net_device *ndev) ++{ ++ struct net_info *_net_info, *next; ++ ++ list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { ++ if (ndev && (_net_info->ndev == ndev)) ++ return _net_info; ++ } ++ return NULL; ++} ++#define wl_to_wiphy(w) (w->wdev->wiphy) ++#define wl_to_prmry_ndev(w) (w->wdev->netdev) ++#define ndev_to_wl(n) (wdev_to_wl(n->ieee80211_ptr)) ++#define wl_to_sr(w) (w->scan_req_int) ++#if defined(STATIC_WL_PRIV_STRUCT) ++#define wl_to_ie(w) (w->ie) ++#define wl_to_conn(w) (w->conn_info) ++#else ++#define wl_to_ie(w) (&w->ie) ++#define wl_to_conn(w) (&w->conn_info) ++#endif ++#define iscan_to_wl(i) ((struct wl_priv *)(i->data)) ++#define wl_to_iscan(w) (w->iscan) ++#define wiphy_from_scan(w) (w->escan_info.wiphy) ++#define wl_get_drv_status_all(wl, stat) \ ++ (wl_get_status_all(wl, WL_STATUS_ ## stat)) ++#define wl_get_drv_status(wl, stat, ndev) \ ++ (wl_get_status_by_netdev(wl, WL_STATUS_ ## stat, ndev)) ++#define wl_set_drv_status(wl, stat, ndev) \ ++ (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 1)) ++#define wl_clr_drv_status(wl, stat, ndev) \ ++ (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 2)) ++#define wl_clr_drv_status_all(wl, stat) \ ++ (wl_set_status_all(wl, WL_STATUS_ ## stat, 2)) ++#define wl_chg_drv_status(wl, stat, ndev) \ ++ (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 4)) ++ ++#define for_each_bss(list, bss, __i) \ ++ for (__i = 0; __i < list->count && __i < WL_AP_MAX; __i++, bss = next_bss(list, bss)) ++ ++#define for_each_ndev(wl, iter, next) \ ++ list_for_each_entry_safe(iter, next, &wl->net_list, list) ++ ++ ++/* In case of WPS from wpa_supplicant, pairwise siute and group suite is 0. ++ * In addtion to that, wpa_version is WPA_VERSION_1 ++ */ ++#define is_wps_conn(_sme) \ ++ ((wl_cfgp2p_find_wpsie((u8 *)_sme->ie, _sme->ie_len) != NULL) && \ ++ (!_sme->crypto.n_ciphers_pairwise) && \ ++ (!_sme->crypto.cipher_group)) ++extern s32 wl_cfg80211_attach(struct net_device *ndev, void *data); ++extern s32 wl_cfg80211_attach_post(struct net_device *ndev); ++extern void wl_cfg80211_detach(void *para); ++ ++extern void wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t *e, ++ void *data); ++void wl_cfg80211_set_parent_dev(void *dev); ++struct device *wl_cfg80211_get_parent_dev(void); ++ ++extern s32 wl_cfg80211_up(void *para); ++extern s32 wl_cfg80211_down(void *para); ++extern s32 wl_cfg80211_notify_ifadd(struct net_device *ndev, s32 idx, s32 bssidx, ++ void* _net_attach); ++extern s32 wl_cfg80211_ifdel_ops(struct net_device *net); ++extern s32 wl_cfg80211_notify_ifdel(void); ++extern s32 wl_cfg80211_is_progress_ifadd(void); ++extern s32 wl_cfg80211_is_progress_ifchange(void); ++extern s32 wl_cfg80211_is_progress_ifadd(void); ++extern s32 wl_cfg80211_notify_ifchange(void); ++extern void wl_cfg80211_dbg_level(u32 level); ++extern s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); ++extern s32 wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len); ++extern s32 wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len); ++extern s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, ++ enum wl_management_type type); ++extern s32 wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len); ++extern int wl_cfg80211_hang(struct net_device *dev, u16 reason); ++extern s32 wl_mode_to_nl80211_iftype(s32 mode); ++int wl_cfg80211_do_driver_init(struct net_device *net); ++void wl_cfg80211_enable_trace(bool set, u32 level); ++extern s32 wl_update_wiphybands(struct wl_priv *wl, bool notify); ++extern s32 wl_cfg80211_if_is_group_owner(void); ++extern chanspec_t wl_ch_host_to_driver(u16 channel); ++extern s32 wl_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add); ++extern void wl_stop_wait_next_action_frame(struct wl_priv *wl, struct net_device *ndev); ++extern s32 wl_cfg80211_set_band(struct net_device *ndev, int band); ++extern int wl_cfg80211_update_power_mode(struct net_device *dev); ++#if defined(DHCP_SCAN_SUPPRESS) ++extern int wl_cfg80211_scan_suppress(struct net_device *dev, int suppress); ++#endif ++extern void wl_cfg80211_add_to_eventbuffer(wl_eventmsg_buf_t *ev, u16 event, bool set); ++extern s32 wl_cfg80211_apply_eventbuffer(struct net_device *ndev, ++ struct wl_priv *wl, wl_eventmsg_buf_t *ev); ++#endif /* _wl_cfg80211_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +new file mode 100644 +index 00000000..c3144ca2 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +@@ -0,0 +1,2339 @@ ++/* ++ * Linux cfgp2p driver ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wl_cfgp2p.c 376685 2013-01-02 06:28:45Z $ ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++static s8 scanparambuf[WLC_IOCTL_SMLEN]; ++static s8 g_mgmt_ie_buf[2048]; ++static bool ++wl_cfgp2p_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type); ++ ++static u32 ++wl_cfgp2p_vndr_ie(struct wl_priv *wl, u8 *iebuf, s32 bssidx, s32 pktflag, ++ s8 *oui, s32 ie_id, s8 *data, s32 datalen, const s8* add_del_cmd); ++ ++static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev); ++static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr, int cmd); ++static int wl_cfgp2p_if_open(struct net_device *net); ++static int wl_cfgp2p_if_stop(struct net_device *net); ++static s32 wl_cfgp2p_cancel_listen(struct wl_priv *wl, struct net_device *ndev, ++ bool notify); ++ ++static const struct net_device_ops wl_cfgp2p_if_ops = { ++ .ndo_open = wl_cfgp2p_if_open, ++ .ndo_stop = wl_cfgp2p_if_stop, ++ .ndo_do_ioctl = wl_cfgp2p_do_ioctl, ++ .ndo_start_xmit = wl_cfgp2p_start_xmit, ++}; ++ ++bool wl_cfgp2p_is_pub_action(void *frame, u32 frame_len) ++{ ++ wifi_p2p_pub_act_frame_t *pact_frm; ++ ++ if (frame == NULL) ++ return false; ++ pact_frm = (wifi_p2p_pub_act_frame_t *)frame; ++ if (frame_len < sizeof(wifi_p2p_pub_act_frame_t) -1) ++ return false; ++ ++ if (pact_frm->category == P2P_PUB_AF_CATEGORY && ++ pact_frm->action == P2P_PUB_AF_ACTION && ++ pact_frm->oui_type == P2P_VER && ++ memcmp(pact_frm->oui, P2P_OUI, sizeof(pact_frm->oui)) == 0) { ++ return true; ++ } ++ ++ return false; ++} ++ ++bool wl_cfgp2p_is_p2p_action(void *frame, u32 frame_len) ++{ ++ wifi_p2p_action_frame_t *act_frm; ++ ++ if (frame == NULL) ++ return false; ++ act_frm = (wifi_p2p_action_frame_t *)frame; ++ if (frame_len < sizeof(wifi_p2p_action_frame_t) -1) ++ return false; ++ ++ if (act_frm->category == P2P_AF_CATEGORY && ++ act_frm->type == P2P_VER && ++ memcmp(act_frm->OUI, P2P_OUI, DOT11_OUI_LEN) == 0) { ++ return true; ++ } ++ ++ return false; ++} ++bool wl_cfgp2p_is_gas_action(void *frame, u32 frame_len) ++{ ++ ++ wifi_p2psd_gas_pub_act_frame_t *sd_act_frm; ++ ++ if (frame == NULL) ++ return false; ++ ++ sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame; ++ if (frame_len < sizeof(wifi_p2psd_gas_pub_act_frame_t) - 1) ++ return false; ++ if (sd_act_frm->category != P2PSD_ACTION_CATEGORY) ++ return false; ++ ++ if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ || ++ sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP || ++ sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ || ++ sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP) ++ return true; ++ else ++ return false; ++ ++} ++void wl_cfgp2p_print_actframe(bool tx, void *frame, u32 frame_len) ++{ ++ wifi_p2p_pub_act_frame_t *pact_frm; ++ wifi_p2p_action_frame_t *act_frm; ++ wifi_p2psd_gas_pub_act_frame_t *sd_act_frm; ++ if (!frame || frame_len <= 2) ++ return; ++ ++ if (wl_cfgp2p_is_pub_action(frame, frame_len)) { ++ pact_frm = (wifi_p2p_pub_act_frame_t *)frame; ++ switch (pact_frm->subtype) { ++ case P2P_PAF_GON_REQ: ++ CFGP2P_ACTION(("%s P2P Group Owner Negotiation Req Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ case P2P_PAF_GON_RSP: ++ CFGP2P_ACTION(("%s P2P Group Owner Negotiation Rsp Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ case P2P_PAF_GON_CONF: ++ CFGP2P_ACTION(("%s P2P Group Owner Negotiation Confirm Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ case P2P_PAF_INVITE_REQ: ++ CFGP2P_ACTION(("%s P2P Invitation Request Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ case P2P_PAF_INVITE_RSP: ++ CFGP2P_ACTION(("%s P2P Invitation Response Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ case P2P_PAF_DEVDIS_REQ: ++ CFGP2P_ACTION(("%s P2P Device Discoverability Request Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ case P2P_PAF_DEVDIS_RSP: ++ CFGP2P_ACTION(("%s P2P Device Discoverability Response Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ case P2P_PAF_PROVDIS_REQ: ++ CFGP2P_ACTION(("%s P2P Provision Discovery Request Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ case P2P_PAF_PROVDIS_RSP: ++ CFGP2P_ACTION(("%s P2P Provision Discovery Response Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ default: ++ CFGP2P_ACTION(("%s Unknown P2P Public Action Frame\n", ++ (tx)? "TX": "RX")); ++ ++ } ++ ++ } else if (wl_cfgp2p_is_p2p_action(frame, frame_len)) { ++ act_frm = (wifi_p2p_action_frame_t *)frame; ++ switch (act_frm->subtype) { ++ case P2P_AF_NOTICE_OF_ABSENCE: ++ CFGP2P_ACTION(("%s P2P Notice of Absence Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ case P2P_AF_PRESENCE_REQ: ++ CFGP2P_ACTION(("%s P2P Presence Request Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ case P2P_AF_PRESENCE_RSP: ++ CFGP2P_ACTION(("%s P2P Presence Response Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ case P2P_AF_GO_DISC_REQ: ++ CFGP2P_ACTION(("%s P2P Discoverability Request Frame\n", ++ (tx)? "TX": "RX")); ++ break; ++ default: ++ CFGP2P_ACTION(("%s Unknown P2P Action Frame\n", ++ (tx)? "TX": "RX")); ++ } ++ ++ } else if (wl_cfgp2p_is_gas_action(frame, frame_len)) { ++ sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame; ++ switch (sd_act_frm->action) { ++ case P2PSD_ACTION_ID_GAS_IREQ: ++ CFGP2P_ACTION(("%s P2P GAS Initial Request\n", ++ (tx)? "TX" : "RX")); ++ break; ++ case P2PSD_ACTION_ID_GAS_IRESP: ++ CFGP2P_ACTION(("%s P2P GAS Initial Response\n", ++ (tx)? "TX" : "RX")); ++ break; ++ case P2PSD_ACTION_ID_GAS_CREQ: ++ CFGP2P_ACTION(("%s P2P GAS Comback Request\n", ++ (tx)? "TX" : "RX")); ++ break; ++ case P2PSD_ACTION_ID_GAS_CRESP: ++ CFGP2P_ACTION(("%s P2P GAS Comback Response\n", ++ (tx)? "TX" : "RX")); ++ break; ++ default: ++ CFGP2P_ACTION(("%s Unknown P2P GAS Frame\n", ++ (tx)? "TX" : "RX")); ++ } ++ ++ ++ } ++} ++ ++/* ++ * Initialize variables related to P2P ++ * ++ */ ++s32 ++wl_cfgp2p_init_priv(struct wl_priv *wl) ++{ ++ if (!(wl->p2p = kzalloc(sizeof(struct p2p_info), GFP_KERNEL))) { ++ CFGP2P_ERR(("struct p2p_info allocation failed\n")); ++ return -ENOMEM; ++ } ++#define INIT_IE(IE_TYPE, BSS_TYPE) \ ++ do { \ ++ memset(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie, 0, \ ++ sizeof(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie)); \ ++ wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie_len = 0; \ ++ } while (0); ++ ++ INIT_IE(probe_req, P2PAPI_BSSCFG_PRIMARY); ++ INIT_IE(probe_res, P2PAPI_BSSCFG_PRIMARY); ++ INIT_IE(assoc_req, P2PAPI_BSSCFG_PRIMARY); ++ INIT_IE(assoc_res, P2PAPI_BSSCFG_PRIMARY); ++ INIT_IE(beacon, P2PAPI_BSSCFG_PRIMARY); ++ INIT_IE(probe_req, P2PAPI_BSSCFG_DEVICE); ++ INIT_IE(probe_res, P2PAPI_BSSCFG_DEVICE); ++ INIT_IE(assoc_req, P2PAPI_BSSCFG_DEVICE); ++ INIT_IE(assoc_res, P2PAPI_BSSCFG_DEVICE); ++ INIT_IE(beacon, P2PAPI_BSSCFG_DEVICE); ++ INIT_IE(probe_req, P2PAPI_BSSCFG_CONNECTION); ++ INIT_IE(probe_res, P2PAPI_BSSCFG_CONNECTION); ++ INIT_IE(assoc_req, P2PAPI_BSSCFG_CONNECTION); ++ INIT_IE(assoc_res, P2PAPI_BSSCFG_CONNECTION); ++ INIT_IE(beacon, P2PAPI_BSSCFG_CONNECTION); ++#undef INIT_IE ++ wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY) = wl_to_prmry_ndev(wl); ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY) = 0; ++ wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = NULL; ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; ++ wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION) = NULL; ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = 0; ++ return BCME_OK; ++ ++} ++/* ++ * Deinitialize variables related to P2P ++ * ++ */ ++void ++wl_cfgp2p_deinit_priv(struct wl_priv *wl) ++{ ++ CFGP2P_DBG(("In\n")); ++ if (wl->p2p) { ++ kfree(wl->p2p); ++ wl->p2p = NULL; ++ } ++ wl->p2p_supported = 0; ++} ++/* ++ * Set P2P functions into firmware ++ */ ++s32 ++wl_cfgp2p_set_firm_p2p(struct wl_priv *wl) ++{ ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ struct ether_addr null_eth_addr = { { 0, 0, 0, 0, 0, 0 } }; ++ s32 ret = BCME_OK; ++ s32 val = 0; ++ /* Do we have to check whether APSTA is enabled or not ? */ ++ wldev_iovar_getint(ndev, "apsta", &val); ++ if (val == 0) { ++ val = 1; ++ ret = wldev_ioctl(ndev, WLC_DOWN, &val, sizeof(s32), true); ++ if (ret < 0) { ++ CFGP2P_ERR(("WLC_DOWN error %d\n", ret)); ++ return ret; ++ } ++ wldev_iovar_setint(ndev, "apsta", val); ++ ret = wldev_ioctl(ndev, WLC_UP, &val, sizeof(s32), true); ++ if (ret < 0) { ++ CFGP2P_ERR(("WLC_UP error %d\n", ret)); ++ return ret; ++ } ++ } ++ ++ /* In case of COB type, firmware has default mac address ++ * After Initializing firmware, we have to set current mac address to ++ * firmware for P2P device address ++ */ ++ ret = wldev_iovar_setbuf_bsscfg(ndev, "p2p_da_override", &null_eth_addr, ++ sizeof(null_eth_addr), wl->ioctl_buf, WLC_IOCTL_MAXLEN, 0, &wl->ioctl_buf_sync); ++ if (ret && ret != BCME_UNSUPPORTED) { ++ CFGP2P_ERR(("failed to update device address ret %d\n", ret)); ++ } ++ return ret; ++} ++ ++/* Create a new P2P BSS. ++ * Parameters: ++ * @mac : MAC address of the BSS to create ++ * @if_type : interface type: WL_P2P_IF_GO or WL_P2P_IF_CLIENT ++ * @chspec : chspec to use if creating a GO BSS. ++ * Returns 0 if success. ++ */ ++s32 ++wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, ++ chanspec_t chspec) ++{ ++ wl_p2p_if_t ifreq; ++ s32 err; ++ u32 scb_timeout = WL_SCB_TIMEOUT; ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ ++ ifreq.type = if_type; ++ ifreq.chspec = chspec; ++ memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet)); ++ ++ CFGP2P_DBG(("---wl p2p_ifadd "MACDBG" %s %u\n", ++ MAC2STRDBG(ifreq.addr.octet), ++ (if_type == WL_P2P_IF_GO) ? "go" : "client", ++ (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT)); ++ ++ err = wldev_iovar_setbuf(ndev, "p2p_ifadd", &ifreq, sizeof(ifreq), ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); ++ ++ if (unlikely(err < 0)) ++ printk("'wl p2p_ifadd' error %d\n", err); ++ else if (if_type == WL_P2P_IF_GO) { ++ err = wldev_ioctl(ndev, WLC_SET_SCB_TIMEOUT, &scb_timeout, sizeof(u32), true); ++ if (unlikely(err < 0)) ++ printk("'wl scb_timeout' error %d\n", err); ++ } ++ return err; ++} ++ ++/* Disable a P2P BSS. ++ * Parameters: ++ * @mac : MAC address of the BSS to create ++ * Returns 0 if success. ++ */ ++s32 ++wl_cfgp2p_ifdisable(struct wl_priv *wl, struct ether_addr *mac) ++{ ++ s32 ret; ++ struct net_device *netdev = wl_to_prmry_ndev(wl); ++ ++ CFGP2P_INFO(("------primary idx %d : wl p2p_ifdis "MACDBG"\n", ++ netdev->ifindex, MAC2STRDBG(mac->octet))); ++ ret = wldev_iovar_setbuf(netdev, "p2p_ifdis", mac, sizeof(*mac), ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); ++ if (unlikely(ret < 0)) { ++ printk("'wl p2p_ifdis' error %d\n", ret); ++ } ++ return ret; ++} ++ ++/* Delete a P2P BSS. ++ * Parameters: ++ * @mac : MAC address of the BSS to create ++ * Returns 0 if success. ++ */ ++s32 ++wl_cfgp2p_ifdel(struct wl_priv *wl, struct ether_addr *mac) ++{ ++ s32 ret; ++ struct net_device *netdev = wl_to_prmry_ndev(wl); ++ ++ CFGP2P_INFO(("------primary idx %d : wl p2p_ifdel "MACDBG"\n", ++ netdev->ifindex, MAC2STRDBG(mac->octet))); ++ ret = wldev_iovar_setbuf(netdev, "p2p_ifdel", mac, sizeof(*mac), ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); ++ if (unlikely(ret < 0)) { ++ printk("'wl p2p_ifdel' error %d\n", ret); ++ } ++ return ret; ++} ++ ++/* Change a P2P Role. ++ * Parameters: ++ * @mac : MAC address of the BSS to change a role ++ * Returns 0 if success. ++ */ ++s32 ++wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, ++ chanspec_t chspec) ++{ ++ wl_p2p_if_t ifreq; ++ s32 err; ++ u32 scb_timeout = WL_SCB_TIMEOUT; ++ ++ struct net_device *netdev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); ++ ++ ifreq.type = if_type; ++ ifreq.chspec = chspec; ++ memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet)); ++ ++ CFGP2P_INFO(("---wl p2p_ifchange "MACDBG" %s %u" ++ " chanspec 0x%04x\n", MAC2STRDBG(ifreq.addr.octet), ++ (if_type == WL_P2P_IF_GO) ? "go" : "client", ++ (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT, ++ ifreq.chspec)); ++ ++ err = wldev_iovar_setbuf(netdev, "p2p_ifupd", &ifreq, sizeof(ifreq), ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); ++ ++ if (unlikely(err < 0)) { ++ printk("'wl p2p_ifupd' error %d\n", err); ++ } else if (if_type == WL_P2P_IF_GO) { ++ err = wldev_ioctl(netdev, WLC_SET_SCB_TIMEOUT, &scb_timeout, sizeof(u32), true); ++ if (unlikely(err < 0)) ++ printk("'wl scb_timeout' error %d\n", err); ++ } ++ return err; ++} ++ ++ ++/* Get the index of a created P2P BSS. ++ * Parameters: ++ * @mac : MAC address of the created BSS ++ * @index : output: index of created BSS ++ * Returns 0 if success. ++ */ ++s32 ++wl_cfgp2p_ifidx(struct wl_priv *wl, struct ether_addr *mac, s32 *index) ++{ ++ s32 ret; ++ u8 getbuf[64]; ++ struct net_device *dev = wl_to_prmry_ndev(wl); ++ ++ CFGP2P_INFO(("---wl p2p_if "MACDBG"\n", MAC2STRDBG(mac->octet))); ++ ++ ret = wldev_iovar_getbuf_bsscfg(dev, "p2p_if", mac, sizeof(*mac), getbuf, ++ sizeof(getbuf), wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY), NULL); ++ ++ if (ret == 0) { ++ memcpy(index, getbuf, sizeof(s32)); ++ CFGP2P_INFO(("---wl p2p_if ==> %d\n", *index)); ++ } ++ ++ return ret; ++} ++ ++static s32 ++wl_cfgp2p_set_discovery(struct wl_priv *wl, s32 on) ++{ ++ s32 ret = BCME_OK; ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ CFGP2P_DBG(("enter\n")); ++ ++ ret = wldev_iovar_setint(ndev, "p2p_disc", on); ++ ++ if (unlikely(ret < 0)) { ++ CFGP2P_ERR(("p2p_disc %d error %d\n", on, ret)); ++ } ++ ++ return ret; ++} ++ ++/* Set the WL driver's P2P mode. ++ * Parameters : ++ * @mode : is one of WL_P2P_DISC_ST_{SCAN,LISTEN,SEARCH}. ++ * @channel : the channel to listen ++ * @listen_ms : the time (milli seconds) to wait ++ * @bssidx : bss index for BSSCFG ++ * Returns 0 if success ++ */ ++ ++s32 ++wl_cfgp2p_set_p2p_mode(struct wl_priv *wl, u8 mode, u32 channel, u16 listen_ms, int bssidx) ++{ ++ wl_p2p_disc_st_t discovery_mode; ++ s32 ret; ++ struct net_device *dev; ++ CFGP2P_DBG(("enter\n")); ++ ++ if (unlikely(bssidx == WL_INVALID || bssidx >= P2PAPI_BSSCFG_MAX)) { ++ CFGP2P_ERR((" %d index out of range\n", bssidx)); ++ return -1; ++ } ++ ++ dev = wl_to_p2p_bss_ndev(wl, bssidx); ++ if (unlikely(dev == NULL)) { ++ CFGP2P_ERR(("bssidx %d is not assigned\n", bssidx)); ++ return BCME_NOTFOUND; ++ } ++ ++ /* Put the WL driver into P2P Listen Mode to respond to P2P probe reqs */ ++ discovery_mode.state = mode; ++ discovery_mode.chspec = wl_ch_host_to_driver(channel); ++ discovery_mode.dwell = listen_ms; ++ ret = wldev_iovar_setbuf_bsscfg(dev, "p2p_state", &discovery_mode, ++ sizeof(discovery_mode), wl->ioctl_buf, WLC_IOCTL_MAXLEN, ++ bssidx, &wl->ioctl_buf_sync); ++ ++ return ret; ++} ++ ++/* Get the index of the P2P Discovery BSS */ ++static s32 ++wl_cfgp2p_get_disc_idx(struct wl_priv *wl, s32 *index) ++{ ++ s32 ret; ++ struct net_device *dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); ++ ++ ret = wldev_iovar_getint(dev, "p2p_dev", index); ++ CFGP2P_INFO(("p2p_dev bsscfg_idx=%d ret=%d\n", *index, ret)); ++ ++ if (unlikely(ret < 0)) { ++ CFGP2P_ERR(("'p2p_dev' error %d\n", ret)); ++ return ret; ++ } ++ return ret; ++} ++ ++s32 ++wl_cfgp2p_init_discovery(struct wl_priv *wl) ++{ ++ ++ s32 index = 0; ++ s32 ret = BCME_OK; ++ ++ CFGP2P_DBG(("enter\n")); ++ ++ if (wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) != 0) { ++ CFGP2P_ERR(("do nothing, already initialized\n")); ++ return ret; ++ } ++ ++ ret = wl_cfgp2p_set_discovery(wl, 1); ++ if (ret < 0) { ++ CFGP2P_ERR(("set discover error\n")); ++ return ret; ++ } ++ /* Enable P2P Discovery in the WL Driver */ ++ ret = wl_cfgp2p_get_disc_idx(wl, &index); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = ++ wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = index; ++ ++ /* Set the initial discovery state to SCAN */ ++ ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); ++ ++ if (unlikely(ret != 0)) { ++ CFGP2P_ERR(("unable to set WL_P2P_DISC_ST_SCAN\n")); ++ wl_cfgp2p_set_discovery(wl, 0); ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; ++ wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = NULL; ++ return 0; ++ } ++ return ret; ++} ++ ++/* Deinitialize P2P Discovery ++ * Parameters : ++ * @wl : wl_private data ++ * Returns 0 if succes ++ */ ++static s32 ++wl_cfgp2p_deinit_discovery(struct wl_priv *wl) ++{ ++ s32 ret = BCME_OK; ++ CFGP2P_DBG(("enter\n")); ++ ++ if (wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) == 0) { ++ CFGP2P_ERR(("do nothing, not initialized\n")); ++ return -1; ++ } ++ /* Set the discovery state to SCAN */ ++ ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); ++ /* Disable P2P discovery in the WL driver (deletes the discovery BSSCFG) */ ++ ret = wl_cfgp2p_set_discovery(wl, 0); ++ ++ /* Clear our saved WPS and P2P IEs for the discovery BSS. The driver ++ * deleted these IEs when wl_cfgp2p_set_discovery() deleted the discovery ++ * BSS. ++ */ ++ ++ /* Clear the saved bsscfg index of the discovery BSSCFG to indicate we ++ * have no discovery BSS. ++ */ ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = WL_INVALID; ++ wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = NULL; ++ ++ return ret; ++ ++} ++/* Enable P2P Discovery ++ * Parameters: ++ * @wl : wl_private data ++ * @ie : probe request ie (WPS IE + P2P IE) ++ * @ie_len : probe request ie length ++ * Returns 0 if success. ++ */ ++s32 ++wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, ++ const u8 *ie, u32 ie_len) ++{ ++ s32 ret = BCME_OK; ++ s32 bssidx = (wl_to_prmry_ndev(wl) == dev) ? ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) : wl_cfgp2p_find_idx(wl, dev); ++ if (wl_get_p2p_status(wl, DISCOVERY_ON)) { ++ CFGP2P_INFO((" DISCOVERY is already initialized, we have nothing to do\n")); ++ goto set_ie; ++ } ++ ++ wl_set_p2p_status(wl, DISCOVERY_ON); ++ ++ CFGP2P_DBG(("enter\n")); ++ ++ ret = wl_cfgp2p_init_discovery(wl); ++ if (unlikely(ret < 0)) { ++ CFGP2P_ERR((" init discovery error %d\n", ret)); ++ goto exit; ++ } ++ /* Set wsec to any non-zero value in the discovery bsscfg to ensure our ++ * P2P probe responses have the privacy bit set in the 802.11 WPA IE. ++ * Some peer devices may not initiate WPS with us if this bit is not set. ++ */ ++ ret = wldev_iovar_setint_bsscfg(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE), ++ "wsec", AES_ENABLED, wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); ++ if (unlikely(ret < 0)) { ++ CFGP2P_ERR((" wsec error %d\n", ret)); ++ } ++set_ie: ++ if (ie_len) { ++ ret = wl_cfgp2p_set_management_ie(wl, dev, ++ bssidx, ++ VNDR_IE_PRBREQ_FLAG, ie, ie_len); ++ ++ if (unlikely(ret < 0)) { ++ CFGP2P_ERR(("set probreq ie occurs error %d\n", ret)); ++ goto exit; ++ } ++ } ++exit: ++ return ret; ++} ++ ++/* Disable P2P Discovery ++ * Parameters: ++ * @wl : wl_private_data ++ * Returns 0 if success. ++ */ ++s32 ++wl_cfgp2p_disable_discovery(struct wl_priv *wl) ++{ ++ s32 ret = BCME_OK; ++ CFGP2P_DBG((" enter\n")); ++ wl_clr_p2p_status(wl, DISCOVERY_ON); ++ ++ if (wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) == 0) { ++ CFGP2P_ERR((" do nothing, not initialized\n")); ++ goto exit; ++ } ++ ++ ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); ++ ++ if (unlikely(ret < 0)) { ++ ++ CFGP2P_ERR(("unable to set WL_P2P_DISC_ST_SCAN\n")); ++ } ++ /* Do a scan abort to stop the driver's scan engine in case it is still ++ * waiting out an action frame tx dwell time. ++ */ ++ wl_clr_p2p_status(wl, DISCOVERY_ON); ++ ret = wl_cfgp2p_deinit_discovery(wl); ++ ++exit: ++ return ret; ++} ++ ++s32 ++wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, ++ u32 num_chans, u16 *channels, ++ s32 search_state, u16 action, u32 bssidx, struct ether_addr *tx_dst_addr) ++{ ++ s32 ret = BCME_OK; ++ s32 memsize; ++ s32 eparams_size; ++ u32 i; ++ s8 *memblk; ++ wl_p2p_scan_t *p2p_params; ++ wl_escan_params_t *eparams; ++ wlc_ssid_t ssid; ++ /* Scan parameters */ ++#define P2PAPI_SCAN_NPROBES 1 ++#define P2PAPI_SCAN_DWELL_TIME_MS 80 ++#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40 ++#define P2PAPI_SCAN_HOME_TIME_MS 60 ++#define P2PAPI_SCAN_NPROBS_TIME_MS 30 ++#define P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS 100 ++ ++ struct net_device *pri_dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); ++ /* Allocate scan params which need space for 3 channels and 0 ssids */ ++ eparams_size = (WL_SCAN_PARAMS_FIXED_SIZE + ++ OFFSETOF(wl_escan_params_t, params)) + ++ num_chans * sizeof(eparams->params.channel_list[0]); ++ ++ memsize = sizeof(wl_p2p_scan_t) + eparams_size; ++ memblk = scanparambuf; ++ if (memsize > sizeof(scanparambuf)) { ++ CFGP2P_ERR((" scanpar buf too small (%u > %u)\n", ++ memsize, sizeof(scanparambuf))); ++ return -1; ++ } ++ memset(memblk, 0, memsize); ++ memset(wl->ioctl_buf, 0, WLC_IOCTL_MAXLEN); ++ if (search_state == WL_P2P_DISC_ST_SEARCH) { ++ /* ++ * If we in SEARCH STATE, we don't need to set SSID explictly ++ * because dongle use P2P WILDCARD internally by default ++ */ ++ wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SEARCH, 0, 0, bssidx); ++ ssid.SSID_len = htod32(0); ++ ++ } else if (search_state == WL_P2P_DISC_ST_SCAN) { ++ /* SCAN STATE 802.11 SCAN ++ * WFD Supplicant has p2p_find command with (type=progressive, type= full) ++ * So if P2P_find command with type=progressive, ++ * we have to set ssid to P2P WILDCARD because ++ * we just do broadcast scan unless setting SSID ++ */ ++ strncpy(ssid.SSID, WL_P2P_WILDCARD_SSID, sizeof(ssid.SSID) - 1); ++ ssid.SSID[sizeof(ssid.SSID) - 1] = 0; ++ ssid.SSID_len = htod32(WL_P2P_WILDCARD_SSID_LEN); ++ wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, bssidx); ++ } ++ else { ++ CFGP2P_ERR((" invalid search state %d\n", search_state)); ++ return -1; ++ } ++ ++ ++ /* Fill in the P2P scan structure at the start of the iovar param block */ ++ p2p_params = (wl_p2p_scan_t*) memblk; ++ p2p_params->type = 'E'; ++ /* Fill in the Scan structure that follows the P2P scan structure */ ++ eparams = (wl_escan_params_t*) (p2p_params + 1); ++ eparams->params.bss_type = DOT11_BSSTYPE_ANY; ++ if (active) ++ eparams->params.scan_type = DOT11_SCANTYPE_ACTIVE; ++ else ++ eparams->params.scan_type = DOT11_SCANTYPE_PASSIVE; ++ ++ if (tx_dst_addr == NULL) ++ memcpy(&eparams->params.bssid, ðer_bcast, ETHER_ADDR_LEN); ++ else ++ memcpy(&eparams->params.bssid, tx_dst_addr, ETHER_ADDR_LEN); ++ ++ if (ssid.SSID_len) ++ memcpy(&eparams->params.ssid, &ssid, sizeof(wlc_ssid_t)); ++ ++ eparams->params.home_time = htod32(P2PAPI_SCAN_HOME_TIME_MS); ++ ++ /* SOCIAL_CHAN_CNT + 1 takes care of the Progressive scan supported by ++ * the supplicant ++ */ ++ if ((num_chans == SOCIAL_CHAN_CNT) || (num_chans == SOCIAL_CHAN_CNT + 1)) ++ eparams->params.active_time = htod32(P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS); ++ else if (num_chans == AF_PEER_SEARCH_CNT) ++ eparams->params.active_time = htod32(P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS); ++ else if (wl_get_drv_status_all(wl, CONNECTED)) ++ eparams->params.active_time = -1; ++ else ++ eparams->params.active_time = htod32(P2PAPI_SCAN_DWELL_TIME_MS); ++ eparams->params.nprobes = htod32((eparams->params.active_time / ++ P2PAPI_SCAN_NPROBS_TIME_MS)); ++ ++ /* Override scan params to find a peer for a connection */ ++ if (num_chans == 1) { ++ eparams->params.active_time = htod32(WL_SCAN_CONNECT_DWELL_TIME_MS); ++ eparams->params.nprobes = htod32(eparams->params.active_time / ++ WL_SCAN_JOIN_PROBE_INTERVAL_MS); ++ } ++ ++ if (eparams->params.nprobes <= 0) ++ eparams->params.nprobes = 1; ++ CFGP2P_DBG(("nprobes # %d, active_time %d\n", ++ eparams->params.nprobes, eparams->params.active_time)); ++ eparams->params.passive_time = htod32(-1); ++ eparams->params.channel_num = htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) | ++ (num_chans & WL_SCAN_PARAMS_COUNT_MASK)); ++ ++ for (i = 0; i < num_chans; i++) { ++ eparams->params.channel_list[i] = wl_ch_host_to_driver(channels[i]); ++ } ++ eparams->version = htod32(ESCAN_REQ_VERSION); ++ eparams->action = htod16(action); ++ eparams->sync_id = htod16(0x1234); ++ CFGP2P_INFO(("SCAN CHANNELS : ")); ++ ++ for (i = 0; i < num_chans; i++) { ++ if (i == 0) CFGP2P_INFO(("%d", channels[i])); ++ else CFGP2P_INFO((",%d", channels[i])); ++ } ++ ++ CFGP2P_INFO(("\n")); ++ ++ ret = wldev_iovar_setbuf_bsscfg(pri_dev, "p2p_scan", ++ memblk, memsize, wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); ++ if (ret == BCME_OK) ++ wl_set_p2p_status(wl, SCANNING); ++ return ret; ++} ++ ++/* search function to reach at common channel to send action frame ++ * Parameters: ++ * @wl : wl_private data ++ * @ndev : net device for bssidx ++ * @bssidx : bssidx for BSS ++ * Returns 0 if success. ++ */ ++s32 ++wl_cfgp2p_act_frm_search(struct wl_priv *wl, struct net_device *ndev, ++ s32 bssidx, s32 channel, struct ether_addr *tx_dst_addr) ++{ ++ s32 ret = 0; ++ u32 chan_cnt = 0; ++ u16 *default_chan_list = NULL; ++ if (!p2p_is_on(wl) || ndev == NULL || bssidx == WL_INVALID) ++ return -BCME_ERROR; ++ CFGP2P_ERR((" Enter\n")); ++ if (bssidx == P2PAPI_BSSCFG_PRIMARY) ++ bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); ++ if (channel) ++ chan_cnt = AF_PEER_SEARCH_CNT; ++ else ++ chan_cnt = SOCIAL_CHAN_CNT; ++ default_chan_list = kzalloc(chan_cnt * sizeof(*default_chan_list), GFP_KERNEL); ++ if (default_chan_list == NULL) { ++ CFGP2P_ERR(("channel list allocation failed \n")); ++ ret = -ENOMEM; ++ goto exit; ++ } ++ if (channel) { ++ u32 i; ++ /* insert same channel to the chan_list */ ++ for (i = 0; i < chan_cnt; i++) { ++ default_chan_list[i] = channel; ++ } ++ } else { ++ default_chan_list[0] = SOCIAL_CHAN_1; ++ default_chan_list[1] = SOCIAL_CHAN_2; ++ default_chan_list[2] = SOCIAL_CHAN_3; ++ } ++ ret = wl_cfgp2p_escan(wl, ndev, true, chan_cnt, ++ default_chan_list, WL_P2P_DISC_ST_SEARCH, ++ WL_SCAN_ACTION_START, bssidx, tx_dst_addr); ++ kfree(default_chan_list); ++exit: ++ return ret; ++} ++ ++/* Check whether pointed-to IE looks like WPA. */ ++#define wl_cfgp2p_is_wpa_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ ++ (const uint8 *)WPS_OUI, WPS_OUI_LEN, WPA_OUI_TYPE) ++/* Check whether pointed-to IE looks like WPS. */ ++#define wl_cfgp2p_is_wps_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ ++ (const uint8 *)WPS_OUI, WPS_OUI_LEN, WPS_OUI_TYPE) ++/* Check whether the given IE looks like WFA P2P IE. */ ++#define wl_cfgp2p_is_p2p_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ ++ (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_P2P) ++/* Check whether the given IE looks like WFA WFDisplay IE. */ ++#define WFA_OUI_TYPE_WFD 0x0a /* WiFi Display OUI TYPE */ ++#define wl_cfgp2p_is_wfd_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ ++ (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_WFD) ++ ++static s32 ++wl_cfgp2p_parse_vndr_ies(u8 *parse, u32 len, ++ struct parsed_vndr_ies *vndr_ies) ++{ ++ s32 err = BCME_OK; ++ vndr_ie_t *vndrie; ++ bcm_tlv_t *ie; ++ struct parsed_vndr_ie_info *parsed_info; ++ u32 count = 0; ++ s32 remained_len; ++ ++ remained_len = (s32)len; ++ memset(vndr_ies, 0, sizeof(*vndr_ies)); ++ ++ WL_INFO(("---> len %d\n", len)); ++ ie = (bcm_tlv_t *) parse; ++ if (!bcm_valid_tlv(ie, remained_len)) ++ ie = NULL; ++ while (ie) { ++ if (count >= MAX_VNDR_IE_NUMBER) ++ break; ++ if (ie->id == DOT11_MNG_VS_ID) { ++ vndrie = (vndr_ie_t *) ie; ++ /* len should be bigger than OUI length + one data length at least */ ++ if (vndrie->len < (VNDR_IE_MIN_LEN + 1)) { ++ CFGP2P_ERR(("%s: invalid vndr ie. length is too small %d\n", ++ __FUNCTION__, vndrie->len)); ++ goto end; ++ } ++ /* if wpa or wme ie, do not add ie */ ++ if (!bcmp(vndrie->oui, (u8*)WPA_OUI, WPA_OUI_LEN) && ++ ((vndrie->data[0] == WPA_OUI_TYPE) || ++ (vndrie->data[0] == WME_OUI_TYPE))) { ++ CFGP2P_DBG(("Found WPA/WME oui. Do not add it\n")); ++ goto end; ++ } ++ ++ parsed_info = &vndr_ies->ie_info[count++]; ++ ++ /* save vndr ie information */ ++ parsed_info->ie_ptr = (char *)vndrie; ++ parsed_info->ie_len = (vndrie->len + TLV_HDR_LEN); ++ memcpy(&parsed_info->vndrie, vndrie, sizeof(vndr_ie_t)); ++ ++ vndr_ies->count = count; ++ ++ CFGP2P_DBG(("\t ** OUI %02x %02x %02x, type 0x%02x \n", ++ parsed_info->vndrie.oui[0], parsed_info->vndrie.oui[1], ++ parsed_info->vndrie.oui[2], parsed_info->vndrie.data[0])); ++ } ++end: ++ ie = bcm_next_tlv(ie, &remained_len); ++ } ++ return err; ++} ++ ++ ++/* Delete and Set a management vndr ie to firmware ++ * Parameters: ++ * @wl : wl_private data ++ * @ndev : net device for bssidx ++ * @bssidx : bssidx for BSS ++ * @pktflag : packet flag for IE (VNDR_IE_PRBREQ_FLAG,VNDR_IE_PRBRSP_FLAG, VNDR_IE_ASSOCRSP_FLAG, ++ * VNDR_IE_ASSOCREQ_FLAG) ++ * @ie : VNDR IE (such as P2P IE , WPS IE) ++ * @ie_len : VNDR IE Length ++ * Returns 0 if success. ++ */ ++ ++s32 ++wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, ++ s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len) ++{ ++ s32 ret = BCME_OK; ++ u8 *curr_ie_buf = NULL; ++ u8 *mgmt_ie_buf = NULL; ++ u32 mgmt_ie_buf_len = 0; ++ u32 *mgmt_ie_len = 0; ++ u32 del_add_ie_buf_len = 0; ++ u32 total_ie_buf_len = 0; ++ u32 parsed_ie_buf_len = 0; ++ struct parsed_vndr_ies old_vndr_ies; ++ struct parsed_vndr_ies new_vndr_ies; ++ s32 i; ++ u8 *ptr; ++ s32 remained_buf_len; ++ ++#define IE_TYPE(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie) ++#define IE_TYPE_LEN(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie_len) ++ memset(g_mgmt_ie_buf, 0, sizeof(g_mgmt_ie_buf)); ++ curr_ie_buf = g_mgmt_ie_buf; ++ CFGP2P_DBG((" bssidx %d, pktflag : 0x%02X\n", bssidx, pktflag)); ++ if (wl->p2p != NULL) { ++ switch (pktflag) { ++ case VNDR_IE_PRBREQ_FLAG : ++ mgmt_ie_buf = IE_TYPE(probe_req, bssidx); ++ mgmt_ie_len = &IE_TYPE_LEN(probe_req, bssidx); ++ mgmt_ie_buf_len = sizeof(IE_TYPE(probe_req, bssidx)); ++ break; ++ case VNDR_IE_PRBRSP_FLAG : ++ mgmt_ie_buf = IE_TYPE(probe_res, bssidx); ++ mgmt_ie_len = &IE_TYPE_LEN(probe_res, bssidx); ++ mgmt_ie_buf_len = sizeof(IE_TYPE(probe_res, bssidx)); ++ break; ++ case VNDR_IE_ASSOCREQ_FLAG : ++ mgmt_ie_buf = IE_TYPE(assoc_req, bssidx); ++ mgmt_ie_len = &IE_TYPE_LEN(assoc_req, bssidx); ++ mgmt_ie_buf_len = sizeof(IE_TYPE(assoc_req, bssidx)); ++ break; ++ case VNDR_IE_ASSOCRSP_FLAG : ++ mgmt_ie_buf = IE_TYPE(assoc_res, bssidx); ++ mgmt_ie_len = &IE_TYPE_LEN(assoc_res, bssidx); ++ mgmt_ie_buf_len = sizeof(IE_TYPE(assoc_res, bssidx)); ++ break; ++ case VNDR_IE_BEACON_FLAG : ++ mgmt_ie_buf = IE_TYPE(beacon, bssidx); ++ mgmt_ie_len = &IE_TYPE_LEN(beacon, bssidx); ++ mgmt_ie_buf_len = sizeof(IE_TYPE(beacon, bssidx)); ++ break; ++ default: ++ mgmt_ie_buf = NULL; ++ mgmt_ie_len = NULL; ++ CFGP2P_ERR(("not suitable type\n")); ++ return -1; ++ } ++ } else if (wl_get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { ++ switch (pktflag) { ++ case VNDR_IE_PRBRSP_FLAG : ++ mgmt_ie_buf = wl->ap_info->probe_res_ie; ++ mgmt_ie_len = &wl->ap_info->probe_res_ie_len; ++ mgmt_ie_buf_len = sizeof(wl->ap_info->probe_res_ie); ++ break; ++ case VNDR_IE_BEACON_FLAG : ++ mgmt_ie_buf = wl->ap_info->beacon_ie; ++ mgmt_ie_len = &wl->ap_info->beacon_ie_len; ++ mgmt_ie_buf_len = sizeof(wl->ap_info->beacon_ie); ++ break; ++ default: ++ mgmt_ie_buf = NULL; ++ mgmt_ie_len = NULL; ++ CFGP2P_ERR(("not suitable type\n")); ++ return -1; ++ } ++ bssidx = 0; ++ } else if (wl_get_mode_by_netdev(wl, ndev) == WL_MODE_BSS) { ++ switch (pktflag) { ++ case VNDR_IE_PRBREQ_FLAG : ++ mgmt_ie_buf = wl->sta_info->probe_req_ie; ++ mgmt_ie_len = &wl->sta_info->probe_req_ie_len; ++ mgmt_ie_buf_len = sizeof(wl->sta_info->probe_req_ie); ++ break; ++ case VNDR_IE_ASSOCREQ_FLAG : ++ mgmt_ie_buf = wl->sta_info->assoc_req_ie; ++ mgmt_ie_len = &wl->sta_info->assoc_req_ie_len; ++ mgmt_ie_buf_len = sizeof(wl->sta_info->assoc_req_ie); ++ break; ++ default: ++ mgmt_ie_buf = NULL; ++ mgmt_ie_len = NULL; ++ CFGP2P_ERR(("not suitable type\n")); ++ return -1; ++ } ++ bssidx = 0; ++ } else { ++ CFGP2P_ERR(("not suitable type\n")); ++ return -1; ++ } ++ ++ if (vndr_ie_len > mgmt_ie_buf_len) { ++ CFGP2P_ERR(("extra IE size too big\n")); ++ ret = -ENOMEM; ++ } else { ++ /* parse and save new vndr_ie in curr_ie_buff before comparing it */ ++ if (vndr_ie && vndr_ie_len && curr_ie_buf) { ++ ptr = curr_ie_buf; ++ ++ wl_cfgp2p_parse_vndr_ies((u8*)vndr_ie, ++ vndr_ie_len, &new_vndr_ies); ++ ++ for (i = 0; i < new_vndr_ies.count; i++) { ++ struct parsed_vndr_ie_info *vndrie_info = ++ &new_vndr_ies.ie_info[i]; ++ ++ memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr, ++ vndrie_info->ie_len); ++ parsed_ie_buf_len += vndrie_info->ie_len; ++ } ++ } ++ ++ if (mgmt_ie_buf != NULL) { ++ if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) && ++ (memcmp(mgmt_ie_buf, curr_ie_buf, parsed_ie_buf_len) == 0)) { ++ CFGP2P_INFO(("Previous mgmt IE is equals to current IE")); ++ goto exit; ++ } ++ ++ /* parse old vndr_ie */ ++ wl_cfgp2p_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, ++ &old_vndr_ies); ++ ++ /* make a command to delete old ie */ ++ for (i = 0; i < old_vndr_ies.count; i++) { ++ struct parsed_vndr_ie_info *vndrie_info = ++ &old_vndr_ies.ie_info[i]; ++ ++ CFGP2P_INFO(("DELETED ID : %d, Len: %d , OUI:%02x:%02x:%02x\n", ++ vndrie_info->vndrie.id, vndrie_info->vndrie.len, ++ vndrie_info->vndrie.oui[0], vndrie_info->vndrie.oui[1], ++ vndrie_info->vndrie.oui[2])); ++ ++ del_add_ie_buf_len = wl_cfgp2p_vndr_ie(wl, curr_ie_buf, ++ bssidx, pktflag, vndrie_info->vndrie.oui, ++ vndrie_info->vndrie.id, ++ vndrie_info->ie_ptr + VNDR_IE_FIXED_LEN, ++ vndrie_info->ie_len - VNDR_IE_FIXED_LEN, ++ "del"); ++ ++ curr_ie_buf += del_add_ie_buf_len; ++ total_ie_buf_len += del_add_ie_buf_len; ++ } ++ } ++ ++ *mgmt_ie_len = 0; ++ /* Add if there is any extra IE */ ++ if (mgmt_ie_buf && parsed_ie_buf_len) { ++ ptr = mgmt_ie_buf; ++ ++ remained_buf_len = mgmt_ie_buf_len; ++ ++ /* make a command to add new ie */ ++ for (i = 0; i < new_vndr_ies.count; i++) { ++ struct parsed_vndr_ie_info *vndrie_info = ++ &new_vndr_ies.ie_info[i]; ++ ++ CFGP2P_INFO(("ADDED ID : %d, Len: %d(%d), OUI:%02x:%02x:%02x\n", ++ vndrie_info->vndrie.id, vndrie_info->vndrie.len, ++ vndrie_info->ie_len - 2, ++ vndrie_info->vndrie.oui[0], vndrie_info->vndrie.oui[1], ++ vndrie_info->vndrie.oui[2])); ++ ++ del_add_ie_buf_len = wl_cfgp2p_vndr_ie(wl, curr_ie_buf, ++ bssidx, pktflag, vndrie_info->vndrie.oui, ++ vndrie_info->vndrie.id, ++ vndrie_info->ie_ptr + VNDR_IE_FIXED_LEN, ++ vndrie_info->ie_len - VNDR_IE_FIXED_LEN, ++ "add"); ++ ++ /* verify remained buf size before copy data */ ++ if (remained_buf_len >= vndrie_info->ie_len) { ++ remained_buf_len -= vndrie_info->ie_len; ++ } else { ++ CFGP2P_ERR(("no space in mgmt_ie_buf: pktflag = %d, " ++ "found vndr ies # = %d(cur %d), remained len %d, " ++ "cur mgmt_ie_len %d, new ie len = %d\n", ++ pktflag, new_vndr_ies.count, i, remained_buf_len, ++ *mgmt_ie_len, vndrie_info->ie_len)); ++ break; ++ } ++ ++ /* save the parsed IE in wl struct */ ++ memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr, ++ vndrie_info->ie_len); ++ *mgmt_ie_len += vndrie_info->ie_len; ++ ++ curr_ie_buf += del_add_ie_buf_len; ++ total_ie_buf_len += del_add_ie_buf_len; ++ } ++ } ++ if (total_ie_buf_len) { ++ ret = wldev_iovar_setbuf_bsscfg(ndev, "vndr_ie", g_mgmt_ie_buf, ++ total_ie_buf_len, wl->ioctl_buf, WLC_IOCTL_MAXLEN, ++ bssidx, &wl->ioctl_buf_sync); ++ if (ret) ++ CFGP2P_ERR(("vndr ie set error : %d\n", ret)); ++ } ++ } ++#undef IE_TYPE ++#undef IE_TYPE_LEN ++exit: ++ return ret; ++} ++ ++/* Clear the manament IE buffer of BSSCFG ++ * Parameters: ++ * @wl : wl_private data ++ * @bssidx : bssidx for BSS ++ * ++ * Returns 0 if success. ++ */ ++s32 ++wl_cfgp2p_clear_management_ie(struct wl_priv *wl, s32 bssidx) ++{ ++ s32 vndrie_flag[] = {VNDR_IE_BEACON_FLAG, VNDR_IE_PRBRSP_FLAG, VNDR_IE_ASSOCRSP_FLAG, ++ VNDR_IE_PRBREQ_FLAG, VNDR_IE_ASSOCREQ_FLAG}; ++ s32 index = -1; ++ struct net_device *ndev = wl_cfgp2p_find_ndev(wl, bssidx); ++#define INIT_IE(IE_TYPE, BSS_TYPE) \ ++ do { \ ++ memset(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie, 0, \ ++ sizeof(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie)); \ ++ wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie_len = 0; \ ++ } while (0); ++ ++ if (bssidx < 0 || ndev == NULL) { ++ CFGP2P_ERR(("invalid %s\n", (bssidx < 0) ? "bssidx" : "ndev")); ++ return BCME_BADARG; ++ } ++ for (index = 0; index < ARRAYSIZE(vndrie_flag); index++) { ++ /* clean up vndr ies in dongle */ ++ wl_cfgp2p_set_management_ie(wl, ndev, bssidx, vndrie_flag[index], NULL, 0); ++ } ++ INIT_IE(probe_req, bssidx); ++ INIT_IE(probe_res, bssidx); ++ INIT_IE(assoc_req, bssidx); ++ INIT_IE(assoc_res, bssidx); ++ INIT_IE(beacon, bssidx); ++ return BCME_OK; ++} ++ ++ ++/* Is any of the tlvs the expected entry? If ++ * not update the tlvs buffer pointer/length. ++ */ ++static bool ++wl_cfgp2p_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type) ++{ ++ /* If the contents match the OUI and the type */ ++ if (ie[TLV_LEN_OFF] >= oui_len + 1 && ++ !bcmp(&ie[TLV_BODY_OFF], oui, oui_len) && ++ type == ie[TLV_BODY_OFF + oui_len]) { ++ return TRUE; ++ } ++ ++ if (tlvs == NULL) ++ return FALSE; ++ /* point to the next ie */ ++ ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN; ++ /* calculate the length of the rest of the buffer */ ++ *tlvs_len -= (int)(ie - *tlvs); ++ /* update the pointer to the start of the buffer */ ++ *tlvs = ie; ++ ++ return FALSE; ++} ++ ++wpa_ie_fixed_t * ++wl_cfgp2p_find_wpaie(u8 *parse, u32 len) ++{ ++ bcm_tlv_t *ie; ++ ++ while ((ie = bcm_parse_tlvs(parse, (u32)len, DOT11_MNG_VS_ID))) { ++ if (wl_cfgp2p_is_wpa_ie((u8*)ie, &parse, &len)) { ++ return (wpa_ie_fixed_t *)ie; ++ } ++ } ++ return NULL; ++} ++ ++wpa_ie_fixed_t * ++wl_cfgp2p_find_wpsie(u8 *parse, u32 len) ++{ ++ bcm_tlv_t *ie; ++ ++ while ((ie = bcm_parse_tlvs(parse, (u32)len, DOT11_MNG_VS_ID))) { ++ if (wl_cfgp2p_is_wps_ie((u8*)ie, &parse, &len)) { ++ return (wpa_ie_fixed_t *)ie; ++ } ++ } ++ return NULL; ++} ++ ++wifi_p2p_ie_t * ++wl_cfgp2p_find_p2pie(u8 *parse, u32 len) ++{ ++ bcm_tlv_t *ie; ++ ++ while ((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_VS_ID))) { ++ if (wl_cfgp2p_is_p2p_ie((uint8*)ie, &parse, &len)) { ++ return (wifi_p2p_ie_t *)ie; ++ } ++ } ++ return NULL; ++} ++ ++wifi_wfd_ie_t * ++wl_cfgp2p_find_wfdie(u8 *parse, u32 len) ++{ ++ bcm_tlv_t *ie; ++ ++ while ((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_VS_ID))) { ++ if (wl_cfgp2p_is_wfd_ie((uint8*)ie, &parse, &len)) { ++ return (wifi_wfd_ie_t *)ie; ++ } ++ } ++ return NULL; ++} ++static u32 ++wl_cfgp2p_vndr_ie(struct wl_priv *wl, u8 *iebuf, s32 bssidx, s32 pktflag, ++ s8 *oui, s32 ie_id, s8 *data, s32 datalen, const s8* add_del_cmd) ++{ ++ vndr_ie_setbuf_t hdr; /* aligned temporary vndr_ie buffer header */ ++ s32 iecount; ++ u32 data_offset; ++ ++ /* Validate the pktflag parameter */ ++ if ((pktflag & ~(VNDR_IE_BEACON_FLAG | VNDR_IE_PRBRSP_FLAG | ++ VNDR_IE_ASSOCRSP_FLAG | VNDR_IE_AUTHRSP_FLAG | ++ VNDR_IE_PRBREQ_FLAG | VNDR_IE_ASSOCREQ_FLAG))) { ++ CFGP2P_ERR(("p2pwl_vndr_ie: Invalid packet flag 0x%x\n", pktflag)); ++ return -1; ++ } ++ ++ /* Copy the vndr_ie SET command ("add"/"del") to the buffer */ ++ strncpy(hdr.cmd, add_del_cmd, VNDR_IE_CMD_LEN - 1); ++ hdr.cmd[VNDR_IE_CMD_LEN - 1] = '\0'; ++ ++ /* Set the IE count - the buffer contains only 1 IE */ ++ iecount = htod32(1); ++ memcpy((void *)&hdr.vndr_ie_buffer.iecount, &iecount, sizeof(s32)); ++ ++ /* Copy packet flags that indicate which packets will contain this IE */ ++ pktflag = htod32(pktflag); ++ memcpy((void *)&hdr.vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag, ++ sizeof(u32)); ++ ++ /* Add the IE ID to the buffer */ ++ hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = ie_id; ++ ++ /* Add the IE length to the buffer */ ++ hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = ++ (uint8) VNDR_IE_MIN_LEN + datalen; ++ ++ /* Add the IE OUI to the buffer */ ++ hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui[0] = oui[0]; ++ hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui[1] = oui[1]; ++ hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui[2] = oui[2]; ++ ++ /* Copy the aligned temporary vndr_ie buffer header to the IE buffer */ ++ memcpy(iebuf, &hdr, sizeof(hdr) - 1); ++ ++ /* Copy the IE data to the IE buffer */ ++ data_offset = ++ (u8*)&hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.data[0] - ++ (u8*)&hdr; ++ memcpy(iebuf + data_offset, data, datalen); ++ return data_offset + datalen; ++ ++} ++ ++/* ++ * Search the bssidx based on dev argument ++ * Parameters: ++ * @wl : wl_private data ++ * @ndev : net device to search bssidx ++ * Returns bssidx for ndev ++ */ ++s32 ++wl_cfgp2p_find_idx(struct wl_priv *wl, struct net_device *ndev) ++{ ++ u32 i; ++ s32 index = -1; ++ ++ if (ndev == NULL) { ++ CFGP2P_ERR((" ndev is NULL\n")); ++ goto exit; ++ } ++ if (!wl->p2p_supported) { ++ return P2PAPI_BSSCFG_PRIMARY; ++ } ++ for (i = 0; i < P2PAPI_BSSCFG_MAX; i++) { ++ if (ndev == wl_to_p2p_bss_ndev(wl, i)) { ++ index = wl_to_p2p_bss_bssidx(wl, i); ++ break; ++ } ++ } ++ if (index == -1) ++ return P2PAPI_BSSCFG_PRIMARY; ++exit: ++ return index; ++} ++ ++struct net_device * ++wl_cfgp2p_find_ndev(struct wl_priv *wl, s32 bssidx) ++{ ++ u32 i; ++ struct net_device *ndev = NULL; ++ if (bssidx < 0) { ++ CFGP2P_ERR((" bsscfg idx is invalid\n")); ++ goto exit; ++ } ++ ++ for (i = 0; i < P2PAPI_BSSCFG_MAX; i++) { ++ if (bssidx == wl_to_p2p_bss_bssidx(wl, i)) { ++ ndev = wl_to_p2p_bss_ndev(wl, i); ++ break; ++ } ++ } ++ ++exit: ++ return ndev; ++} ++ ++/* ++ * Callback function for WLC_E_P2P_DISC_LISTEN_COMPLETE ++ */ ++s32 ++wl_cfgp2p_listen_complete(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data) ++{ ++ s32 ret = BCME_OK; ++ struct net_device *netdev; ++ if (!wl || !wl->p2p) ++ return BCME_ERROR; ++ if (wl->p2p_net == ndev) { ++ netdev = wl_to_prmry_ndev(wl); ++ } else { ++ netdev = ndev; ++ } ++ CFGP2P_DBG((" Enter\n")); ++ if (wl_get_p2p_status(wl, LISTEN_EXPIRED) == 0) { ++ wl_set_p2p_status(wl, LISTEN_EXPIRED); ++ if (timer_pending(&wl->p2p->listen_timer)) { ++ del_timer_sync(&wl->p2p->listen_timer); ++ } ++ ++ if (wl->afx_hdl->is_listen == TRUE && ++ wl_get_drv_status_all(wl, FINDING_COMMON_CHANNEL)) { ++ WL_DBG(("Listen DONE for action frame\n")); ++ complete(&wl->act_frm_scan); ++ } ++#ifdef WL_CFG80211_SYNC_GON ++ else if (wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM_LISTEN)) { ++ wl_clr_drv_status(wl, WAITING_NEXT_ACT_FRM_LISTEN, netdev); ++ WL_DBG(("Listen DONE and wake up wait_next_af !!(%d)\n", ++ jiffies_to_msecs(jiffies - wl->af_tx_sent_jiffies))); ++ ++ if (wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM)) ++ wl_clr_drv_status(wl, WAITING_NEXT_ACT_FRM, netdev); ++ ++ complete(&wl->wait_next_af); ++ } ++#endif /* WL_CFG80211_SYNC_GON */ ++ ++#ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++ if (wl_get_drv_status_all(wl, REMAINING_ON_CHANNEL)) { ++#else ++ if (wl_get_drv_status_all(wl, REMAINING_ON_CHANNEL) || ++ wl_get_drv_status_all(wl, FAKE_REMAINING_ON_CHANNEL)) { ++#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ ++ WL_DBG(("Listen DONE for ramain on channel expired\n")); ++ wl_clr_drv_status(wl, REMAINING_ON_CHANNEL, netdev); ++#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++ wl_clr_drv_status(wl, FAKE_REMAINING_ON_CHANNEL, netdev); ++#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ ++ if (ndev && (ndev->ieee80211_ptr != NULL)) { ++ cfg80211_remain_on_channel_expired(ndev, wl->last_roc_id, ++ &wl->remain_on_chan, wl->remain_on_chan_type, GFP_KERNEL); ++ } ++ } ++ if (wl_add_remove_eventmsg(wl_to_prmry_ndev(wl), ++ WLC_E_P2P_PROBREQ_MSG, false) != BCME_OK) { ++ CFGP2P_ERR((" failed to unset WLC_E_P2P_PROPREQ_MSG\n")); ++ } ++ } else ++ wl_clr_p2p_status(wl, LISTEN_EXPIRED); ++ ++ return ret; ++ ++} ++ ++/* ++ * Timer expire callback function for LISTEN ++ * We can't report cfg80211_remain_on_channel_expired from Timer ISR context, ++ * so lets do it from thread context. ++ */ ++void ++wl_cfgp2p_listen_expired(unsigned long data) ++{ ++ wl_event_msg_t msg; ++ struct wl_priv *wl = (struct wl_priv *) data; ++ CFGP2P_DBG((" Enter\n")); ++ bzero(&msg, sizeof(wl_event_msg_t)); ++ msg.event_type = hton32(WLC_E_P2P_DISC_LISTEN_COMPLETE); ++ wl_cfg80211_event(wl->p2p_net ? wl->p2p_net : ++ wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE), &msg, NULL); ++} ++/* ++ * Routine for cancelling the P2P LISTEN ++ */ ++static s32 ++wl_cfgp2p_cancel_listen(struct wl_priv *wl, struct net_device *ndev, ++ bool notify) ++{ ++ WL_DBG(("Enter \n")); ++ /* Irrespective of whether timer is running or not, reset ++ * the LISTEN state. ++ */ ++ if (timer_pending(&wl->p2p->listen_timer)) { ++ del_timer_sync(&wl->p2p->listen_timer); ++ if (notify) ++ if (ndev && ndev->ieee80211_ptr) { ++ cfg80211_remain_on_channel_expired(ndev, wl->last_roc_id, ++ &wl->remain_on_chan, wl->remain_on_chan_type, ++ GFP_KERNEL); ++ } ++ } ++ return 0; ++} ++/* ++ * Do a P2P Listen on the given channel for the given duration. ++ * A listen consists of sitting idle and responding to P2P probe requests ++ * with a P2P probe response. ++ * ++ * This fn assumes dongle p2p device discovery is already enabled. ++ * Parameters : ++ * @wl : wl_private data ++ * @channel : channel to listen ++ * @duration_ms : the time (milli seconds) to wait ++ */ ++s32 ++wl_cfgp2p_discover_listen(struct wl_priv *wl, s32 channel, u32 duration_ms) ++{ ++#define EXTRA_DELAY_TIME 100 ++ s32 ret = BCME_OK; ++ struct timer_list *_timer; ++ s32 extra_delay; ++ struct net_device *netdev = wl_to_prmry_ndev(wl); ++ ++ CFGP2P_DBG((" Enter Listen Channel : %d, Duration : %d\n", channel, duration_ms)); ++ if (unlikely(wl_get_p2p_status(wl, DISCOVERY_ON) == 0)) { ++ ++ CFGP2P_ERR((" Discovery is not set, so we have noting to do\n")); ++ ++ ret = BCME_NOTREADY; ++ goto exit; ++ } ++ if (timer_pending(&wl->p2p->listen_timer)) { ++ CFGP2P_DBG(("previous LISTEN is not completed yet\n")); ++ goto exit; ++ ++ } ++#ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++ else ++ wl_clr_p2p_status(wl, LISTEN_EXPIRED); ++#endif /* not WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ ++ if (wl_add_remove_eventmsg(netdev, WLC_E_P2P_PROBREQ_MSG, true) != BCME_OK) { ++ CFGP2P_ERR((" failed to set WLC_E_P2P_PROPREQ_MSG\n")); ++ } ++ ++ ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_LISTEN, channel, (u16) duration_ms, ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); ++ _timer = &wl->p2p->listen_timer; ++ ++ /* We will wait to receive WLC_E_P2P_DISC_LISTEN_COMPLETE from dongle , ++ * otherwise we will wait up to duration_ms + 100ms + duration / 10 ++ */ ++ if (ret == BCME_OK) { ++ extra_delay = EXTRA_DELAY_TIME + (duration_ms / 10); ++ } else { ++ /* if failed to set listen, it doesn't need to wait whole duration. */ ++ duration_ms = 100 + duration_ms / 20; ++ extra_delay = 0; ++ } ++ ++ INIT_TIMER(_timer, wl_cfgp2p_listen_expired, duration_ms, extra_delay); ++#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++ wl_clr_p2p_status(wl, LISTEN_EXPIRED); ++#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ ++ ++#undef EXTRA_DELAY_TIME ++exit: ++ return ret; ++} ++ ++ ++s32 ++wl_cfgp2p_discover_enable_search(struct wl_priv *wl, u8 enable) ++{ ++ s32 ret = BCME_OK; ++ CFGP2P_DBG((" Enter\n")); ++ if (!wl_get_p2p_status(wl, DISCOVERY_ON)) { ++ ++ CFGP2P_DBG((" do nothing, discovery is off\n")); ++ return ret; ++ } ++ if (wl_get_p2p_status(wl, SEARCH_ENABLED) == enable) { ++ CFGP2P_DBG(("already : %d\n", enable)); ++ return ret; ++ } ++ ++ wl_chg_p2p_status(wl, SEARCH_ENABLED); ++ /* When disabling Search, reset the WL driver's p2p discovery state to ++ * WL_P2P_DISC_ST_SCAN. ++ */ ++ if (!enable) { ++ wl_clr_p2p_status(wl, SCANNING); ++ ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, ++ wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Callback function for WLC_E_ACTION_FRAME_COMPLETE, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE ++ */ ++s32 ++wl_cfgp2p_action_tx_complete(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data) ++{ ++ s32 ret = BCME_OK; ++ u32 event_type = ntoh32(e->event_type); ++ u32 status = ntoh32(e->status); ++ CFGP2P_DBG((" Enter\n")); ++ if (event_type == WLC_E_ACTION_FRAME_COMPLETE) { ++ ++ CFGP2P_INFO((" WLC_E_ACTION_FRAME_COMPLETE is received : %d\n", status)); ++ if (status == WLC_E_STATUS_SUCCESS) { ++ wl_set_p2p_status(wl, ACTION_TX_COMPLETED); ++ CFGP2P_DBG(("WLC_E_ACTION_FRAME_COMPLETE : ACK\n")); ++ } ++ else { ++ wl_set_p2p_status(wl, ACTION_TX_NOACK); ++ CFGP2P_INFO(("WLC_E_ACTION_FRAME_COMPLETE : NO ACK\n")); ++ wl_stop_wait_next_action_frame(wl, ndev); ++ } ++ } else { ++ CFGP2P_INFO((" WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE is received," ++ "status : %d\n", status)); ++ ++ if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) ++ complete(&wl->send_af_done); ++ } ++ return ret; ++} ++/* Send an action frame immediately without doing channel synchronization. ++ * ++ * This function does not wait for a completion event before returning. ++ * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action ++ * frame is transmitted. ++ * The WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE event will be received when an ++ * 802.11 ack has been received for the sent action frame. ++ */ ++s32 ++wl_cfgp2p_tx_action_frame(struct wl_priv *wl, struct net_device *dev, ++ wl_af_params_t *af_params, s32 bssidx) ++{ ++ s32 ret = BCME_OK; ++ s32 timeout = 0; ++ wl_eventmsg_buf_t buf; ++ ++ ++ CFGP2P_INFO(("\n")); ++ CFGP2P_INFO(("channel : %u , dwell time : %u\n", ++ af_params->channel, af_params->dwell_time)); ++ ++ wl_clr_p2p_status(wl, ACTION_TX_COMPLETED); ++ wl_clr_p2p_status(wl, ACTION_TX_NOACK); ++ ++ bzero(&buf, sizeof(wl_eventmsg_buf_t)); ++ wl_cfg80211_add_to_eventbuffer(&buf, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE, true); ++ wl_cfg80211_add_to_eventbuffer(&buf, WLC_E_ACTION_FRAME_COMPLETE, true); ++ if ((ret = wl_cfg80211_apply_eventbuffer(wl_to_prmry_ndev(wl), wl, &buf)) < 0) ++ return ret; ++ ++#define MAX_WAIT_TIME 2000 ++ if (bssidx == P2PAPI_BSSCFG_PRIMARY) ++ bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); ++ ++ wl->af_sent_channel = af_params->channel; ++#ifdef WL_CFG80211_SYNC_GON ++ wl->af_tx_sent_jiffies = jiffies; ++#endif /* WL_CFG80211_SYNC_GON */ ++ ++ ret = wldev_iovar_setbuf_bsscfg(dev, "actframe", af_params, sizeof(*af_params), ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); ++ ++ if (ret < 0) { ++ CFGP2P_ERR((" sending action frame is failed\n")); ++ goto exit; ++ } ++ ++ timeout = wait_for_completion_timeout(&wl->send_af_done, msecs_to_jiffies(MAX_WAIT_TIME)); ++ ++ if (timeout > 0 && wl_get_p2p_status(wl, ACTION_TX_COMPLETED)) { ++ CFGP2P_INFO(("tx action frame operation is completed\n")); ++ ret = BCME_OK; ++ } else { ++ ret = BCME_ERROR; ++ CFGP2P_INFO(("tx action frame operation is failed\n")); ++ } ++ /* clear status bit for action tx */ ++ wl_clr_p2p_status(wl, ACTION_TX_COMPLETED); ++ wl_clr_p2p_status(wl, ACTION_TX_NOACK); ++ ++exit: ++ CFGP2P_INFO((" via act frame iovar : status = %d\n", ret)); ++ ++ bzero(&buf, sizeof(wl_eventmsg_buf_t)); ++ wl_cfg80211_add_to_eventbuffer(&buf, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE, false); ++ wl_cfg80211_add_to_eventbuffer(&buf, WLC_E_ACTION_FRAME_COMPLETE, false); ++ if ((ret = wl_cfg80211_apply_eventbuffer(wl_to_prmry_ndev(wl), wl, &buf)) < 0) ++ WL_ERR(("TX frame events revert back failed \n")); ++ ++#undef MAX_WAIT_TIME ++ return ret; ++} ++ ++/* Generate our P2P Device Address and P2P Interface Address from our primary ++ * MAC address. ++ */ ++void ++wl_cfgp2p_generate_bss_mac(struct ether_addr *primary_addr, ++ struct ether_addr *out_dev_addr, struct ether_addr *out_int_addr) ++{ ++ memset(out_dev_addr, 0, sizeof(*out_dev_addr)); ++ memset(out_int_addr, 0, sizeof(*out_int_addr)); ++ ++ /* Generate the P2P Device Address. This consists of the device's ++ * primary MAC address with the locally administered bit set. ++ */ ++ memcpy(out_dev_addr, primary_addr, sizeof(*out_dev_addr)); ++ out_dev_addr->octet[0] |= 0x02; ++ ++ /* Generate the P2P Interface Address. If the discovery and connection ++ * BSSCFGs need to simultaneously co-exist, then this address must be ++ * different from the P2P Device Address. ++ */ ++ memcpy(out_int_addr, out_dev_addr, sizeof(*out_int_addr)); ++ out_int_addr->octet[4] ^= 0x80; ++ ++} ++ ++/* P2P IF Address change to Virtual Interface MAC Address */ ++void ++wl_cfg80211_change_ifaddr(u8* buf, struct ether_addr *p2p_int_addr, u8 element_id) ++{ ++ wifi_p2p_ie_t *ie = (wifi_p2p_ie_t*) buf; ++ u16 len = ie->len; ++ u8 *subel; ++ u8 subelt_id; ++ u16 subelt_len; ++ CFGP2P_DBG((" Enter\n")); ++ ++ /* Point subel to the P2P IE's subelt field. ++ * Subtract the preceding fields (id, len, OUI, oui_type) from the length. ++ */ ++ subel = ie->subelts; ++ len -= 4; /* exclude OUI + OUI_TYPE */ ++ ++ while (len >= 3) { ++ /* attribute id */ ++ subelt_id = *subel; ++ subel += 1; ++ len -= 1; ++ ++ /* 2-byte little endian */ ++ subelt_len = *subel++; ++ subelt_len |= *subel++ << 8; ++ ++ len -= 2; ++ len -= subelt_len; /* for the remaining subelt fields */ ++ ++ if (subelt_id == element_id) { ++ if (subelt_id == P2P_SEID_INTINTADDR) { ++ memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); ++ CFGP2P_INFO(("Intended P2P Interface Address ATTR FOUND\n")); ++ } else if (subelt_id == P2P_SEID_DEV_ID) { ++ memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); ++ CFGP2P_INFO(("Device ID ATTR FOUND\n")); ++ } else if (subelt_id == P2P_SEID_DEV_INFO) { ++ memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); ++ CFGP2P_INFO(("Device INFO ATTR FOUND\n")); ++ } else if (subelt_id == P2P_SEID_GROUP_ID) { ++ memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); ++ CFGP2P_INFO(("GROUP ID ATTR FOUND\n")); ++ } return; ++ } else { ++ CFGP2P_DBG(("OTHER id : %d\n", subelt_id)); ++ } ++ subel += subelt_len; ++ } ++} ++/* ++ * Check if a BSS is up. ++ * This is a common implementation called by most OSL implementations of ++ * p2posl_bss_isup(). DO NOT call this function directly from the ++ * common code -- call p2posl_bss_isup() instead to allow the OSL to ++ * override the common implementation if necessary. ++ */ ++bool ++wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx) ++{ ++ s32 result, val; ++ bool isup = false; ++ s8 getbuf[64]; ++ ++ /* Check if the BSS is up */ ++ *(int*)getbuf = -1; ++ result = wldev_iovar_getbuf_bsscfg(ndev, "bss", &bsscfg_idx, ++ sizeof(bsscfg_idx), getbuf, sizeof(getbuf), 0, NULL); ++ if (result != 0) { ++ CFGP2P_ERR(("'wl bss -C %d' failed: %d\n", bsscfg_idx, result)); ++ CFGP2P_ERR(("NOTE: this ioctl error is normal " ++ "when the BSS has not been created yet.\n")); ++ } else { ++ val = *(int*)getbuf; ++ val = dtoh32(val); ++ CFGP2P_INFO(("---wl bss -C %d ==> %d\n", bsscfg_idx, val)); ++ isup = (val ? TRUE : FALSE); ++ } ++ return isup; ++} ++ ++ ++/* Bring up or down a BSS */ ++s32 ++wl_cfgp2p_bss(struct wl_priv *wl, struct net_device *ndev, s32 bsscfg_idx, s32 up) ++{ ++ s32 ret = BCME_OK; ++ s32 val = up ? 1 : 0; ++ ++ struct { ++ s32 cfg; ++ s32 val; ++ } bss_setbuf; ++ ++ bss_setbuf.cfg = htod32(bsscfg_idx); ++ bss_setbuf.val = htod32(val); ++ CFGP2P_INFO(("---wl bss -C %d %s\n", bsscfg_idx, up ? "up" : "down")); ++ ret = wldev_iovar_setbuf(ndev, "bss", &bss_setbuf, sizeof(bss_setbuf), ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); ++ ++ if (ret != 0) { ++ CFGP2P_ERR(("'bss %d' failed with %d\n", up, ret)); ++ } ++ ++ return ret; ++} ++ ++/* Check if 'p2p' is supported in the driver */ ++s32 ++wl_cfgp2p_supported(struct wl_priv *wl, struct net_device *ndev) ++{ ++ s32 ret = BCME_OK; ++ s32 p2p_supported = 0; ++ ret = wldev_iovar_getint(ndev, "p2p", ++ &p2p_supported); ++ if (ret < 0) { ++ CFGP2P_ERR(("wl p2p error %d\n", ret)); ++ return 0; ++ } ++ if (p2p_supported == 1) { ++ CFGP2P_INFO(("p2p is supported\n")); ++ } else { ++ CFGP2P_INFO(("p2p is unsupported\n")); ++ p2p_supported = 0; ++ } ++ return p2p_supported; ++} ++ ++/* Cleanup P2P resources */ ++s32 ++wl_cfgp2p_down(struct wl_priv *wl) ++{ ++ s32 i = 0, index = -1; ++ wl_cfgp2p_cancel_listen(wl, ++ wl->p2p_net ? wl->p2p_net : wl_to_prmry_ndev(wl), TRUE); ++ for (i = 0; i < P2PAPI_BSSCFG_MAX; i++) { ++ index = wl_to_p2p_bss_bssidx(wl, i); ++ if (index != WL_INVALID) ++ wl_cfgp2p_clear_management_ie(wl, index); ++ } ++ wl_cfgp2p_deinit_priv(wl); ++ return 0; ++} ++ ++s32 ++wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) ++{ ++ s32 ret = -1; ++ int count, start, duration; ++ wl_p2p_sched_t dongle_noa; ++ ++ CFGP2P_DBG((" Enter\n")); ++ ++ memset(&dongle_noa, 0, sizeof(dongle_noa)); ++ ++ if (wl->p2p && wl->p2p->vif_created) { ++ ++ wl->p2p->noa.desc[0].start = 0; ++ ++ sscanf(buf, "%10d %10d %10d", &count, &start, &duration); ++ CFGP2P_DBG(("set_p2p_noa count %d start %d duration %d\n", ++ count, start, duration)); ++ if (count != -1) ++ wl->p2p->noa.desc[0].count = count; ++ ++ /* supplicant gives interval as start */ ++ if (start != -1) ++ wl->p2p->noa.desc[0].interval = start; ++ ++ if (duration != -1) ++ wl->p2p->noa.desc[0].duration = duration; ++ ++ if (wl->p2p->noa.desc[0].count != 255) { ++ wl->p2p->noa.desc[0].start = 200; ++ dongle_noa.type = WL_P2P_SCHED_TYPE_REQ_ABS; ++ dongle_noa.action = WL_P2P_SCHED_ACTION_GOOFF; ++ dongle_noa.option = WL_P2P_SCHED_OPTION_TSFOFS; ++ } ++ else { ++ /* Continuous NoA interval. */ ++ dongle_noa.action = WL_P2P_SCHED_ACTION_NONE; ++ dongle_noa.type = WL_P2P_SCHED_TYPE_ABS; ++ if ((wl->p2p->noa.desc[0].interval == 102) || ++ (wl->p2p->noa.desc[0].interval == 100)) { ++ wl->p2p->noa.desc[0].start = 100 - ++ wl->p2p->noa.desc[0].duration; ++ dongle_noa.option = WL_P2P_SCHED_OPTION_BCNPCT; ++ } ++ else { ++ dongle_noa.option = WL_P2P_SCHED_OPTION_NORMAL; ++ } ++ } ++ /* Put the noa descriptor in dongle format for dongle */ ++ dongle_noa.desc[0].count = htod32(wl->p2p->noa.desc[0].count); ++ if (dongle_noa.option == WL_P2P_SCHED_OPTION_BCNPCT) { ++ dongle_noa.desc[0].start = htod32(wl->p2p->noa.desc[0].start); ++ dongle_noa.desc[0].duration = htod32(wl->p2p->noa.desc[0].duration); ++ } ++ else { ++ dongle_noa.desc[0].start = htod32(wl->p2p->noa.desc[0].start*1000); ++ dongle_noa.desc[0].duration = htod32(wl->p2p->noa.desc[0].duration*1000); ++ } ++ dongle_noa.desc[0].interval = htod32(wl->p2p->noa.desc[0].interval*1000); ++ ++ ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), ++ "p2p_noa", &dongle_noa, sizeof(dongle_noa), wl->ioctl_buf, WLC_IOCTL_MAXLEN, ++ &wl->ioctl_buf_sync); ++ ++ if (ret < 0) { ++ CFGP2P_ERR(("fw set p2p_noa failed %d\n", ret)); ++ } ++ } ++ else { ++ CFGP2P_ERR(("ERROR: set_noa in non-p2p mode\n")); ++ } ++ return ret; ++} ++s32 ++wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int buf_len) ++{ ++ ++ wifi_p2p_noa_desc_t *noa_desc; ++ int len = 0, i; ++ char _buf[200]; ++ ++ CFGP2P_DBG((" Enter\n")); ++ buf[0] = '\0'; ++ if (wl->p2p && wl->p2p->vif_created) { ++ if (wl->p2p->noa.desc[0].count || wl->p2p->ops.ops) { ++ _buf[0] = 1; /* noa index */ ++ _buf[1] = (wl->p2p->ops.ops ? 0x80: 0) | ++ (wl->p2p->ops.ctw & 0x7f); /* ops + ctw */ ++ len += 2; ++ if (wl->p2p->noa.desc[0].count) { ++ noa_desc = (wifi_p2p_noa_desc_t*)&_buf[len]; ++ noa_desc->cnt_type = wl->p2p->noa.desc[0].count; ++ noa_desc->duration = wl->p2p->noa.desc[0].duration; ++ noa_desc->interval = wl->p2p->noa.desc[0].interval; ++ noa_desc->start = wl->p2p->noa.desc[0].start; ++ len += sizeof(wifi_p2p_noa_desc_t); ++ } ++ if (buf_len <= len * 2) { ++ CFGP2P_ERR(("ERROR: buf_len %d in not enough for" ++ "returning noa in string format\n", buf_len)); ++ return -1; ++ } ++ /* We have to convert the buffer data into ASCII strings */ ++ for (i = 0; i < len; i++) { ++ snprintf(buf, 3, "%02x", _buf[i]); ++ buf += 2; ++ } ++ buf[i*2] = '\0'; ++ } ++ } ++ else { ++ CFGP2P_ERR(("ERROR: get_noa in non-p2p mode\n")); ++ return -1; ++ } ++ return len * 2; ++} ++s32 ++wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) ++{ ++ int ps, ctw; ++ int ret = -1; ++ s32 legacy_ps; ++ ++ CFGP2P_DBG((" Enter\n")); ++ if (wl->p2p && wl->p2p->vif_created) { ++ sscanf(buf, "%10d %10d %10d", &legacy_ps, &ps, &ctw); ++ CFGP2P_DBG((" Enter legacy_ps %d ps %d ctw %d\n", legacy_ps, ps, ctw)); ++ if (ctw != -1) { ++ wl->p2p->ops.ctw = ctw; ++ ret = 0; ++ } ++ if (ps != -1) { ++ wl->p2p->ops.ops = ps; ++ ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), ++ "p2p_ops", &wl->p2p->ops, sizeof(wl->p2p->ops), ++ wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); ++ if (ret < 0) { ++ CFGP2P_ERR(("fw set p2p_ops failed %d\n", ret)); ++ } ++ } ++ ++ if ((legacy_ps != -1) && ((legacy_ps == PM_MAX) || (legacy_ps == PM_OFF))) { ++#if defined(SUPPORT_PM2_ONLY) ++ if (legacy_ps == PM_MAX) ++ legacy_ps = PM_FAST; ++#endif /* SUPPORT_PM2_ONLY */ ++ ++ ret = wldev_ioctl(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), ++ WLC_SET_PM, &legacy_ps, sizeof(legacy_ps), true); ++ if (unlikely(ret)) { ++ CFGP2P_ERR(("error (%d)\n", ret)); ++ } else { ++ wl_cfg80211_update_power_mode(ndev); ++ } ++ } ++ else ++ CFGP2P_ERR(("ilegal setting\n")); ++ } ++ else { ++ CFGP2P_ERR(("ERROR: set_p2p_ps in non-p2p mode\n")); ++ ret = -1; ++ } ++ return ret; ++} ++ ++u8 * ++wl_cfgp2p_retreive_p2pattrib(void *buf, u8 element_id) ++{ ++ wifi_p2p_ie_t *ie = NULL; ++ u16 len = 0; ++ u8 *subel; ++ u8 subelt_id; ++ u16 subelt_len; ++ ++ if (!buf) { ++ WL_ERR(("P2P IE not present")); ++ return 0; ++ } ++ ++ ie = (wifi_p2p_ie_t*) buf; ++ len = ie->len; ++ ++ /* Point subel to the P2P IE's subelt field. ++ * Subtract the preceding fields (id, len, OUI, oui_type) from the length. ++ */ ++ subel = ie->subelts; ++ len -= 4; /* exclude OUI + OUI_TYPE */ ++ ++ while (len >= 3) { ++ /* attribute id */ ++ subelt_id = *subel; ++ subel += 1; ++ len -= 1; ++ ++ /* 2-byte little endian */ ++ subelt_len = *subel++; ++ subelt_len |= *subel++ << 8; ++ ++ len -= 2; ++ len -= subelt_len; /* for the remaining subelt fields */ ++ ++ if (subelt_id == element_id) { ++ /* This will point to start of subelement attrib after ++ * attribute id & len ++ */ ++ return subel; ++ } ++ ++ /* Go to next subelement */ ++ subel += subelt_len; ++ } ++ ++ /* Not Found */ ++ return NULL; ++} ++ ++#define P2P_GROUP_CAPAB_GO_BIT 0x01 ++u8 * ++wl_cfgp2p_retreive_p2p_dev_addr(wl_bss_info_t *bi, u32 bi_length) ++{ ++ wifi_p2p_ie_t * p2p_ie = NULL; ++ u8 *capability = NULL; ++ bool p2p_go = 0; ++ u8 *ptr = NULL; ++ ++ if (!(p2p_ie = wl_cfgp2p_find_p2pie(((u8 *) bi) + bi->ie_offset, bi->ie_length))) { ++ WL_ERR(("P2P IE not found")); ++ return NULL; ++ } ++ ++ if (!(capability = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_P2P_INFO))) { ++ WL_ERR(("P2P Capability attribute not found")); ++ return NULL; ++ } ++ ++ /* Check Group capability for Group Owner bit */ ++ p2p_go = capability[1] & P2P_GROUP_CAPAB_GO_BIT; ++ if (!p2p_go) { ++ return bi->BSSID.octet; ++ } ++ ++ /* In probe responses, DEVICE INFO attribute will be present */ ++ if (!(ptr = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_INFO))) { ++ /* If DEVICE_INFO is not found, this might be a beacon frame. ++ * check for DEVICE_ID in the beacon frame. ++ */ ++ ptr = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_ID); ++ } ++ ++ if (!ptr) ++ WL_ERR((" Both DEVICE_ID & DEVICE_INFO attribute not present in P2P IE ")); ++ ++ return ptr; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) ++static void ++wl_cfgp2p_ethtool_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) ++{ ++ snprintf(info->driver, sizeof(info->driver), "p2p"); ++ snprintf(info->version, sizeof(info->version), "%lu", (unsigned long)(0)); ++} ++ ++struct ethtool_ops cfgp2p_ethtool_ops = { ++ .get_drvinfo = wl_cfgp2p_ethtool_get_drvinfo ++}; ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ ++ ++s32 ++wl_cfgp2p_register_ndev(struct wl_priv *wl) ++{ ++ int ret = 0; ++ struct net_device* net = NULL; ++ struct wireless_dev *wdev = NULL; ++ uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x33, 0x22, 0x11 }; ++ ++ if (wl->p2p_net) { ++ CFGP2P_ERR(("p2p_net defined already.\n")); ++ return -EINVAL; ++ } ++ ++ /* Allocate etherdev, including space for private structure */ ++ if (!(net = alloc_etherdev(sizeof(struct wl_priv *)))) { ++ CFGP2P_ERR(("%s: OOM - alloc_etherdev\n", __FUNCTION__)); ++ return -ENODEV; ++ } ++ ++ wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); ++ if (unlikely(!wdev)) { ++ WL_ERR(("Could not allocate wireless device\n")); ++ free_netdev(net); ++ return -ENOMEM; ++ } ++ ++ strncpy(net->name, "p2p%d", sizeof(net->name) - 1); ++ net->name[IFNAMSIZ - 1] = '\0'; ++ ++ /* Copy the reference to wl_priv */ ++ memcpy((void *)netdev_priv(net), &wl, sizeof(struct wl_priv *)); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) ++ ASSERT(!net->open); ++ net->do_ioctl = wl_cfgp2p_do_ioctl; ++ net->hard_start_xmit = wl_cfgp2p_start_xmit; ++ net->open = wl_cfgp2p_if_open; ++ net->stop = wl_cfgp2p_if_stop; ++#else ++ ASSERT(!net->netdev_ops); ++ net->netdev_ops = &wl_cfgp2p_if_ops; ++#endif ++ ++ /* Register with a dummy MAC addr */ ++ memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); ++ ++ wdev->wiphy = wl->wdev->wiphy; ++ ++ wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); ++ ++ net->ieee80211_ptr = wdev; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) ++ net->ethtool_ops = &cfgp2p_ethtool_ops; ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ ++ ++ SET_NETDEV_DEV(net, wiphy_dev(wdev->wiphy)); ++ ++ /* Associate p2p0 network interface with new wdev */ ++ wdev->netdev = net; ++ ++ ret = register_netdev(net); ++ if (ret) { ++ CFGP2P_ERR((" register_netdevice failed (%d)\n", ret)); ++ free_netdev(net); ++ kfree(wdev); ++ return -ENODEV; ++ } ++ ++ /* store p2p net ptr for further reference. Note that iflist won't have this ++ * entry as there corresponding firmware interface is a "Hidden" interface. ++ */ ++ wl->p2p_wdev = wdev; ++ wl->p2p_net = net; ++ ++ printk("%s: P2P Interface Registered\n", net->name); ++ ++ return ret; ++} ++ ++s32 ++wl_cfgp2p_unregister_ndev(struct wl_priv *wl) ++{ ++ ++ if (!wl || !wl->p2p_net) { ++ CFGP2P_ERR(("Invalid Ptr\n")); ++ return -EINVAL; ++ } ++ ++ unregister_netdev(wl->p2p_net); ++ free_netdev(wl->p2p_net); ++ ++ return 0; ++} ++static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev) ++{ ++ if (skb) ++ { ++ CFGP2P_DBG(("(%s) is not used for data operations.Droping the packet.\n", ++ ndev->name)); ++ dev_kfree_skb_any(skb); ++ } ++ ++ return 0; ++} ++ ++static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr, int cmd) ++{ ++ int ret = 0; ++ struct wl_priv *wl = *(struct wl_priv **)netdev_priv(net); ++ struct net_device *ndev = wl_to_prmry_ndev(wl); ++ ++ /* There is no ifidx corresponding to p2p0 in our firmware. So we should ++ * not Handle any IOCTL cmds on p2p0 other than ANDROID PRIVATE CMDs. ++ * For Android PRIV CMD handling map it to primary I/F ++ */ ++ if (cmd == SIOCDEVPRIVATE+1) { ++ ret = wl_android_priv_cmd(ndev, ifr, cmd); ++ ++ } else { ++ CFGP2P_ERR(("%s: IOCTL req 0x%x on p2p0 I/F. Ignoring. \n", ++ __FUNCTION__, cmd)); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++static int wl_cfgp2p_if_open(struct net_device *net) ++{ ++ extern struct wl_priv *wlcfg_drv_priv; ++ struct wireless_dev *wdev = net->ieee80211_ptr; ++ struct wl_priv *wl = NULL; ++ wl = wlcfg_drv_priv; ++ if (!wdev || !wl || !wl->p2p) ++ return -EINVAL; ++ WL_TRACE(("Enter\n")); ++ /* If suppose F/W download (ifconfig wlan0 up) hasn't been done by now, ++ * do it here. This will make sure that in concurrent mode, supplicant ++ * is not dependent on a particular order of interface initialization. ++ * i.e you may give wpa_supp -iwlan0 -N -ip2p0 or wpa_supp -ip2p0 -N ++ * -iwlan0. ++ */ ++ wdev->wiphy->interface_modes |= (BIT(NL80211_IFTYPE_P2P_CLIENT) ++ | BIT(NL80211_IFTYPE_P2P_GO)); ++ wl_cfg80211_do_driver_init(net); ++ ++ return 0; ++} ++ ++static int wl_cfgp2p_if_stop(struct net_device *net) ++{ ++ extern struct wl_priv *wlcfg_drv_priv; ++ struct wl_priv *wl = NULL; ++ unsigned long flags; ++ struct wireless_dev *wdev = net->ieee80211_ptr; ++ int clear_flag = 0; ++ if (!wdev) ++ return -EINVAL; ++ ++ WL_TRACE(("Enter\n")); ++ wl = wlcfg_drv_priv; ++ if (!wl) ++ return -EINVAL; ++ spin_lock_irqsave(&wl->cfgdrv_lock, flags); ++ if (wl->scan_request && wl->scan_request->dev == net) { ++ cfg80211_scan_done(wl->scan_request, true); ++ wl->scan_request = NULL; ++ clear_flag = 1; ++ } ++ spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); ++ if (clear_flag) ++ wl_clr_drv_status(wl, SCANNING, net); ++ wdev->wiphy->interface_modes = (wdev->wiphy->interface_modes) ++ & (~(BIT(NL80211_IFTYPE_P2P_CLIENT)| ++ BIT(NL80211_IFTYPE_P2P_GO))); ++ return 0; ++} ++ ++bool wl_cfgp2p_is_ifops(const struct net_device_ops *if_ops) ++{ ++ return (if_ops == &wl_cfgp2p_if_ops); ++} +diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h +new file mode 100644 +index 00000000..4c64db61 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h +@@ -0,0 +1,342 @@ ++/* ++ * Linux cfgp2p driver ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wl_cfgp2p.h 376685 2013-01-02 06:28:45Z $ ++ */ ++#ifndef _wl_cfgp2p_h_ ++#define _wl_cfgp2p_h_ ++#include ++#include ++ ++struct wl_priv; ++extern u32 wl_dbg_level; ++ ++typedef struct wifi_p2p_ie wifi_wfd_ie_t; ++/* Enumeration of the usages of the BSSCFGs used by the P2P Library. Do not ++ * confuse this with a bsscfg index. This value is an index into the ++ * saved_ie[] array of structures which in turn contains a bsscfg index field. ++ */ ++typedef enum { ++ P2PAPI_BSSCFG_PRIMARY, /* maps to driver's primary bsscfg */ ++ P2PAPI_BSSCFG_DEVICE, /* maps to driver's P2P device discovery bsscfg */ ++ P2PAPI_BSSCFG_CONNECTION, /* maps to driver's P2P connection bsscfg */ ++ P2PAPI_BSSCFG_MAX ++} p2p_bsscfg_type_t; ++ ++/* vendor ies max buffer length for probe response or beacon */ ++#define VNDR_IES_MAX_BUF_LEN 1400 ++/* normal vendor ies buffer length */ ++#define VNDR_IES_BUF_LEN 512 ++ ++/* Structure to hold all saved P2P and WPS IEs for a BSSCFG */ ++struct p2p_saved_ie { ++ u8 p2p_probe_req_ie[VNDR_IES_BUF_LEN]; ++ u8 p2p_probe_res_ie[VNDR_IES_MAX_BUF_LEN]; ++ u8 p2p_assoc_req_ie[VNDR_IES_BUF_LEN]; ++ u8 p2p_assoc_res_ie[VNDR_IES_BUF_LEN]; ++ u8 p2p_beacon_ie[VNDR_IES_MAX_BUF_LEN]; ++ u32 p2p_probe_req_ie_len; ++ u32 p2p_probe_res_ie_len; ++ u32 p2p_assoc_req_ie_len; ++ u32 p2p_assoc_res_ie_len; ++ u32 p2p_beacon_ie_len; ++}; ++ ++struct p2p_bss { ++ u32 bssidx; ++ struct net_device *dev; ++ struct p2p_saved_ie saved_ie; ++ void *private_data; ++}; ++ ++struct p2p_info { ++ bool on; /* p2p on/off switch */ ++ bool scan; ++ bool vif_created; ++ s8 vir_ifname[IFNAMSIZ]; ++ unsigned long status; ++ struct ether_addr dev_addr; ++ struct ether_addr int_addr; ++ struct p2p_bss bss_idx[P2PAPI_BSSCFG_MAX]; ++ struct timer_list listen_timer; ++ wl_p2p_sched_t noa; ++ wl_p2p_ops_t ops; ++ wlc_ssid_t ssid; ++}; ++ ++#define MAX_VNDR_IE_NUMBER 5 ++ ++struct parsed_vndr_ie_info { ++ char *ie_ptr; ++ u32 ie_len; /* total length including id & length field */ ++ vndr_ie_t vndrie; ++}; ++ ++struct parsed_vndr_ies { ++ u32 count; ++ struct parsed_vndr_ie_info ie_info[MAX_VNDR_IE_NUMBER]; ++}; ++ ++/* dongle status */ ++enum wl_cfgp2p_status { ++ WLP2P_STATUS_DISCOVERY_ON = 0, ++ WLP2P_STATUS_SEARCH_ENABLED, ++ WLP2P_STATUS_IF_ADD, ++ WLP2P_STATUS_IF_DEL, ++ WLP2P_STATUS_IF_DELETING, ++ WLP2P_STATUS_IF_CHANGING, ++ WLP2P_STATUS_IF_CHANGED, ++ WLP2P_STATUS_LISTEN_EXPIRED, ++ WLP2P_STATUS_ACTION_TX_COMPLETED, ++ WLP2P_STATUS_ACTION_TX_NOACK, ++ WLP2P_STATUS_SCANNING, ++ WLP2P_STATUS_GO_NEG_PHASE, ++ WLP2P_STATUS_DISC_IN_PROGRESS ++}; ++ ++ ++#define wl_to_p2p_bss_ndev(wl, type) ((wl)->p2p->bss_idx[type].dev) ++#define wl_to_p2p_bss_bssidx(wl, type) ((wl)->p2p->bss_idx[type].bssidx) ++#define wl_to_p2p_bss_saved_ie(wl, type) ((wl)->p2p->bss_idx[type].saved_ie) ++#define wl_to_p2p_bss_private(wl, type) ((wl)->p2p->bss_idx[type].private_data) ++#define wl_to_p2p_bss(wl, type) ((wl)->p2p->bss_idx[type]) ++#define wl_get_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0 : test_bit(WLP2P_STATUS_ ## stat, \ ++ &(wl)->p2p->status)) ++#define wl_set_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0 : set_bit(WLP2P_STATUS_ ## stat, \ ++ &(wl)->p2p->status)) ++#define wl_clr_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0 : clear_bit(WLP2P_STATUS_ ## stat, \ ++ &(wl)->p2p->status)) ++#define wl_chg_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:change_bit(WLP2P_STATUS_ ## stat, \ ++ &(wl)->p2p->status)) ++#define p2p_on(wl) ((wl)->p2p->on) ++#define p2p_scan(wl) ((wl)->p2p->scan) ++#define p2p_is_on(wl) ((wl)->p2p && (wl)->p2p->on) ++ ++/* dword align allocation */ ++#define WLC_IOCTL_MAXLEN 8192 ++ ++#define CFGP2P_ERROR_TEXT "CFGP2P-ERROR) " ++ ++#define CFGP2P_ERR(args) \ ++ do { \ ++ if (wl_dbg_level & WL_DBG_ERR) { \ ++ printk(KERN_INFO CFGP2P_ERROR_TEXT "%s : ", __func__); \ ++ printk args; \ ++ } \ ++ } while (0) ++#define CFGP2P_INFO(args) \ ++ do { \ ++ if (wl_dbg_level & WL_DBG_INFO) { \ ++ printk(KERN_INFO "CFGP2P-INFO) %s : ", __func__); \ ++ printk args; \ ++ } \ ++ } while (0) ++#define CFGP2P_DBG(args) \ ++ do { \ ++ if (wl_dbg_level & WL_DBG_DBG) { \ ++ printk(KERN_DEBUG "CFGP2P-DEBUG) %s :", __func__); \ ++ printk args; \ ++ } \ ++ } while (0) ++ ++#define CFGP2P_ACTION(args) \ ++ do { \ ++ if (wl_dbg_level & WL_DBG_P2P_ACTION) { \ ++ printk(KERN_DEBUG "CFGP2P-ACTION) %s :", __func__); \ ++ printk args; \ ++ } \ ++ } while (0) ++#define INIT_TIMER(timer, func, duration, extra_delay) \ ++ do { \ ++ init_timer(timer); \ ++ timer->function = func; \ ++ timer->expires = jiffies + msecs_to_jiffies(duration + extra_delay); \ ++ timer->data = (unsigned long) wl; \ ++ add_timer(timer); \ ++ } while (0); ++extern void ++wl_cfgp2p_listen_expired(unsigned long data); ++extern bool ++wl_cfgp2p_is_pub_action(void *frame, u32 frame_len); ++extern bool ++wl_cfgp2p_is_p2p_action(void *frame, u32 frame_len); ++extern bool ++wl_cfgp2p_is_gas_action(void *frame, u32 frame_len); ++extern void ++wl_cfgp2p_print_actframe(bool tx, void *frame, u32 frame_len); ++extern s32 ++wl_cfgp2p_init_priv(struct wl_priv *wl); ++extern void ++wl_cfgp2p_deinit_priv(struct wl_priv *wl); ++extern s32 ++wl_cfgp2p_set_firm_p2p(struct wl_priv *wl); ++extern s32 ++wl_cfgp2p_set_p2p_mode(struct wl_priv *wl, u8 mode, ++ u32 channel, u16 listen_ms, int bssidx); ++extern s32 ++wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, ++ chanspec_t chspec); ++extern s32 ++wl_cfgp2p_ifdisable(struct wl_priv *wl, struct ether_addr *mac); ++extern s32 ++wl_cfgp2p_ifdel(struct wl_priv *wl, struct ether_addr *mac); ++extern s32 ++wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, chanspec_t chspec); ++ ++extern s32 ++wl_cfgp2p_ifidx(struct wl_priv *wl, struct ether_addr *mac, s32 *index); ++ ++extern s32 ++wl_cfgp2p_init_discovery(struct wl_priv *wl); ++extern s32 ++wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, const u8 *ie, u32 ie_len); ++extern s32 ++wl_cfgp2p_disable_discovery(struct wl_priv *wl); ++extern s32 ++wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, u32 num_chans, ++ u16 *channels, ++ s32 search_state, u16 action, u32 bssidx, struct ether_addr *tx_dst_addr); ++ ++extern s32 ++wl_cfgp2p_act_frm_search(struct wl_priv *wl, struct net_device *ndev, ++ s32 bssidx, s32 channel, struct ether_addr *tx_dst_addr); ++ ++extern wpa_ie_fixed_t * ++wl_cfgp2p_find_wpaie(u8 *parse, u32 len); ++ ++extern wpa_ie_fixed_t * ++wl_cfgp2p_find_wpsie(u8 *parse, u32 len); ++ ++extern wifi_p2p_ie_t * ++wl_cfgp2p_find_p2pie(u8 *parse, u32 len); ++ ++extern wifi_wfd_ie_t * ++wl_cfgp2p_find_wfdie(u8 *parse, u32 len); ++extern s32 ++wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, ++ s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len); ++extern s32 ++wl_cfgp2p_clear_management_ie(struct wl_priv *wl, s32 bssidx); ++ ++extern s32 ++wl_cfgp2p_find_idx(struct wl_priv *wl, struct net_device *ndev); ++extern struct net_device * ++wl_cfgp2p_find_ndev(struct wl_priv *wl, s32 bssidx); ++ ++ ++extern s32 ++wl_cfgp2p_listen_complete(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data); ++extern s32 ++wl_cfgp2p_discover_listen(struct wl_priv *wl, s32 channel, u32 duration_ms); ++ ++extern s32 ++wl_cfgp2p_discover_enable_search(struct wl_priv *wl, u8 enable); ++ ++extern s32 ++wl_cfgp2p_action_tx_complete(struct wl_priv *wl, struct net_device *ndev, ++ const wl_event_msg_t *e, void *data); ++extern s32 ++wl_cfgp2p_tx_action_frame(struct wl_priv *wl, struct net_device *dev, ++ wl_af_params_t *af_params, s32 bssidx); ++ ++extern void ++wl_cfgp2p_generate_bss_mac(struct ether_addr *primary_addr, struct ether_addr *out_dev_addr, ++ struct ether_addr *out_int_addr); ++ ++extern void ++wl_cfg80211_change_ifaddr(u8* buf, struct ether_addr *p2p_int_addr, u8 element_id); ++extern bool ++wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx); ++ ++extern s32 ++wl_cfgp2p_bss(struct wl_priv *wl, struct net_device *ndev, s32 bsscfg_idx, s32 up); ++ ++ ++extern s32 ++wl_cfgp2p_supported(struct wl_priv *wl, struct net_device *ndev); ++ ++extern s32 ++wl_cfgp2p_down(struct wl_priv *wl); ++ ++extern s32 ++wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len); ++ ++extern s32 ++wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len); ++ ++extern s32 ++wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len); ++ ++extern u8 * ++wl_cfgp2p_retreive_p2pattrib(void *buf, u8 element_id); ++ ++extern u8 * ++wl_cfgp2p_retreive_p2p_dev_addr(wl_bss_info_t *bi, u32 bi_length); ++ ++extern s32 ++wl_cfgp2p_register_ndev(struct wl_priv *wl); ++ ++extern s32 ++wl_cfgp2p_unregister_ndev(struct wl_priv *wl); ++ ++extern bool ++wl_cfgp2p_is_ifops(const struct net_device_ops *if_ops); ++ ++/* WiFi Direct */ ++#define SOCIAL_CHAN_1 1 ++#define SOCIAL_CHAN_2 6 ++#define SOCIAL_CHAN_3 11 ++#define IS_P2P_SOCIAL_CHANNEL(channel) ((channel == SOCIAL_CHAN_1) || \ ++ (channel == SOCIAL_CHAN_2) || \ ++ (channel == SOCIAL_CHAN_3)) ++#define SOCIAL_CHAN_CNT 3 ++#define AF_PEER_SEARCH_CNT 2 ++#define WL_P2P_WILDCARD_SSID "DIRECT-" ++#define WL_P2P_WILDCARD_SSID_LEN 7 ++#define WL_P2P_INTERFACE_PREFIX "p2p" ++#define WL_P2P_TEMP_CHAN 11 ++ ++/* If the provision discovery is for JOIN operations, ++ * then we need not do an internal scan to find GO. ++ */ ++#define IS_PROV_DISC_WITHOUT_GROUP_ID(p2p_ie, len) \ ++ (wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_GROUP_ID) == NULL) ++ ++#define IS_GAS_REQ(frame, len) (wl_cfgp2p_is_gas_action(frame, len) && \ ++ ((frame->action == P2PSD_ACTION_ID_GAS_IREQ) || \ ++ (frame->action == P2PSD_ACTION_ID_GAS_CREQ))) ++#define IS_P2P_PUB_ACT_REQ(frame, p2p_ie, len) \ ++ (wl_cfgp2p_is_pub_action(frame, len) && \ ++ ((frame->subtype == P2P_PAF_GON_REQ) || \ ++ (frame->subtype == P2P_PAF_INVITE_REQ) || \ ++ ((frame->subtype == P2P_PAF_PROVDIS_REQ) && \ ++ IS_PROV_DISC_WITHOUT_GROUP_ID(p2p_ie, len)))) ++#define IS_P2P_PUB_ACT_RSP_SUBTYPE(subtype) ((subtype == P2P_PAF_GON_RSP) || \ ++ ((subtype == P2P_PAF_GON_CONF) || \ ++ (subtype == P2P_PAF_INVITE_RSP) || \ ++ (subtype == P2P_PAF_PROVDIS_RSP))) ++#define IS_P2P_SOCIAL(ch) ((ch == SOCIAL_CHAN_1) || (ch == SOCIAL_CHAN_2) || (ch == SOCIAL_CHAN_3)) ++#define IS_P2P_SSID(ssid, len) (!memcmp(ssid, WL_P2P_WILDCARD_SSID, WL_P2P_WILDCARD_SSID_LEN) && \ ++ (len == WL_P2P_WILDCARD_SSID_LEN)) ++#endif /* _wl_cfgp2p_h_ */ +diff --git a/drivers/net/wireless/bcmdhd/wl_dbg.h b/drivers/net/wireless/bcmdhd/wl_dbg.h +new file mode 100644 +index 00000000..b5e70807 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/wl_dbg.h +@@ -0,0 +1,63 @@ ++/* ++ * Minimal debug/trace/assert driver definitions for ++ * Broadcom 802.11 Networking Adapter. ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wl_dbg.h 326635 2012-04-10 03:15:29Z $ ++ */ ++ ++ ++#ifndef _wl_dbg_h_ ++#define _wl_dbg_h_ ++ ++ ++extern uint32 wl_msg_level; ++extern uint32 wl_msg_level2; ++ ++#define WL_TIMESTAMP() ++ ++#if 0 && (VERSION_MAJOR > 9) ++#include ++#define WL_PRINT(args) do { printf args; IO8Log args; } while (0) ++#else ++#define WL_PRINT(args) do { WL_TIMESTAMP(); printf args; } while (0) ++#endif ++ ++ ++ ++#define WL_NONE(args) ++ ++#define WL_ERROR(args) ++#define WL_TRACE(args) ++#define WL_APSTA_UPDN(args) ++#define WL_APSTA_RX(args) ++#ifdef WLMSG_WSEC ++#define WL_WSEC(args) WL_PRINT(args) ++#define WL_WSEC_DUMP(args) WL_PRINT(args) ++#else ++#define WL_WSEC(args) ++#define WL_WSEC_DUMP(args) ++#endif ++ ++extern uint32 wl_msg_level; ++extern uint32 wl_msg_level2; ++#endif +diff --git a/drivers/net/wireless/bcmdhd/wl_iw.c b/drivers/net/wireless/bcmdhd/wl_iw.c +new file mode 100644 +index 00000000..c9b24ebc +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/wl_iw.c +@@ -0,0 +1,3589 @@ ++/* ++ * Linux Wireless Extensions support ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wl_iw.c 347632 2012-07-27 11:00:35Z $ ++ */ ++ ++#if defined(USE_IW) ++#define LINUX_PORT ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++ ++typedef const struct si_pub si_t; ++#include ++ ++ ++#include ++#include ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++#include ++#endif ++#if defined(SOFTAP) ++struct net_device *ap_net_dev = NULL; ++tsk_ctl_t ap_eth_ctl; ++#endif ++ ++extern bool wl_iw_conn_status_str(uint32 event_type, uint32 status, ++ uint32 reason, char* stringBuf, uint buflen); ++ ++uint wl_msg_level = WL_ERROR_VAL; ++ ++#define MAX_WLIW_IOCTL_LEN 1024 ++ ++ ++#define htod32(i) i ++#define htod16(i) i ++#define dtoh32(i) i ++#define dtoh16(i) i ++#define htodchanspec(i) i ++#define dtohchanspec(i) i ++ ++extern struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); ++extern int dhd_wait_pend8021x(struct net_device *dev); ++ ++#if WIRELESS_EXT < 19 ++#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) ++#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST) ++#endif ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) ++#define DAEMONIZE(a) daemonize(a); \ ++ allow_signal(SIGKILL); \ ++ allow_signal(SIGTERM); ++#else ++#define RAISE_RX_SOFTIRQ() \ ++ cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ) ++#define DAEMONIZE(a) daemonize(); \ ++ do { if (a) \ ++ strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \ ++ } while (0); ++#endif ++ ++#define ISCAN_STATE_IDLE 0 ++#define ISCAN_STATE_SCANING 1 ++ ++ ++#define WLC_IW_ISCAN_MAXLEN 2048 ++typedef struct iscan_buf { ++ struct iscan_buf * next; ++ char iscan_buf[WLC_IW_ISCAN_MAXLEN]; ++} iscan_buf_t; ++ ++typedef struct iscan_info { ++ struct net_device *dev; ++ struct timer_list timer; ++ uint32 timer_ms; ++ uint32 timer_on; ++ int iscan_state; ++ iscan_buf_t * list_hdr; ++ iscan_buf_t * list_cur; ++ ++ ++ long sysioc_pid; ++ struct semaphore sysioc_sem; ++ struct completion sysioc_exited; ++ ++ ++ char ioctlbuf[WLC_IOCTL_SMLEN]; ++} iscan_info_t; ++iscan_info_t *g_iscan = NULL; ++static void wl_iw_timerfunc(ulong data); ++static void wl_iw_set_event_mask(struct net_device *dev); ++static int wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action); ++ ++ ++typedef struct priv_link { ++ wl_iw_t *wliw; ++} priv_link_t; ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)) ++#define WL_DEV_LINK(dev) (priv_link_t*)(dev->priv) ++#else ++#define WL_DEV_LINK(dev) (priv_link_t*)netdev_priv(dev) ++#endif ++ ++ ++#define IW_DEV_IF(dev) ((wl_iw_t*)(WL_DEV_LINK(dev))->wliw) ++ ++static void swap_key_from_BE( ++ wl_wsec_key_t *key ++) ++{ ++ key->index = htod32(key->index); ++ key->len = htod32(key->len); ++ key->algo = htod32(key->algo); ++ key->flags = htod32(key->flags); ++ key->rxiv.hi = htod32(key->rxiv.hi); ++ key->rxiv.lo = htod16(key->rxiv.lo); ++ key->iv_initialized = htod32(key->iv_initialized); ++} ++ ++static void swap_key_to_BE( ++ wl_wsec_key_t *key ++) ++{ ++ key->index = dtoh32(key->index); ++ key->len = dtoh32(key->len); ++ key->algo = dtoh32(key->algo); ++ key->flags = dtoh32(key->flags); ++ key->rxiv.hi = dtoh32(key->rxiv.hi); ++ key->rxiv.lo = dtoh16(key->rxiv.lo); ++ key->iv_initialized = dtoh32(key->iv_initialized); ++} ++ ++static int ++dev_wlc_ioctl( ++ struct net_device *dev, ++ int cmd, ++ void *arg, ++ int len ++) ++{ ++ struct ifreq ifr; ++ wl_ioctl_t ioc; ++ mm_segment_t fs; ++ int ret; ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ioc.cmd = cmd; ++ ioc.buf = arg; ++ ioc.len = len; ++ ++ strcpy(ifr.ifr_name, dev->name); ++ ifr.ifr_data = (caddr_t) &ioc; ++ ++#ifndef LINUX_HYBRID ++ ++ dev_open(dev); ++#endif ++ ++ fs = get_fs(); ++ set_fs(get_ds()); ++#if defined(WL_USE_NETDEV_OPS) ++ ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE); ++#else ++ ret = dev->do_ioctl(dev, &ifr, SIOCDEVPRIVATE); ++#endif ++ set_fs(fs); ++ ++ return ret; ++} ++ ++ ++ ++static int ++dev_wlc_intvar_set( ++ struct net_device *dev, ++ char *name, ++ int val) ++{ ++ char buf[WLC_IOCTL_SMLEN]; ++ uint len; ++ ++ val = htod32(val); ++ len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf)); ++ ASSERT(len); ++ ++ return (dev_wlc_ioctl(dev, WLC_SET_VAR, buf, len)); ++} ++ ++static int ++dev_iw_iovar_setbuf( ++ struct net_device *dev, ++ char *iovar, ++ void *param, ++ int paramlen, ++ void *bufptr, ++ int buflen) ++{ ++ int iolen; ++ ++ iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen); ++ ASSERT(iolen); ++ BCM_REFERENCE(iolen); ++ ++ return (dev_wlc_ioctl(dev, WLC_SET_VAR, bufptr, iolen)); ++} ++ ++static int ++dev_iw_iovar_getbuf( ++ struct net_device *dev, ++ char *iovar, ++ void *param, ++ int paramlen, ++ void *bufptr, ++ int buflen) ++{ ++ int iolen; ++ ++ iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen); ++ ASSERT(iolen); ++ BCM_REFERENCE(iolen); ++ ++ return (dev_wlc_ioctl(dev, WLC_GET_VAR, bufptr, buflen)); ++} ++ ++#if WIRELESS_EXT > 17 ++static int ++dev_wlc_bufvar_set( ++ struct net_device *dev, ++ char *name, ++ char *buf, int len) ++{ ++ char *ioctlbuf; ++ uint buflen; ++ int error; ++ ++ ioctlbuf = kmalloc(MAX_WLIW_IOCTL_LEN, GFP_KERNEL); ++ if (!ioctlbuf) ++ return -ENOMEM; ++ ++ buflen = bcm_mkiovar(name, buf, len, ioctlbuf, MAX_WLIW_IOCTL_LEN); ++ ASSERT(buflen); ++ error = dev_wlc_ioctl(dev, WLC_SET_VAR, ioctlbuf, buflen); ++ ++ kfree(ioctlbuf); ++ return error; ++} ++#endif ++ ++ ++ ++static int ++dev_wlc_bufvar_get( ++ struct net_device *dev, ++ char *name, ++ char *buf, int buflen) ++{ ++ char *ioctlbuf; ++ int error; ++ ++ uint len; ++ ++ ioctlbuf = kmalloc(MAX_WLIW_IOCTL_LEN, GFP_KERNEL); ++ if (!ioctlbuf) ++ return -ENOMEM; ++ len = bcm_mkiovar(name, NULL, 0, ioctlbuf, MAX_WLIW_IOCTL_LEN); ++ ASSERT(len); ++ BCM_REFERENCE(len); ++ error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)ioctlbuf, MAX_WLIW_IOCTL_LEN); ++ if (!error) ++ bcopy(ioctlbuf, buf, buflen); ++ ++ kfree(ioctlbuf); ++ return (error); ++} ++ ++ ++ ++static int ++dev_wlc_intvar_get( ++ struct net_device *dev, ++ char *name, ++ int *retval) ++{ ++ union { ++ char buf[WLC_IOCTL_SMLEN]; ++ int val; ++ } var; ++ int error; ++ ++ uint len; ++ uint data_null; ++ ++ len = bcm_mkiovar(name, (char *)(&data_null), 0, (char *)(&var), sizeof(var.buf)); ++ ASSERT(len); ++ error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)&var, len); ++ ++ *retval = dtoh32(var.val); ++ ++ return (error); ++} ++ ++ ++#if WIRELESS_EXT < 13 ++struct iw_request_info ++{ ++ __u16 cmd; ++ __u16 flags; ++}; ++ ++typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info, ++ void *wrqu, char *extra); ++#endif ++ ++#if WIRELESS_EXT > 12 ++static int ++wl_iw_set_leddc( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra ++) ++{ ++ int dc = *(int *)extra; ++ int error; ++ ++ error = dev_wlc_intvar_set(dev, "leddc", dc); ++ return error; ++} ++ ++static int ++wl_iw_set_vlanmode( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra ++) ++{ ++ int mode = *(int *)extra; ++ int error; ++ ++ mode = htod32(mode); ++ error = dev_wlc_intvar_set(dev, "vlan_mode", mode); ++ return error; ++} ++ ++static int ++wl_iw_set_pm( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra ++) ++{ ++ int pm = *(int *)extra; ++ int error; ++ ++ pm = htod32(pm); ++ error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm)); ++ return error; ++} ++#endif ++ ++int ++wl_iw_send_priv_event( ++ struct net_device *dev, ++ char *flag ++) ++{ ++ union iwreq_data wrqu; ++ char extra[IW_CUSTOM_MAX + 1]; ++ int cmd; ++ ++ cmd = IWEVCUSTOM; ++ memset(&wrqu, 0, sizeof(wrqu)); ++ if (strlen(flag) > sizeof(extra)) ++ return -1; ++ ++ strcpy(extra, flag); ++ wrqu.data.length = strlen(extra); ++ wireless_send_event(dev, cmd, &wrqu, extra); ++ WL_TRACE(("Send IWEVCUSTOM Event as %s\n", extra)); ++ ++ return 0; ++} ++ ++static int ++wl_iw_config_commit( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ void *zwrq, ++ char *extra ++) ++{ ++ wlc_ssid_t ssid; ++ int error; ++ struct sockaddr bssid; ++ ++ WL_TRACE(("%s: SIOCSIWCOMMIT\n", dev->name)); ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)))) ++ return error; ++ ++ ssid.SSID_len = dtoh32(ssid.SSID_len); ++ ++ if (!ssid.SSID_len) ++ return 0; ++ ++ bzero(&bssid, sizeof(struct sockaddr)); ++ if ((error = dev_wlc_ioctl(dev, WLC_REASSOC, &bssid, ETHER_ADDR_LEN))) { ++ WL_ERROR(("%s: WLC_REASSOC failed (%d)\n", __FUNCTION__, error)); ++ return error; ++ } ++ ++ return 0; ++} ++ ++static int ++wl_iw_get_name( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *cwrq, ++ char *extra ++) ++{ ++ int phytype, err; ++ uint band[3]; ++ char cap[5]; ++ ++ WL_TRACE(("%s: SIOCGIWNAME\n", dev->name)); ++ ++ cap[0] = 0; ++ if ((err = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype))) < 0) ++ goto done; ++ if ((err = dev_wlc_ioctl(dev, WLC_GET_BANDLIST, band, sizeof(band))) < 0) ++ goto done; ++ ++ band[0] = dtoh32(band[0]); ++ switch (phytype) { ++ case WLC_PHY_TYPE_A: ++ strcpy(cap, "a"); ++ break; ++ case WLC_PHY_TYPE_B: ++ strcpy(cap, "b"); ++ break; ++ case WLC_PHY_TYPE_LP: ++ case WLC_PHY_TYPE_G: ++ if (band[0] >= 2) ++ strcpy(cap, "abg"); ++ else ++ strcpy(cap, "bg"); ++ break; ++ case WLC_PHY_TYPE_N: ++ if (band[0] >= 2) ++ strcpy(cap, "abgn"); ++ else ++ strcpy(cap, "bgn"); ++ break; ++ } ++done: ++ snprintf(cwrq->name, IFNAMSIZ, "IEEE 802.11%s", cap); ++ return 0; ++} ++ ++static int ++wl_iw_set_freq( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_freq *fwrq, ++ char *extra ++) ++{ ++ int error, chan; ++ uint sf = 0; ++ ++ WL_TRACE(("%s: SIOCSIWFREQ\n", dev->name)); ++ ++ ++ if (fwrq->e == 0 && fwrq->m < MAXCHANNEL) { ++ chan = fwrq->m; ++ } ++ ++ ++ else { ++ ++ if (fwrq->e >= 6) { ++ fwrq->e -= 6; ++ while (fwrq->e--) ++ fwrq->m *= 10; ++ } else if (fwrq->e < 6) { ++ while (fwrq->e++ < 6) ++ fwrq->m /= 10; ++ } ++ ++ if (fwrq->m > 4000 && fwrq->m < 5000) ++ sf = WF_CHAN_FACTOR_4_G; ++ ++ chan = wf_mhz2channel(fwrq->m, sf); ++ } ++ chan = htod32(chan); ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &chan, sizeof(chan)))) ++ return error; ++ ++ ++ return -EINPROGRESS; ++} ++ ++static int ++wl_iw_get_freq( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_freq *fwrq, ++ char *extra ++) ++{ ++ channel_info_t ci; ++ int error; ++ ++ WL_TRACE(("%s: SIOCGIWFREQ\n", dev->name)); ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci)))) ++ return error; ++ ++ ++ fwrq->m = dtoh32(ci.hw_channel); ++ fwrq->e = dtoh32(0); ++ return 0; ++} ++ ++static int ++wl_iw_set_mode( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ __u32 *uwrq, ++ char *extra ++) ++{ ++ int infra = 0, ap = 0, error = 0; ++ ++ WL_TRACE(("%s: SIOCSIWMODE\n", dev->name)); ++ ++ switch (*uwrq) { ++ case IW_MODE_MASTER: ++ infra = ap = 1; ++ break; ++ case IW_MODE_ADHOC: ++ case IW_MODE_AUTO: ++ break; ++ case IW_MODE_INFRA: ++ infra = 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ infra = htod32(infra); ++ ap = htod32(ap); ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra))) || ++ (error = dev_wlc_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap)))) ++ return error; ++ ++ ++ return -EINPROGRESS; ++} ++ ++static int ++wl_iw_get_mode( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ __u32 *uwrq, ++ char *extra ++) ++{ ++ int error, infra = 0, ap = 0; ++ ++ WL_TRACE(("%s: SIOCGIWMODE\n", dev->name)); ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra))) || ++ (error = dev_wlc_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)))) ++ return error; ++ ++ infra = dtoh32(infra); ++ ap = dtoh32(ap); ++ *uwrq = infra ? ap ? IW_MODE_MASTER : IW_MODE_INFRA : IW_MODE_ADHOC; ++ ++ return 0; ++} ++ ++static int ++wl_iw_get_range( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ struct iw_range *range = (struct iw_range *) extra; ++ static int channels[MAXCHANNEL+1]; ++ wl_uint32_list_t *list = (wl_uint32_list_t *) channels; ++ wl_rateset_t rateset; ++ int error, i, k; ++ uint sf, ch; ++ ++ int phytype; ++ int bw_cap = 0, sgi_tx = 0, nmode = 0; ++ channel_info_t ci; ++ uint8 nrate_list2copy = 0; ++ uint16 nrate_list[4][8] = { {13, 26, 39, 52, 78, 104, 117, 130}, ++ {14, 29, 43, 58, 87, 116, 130, 144}, ++ {27, 54, 81, 108, 162, 216, 243, 270}, ++ {30, 60, 90, 120, 180, 240, 270, 300}}; ++ ++ WL_TRACE(("%s: SIOCGIWRANGE\n", dev->name)); ++ ++ if (!extra) ++ return -EINVAL; ++ ++ dwrq->length = sizeof(struct iw_range); ++ memset(range, 0, sizeof(*range)); ++ ++ ++ range->min_nwid = range->max_nwid = 0; ++ ++ ++ list->count = htod32(MAXCHANNEL); ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_VALID_CHANNELS, channels, sizeof(channels)))) ++ return error; ++ for (i = 0; i < dtoh32(list->count) && i < IW_MAX_FREQUENCIES; i++) { ++ range->freq[i].i = dtoh32(list->element[i]); ++ ++ ch = dtoh32(list->element[i]); ++ if (ch <= CH_MAX_2G_CHANNEL) ++ sf = WF_CHAN_FACTOR_2_4_G; ++ else ++ sf = WF_CHAN_FACTOR_5_G; ++ ++ range->freq[i].m = wf_channel2mhz(ch, sf); ++ range->freq[i].e = 6; ++ } ++ range->num_frequency = range->num_channels = i; ++ ++ ++ range->max_qual.qual = 5; ++ ++ range->max_qual.level = 0x100 - 200; ++ ++ range->max_qual.noise = 0x100 - 200; ++ ++ range->sensitivity = 65535; ++ ++#if WIRELESS_EXT > 11 ++ ++ range->avg_qual.qual = 3; ++ ++ range->avg_qual.level = 0x100 + WL_IW_RSSI_GOOD; ++ ++ range->avg_qual.noise = 0x100 - 75; ++#endif ++ ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset)))) ++ return error; ++ rateset.count = dtoh32(rateset.count); ++ range->num_bitrates = rateset.count; ++ for (i = 0; i < rateset.count && i < IW_MAX_BITRATES; i++) ++ range->bitrate[i] = (rateset.rates[i] & 0x7f) * 500000; ++ dev_wlc_intvar_get(dev, "nmode", &nmode); ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype)))) ++ return error; ++ ++ if (nmode == 1 && ((phytype == WLC_PHY_TYPE_SSN) || (phytype == WLC_PHY_TYPE_LCN) || ++ (phytype == WLC_PHY_TYPE_LCN40))) { ++ dev_wlc_intvar_get(dev, "mimo_bw_cap", &bw_cap); ++ dev_wlc_intvar_get(dev, "sgi_tx", &sgi_tx); ++ dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t)); ++ ci.hw_channel = dtoh32(ci.hw_channel); ++ ++ if (bw_cap == 0 || ++ (bw_cap == 2 && ci.hw_channel <= 14)) { ++ if (sgi_tx == 0) ++ nrate_list2copy = 0; ++ else ++ nrate_list2copy = 1; ++ } ++ if (bw_cap == 1 || ++ (bw_cap == 2 && ci.hw_channel >= 36)) { ++ if (sgi_tx == 0) ++ nrate_list2copy = 2; ++ else ++ nrate_list2copy = 3; ++ } ++ range->num_bitrates += 8; ++ for (k = 0; i < range->num_bitrates; k++, i++) { ++ ++ range->bitrate[i] = (nrate_list[nrate_list2copy][k]) * 500000; ++ } ++ } ++ ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &i, sizeof(i)))) ++ return error; ++ i = dtoh32(i); ++ if (i == WLC_PHY_TYPE_A) ++ range->throughput = 24000000; ++ else ++ range->throughput = 1500000; ++ ++ ++ range->min_rts = 0; ++ range->max_rts = 2347; ++ range->min_frag = 256; ++ range->max_frag = 2346; ++ ++ range->max_encoding_tokens = DOT11_MAX_DEFAULT_KEYS; ++ range->num_encoding_sizes = 4; ++ range->encoding_size[0] = WEP1_KEY_SIZE; ++ range->encoding_size[1] = WEP128_KEY_SIZE; ++#if WIRELESS_EXT > 17 ++ range->encoding_size[2] = TKIP_KEY_SIZE; ++#else ++ range->encoding_size[2] = 0; ++#endif ++ range->encoding_size[3] = AES_KEY_SIZE; ++ ++ ++ range->min_pmp = 0; ++ range->max_pmp = 0; ++ range->min_pmt = 0; ++ range->max_pmt = 0; ++ range->pmp_flags = 0; ++ range->pm_capa = 0; ++ ++ ++ range->num_txpower = 2; ++ range->txpower[0] = 1; ++ range->txpower[1] = 255; ++ range->txpower_capa = IW_TXPOW_MWATT; ++ ++#if WIRELESS_EXT > 10 ++ range->we_version_compiled = WIRELESS_EXT; ++ range->we_version_source = 19; ++ ++ ++ range->retry_capa = IW_RETRY_LIMIT; ++ range->retry_flags = IW_RETRY_LIMIT; ++ range->r_time_flags = 0; ++ ++ range->min_retry = 1; ++ range->max_retry = 255; ++ ++ range->min_r_time = 0; ++ range->max_r_time = 0; ++#endif ++ ++#if WIRELESS_EXT > 17 ++ range->enc_capa = IW_ENC_CAPA_WPA; ++ range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP; ++ range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP; ++ range->enc_capa |= IW_ENC_CAPA_WPA2; ++#if (defined(BCMSUP_PSK) && defined(WLFBT)) ++ ++ range->enc_capa |= IW_ENC_CAPA_4WAY_HANDSHAKE; ++#endif ++ ++ ++ IW_EVENT_CAPA_SET_KERNEL(range->event_capa); ++ ++ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); ++ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); ++ IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP); ++ IW_EVENT_CAPA_SET(range->event_capa, IWEVMICHAELMICFAILURE); ++ IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCREQIE); ++ IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCRESPIE); ++ IW_EVENT_CAPA_SET(range->event_capa, IWEVPMKIDCAND); ++ ++#if WIRELESS_EXT >= 22 && defined(IW_SCAN_CAPA_ESSID) ++ ++ range->scan_capa = IW_SCAN_CAPA_ESSID; ++#endif ++#endif ++ ++ return 0; ++} ++ ++static int ++rssi_to_qual(int rssi) ++{ ++ if (rssi <= WL_IW_RSSI_NO_SIGNAL) ++ return 0; ++ else if (rssi <= WL_IW_RSSI_VERY_LOW) ++ return 1; ++ else if (rssi <= WL_IW_RSSI_LOW) ++ return 2; ++ else if (rssi <= WL_IW_RSSI_GOOD) ++ return 3; ++ else if (rssi <= WL_IW_RSSI_VERY_GOOD) ++ return 4; ++ else ++ return 5; ++} ++ ++static int ++wl_iw_set_spy( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ wl_iw_t *iw = IW_DEV_IF(dev); ++ struct sockaddr *addr = (struct sockaddr *) extra; ++ int i; ++ ++ WL_TRACE(("%s: SIOCSIWSPY\n", dev->name)); ++ ++ if (!extra) ++ return -EINVAL; ++ ++ iw->spy_num = MIN(ARRAYSIZE(iw->spy_addr), dwrq->length); ++ for (i = 0; i < iw->spy_num; i++) ++ memcpy(&iw->spy_addr[i], addr[i].sa_data, ETHER_ADDR_LEN); ++ memset(iw->spy_qual, 0, sizeof(iw->spy_qual)); ++ ++ return 0; ++} ++ ++static int ++wl_iw_get_spy( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ wl_iw_t *iw = IW_DEV_IF(dev); ++ struct sockaddr *addr = (struct sockaddr *) extra; ++ struct iw_quality *qual = (struct iw_quality *) &addr[iw->spy_num]; ++ int i; ++ ++ WL_TRACE(("%s: SIOCGIWSPY\n", dev->name)); ++ ++ if (!extra) ++ return -EINVAL; ++ ++ dwrq->length = iw->spy_num; ++ for (i = 0; i < iw->spy_num; i++) { ++ memcpy(addr[i].sa_data, &iw->spy_addr[i], ETHER_ADDR_LEN); ++ addr[i].sa_family = AF_UNIX; ++ memcpy(&qual[i], &iw->spy_qual[i], sizeof(struct iw_quality)); ++ iw->spy_qual[i].updated = 0; ++ } ++ ++ return 0; ++} ++ ++static int ++wl_iw_set_wap( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct sockaddr *awrq, ++ char *extra ++) ++{ ++ int error = -EINVAL; ++ ++ WL_TRACE(("%s: SIOCSIWAP\n", dev->name)); ++ ++ if (awrq->sa_family != ARPHRD_ETHER) { ++ WL_ERROR(("%s: Invalid Header...sa_family\n", __FUNCTION__)); ++ return -EINVAL; ++ } ++ ++ ++ if (ETHER_ISBCAST(awrq->sa_data) || ETHER_ISNULLADDR(awrq->sa_data)) { ++ scb_val_t scbval; ++ bzero(&scbval, sizeof(scb_val_t)); ++ if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)))) { ++ WL_ERROR(("%s: WLC_DISASSOC failed (%d).\n", __FUNCTION__, error)); ++ } ++ return 0; ++ } ++ ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_REASSOC, awrq->sa_data, ETHER_ADDR_LEN))) { ++ WL_ERROR(("%s: WLC_REASSOC failed (%d).\n", __FUNCTION__, error)); ++ return error; ++ } ++ ++ return 0; ++} ++ ++static int ++wl_iw_get_wap( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct sockaddr *awrq, ++ char *extra ++) ++{ ++ WL_TRACE(("%s: SIOCGIWAP\n", dev->name)); ++ ++ awrq->sa_family = ARPHRD_ETHER; ++ memset(awrq->sa_data, 0, ETHER_ADDR_LEN); ++ ++ ++ (void) dev_wlc_ioctl(dev, WLC_GET_BSSID, awrq->sa_data, ETHER_ADDR_LEN); ++ ++ return 0; ++} ++ ++#if WIRELESS_EXT > 17 ++static int ++wl_iw_mlme( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct sockaddr *awrq, ++ char *extra ++) ++{ ++ struct iw_mlme *mlme; ++ scb_val_t scbval; ++ int error = -EINVAL; ++ ++ WL_TRACE(("%s: SIOCSIWMLME\n", dev->name)); ++ ++ mlme = (struct iw_mlme *)extra; ++ if (mlme == NULL) { ++ WL_ERROR(("Invalid ioctl data.\n")); ++ return error; ++ } ++ ++ scbval.val = mlme->reason_code; ++ bcopy(&mlme->addr.sa_data, &scbval.ea, ETHER_ADDR_LEN); ++ ++ if (mlme->cmd == IW_MLME_DISASSOC) { ++ scbval.val = htod32(scbval.val); ++ error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)); ++ } ++ else if (mlme->cmd == IW_MLME_DEAUTH) { ++ scbval.val = htod32(scbval.val); ++ error = dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scbval, ++ sizeof(scb_val_t)); ++ } ++ else { ++ WL_ERROR(("%s: Invalid ioctl data.\n", __FUNCTION__)); ++ return error; ++ } ++ ++ return error; ++} ++#endif ++ ++static int ++wl_iw_get_aplist( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ wl_scan_results_t *list; ++ struct sockaddr *addr = (struct sockaddr *) extra; ++ struct iw_quality qual[IW_MAX_AP]; ++ wl_bss_info_t *bi = NULL; ++ int error, i; ++ uint buflen = dwrq->length; ++ ++ WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name)); ++ ++ if (!extra) ++ return -EINVAL; ++ ++ ++ list = kmalloc(buflen, GFP_KERNEL); ++ if (!list) ++ return -ENOMEM; ++ memset(list, 0, buflen); ++ list->buflen = htod32(buflen); ++ if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) { ++ WL_ERROR(("%d: Scan results error %d\n", __LINE__, error)); ++ kfree(list); ++ return error; ++ } ++ list->buflen = dtoh32(list->buflen); ++ list->version = dtoh32(list->version); ++ list->count = dtoh32(list->count); ++ ASSERT(list->version == WL_BSS_INFO_VERSION); ++ ++ for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) { ++ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; ++ ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + ++ buflen)); ++ ++ ++ if (!(dtoh16(bi->capability) & DOT11_CAP_ESS)) ++ continue; ++ ++ ++ memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN); ++ addr[dwrq->length].sa_family = ARPHRD_ETHER; ++ qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI)); ++ qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI); ++ qual[dwrq->length].noise = 0x100 + bi->phy_noise; ++ ++ ++#if WIRELESS_EXT > 18 ++ qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; ++#else ++ qual[dwrq->length].updated = 7; ++#endif ++ ++ dwrq->length++; ++ } ++ ++ kfree(list); ++ ++ if (dwrq->length) { ++ memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length); ++ ++ dwrq->flags = 1; ++ } ++ ++ return 0; ++} ++ ++static int ++wl_iw_iscan_get_aplist( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ wl_scan_results_t *list; ++ iscan_buf_t * buf; ++ iscan_info_t *iscan = g_iscan; ++ ++ struct sockaddr *addr = (struct sockaddr *) extra; ++ struct iw_quality qual[IW_MAX_AP]; ++ wl_bss_info_t *bi = NULL; ++ int i; ++ ++ WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name)); ++ ++ if (!extra) ++ return -EINVAL; ++ ++ if ((!iscan) || (iscan->sysioc_pid < 0)) { ++ return wl_iw_get_aplist(dev, info, dwrq, extra); ++ } ++ ++ buf = iscan->list_hdr; ++ ++ while (buf) { ++ list = &((wl_iscan_results_t*)buf->iscan_buf)->results; ++ ASSERT(list->version == WL_BSS_INFO_VERSION); ++ ++ bi = NULL; ++ for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) { ++ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; ++ ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + ++ WLC_IW_ISCAN_MAXLEN)); ++ ++ ++ if (!(dtoh16(bi->capability) & DOT11_CAP_ESS)) ++ continue; ++ ++ ++ memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN); ++ addr[dwrq->length].sa_family = ARPHRD_ETHER; ++ qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI)); ++ qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI); ++ qual[dwrq->length].noise = 0x100 + bi->phy_noise; ++ ++ ++#if WIRELESS_EXT > 18 ++ qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; ++#else ++ qual[dwrq->length].updated = 7; ++#endif ++ ++ dwrq->length++; ++ } ++ buf = buf->next; ++ } ++ if (dwrq->length) { ++ memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length); ++ ++ dwrq->flags = 1; ++ } ++ ++ return 0; ++} ++ ++#if WIRELESS_EXT > 13 ++static int ++wl_iw_set_scan( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra ++) ++{ ++ wlc_ssid_t ssid; ++ ++ WL_TRACE(("%s: SIOCSIWSCAN\n", dev->name)); ++ ++ ++ memset(&ssid, 0, sizeof(ssid)); ++ ++#if WIRELESS_EXT > 17 ++ ++ if (wrqu->data.length == sizeof(struct iw_scan_req)) { ++ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { ++ struct iw_scan_req *req = (struct iw_scan_req *)extra; ++ ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len); ++ memcpy(ssid.SSID, req->essid, ssid.SSID_len); ++ ssid.SSID_len = htod32(ssid.SSID_len); ++ } ++ } ++#endif ++ ++ (void) dev_wlc_ioctl(dev, WLC_SCAN, &ssid, sizeof(ssid)); ++ ++ return 0; ++} ++ ++static int ++wl_iw_iscan_set_scan( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra ++) ++{ ++ wlc_ssid_t ssid; ++ iscan_info_t *iscan = g_iscan; ++ ++ WL_TRACE(("%s: SIOCSIWSCAN\n", dev->name)); ++ ++ ++ if ((!iscan) || (iscan->sysioc_pid < 0)) { ++ return wl_iw_set_scan(dev, info, wrqu, extra); ++ } ++ if (iscan->iscan_state == ISCAN_STATE_SCANING) { ++ return 0; ++ } ++ ++ ++ memset(&ssid, 0, sizeof(ssid)); ++ ++#if WIRELESS_EXT > 17 ++ ++ if (wrqu->data.length == sizeof(struct iw_scan_req)) { ++ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { ++ struct iw_scan_req *req = (struct iw_scan_req *)extra; ++ ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len); ++ memcpy(ssid.SSID, req->essid, ssid.SSID_len); ++ ssid.SSID_len = htod32(ssid.SSID_len); ++ } ++ } ++#endif ++ ++ iscan->list_cur = iscan->list_hdr; ++ iscan->iscan_state = ISCAN_STATE_SCANING; ++ ++ ++ wl_iw_set_event_mask(dev); ++ wl_iw_iscan(iscan, &ssid, WL_SCAN_ACTION_START); ++ ++ iscan->timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms); ++ add_timer(&iscan->timer); ++ iscan->timer_on = 1; ++ ++ return 0; ++} ++ ++#if WIRELESS_EXT > 17 ++static bool ++ie_is_wpa_ie(uint8 **wpaie, uint8 **tlvs, int *tlvs_len) ++{ ++ ++ ++ uint8 *ie = *wpaie; ++ ++ ++ if ((ie[1] >= 6) && ++ !bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x01"), 4)) { ++ return TRUE; ++ } ++ ++ ++ ie += ie[1] + 2; ++ ++ *tlvs_len -= (int)(ie - *tlvs); ++ ++ *tlvs = ie; ++ return FALSE; ++} ++ ++static bool ++ie_is_wps_ie(uint8 **wpsie, uint8 **tlvs, int *tlvs_len) ++{ ++ ++ ++ uint8 *ie = *wpsie; ++ ++ ++ if ((ie[1] >= 4) && ++ !bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x04"), 4)) { ++ return TRUE; ++ } ++ ++ ++ ie += ie[1] + 2; ++ ++ *tlvs_len -= (int)(ie - *tlvs); ++ ++ *tlvs = ie; ++ return FALSE; ++} ++#endif ++ ++ ++static int ++wl_iw_handle_scanresults_ies(char **event_p, char *end, ++ struct iw_request_info *info, wl_bss_info_t *bi) ++{ ++#if WIRELESS_EXT > 17 ++ struct iw_event iwe; ++ char *event; ++ ++ event = *event_p; ++ if (bi->ie_length) { ++ ++ bcm_tlv_t *ie; ++ uint8 *ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t); ++ int ptr_len = bi->ie_length; ++ ++ if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_RSN_ID))) { ++ iwe.cmd = IWEVGENIE; ++ iwe.u.data.length = ie->len + 2; ++ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie); ++ } ++ ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t); ++ ++#if defined(WLFBT) ++ if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_MDIE_ID))) { ++ iwe.cmd = IWEVGENIE; ++ iwe.u.data.length = ie->len + 2; ++ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie); ++ } ++ ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t); ++#endif ++ ++ while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) { ++ ++ if (ie_is_wps_ie(((uint8 **)&ie), &ptr, &ptr_len)) { ++ iwe.cmd = IWEVGENIE; ++ iwe.u.data.length = ie->len + 2; ++ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie); ++ break; ++ } ++ } ++ ++ ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t); ++ ptr_len = bi->ie_length; ++ while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) { ++ if (ie_is_wpa_ie(((uint8 **)&ie), &ptr, &ptr_len)) { ++ iwe.cmd = IWEVGENIE; ++ iwe.u.data.length = ie->len + 2; ++ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie); ++ break; ++ } ++ } ++ ++ *event_p = event; ++ } ++ ++#endif ++ return 0; ++} ++static int ++wl_iw_get_scan( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ channel_info_t ci; ++ wl_scan_results_t *list; ++ struct iw_event iwe; ++ wl_bss_info_t *bi = NULL; ++ int error, i, j; ++ char *event = extra, *end = extra + dwrq->length, *value; ++ uint buflen = dwrq->length; ++ ++ WL_TRACE(("%s: SIOCGIWSCAN\n", dev->name)); ++ ++ if (!extra) ++ return -EINVAL; ++ ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci)))) ++ return error; ++ ci.scan_channel = dtoh32(ci.scan_channel); ++ if (ci.scan_channel) ++ return -EAGAIN; ++ ++ ++ list = kmalloc(buflen, GFP_KERNEL); ++ if (!list) ++ return -ENOMEM; ++ memset(list, 0, buflen); ++ list->buflen = htod32(buflen); ++ if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) { ++ kfree(list); ++ return error; ++ } ++ list->buflen = dtoh32(list->buflen); ++ list->version = dtoh32(list->version); ++ list->count = dtoh32(list->count); ++ ++ ASSERT(list->version == WL_BSS_INFO_VERSION); ++ ++ for (i = 0; i < list->count && i < IW_MAX_AP; i++) { ++ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; ++ ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + ++ buflen)); ++ ++ ++ iwe.cmd = SIOCGIWAP; ++ iwe.u.ap_addr.sa_family = ARPHRD_ETHER; ++ memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN); ++ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN); ++ ++ ++ iwe.u.data.length = dtoh32(bi->SSID_len); ++ iwe.cmd = SIOCGIWESSID; ++ iwe.u.data.flags = 1; ++ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID); ++ ++ ++ if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) { ++ iwe.cmd = SIOCGIWMODE; ++ if (dtoh16(bi->capability) & DOT11_CAP_ESS) ++ iwe.u.mode = IW_MODE_INFRA; ++ else ++ iwe.u.mode = IW_MODE_ADHOC; ++ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN); ++ } ++ ++ ++ iwe.cmd = SIOCGIWFREQ; ++ iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec), ++ CHSPEC_CHANNEL(bi->chanspec) <= CH_MAX_2G_CHANNEL ? ++ WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G); ++ iwe.u.freq.e = 6; ++ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN); ++ ++ ++ iwe.cmd = IWEVQUAL; ++ iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI)); ++ iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI); ++ iwe.u.qual.noise = 0x100 + bi->phy_noise; ++ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN); ++ ++ ++ wl_iw_handle_scanresults_ies(&event, end, info, bi); ++ ++ ++ iwe.cmd = SIOCGIWENCODE; ++ if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY) ++ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; ++ else ++ iwe.u.data.flags = IW_ENCODE_DISABLED; ++ iwe.u.data.length = 0; ++ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event); ++ ++ ++ if (bi->rateset.count) { ++ value = event + IW_EV_LCP_LEN; ++ iwe.cmd = SIOCGIWRATE; ++ ++ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; ++ for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) { ++ iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000; ++ value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe, ++ IW_EV_PARAM_LEN); ++ } ++ event = value; ++ } ++ } ++ ++ kfree(list); ++ ++ dwrq->length = event - extra; ++ dwrq->flags = 0; ++ ++ return 0; ++} ++ ++static int ++wl_iw_iscan_get_scan( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ wl_scan_results_t *list; ++ struct iw_event iwe; ++ wl_bss_info_t *bi = NULL; ++ int ii, j; ++ int apcnt; ++ char *event = extra, *end = extra + dwrq->length, *value; ++ iscan_info_t *iscan = g_iscan; ++ iscan_buf_t * p_buf; ++ ++ WL_TRACE(("%s: SIOCGIWSCAN\n", dev->name)); ++ ++ if (!extra) ++ return -EINVAL; ++ ++ ++ if ((!iscan) || (iscan->sysioc_pid < 0)) { ++ return wl_iw_get_scan(dev, info, dwrq, extra); ++ } ++ ++ ++ if (iscan->iscan_state == ISCAN_STATE_SCANING) ++ return -EAGAIN; ++ ++ apcnt = 0; ++ p_buf = iscan->list_hdr; ++ ++ while (p_buf != iscan->list_cur) { ++ list = &((wl_iscan_results_t*)p_buf->iscan_buf)->results; ++ ++ if (list->version != WL_BSS_INFO_VERSION) { ++ WL_ERROR(("list->version %d != WL_BSS_INFO_VERSION\n", list->version)); ++ } ++ ++ bi = NULL; ++ for (ii = 0; ii < list->count && apcnt < IW_MAX_AP; apcnt++, ii++) { ++ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; ++ ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + ++ WLC_IW_ISCAN_MAXLEN)); ++ ++ ++ if (event + ETHER_ADDR_LEN + bi->SSID_len + IW_EV_UINT_LEN + IW_EV_FREQ_LEN + ++ IW_EV_QUAL_LEN >= end) ++ return -E2BIG; ++ ++ iwe.cmd = SIOCGIWAP; ++ iwe.u.ap_addr.sa_family = ARPHRD_ETHER; ++ memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN); ++ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN); ++ ++ ++ iwe.u.data.length = dtoh32(bi->SSID_len); ++ iwe.cmd = SIOCGIWESSID; ++ iwe.u.data.flags = 1; ++ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID); ++ ++ ++ if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) { ++ iwe.cmd = SIOCGIWMODE; ++ if (dtoh16(bi->capability) & DOT11_CAP_ESS) ++ iwe.u.mode = IW_MODE_INFRA; ++ else ++ iwe.u.mode = IW_MODE_ADHOC; ++ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN); ++ } ++ ++ ++ iwe.cmd = SIOCGIWFREQ; ++ iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec), ++ CHSPEC_CHANNEL(bi->chanspec) <= CH_MAX_2G_CHANNEL ? ++ WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G); ++ iwe.u.freq.e = 6; ++ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN); ++ ++ ++ iwe.cmd = IWEVQUAL; ++ iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI)); ++ iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI); ++ iwe.u.qual.noise = 0x100 + bi->phy_noise; ++ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN); ++ ++ ++ wl_iw_handle_scanresults_ies(&event, end, info, bi); ++ ++ ++ iwe.cmd = SIOCGIWENCODE; ++ if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY) ++ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; ++ else ++ iwe.u.data.flags = IW_ENCODE_DISABLED; ++ iwe.u.data.length = 0; ++ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event); ++ ++ ++ if (bi->rateset.count <= sizeof(bi->rateset.rates)) { ++ if (event + IW_MAX_BITRATES*IW_EV_PARAM_LEN >= end) ++ return -E2BIG; ++ ++ value = event + IW_EV_LCP_LEN; ++ iwe.cmd = SIOCGIWRATE; ++ ++ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; ++ for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) { ++ iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000; ++ value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe, ++ IW_EV_PARAM_LEN); ++ } ++ event = value; ++ } ++ } ++ p_buf = p_buf->next; ++ } ++ ++ dwrq->length = event - extra; ++ dwrq->flags = 0; ++ ++ return 0; ++} ++ ++#endif ++ ++ ++static int ++wl_iw_set_essid( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ wlc_ssid_t ssid; ++ int error; ++ ++ WL_TRACE(("%s: SIOCSIWESSID\n", dev->name)); ++ ++ ++ memset(&ssid, 0, sizeof(ssid)); ++ if (dwrq->length && extra) { ++#if WIRELESS_EXT > 20 ++ ssid.SSID_len = MIN(sizeof(ssid.SSID), dwrq->length); ++#else ++ ssid.SSID_len = MIN(sizeof(ssid.SSID), dwrq->length-1); ++#endif ++ memcpy(ssid.SSID, extra, ssid.SSID_len); ++ ssid.SSID_len = htod32(ssid.SSID_len); ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid)))) ++ return error; ++ } ++ ++ else { ++ scb_val_t scbval; ++ bzero(&scbval, sizeof(scb_val_t)); ++ if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)))) ++ return error; ++ } ++ return 0; ++} ++ ++static int ++wl_iw_get_essid( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ wlc_ssid_t ssid; ++ int error; ++ ++ WL_TRACE(("%s: SIOCGIWESSID\n", dev->name)); ++ ++ if (!extra) ++ return -EINVAL; ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)))) { ++ WL_ERROR(("Error getting the SSID\n")); ++ return error; ++ } ++ ++ ssid.SSID_len = dtoh32(ssid.SSID_len); ++ ++ ++ memcpy(extra, ssid.SSID, ssid.SSID_len); ++ ++ dwrq->length = ssid.SSID_len; ++ ++ dwrq->flags = 1; ++ ++ return 0; ++} ++ ++static int ++wl_iw_set_nick( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ wl_iw_t *iw = IW_DEV_IF(dev); ++ WL_TRACE(("%s: SIOCSIWNICKN\n", dev->name)); ++ ++ if (!extra) ++ return -EINVAL; ++ ++ ++ if (dwrq->length > sizeof(iw->nickname)) ++ return -E2BIG; ++ ++ memcpy(iw->nickname, extra, dwrq->length); ++ iw->nickname[dwrq->length - 1] = '\0'; ++ ++ return 0; ++} ++ ++static int ++wl_iw_get_nick( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ wl_iw_t *iw = IW_DEV_IF(dev); ++ WL_TRACE(("%s: SIOCGIWNICKN\n", dev->name)); ++ ++ if (!extra) ++ return -EINVAL; ++ ++ strcpy(extra, iw->nickname); ++ dwrq->length = strlen(extra) + 1; ++ ++ return 0; ++} ++ ++static int wl_iw_set_rate( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ wl_rateset_t rateset; ++ int error, rate, i, error_bg, error_a; ++ ++ WL_TRACE(("%s: SIOCSIWRATE\n", dev->name)); ++ ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset)))) ++ return error; ++ ++ rateset.count = dtoh32(rateset.count); ++ ++ if (vwrq->value < 0) { ++ ++ rate = rateset.rates[rateset.count - 1] & 0x7f; ++ } else if (vwrq->value < rateset.count) { ++ ++ rate = rateset.rates[vwrq->value] & 0x7f; ++ } else { ++ ++ rate = vwrq->value / 500000; ++ } ++ ++ if (vwrq->fixed) { ++ ++ error_bg = dev_wlc_intvar_set(dev, "bg_rate", rate); ++ error_a = dev_wlc_intvar_set(dev, "a_rate", rate); ++ ++ if (error_bg && error_a) ++ return (error_bg | error_a); ++ } else { ++ ++ ++ error_bg = dev_wlc_intvar_set(dev, "bg_rate", 0); ++ ++ error_a = dev_wlc_intvar_set(dev, "a_rate", 0); ++ ++ if (error_bg && error_a) ++ return (error_bg | error_a); ++ ++ ++ for (i = 0; i < rateset.count; i++) ++ if ((rateset.rates[i] & 0x7f) > rate) ++ break; ++ rateset.count = htod32(i); ++ ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_RATESET, &rateset, sizeof(rateset)))) ++ return error; ++ } ++ ++ return 0; ++} ++ ++static int wl_iw_get_rate( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error, rate; ++ ++ WL_TRACE(("%s: SIOCGIWRATE\n", dev->name)); ++ ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate)))) ++ return error; ++ rate = dtoh32(rate); ++ vwrq->value = rate * 500000; ++ ++ return 0; ++} ++ ++static int ++wl_iw_set_rts( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error, rts; ++ ++ WL_TRACE(("%s: SIOCSIWRTS\n", dev->name)); ++ ++ if (vwrq->disabled) ++ rts = DOT11_DEFAULT_RTS_LEN; ++ else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_RTS_LEN) ++ return -EINVAL; ++ else ++ rts = vwrq->value; ++ ++ if ((error = dev_wlc_intvar_set(dev, "rtsthresh", rts))) ++ return error; ++ ++ return 0; ++} ++ ++static int ++wl_iw_get_rts( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error, rts; ++ ++ WL_TRACE(("%s: SIOCGIWRTS\n", dev->name)); ++ ++ if ((error = dev_wlc_intvar_get(dev, "rtsthresh", &rts))) ++ return error; ++ ++ vwrq->value = rts; ++ vwrq->disabled = (rts >= DOT11_DEFAULT_RTS_LEN); ++ vwrq->fixed = 1; ++ ++ return 0; ++} ++ ++static int ++wl_iw_set_frag( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error, frag; ++ ++ WL_TRACE(("%s: SIOCSIWFRAG\n", dev->name)); ++ ++ if (vwrq->disabled) ++ frag = DOT11_DEFAULT_FRAG_LEN; ++ else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_FRAG_LEN) ++ return -EINVAL; ++ else ++ frag = vwrq->value; ++ ++ if ((error = dev_wlc_intvar_set(dev, "fragthresh", frag))) ++ return error; ++ ++ return 0; ++} ++ ++static int ++wl_iw_get_frag( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error, fragthreshold; ++ ++ WL_TRACE(("%s: SIOCGIWFRAG\n", dev->name)); ++ ++ if ((error = dev_wlc_intvar_get(dev, "fragthresh", &fragthreshold))) ++ return error; ++ ++ vwrq->value = fragthreshold; ++ vwrq->disabled = (fragthreshold >= DOT11_DEFAULT_FRAG_LEN); ++ vwrq->fixed = 1; ++ ++ return 0; ++} ++ ++static int ++wl_iw_set_txpow( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error, disable; ++ uint16 txpwrmw; ++ WL_TRACE(("%s: SIOCSIWTXPOW\n", dev->name)); ++ ++ ++ disable = vwrq->disabled ? WL_RADIO_SW_DISABLE : 0; ++ disable += WL_RADIO_SW_DISABLE << 16; ++ ++ disable = htod32(disable); ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_RADIO, &disable, sizeof(disable)))) ++ return error; ++ ++ ++ if (disable & WL_RADIO_SW_DISABLE) ++ return 0; ++ ++ ++ if (!(vwrq->flags & IW_TXPOW_MWATT)) ++ return -EINVAL; ++ ++ ++ if (vwrq->value < 0) ++ return 0; ++ ++ if (vwrq->value > 0xffff) txpwrmw = 0xffff; ++ else txpwrmw = (uint16)vwrq->value; ++ ++ ++ error = dev_wlc_intvar_set(dev, "qtxpower", (int)(bcm_mw_to_qdbm(txpwrmw))); ++ return error; ++} ++ ++static int ++wl_iw_get_txpow( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error, disable, txpwrdbm; ++ uint8 result; ++ ++ WL_TRACE(("%s: SIOCGIWTXPOW\n", dev->name)); ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_RADIO, &disable, sizeof(disable))) || ++ (error = dev_wlc_intvar_get(dev, "qtxpower", &txpwrdbm))) ++ return error; ++ ++ disable = dtoh32(disable); ++ result = (uint8)(txpwrdbm & ~WL_TXPWR_OVERRIDE); ++ vwrq->value = (int32)bcm_qdbm_to_mw(result); ++ vwrq->fixed = 0; ++ vwrq->disabled = (disable & (WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE)) ? 1 : 0; ++ vwrq->flags = IW_TXPOW_MWATT; ++ ++ return 0; ++} ++ ++#if WIRELESS_EXT > 10 ++static int ++wl_iw_set_retry( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error, lrl, srl; ++ ++ WL_TRACE(("%s: SIOCSIWRETRY\n", dev->name)); ++ ++ ++ if (vwrq->disabled || (vwrq->flags & IW_RETRY_LIFETIME)) ++ return -EINVAL; ++ ++ ++ if (vwrq->flags & IW_RETRY_LIMIT) { ++ ++#if WIRELESS_EXT > 20 ++ if ((vwrq->flags & IW_RETRY_LONG) ||(vwrq->flags & IW_RETRY_MAX) || ++ !((vwrq->flags & IW_RETRY_SHORT) || (vwrq->flags & IW_RETRY_MIN))) { ++#else ++ if ((vwrq->flags & IW_RETRY_MAX) || !(vwrq->flags & IW_RETRY_MIN)) { ++#endif ++ ++ lrl = htod32(vwrq->value); ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_LRL, &lrl, sizeof(lrl)))) ++ return error; ++ } ++ ++#if WIRELESS_EXT > 20 ++ if ((vwrq->flags & IW_RETRY_SHORT) ||(vwrq->flags & IW_RETRY_MIN) || ++ !((vwrq->flags & IW_RETRY_LONG) || (vwrq->flags & IW_RETRY_MAX))) { ++#else ++ if ((vwrq->flags & IW_RETRY_MIN) || !(vwrq->flags & IW_RETRY_MAX)) { ++#endif ++ ++ srl = htod32(vwrq->value); ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_SRL, &srl, sizeof(srl)))) ++ return error; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ++wl_iw_get_retry( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error, lrl, srl; ++ ++ WL_TRACE(("%s: SIOCGIWRETRY\n", dev->name)); ++ ++ vwrq->disabled = 0; ++ ++ ++ if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) ++ return -EINVAL; ++ ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_LRL, &lrl, sizeof(lrl))) || ++ (error = dev_wlc_ioctl(dev, WLC_GET_SRL, &srl, sizeof(srl)))) ++ return error; ++ ++ lrl = dtoh32(lrl); ++ srl = dtoh32(srl); ++ ++ ++ if (vwrq->flags & IW_RETRY_MAX) { ++ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; ++ vwrq->value = lrl; ++ } else { ++ vwrq->flags = IW_RETRY_LIMIT; ++ vwrq->value = srl; ++ if (srl != lrl) ++ vwrq->flags |= IW_RETRY_MIN; ++ } ++ ++ return 0; ++} ++#endif ++ ++static int ++wl_iw_set_encode( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ wl_wsec_key_t key; ++ int error, val, wsec; ++ ++ WL_TRACE(("%s: SIOCSIWENCODE\n", dev->name)); ++ ++ memset(&key, 0, sizeof(key)); ++ ++ if ((dwrq->flags & IW_ENCODE_INDEX) == 0) { ++ ++ for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) { ++ val = htod32(key.index); ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val)))) ++ return error; ++ val = dtoh32(val); ++ if (val) ++ break; ++ } ++ ++ if (key.index == DOT11_MAX_DEFAULT_KEYS) ++ key.index = 0; ++ } else { ++ key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1; ++ if (key.index >= DOT11_MAX_DEFAULT_KEYS) ++ return -EINVAL; ++ } ++ ++ ++ wsec = (dwrq->flags & IW_ENCODE_DISABLED) ? 0 : WEP_ENABLED; ++ ++ if ((error = dev_wlc_intvar_set(dev, "wsec", wsec))) ++ return error; ++ ++ ++ if (!extra || !dwrq->length || (dwrq->flags & IW_ENCODE_NOKEY)) { ++ ++ val = htod32(key.index); ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY, &val, sizeof(val)))) ++ return error; ++ } else { ++ key.len = dwrq->length; ++ ++ if (dwrq->length > sizeof(key.data)) ++ return -EINVAL; ++ ++ memcpy(key.data, extra, dwrq->length); ++ ++ key.flags = WL_PRIMARY_KEY; ++ switch (key.len) { ++ case WEP1_KEY_SIZE: ++ key.algo = CRYPTO_ALGO_WEP1; ++ break; ++ case WEP128_KEY_SIZE: ++ key.algo = CRYPTO_ALGO_WEP128; ++ break; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) ++ case TKIP_KEY_SIZE: ++ key.algo = CRYPTO_ALGO_TKIP; ++ break; ++#endif ++ case AES_KEY_SIZE: ++ key.algo = CRYPTO_ALGO_AES_CCM; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ++ swap_key_from_BE(&key); ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)))) ++ return error; ++ } ++ ++ ++ val = (dwrq->flags & IW_ENCODE_RESTRICTED) ? 1 : 0; ++ val = htod32(val); ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val)))) ++ return error; ++ ++ return 0; ++} ++ ++static int ++wl_iw_get_encode( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ wl_wsec_key_t key; ++ int error, val, wsec, auth; ++ ++ WL_TRACE(("%s: SIOCGIWENCODE\n", dev->name)); ++ ++ ++ bzero(&key, sizeof(wl_wsec_key_t)); ++ ++ if ((dwrq->flags & IW_ENCODE_INDEX) == 0) { ++ ++ for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) { ++ val = key.index; ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val)))) ++ return error; ++ val = dtoh32(val); ++ if (val) ++ break; ++ } ++ } else ++ key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1; ++ ++ if (key.index >= DOT11_MAX_DEFAULT_KEYS) ++ key.index = 0; ++ ++ ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec))) || ++ (error = dev_wlc_ioctl(dev, WLC_GET_AUTH, &auth, sizeof(auth)))) ++ return error; ++ ++ swap_key_to_BE(&key); ++ ++ wsec = dtoh32(wsec); ++ auth = dtoh32(auth); ++ ++ dwrq->length = MIN(IW_ENCODING_TOKEN_MAX, key.len); ++ ++ ++ dwrq->flags = key.index + 1; ++ if (!(wsec & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED))) { ++ ++ dwrq->flags |= IW_ENCODE_DISABLED; ++ } ++ if (auth) { ++ ++ dwrq->flags |= IW_ENCODE_RESTRICTED; ++ } ++ ++ ++ if (dwrq->length && extra) ++ memcpy(extra, key.data, dwrq->length); ++ ++ return 0; ++} ++ ++static int ++wl_iw_set_power( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error, pm; ++ ++ WL_TRACE(("%s: SIOCSIWPOWER\n", dev->name)); ++ ++ pm = vwrq->disabled ? PM_OFF : PM_MAX; ++ ++ pm = htod32(pm); ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm)))) ++ return error; ++ ++ return 0; ++} ++ ++static int ++wl_iw_get_power( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error, pm; ++ ++ WL_TRACE(("%s: SIOCGIWPOWER\n", dev->name)); ++ ++ if ((error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm)))) ++ return error; ++ ++ pm = dtoh32(pm); ++ vwrq->disabled = pm ? 0 : 1; ++ vwrq->flags = IW_POWER_ALL_R; ++ ++ return 0; ++} ++ ++#if WIRELESS_EXT > 17 ++static int ++wl_iw_set_wpaie( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *iwp, ++ char *extra ++) ++{ ++ dev_wlc_bufvar_set(dev, "wpaie", extra, iwp->length); ++ ++ return 0; ++} ++ ++static int ++wl_iw_get_wpaie( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *iwp, ++ char *extra ++) ++{ ++ WL_TRACE(("%s: SIOCGIWGENIE\n", dev->name)); ++ iwp->length = 64; ++ dev_wlc_bufvar_get(dev, "wpaie", extra, iwp->length); ++ return 0; ++} ++ ++static int ++wl_iw_set_encodeext( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *dwrq, ++ char *extra ++) ++{ ++ wl_wsec_key_t key; ++ int error; ++ struct iw_encode_ext *iwe; ++ ++ WL_TRACE(("%s: SIOCSIWENCODEEXT\n", dev->name)); ++ ++ memset(&key, 0, sizeof(key)); ++ iwe = (struct iw_encode_ext *)extra; ++ ++ ++ if (dwrq->flags & IW_ENCODE_DISABLED) { ++ ++ } ++ ++ ++ key.index = 0; ++ if (dwrq->flags & IW_ENCODE_INDEX) ++ key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1; ++ ++ key.len = iwe->key_len; ++ ++ ++ if (!ETHER_ISMULTI(iwe->addr.sa_data)) ++ bcopy((void *)&iwe->addr.sa_data, (char *)&key.ea, ETHER_ADDR_LEN); ++ ++ ++ if (key.len == 0) { ++ if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { ++ WL_WSEC(("Changing the the primary Key to %d\n", key.index)); ++ ++ key.index = htod32(key.index); ++ error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY, ++ &key.index, sizeof(key.index)); ++ if (error) ++ return error; ++ } ++ ++ else { ++ swap_key_from_BE(&key); ++ error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); ++ if (error) ++ return error; ++ } ++ } ++#if (defined(BCMSUP_PSK) && defined(WLFBT)) ++ ++ else if (iwe->alg == IW_ENCODE_ALG_PMK) { ++ int j; ++ wsec_pmk_t pmk; ++ char keystring[WSEC_MAX_PSK_LEN + 1]; ++ char* charptr = keystring; ++ uint len; ++ ++ ++ for (j = 0; j < (WSEC_MAX_PSK_LEN / 2); j++) { ++ sprintf(charptr, "%02x", iwe->key[j]); ++ charptr += 2; ++ } ++ len = strlen(keystring); ++ pmk.key_len = htod16(len); ++ bcopy(keystring, pmk.key, len); ++ pmk.flags = htod16(WSEC_PASSPHRASE); ++ ++ error = dev_wlc_ioctl(dev, WLC_SET_WSEC_PMK, &pmk, sizeof(pmk)); ++ if (error) ++ return error; ++ } ++#endif ++ ++ else { ++ if (iwe->key_len > sizeof(key.data)) ++ return -EINVAL; ++ ++ WL_WSEC(("Setting the key index %d\n", key.index)); ++ if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { ++ WL_WSEC(("key is a Primary Key\n")); ++ key.flags = WL_PRIMARY_KEY; ++ } ++ ++ bcopy((void *)iwe->key, key.data, iwe->key_len); ++ ++ if (iwe->alg == IW_ENCODE_ALG_TKIP) { ++ uint8 keybuf[8]; ++ bcopy(&key.data[24], keybuf, sizeof(keybuf)); ++ bcopy(&key.data[16], &key.data[24], sizeof(keybuf)); ++ bcopy(keybuf, &key.data[16], sizeof(keybuf)); ++ } ++ ++ ++ if (iwe->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { ++ uchar *ivptr; ++ ivptr = (uchar *)iwe->rx_seq; ++ key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | ++ (ivptr[3] << 8) | ivptr[2]; ++ key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; ++ key.iv_initialized = TRUE; ++ } ++ ++ switch (iwe->alg) { ++ case IW_ENCODE_ALG_NONE: ++ key.algo = CRYPTO_ALGO_OFF; ++ break; ++ case IW_ENCODE_ALG_WEP: ++ if (iwe->key_len == WEP1_KEY_SIZE) ++ key.algo = CRYPTO_ALGO_WEP1; ++ else ++ key.algo = CRYPTO_ALGO_WEP128; ++ break; ++ case IW_ENCODE_ALG_TKIP: ++ key.algo = CRYPTO_ALGO_TKIP; ++ break; ++ case IW_ENCODE_ALG_CCMP: ++ key.algo = CRYPTO_ALGO_AES_CCM; ++ break; ++ default: ++ break; ++ } ++ swap_key_from_BE(&key); ++ ++ dhd_wait_pend8021x(dev); ++ ++ error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); ++ if (error) ++ return error; ++ } ++ return 0; ++} ++ ++ ++#if WIRELESS_EXT > 17 ++struct { ++ pmkid_list_t pmkids; ++ pmkid_t foo[MAXPMKID-1]; ++} pmkid_list; ++static int ++wl_iw_set_pmksa( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ struct iw_pmksa *iwpmksa; ++ uint i; ++ char eabuf[ETHER_ADDR_STR_LEN]; ++ pmkid_t * pmkid_array = pmkid_list.pmkids.pmkid; ++ ++ WL_TRACE(("%s: SIOCSIWPMKSA\n", dev->name)); ++ iwpmksa = (struct iw_pmksa *)extra; ++ bzero((char *)eabuf, ETHER_ADDR_STR_LEN); ++ if (iwpmksa->cmd == IW_PMKSA_FLUSH) { ++ WL_TRACE(("wl_iw_set_pmksa - IW_PMKSA_FLUSH\n")); ++ bzero((char *)&pmkid_list, sizeof(pmkid_list)); ++ } ++ if (iwpmksa->cmd == IW_PMKSA_REMOVE) { ++ pmkid_list_t pmkid, *pmkidptr; ++ pmkidptr = &pmkid; ++ bcopy(&iwpmksa->bssid.sa_data[0], &pmkidptr->pmkid[0].BSSID, ETHER_ADDR_LEN); ++ bcopy(&iwpmksa->pmkid[0], &pmkidptr->pmkid[0].PMKID, WPA2_PMKID_LEN); ++ { ++ uint j; ++ WL_TRACE(("wl_iw_set_pmksa,IW_PMKSA_REMOVE - PMKID: %s = ", ++ bcm_ether_ntoa(&pmkidptr->pmkid[0].BSSID, ++ eabuf))); ++ for (j = 0; j < WPA2_PMKID_LEN; j++) ++ WL_TRACE(("%02x ", pmkidptr->pmkid[0].PMKID[j])); ++ WL_TRACE(("\n")); ++ } ++ for (i = 0; i < pmkid_list.pmkids.npmkid; i++) ++ if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_array[i].BSSID, ++ ETHER_ADDR_LEN)) ++ break; ++ for (; i < pmkid_list.pmkids.npmkid; i++) { ++ bcopy(&pmkid_array[i+1].BSSID, ++ &pmkid_array[i].BSSID, ++ ETHER_ADDR_LEN); ++ bcopy(&pmkid_array[i+1].PMKID, ++ &pmkid_array[i].PMKID, ++ WPA2_PMKID_LEN); ++ } ++ pmkid_list.pmkids.npmkid--; ++ } ++ if (iwpmksa->cmd == IW_PMKSA_ADD) { ++ bcopy(&iwpmksa->bssid.sa_data[0], ++ &pmkid_array[pmkid_list.pmkids.npmkid].BSSID, ++ ETHER_ADDR_LEN); ++ bcopy(&iwpmksa->pmkid[0], &pmkid_array[pmkid_list.pmkids.npmkid].PMKID, ++ WPA2_PMKID_LEN); ++ { ++ uint j; ++ uint k; ++ k = pmkid_list.pmkids.npmkid; ++ BCM_REFERENCE(k); ++ WL_TRACE(("wl_iw_set_pmksa,IW_PMKSA_ADD - PMKID: %s = ", ++ bcm_ether_ntoa(&pmkid_array[k].BSSID, ++ eabuf))); ++ for (j = 0; j < WPA2_PMKID_LEN; j++) ++ WL_TRACE(("%02x ", pmkid_array[k].PMKID[j])); ++ WL_TRACE(("\n")); ++ } ++ pmkid_list.pmkids.npmkid++; ++ } ++ WL_TRACE(("PRINTING pmkid LIST - No of elements %d\n", pmkid_list.pmkids.npmkid)); ++ for (i = 0; i < pmkid_list.pmkids.npmkid; i++) { ++ uint j; ++ WL_TRACE(("PMKID[%d]: %s = ", i, ++ bcm_ether_ntoa(&pmkid_array[i].BSSID, ++ eabuf))); ++ for (j = 0; j < WPA2_PMKID_LEN; j++) ++ WL_TRACE(("%02x ", pmkid_array[i].PMKID[j])); ++ printf("\n"); ++ } ++ WL_TRACE(("\n")); ++ dev_wlc_bufvar_set(dev, "pmkid_info", (char *)&pmkid_list, sizeof(pmkid_list)); ++ return 0; ++} ++#endif ++ ++static int ++wl_iw_get_encodeext( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ WL_TRACE(("%s: SIOCGIWENCODEEXT\n", dev->name)); ++ return 0; ++} ++ ++static int ++wl_iw_set_wpaauth( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error = 0; ++ int paramid; ++ int paramval; ++ uint32 cipher_combined; ++ int val = 0; ++ wl_iw_t *iw = IW_DEV_IF(dev); ++ ++ WL_TRACE(("%s: SIOCSIWAUTH\n", dev->name)); ++ ++ paramid = vwrq->flags & IW_AUTH_INDEX; ++ paramval = vwrq->value; ++ ++ WL_TRACE(("%s: SIOCSIWAUTH, paramid = 0x%0x, paramval = 0x%0x\n", ++ dev->name, paramid, paramval)); ++ ++ switch (paramid) { ++ ++ case IW_AUTH_WPA_VERSION: ++ ++ if (paramval & IW_AUTH_WPA_VERSION_DISABLED) ++ val = WPA_AUTH_DISABLED; ++ else if (paramval & (IW_AUTH_WPA_VERSION_WPA)) ++ val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; ++ else if (paramval & IW_AUTH_WPA_VERSION_WPA2) ++ val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; ++ WL_TRACE(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val)); ++ if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) ++ return error; ++ break; ++ ++ case IW_AUTH_CIPHER_PAIRWISE: ++ case IW_AUTH_CIPHER_GROUP: ++ ++ if (paramid == IW_AUTH_CIPHER_PAIRWISE) { ++ iw->pwsec = paramval; ++ } ++ else { ++ iw->gwsec = paramval; ++ } ++ ++ if ((error = dev_wlc_intvar_get(dev, "wsec", &val))) ++ return error; ++ ++ cipher_combined = iw->gwsec | iw->pwsec; ++ val &= ~(WEP_ENABLED | TKIP_ENABLED | AES_ENABLED); ++ if (cipher_combined & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104)) ++ val |= WEP_ENABLED; ++ if (cipher_combined & IW_AUTH_CIPHER_TKIP) ++ val |= TKIP_ENABLED; ++ if (cipher_combined & IW_AUTH_CIPHER_CCMP) ++ val |= AES_ENABLED; ++ ++ if (iw->privacy_invoked && !val) { ++ WL_WSEC(("%s: %s: 'Privacy invoked' TRUE but clearing wsec, assuming " ++ "we're a WPS enrollee\n", dev->name, __FUNCTION__)); ++ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) { ++ WL_WSEC(("Failed to set iovar is_WPS_enrollee\n")); ++ return error; ++ } ++ } else if (val) { ++ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) { ++ WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n")); ++ return error; ++ } ++ } ++ ++ if ((error = dev_wlc_intvar_set(dev, "wsec", val))) ++ return error; ++#ifdef WLFBT ++ if ((paramid == IW_AUTH_CIPHER_PAIRWISE) && (val | AES_ENABLED)) { ++ if ((error = dev_wlc_intvar_set(dev, "sup_wpa", 1))) ++ return error; ++ } ++ else if (val == 0) { ++ if ((error = dev_wlc_intvar_set(dev, "sup_wpa", 0))) ++ return error; ++ } ++#endif ++ break; ++ ++ case IW_AUTH_KEY_MGMT: ++ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) ++ return error; ++ ++ if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { ++ if (paramval & IW_AUTH_KEY_MGMT_PSK) ++ val = WPA_AUTH_PSK; ++ else ++ val = WPA_AUTH_UNSPECIFIED; ++ } ++ else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { ++ if (paramval & IW_AUTH_KEY_MGMT_PSK) ++ val = WPA2_AUTH_PSK; ++ else ++ val = WPA2_AUTH_UNSPECIFIED; ++ } ++ WL_TRACE(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val)); ++ if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) ++ return error; ++ break; ++ ++ case IW_AUTH_TKIP_COUNTERMEASURES: ++ dev_wlc_bufvar_set(dev, "tkip_countermeasures", (char *)¶mval, 1); ++ break; ++ ++ case IW_AUTH_80211_AUTH_ALG: ++ ++ WL_ERROR(("Setting the D11auth %d\n", paramval)); ++ if (paramval & IW_AUTH_ALG_OPEN_SYSTEM) ++ val = 0; ++ else if (paramval & IW_AUTH_ALG_SHARED_KEY) ++ val = 1; ++ else ++ error = 1; ++ if (!error && (error = dev_wlc_intvar_set(dev, "auth", val))) ++ return error; ++ break; ++ ++ case IW_AUTH_WPA_ENABLED: ++ if (paramval == 0) { ++ val = 0; ++ WL_TRACE(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val)); ++ error = dev_wlc_intvar_set(dev, "wpa_auth", val); ++ return error; ++ } ++ else { ++ ++ } ++ break; ++ ++ case IW_AUTH_DROP_UNENCRYPTED: ++ dev_wlc_bufvar_set(dev, "wsec_restrict", (char *)¶mval, 1); ++ break; ++ ++ case IW_AUTH_RX_UNENCRYPTED_EAPOL: ++ dev_wlc_bufvar_set(dev, "rx_unencrypted_eapol", (char *)¶mval, 1); ++ break; ++ ++#if WIRELESS_EXT > 17 ++ ++ case IW_AUTH_ROAMING_CONTROL: ++ WL_TRACE(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__)); ++ ++ break; ++ ++ case IW_AUTH_PRIVACY_INVOKED: { ++ int wsec; ++ ++ if (paramval == 0) { ++ iw->privacy_invoked = FALSE; ++ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) { ++ WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n")); ++ return error; ++ } ++ } else { ++ iw->privacy_invoked = TRUE; ++ if ((error = dev_wlc_intvar_get(dev, "wsec", &wsec))) ++ return error; ++ ++ if (!WSEC_ENABLED(wsec)) { ++ ++ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) { ++ WL_WSEC(("Failed to set iovar is_WPS_enrollee\n")); ++ return error; ++ } ++ } else { ++ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) { ++ WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n")); ++ return error; ++ } ++ } ++ } ++ break; ++ } ++ ++ ++#endif ++ ++ ++ default: ++ break; ++ } ++ return 0; ++} ++#define VAL_PSK(_val) (((_val) & WPA_AUTH_PSK) || ((_val) & WPA2_AUTH_PSK)) ++ ++static int ++wl_iw_get_wpaauth( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra ++) ++{ ++ int error; ++ int paramid; ++ int paramval = 0; ++ int val; ++ wl_iw_t *iw = IW_DEV_IF(dev); ++ ++ WL_TRACE(("%s: SIOCGIWAUTH\n", dev->name)); ++ ++ paramid = vwrq->flags & IW_AUTH_INDEX; ++ ++ switch (paramid) { ++ case IW_AUTH_WPA_VERSION: ++ ++ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) ++ return error; ++ if (val & (WPA_AUTH_NONE | WPA_AUTH_DISABLED)) ++ paramval = IW_AUTH_WPA_VERSION_DISABLED; ++ else if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) ++ paramval = IW_AUTH_WPA_VERSION_WPA; ++ else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) ++ paramval = IW_AUTH_WPA_VERSION_WPA2; ++ break; ++ ++ case IW_AUTH_CIPHER_PAIRWISE: ++ paramval = iw->pwsec; ++ break; ++ ++ case IW_AUTH_CIPHER_GROUP: ++ paramval = iw->gwsec; ++ break; ++ ++ case IW_AUTH_KEY_MGMT: ++ ++ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) ++ return error; ++ if (VAL_PSK(val)) ++ paramval = IW_AUTH_KEY_MGMT_PSK; ++ else ++ paramval = IW_AUTH_KEY_MGMT_802_1X; ++ ++ break; ++ case IW_AUTH_TKIP_COUNTERMEASURES: ++ dev_wlc_bufvar_get(dev, "tkip_countermeasures", (char *)¶mval, 1); ++ break; ++ ++ case IW_AUTH_DROP_UNENCRYPTED: ++ dev_wlc_bufvar_get(dev, "wsec_restrict", (char *)¶mval, 1); ++ break; ++ ++ case IW_AUTH_RX_UNENCRYPTED_EAPOL: ++ dev_wlc_bufvar_get(dev, "rx_unencrypted_eapol", (char *)¶mval, 1); ++ break; ++ ++ case IW_AUTH_80211_AUTH_ALG: ++ ++ if ((error = dev_wlc_intvar_get(dev, "auth", &val))) ++ return error; ++ if (!val) ++ paramval = IW_AUTH_ALG_OPEN_SYSTEM; ++ else ++ paramval = IW_AUTH_ALG_SHARED_KEY; ++ break; ++ case IW_AUTH_WPA_ENABLED: ++ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) ++ return error; ++ if (val) ++ paramval = TRUE; ++ else ++ paramval = FALSE; ++ break; ++ ++#if WIRELESS_EXT > 17 ++ ++ case IW_AUTH_ROAMING_CONTROL: ++ WL_ERROR(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__)); ++ ++ break; ++ ++ case IW_AUTH_PRIVACY_INVOKED: ++ paramval = iw->privacy_invoked; ++ break; ++ ++#endif ++ } ++ vwrq->value = paramval; ++ return 0; ++} ++#endif ++ ++static const iw_handler wl_iw_handler[] = ++{ ++ (iw_handler) wl_iw_config_commit, ++ (iw_handler) wl_iw_get_name, ++ (iw_handler) NULL, ++ (iw_handler) NULL, ++ (iw_handler) wl_iw_set_freq, ++ (iw_handler) wl_iw_get_freq, ++ (iw_handler) wl_iw_set_mode, ++ (iw_handler) wl_iw_get_mode, ++ (iw_handler) NULL, ++ (iw_handler) NULL, ++ (iw_handler) NULL, ++ (iw_handler) wl_iw_get_range, ++ (iw_handler) NULL, ++ (iw_handler) NULL, ++ (iw_handler) NULL, ++ (iw_handler) NULL, ++ (iw_handler) wl_iw_set_spy, ++ (iw_handler) wl_iw_get_spy, ++ (iw_handler) NULL, ++ (iw_handler) NULL, ++ (iw_handler) wl_iw_set_wap, ++ (iw_handler) wl_iw_get_wap, ++#if WIRELESS_EXT > 17 ++ (iw_handler) wl_iw_mlme, ++#else ++ (iw_handler) NULL, ++#endif ++ (iw_handler) wl_iw_iscan_get_aplist, ++#if WIRELESS_EXT > 13 ++ (iw_handler) wl_iw_iscan_set_scan, ++ (iw_handler) wl_iw_iscan_get_scan, ++#else ++ (iw_handler) NULL, ++ (iw_handler) NULL, ++#endif ++ (iw_handler) wl_iw_set_essid, ++ (iw_handler) wl_iw_get_essid, ++ (iw_handler) wl_iw_set_nick, ++ (iw_handler) wl_iw_get_nick, ++ (iw_handler) NULL, ++ (iw_handler) NULL, ++ (iw_handler) wl_iw_set_rate, ++ (iw_handler) wl_iw_get_rate, ++ (iw_handler) wl_iw_set_rts, ++ (iw_handler) wl_iw_get_rts, ++ (iw_handler) wl_iw_set_frag, ++ (iw_handler) wl_iw_get_frag, ++ (iw_handler) wl_iw_set_txpow, ++ (iw_handler) wl_iw_get_txpow, ++#if WIRELESS_EXT > 10 ++ (iw_handler) wl_iw_set_retry, ++ (iw_handler) wl_iw_get_retry, ++#endif ++ (iw_handler) wl_iw_set_encode, ++ (iw_handler) wl_iw_get_encode, ++ (iw_handler) wl_iw_set_power, ++ (iw_handler) wl_iw_get_power, ++#if WIRELESS_EXT > 17 ++ (iw_handler) NULL, ++ (iw_handler) NULL, ++ (iw_handler) wl_iw_set_wpaie, ++ (iw_handler) wl_iw_get_wpaie, ++ (iw_handler) wl_iw_set_wpaauth, ++ (iw_handler) wl_iw_get_wpaauth, ++ (iw_handler) wl_iw_set_encodeext, ++ (iw_handler) wl_iw_get_encodeext, ++ (iw_handler) wl_iw_set_pmksa, ++#endif ++}; ++ ++#if WIRELESS_EXT > 12 ++enum { ++ WL_IW_SET_LEDDC = SIOCIWFIRSTPRIV, ++ WL_IW_SET_VLANMODE, ++ WL_IW_SET_PM ++}; ++ ++static iw_handler wl_iw_priv_handler[] = { ++ wl_iw_set_leddc, ++ wl_iw_set_vlanmode, ++ wl_iw_set_pm ++}; ++ ++static struct iw_priv_args wl_iw_priv_args[] = { ++ { ++ WL_IW_SET_LEDDC, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, ++ 0, ++ "set_leddc" ++ }, ++ { ++ WL_IW_SET_VLANMODE, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, ++ 0, ++ "set_vlanmode" ++ }, ++ { ++ WL_IW_SET_PM, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, ++ 0, ++ "set_pm" ++ } ++}; ++ ++const struct iw_handler_def wl_iw_handler_def = ++{ ++ .num_standard = ARRAYSIZE(wl_iw_handler), ++ .num_private = ARRAY_SIZE(wl_iw_priv_handler), ++ .num_private_args = ARRAY_SIZE(wl_iw_priv_args), ++ .standard = (iw_handler *) wl_iw_handler, ++ .private = wl_iw_priv_handler, ++ .private_args = wl_iw_priv_args, ++#if WIRELESS_EXT >= 19 ++ get_wireless_stats: dhd_get_wireless_stats, ++#endif ++ }; ++#endif ++ ++int ++wl_iw_ioctl( ++ struct net_device *dev, ++ struct ifreq *rq, ++ int cmd ++) ++{ ++ struct iwreq *wrq = (struct iwreq *) rq; ++ struct iw_request_info info; ++ iw_handler handler; ++ char *extra = NULL; ++ size_t token_size = 1; ++ int max_tokens = 0, ret = 0; ++ ++ if (cmd < SIOCIWFIRST || ++ IW_IOCTL_IDX(cmd) >= ARRAYSIZE(wl_iw_handler) || ++ !(handler = wl_iw_handler[IW_IOCTL_IDX(cmd)])) ++ return -EOPNOTSUPP; ++ ++ switch (cmd) { ++ ++ case SIOCSIWESSID: ++ case SIOCGIWESSID: ++ case SIOCSIWNICKN: ++ case SIOCGIWNICKN: ++ max_tokens = IW_ESSID_MAX_SIZE + 1; ++ break; ++ ++ case SIOCSIWENCODE: ++ case SIOCGIWENCODE: ++#if WIRELESS_EXT > 17 ++ case SIOCSIWENCODEEXT: ++ case SIOCGIWENCODEEXT: ++#endif ++ max_tokens = IW_ENCODING_TOKEN_MAX; ++ break; ++ ++ case SIOCGIWRANGE: ++ max_tokens = sizeof(struct iw_range); ++ break; ++ ++ case SIOCGIWAPLIST: ++ token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality); ++ max_tokens = IW_MAX_AP; ++ break; ++ ++#if WIRELESS_EXT > 13 ++ case SIOCGIWSCAN: ++ if (g_iscan) ++ max_tokens = wrq->u.data.length; ++ else ++ max_tokens = IW_SCAN_MAX_DATA; ++ break; ++#endif ++ ++ case SIOCSIWSPY: ++ token_size = sizeof(struct sockaddr); ++ max_tokens = IW_MAX_SPY; ++ break; ++ ++ case SIOCGIWSPY: ++ token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality); ++ max_tokens = IW_MAX_SPY; ++ break; ++ default: ++ break; ++ } ++ ++ if (max_tokens && wrq->u.data.pointer) { ++ if (wrq->u.data.length > max_tokens) ++ return -E2BIG; ++ ++ if (!(extra = kmalloc(max_tokens * token_size, GFP_KERNEL))) ++ return -ENOMEM; ++ ++ if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length * token_size)) { ++ kfree(extra); ++ return -EFAULT; ++ } ++ } ++ ++ info.cmd = cmd; ++ info.flags = 0; ++ ++ ret = handler(dev, &info, &wrq->u, extra); ++ ++ if (extra) { ++ if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length * token_size)) { ++ kfree(extra); ++ return -EFAULT; ++ } ++ ++ kfree(extra); ++ } ++ ++ return ret; ++} ++ ++ ++bool ++wl_iw_conn_status_str(uint32 event_type, uint32 status, uint32 reason, ++ char* stringBuf, uint buflen) ++{ ++ typedef struct conn_fail_event_map_t { ++ uint32 inEvent; ++ uint32 inStatus; ++ uint32 inReason; ++ const char* outName; ++ const char* outCause; ++ } conn_fail_event_map_t; ++ ++ ++# define WL_IW_DONT_CARE 9999 ++ const conn_fail_event_map_t event_map [] = { ++ ++ ++ {WLC_E_SET_SSID, WLC_E_STATUS_SUCCESS, WL_IW_DONT_CARE, ++ "Conn", "Success"}, ++ {WLC_E_SET_SSID, WLC_E_STATUS_NO_NETWORKS, WL_IW_DONT_CARE, ++ "Conn", "NoNetworks"}, ++ {WLC_E_SET_SSID, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE, ++ "Conn", "ConfigMismatch"}, ++ {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_PRUNE_ENCR_MISMATCH, ++ "Conn", "EncrypMismatch"}, ++ {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_RSN_MISMATCH, ++ "Conn", "RsnMismatch"}, ++ {WLC_E_AUTH, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE, ++ "Conn", "AuthTimeout"}, ++ {WLC_E_AUTH, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE, ++ "Conn", "AuthFail"}, ++ {WLC_E_AUTH, WLC_E_STATUS_NO_ACK, WL_IW_DONT_CARE, ++ "Conn", "AuthNoAck"}, ++ {WLC_E_REASSOC, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE, ++ "Conn", "ReassocFail"}, ++ {WLC_E_REASSOC, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE, ++ "Conn", "ReassocTimeout"}, ++ {WLC_E_REASSOC, WLC_E_STATUS_ABORT, WL_IW_DONT_CARE, ++ "Conn", "ReassocAbort"}, ++ {WLC_E_PSK_SUP, WLC_SUP_KEYED, WL_IW_DONT_CARE, ++ "Sup", "ConnSuccess"}, ++ {WLC_E_PSK_SUP, WL_IW_DONT_CARE, WL_IW_DONT_CARE, ++ "Sup", "WpaHandshakeFail"}, ++ {WLC_E_DEAUTH_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE, ++ "Conn", "Deauth"}, ++ {WLC_E_DISASSOC_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE, ++ "Conn", "DisassocInd"}, ++ {WLC_E_DISASSOC, WL_IW_DONT_CARE, WL_IW_DONT_CARE, ++ "Conn", "Disassoc"} ++ }; ++ ++ const char* name = ""; ++ const char* cause = NULL; ++ int i; ++ ++ ++ for (i = 0; i < sizeof(event_map)/sizeof(event_map[0]); i++) { ++ const conn_fail_event_map_t* row = &event_map[i]; ++ if (row->inEvent == event_type && ++ (row->inStatus == status || row->inStatus == WL_IW_DONT_CARE) && ++ (row->inReason == reason || row->inReason == WL_IW_DONT_CARE)) { ++ name = row->outName; ++ cause = row->outCause; ++ break; ++ } ++ } ++ ++ ++ if (cause) { ++ memset(stringBuf, 0, buflen); ++ snprintf(stringBuf, buflen, "%s %s %02d %02d", ++ name, cause, status, reason); ++ WL_TRACE(("Connection status: %s\n", stringBuf)); ++ return TRUE; ++ } else { ++ return FALSE; ++ } ++} ++ ++#if (WIRELESS_EXT > 14) ++ ++static bool ++wl_iw_check_conn_fail(wl_event_msg_t *e, char* stringBuf, uint buflen) ++{ ++ uint32 event = ntoh32(e->event_type); ++ uint32 status = ntoh32(e->status); ++ uint32 reason = ntoh32(e->reason); ++ ++ if (wl_iw_conn_status_str(event, status, reason, stringBuf, buflen)) { ++ return TRUE; ++ } else ++ { ++ return FALSE; ++ } ++} ++#endif ++ ++#ifndef IW_CUSTOM_MAX ++#define IW_CUSTOM_MAX 256 ++#endif ++ ++void ++wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) ++{ ++#if WIRELESS_EXT > 13 ++ union iwreq_data wrqu; ++ char extra[IW_CUSTOM_MAX + 1]; ++ int cmd = 0; ++ uint32 event_type = ntoh32(e->event_type); ++ uint16 flags = ntoh16(e->flags); ++ uint32 datalen = ntoh32(e->datalen); ++ uint32 status = ntoh32(e->status); ++ ++ memset(&wrqu, 0, sizeof(wrqu)); ++ memset(extra, 0, sizeof(extra)); ++ ++ memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN); ++ wrqu.addr.sa_family = ARPHRD_ETHER; ++ ++ switch (event_type) { ++ case WLC_E_TXFAIL: ++ cmd = IWEVTXDROP; ++ break; ++#if WIRELESS_EXT > 14 ++ case WLC_E_JOIN: ++ case WLC_E_ASSOC_IND: ++ case WLC_E_REASSOC_IND: ++ cmd = IWEVREGISTERED; ++ break; ++ case WLC_E_DEAUTH_IND: ++ case WLC_E_DISASSOC_IND: ++ cmd = SIOCGIWAP; ++ wrqu.data.length = strlen(extra); ++ bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN); ++ bzero(&extra, ETHER_ADDR_LEN); ++ break; ++ ++ case WLC_E_LINK: ++ case WLC_E_NDIS_LINK: ++ cmd = SIOCGIWAP; ++ wrqu.data.length = strlen(extra); ++ if (!(flags & WLC_EVENT_MSG_LINK)) { ++ bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN); ++ bzero(&extra, ETHER_ADDR_LEN); ++ } ++ break; ++ case WLC_E_ACTION_FRAME: ++ cmd = IWEVCUSTOM; ++ if (datalen + 1 <= sizeof(extra)) { ++ wrqu.data.length = datalen + 1; ++ extra[0] = WLC_E_ACTION_FRAME; ++ memcpy(&extra[1], data, datalen); ++ WL_TRACE(("WLC_E_ACTION_FRAME len %d \n", wrqu.data.length)); ++ } ++ break; ++ ++ case WLC_E_ACTION_FRAME_COMPLETE: ++ cmd = IWEVCUSTOM; ++ if (sizeof(status) + 1 <= sizeof(extra)) { ++ wrqu.data.length = sizeof(status) + 1; ++ extra[0] = WLC_E_ACTION_FRAME_COMPLETE; ++ memcpy(&extra[1], &status, sizeof(status)); ++ WL_TRACE(("wl_iw_event status %d \n", status)); ++ } ++ break; ++#endif ++#if WIRELESS_EXT > 17 ++ case WLC_E_MIC_ERROR: { ++ struct iw_michaelmicfailure *micerrevt = (struct iw_michaelmicfailure *)&extra; ++ cmd = IWEVMICHAELMICFAILURE; ++ wrqu.data.length = sizeof(struct iw_michaelmicfailure); ++ if (flags & WLC_EVENT_MSG_GROUP) ++ micerrevt->flags |= IW_MICFAILURE_GROUP; ++ else ++ micerrevt->flags |= IW_MICFAILURE_PAIRWISE; ++ memcpy(micerrevt->src_addr.sa_data, &e->addr, ETHER_ADDR_LEN); ++ micerrevt->src_addr.sa_family = ARPHRD_ETHER; ++ ++ break; ++ } ++ ++ case WLC_E_ASSOC_REQ_IE: ++ cmd = IWEVASSOCREQIE; ++ wrqu.data.length = datalen; ++ if (datalen < sizeof(extra)) ++ memcpy(extra, data, datalen); ++ break; ++ ++ case WLC_E_ASSOC_RESP_IE: ++ cmd = IWEVASSOCRESPIE; ++ wrqu.data.length = datalen; ++ if (datalen < sizeof(extra)) ++ memcpy(extra, data, datalen); ++ break; ++ ++ case WLC_E_PMKID_CACHE: { ++ struct iw_pmkid_cand *iwpmkidcand = (struct iw_pmkid_cand *)&extra; ++ pmkid_cand_list_t *pmkcandlist; ++ pmkid_cand_t *pmkidcand; ++ int count; ++ ++ if (data == NULL) ++ break; ++ ++ cmd = IWEVPMKIDCAND; ++ pmkcandlist = data; ++ count = ntoh32_ua((uint8 *)&pmkcandlist->npmkid_cand); ++ wrqu.data.length = sizeof(struct iw_pmkid_cand); ++ pmkidcand = pmkcandlist->pmkid_cand; ++ while (count) { ++ bzero(iwpmkidcand, sizeof(struct iw_pmkid_cand)); ++ if (pmkidcand->preauth) ++ iwpmkidcand->flags |= IW_PMKID_CAND_PREAUTH; ++ bcopy(&pmkidcand->BSSID, &iwpmkidcand->bssid.sa_data, ++ ETHER_ADDR_LEN); ++ wireless_send_event(dev, cmd, &wrqu, extra); ++ pmkidcand++; ++ count--; ++ } ++ break; ++ } ++#endif ++ ++ case WLC_E_SCAN_COMPLETE: ++#if WIRELESS_EXT > 14 ++ cmd = SIOCGIWSCAN; ++#endif ++ WL_TRACE(("event WLC_E_SCAN_COMPLETE\n")); ++ if ((g_iscan) && (g_iscan->sysioc_pid >= 0) && ++ (g_iscan->iscan_state != ISCAN_STATE_IDLE)) ++ up(&g_iscan->sysioc_sem); ++ break; ++ ++ default: ++ ++ break; ++ } ++ ++ if (cmd) { ++ if (cmd == SIOCGIWSCAN) ++ wireless_send_event(dev, cmd, &wrqu, NULL); ++ else ++ wireless_send_event(dev, cmd, &wrqu, extra); ++ } ++ ++#if WIRELESS_EXT > 14 ++ ++ memset(extra, 0, sizeof(extra)); ++ if (wl_iw_check_conn_fail(e, extra, sizeof(extra))) { ++ cmd = IWEVCUSTOM; ++ wrqu.data.length = strlen(extra); ++ wireless_send_event(dev, cmd, &wrqu, extra); ++ } ++#endif ++ ++#endif ++} ++ ++int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats) ++{ ++ int res = 0; ++ wl_cnt_t cnt; ++ int phy_noise; ++ int rssi; ++ scb_val_t scb_val; ++ ++ phy_noise = 0; ++ if ((res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise, sizeof(phy_noise)))) ++ goto done; ++ ++ phy_noise = dtoh32(phy_noise); ++ WL_TRACE(("wl_iw_get_wireless_stats phy noise=%d\n *****", phy_noise)); ++ ++ scb_val.val = 0; ++ if ((res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t)))) ++ goto done; ++ ++ rssi = dtoh32(scb_val.val); ++ WL_TRACE(("wl_iw_get_wireless_stats rssi=%d ****** \n", rssi)); ++ if (rssi <= WL_IW_RSSI_NO_SIGNAL) ++ wstats->qual.qual = 0; ++ else if (rssi <= WL_IW_RSSI_VERY_LOW) ++ wstats->qual.qual = 1; ++ else if (rssi <= WL_IW_RSSI_LOW) ++ wstats->qual.qual = 2; ++ else if (rssi <= WL_IW_RSSI_GOOD) ++ wstats->qual.qual = 3; ++ else if (rssi <= WL_IW_RSSI_VERY_GOOD) ++ wstats->qual.qual = 4; ++ else ++ wstats->qual.qual = 5; ++ ++ ++ wstats->qual.level = 0x100 + rssi; ++ wstats->qual.noise = 0x100 + phy_noise; ++#if WIRELESS_EXT > 18 ++ wstats->qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM); ++#else ++ wstats->qual.updated |= 7; ++#endif ++ ++#if WIRELESS_EXT > 11 ++ WL_TRACE(("wl_iw_get_wireless_stats counters=%d\n *****", (int)sizeof(wl_cnt_t))); ++ ++ memset(&cnt, 0, sizeof(wl_cnt_t)); ++ res = dev_wlc_bufvar_get(dev, "counters", (char *)&cnt, sizeof(wl_cnt_t)); ++ if (res) ++ { ++ WL_ERROR(("wl_iw_get_wireless_stats counters failed error=%d ****** \n", res)); ++ goto done; ++ } ++ ++ cnt.version = dtoh16(cnt.version); ++ if (cnt.version != WL_CNT_T_VERSION) { ++ WL_TRACE(("\tIncorrect version of counters struct: expected %d; got %d\n", ++ WL_CNT_T_VERSION, cnt.version)); ++ goto done; ++ } ++ ++ wstats->discard.nwid = 0; ++ wstats->discard.code = dtoh32(cnt.rxundec); ++ wstats->discard.fragment = dtoh32(cnt.rxfragerr); ++ wstats->discard.retries = dtoh32(cnt.txfail); ++ wstats->discard.misc = dtoh32(cnt.rxrunt) + dtoh32(cnt.rxgiant); ++ wstats->miss.beacon = 0; ++ ++ WL_TRACE(("wl_iw_get_wireless_stats counters txframe=%d txbyte=%d\n", ++ dtoh32(cnt.txframe), dtoh32(cnt.txbyte))); ++ WL_TRACE(("wl_iw_get_wireless_stats counters rxfrmtoolong=%d\n", dtoh32(cnt.rxfrmtoolong))); ++ WL_TRACE(("wl_iw_get_wireless_stats counters rxbadplcp=%d\n", dtoh32(cnt.rxbadplcp))); ++ WL_TRACE(("wl_iw_get_wireless_stats counters rxundec=%d\n", dtoh32(cnt.rxundec))); ++ WL_TRACE(("wl_iw_get_wireless_stats counters rxfragerr=%d\n", dtoh32(cnt.rxfragerr))); ++ WL_TRACE(("wl_iw_get_wireless_stats counters txfail=%d\n", dtoh32(cnt.txfail))); ++ WL_TRACE(("wl_iw_get_wireless_stats counters rxrunt=%d\n", dtoh32(cnt.rxrunt))); ++ WL_TRACE(("wl_iw_get_wireless_stats counters rxgiant=%d\n", dtoh32(cnt.rxgiant))); ++ ++#endif ++ ++done: ++ return res; ++} ++ ++static void ++wl_iw_timerfunc(ulong data) ++{ ++ iscan_info_t *iscan = (iscan_info_t *)data; ++ iscan->timer_on = 0; ++ if (iscan->iscan_state != ISCAN_STATE_IDLE) { ++ WL_TRACE(("timer trigger\n")); ++ up(&iscan->sysioc_sem); ++ } ++} ++ ++static void ++wl_iw_set_event_mask(struct net_device *dev) ++{ ++ char eventmask[WL_EVENTING_MASK_LEN]; ++ char iovbuf[WL_EVENTING_MASK_LEN + 12]; ++ ++ dev_iw_iovar_getbuf(dev, "event_msgs", "", 0, iovbuf, sizeof(iovbuf)); ++ bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN); ++ setbit(eventmask, WLC_E_SCAN_COMPLETE); ++ dev_iw_iovar_setbuf(dev, "event_msgs", eventmask, WL_EVENTING_MASK_LEN, ++ iovbuf, sizeof(iovbuf)); ++ ++} ++ ++static int ++wl_iw_iscan_prep(wl_scan_params_t *params, wlc_ssid_t *ssid) ++{ ++ int err = 0; ++ ++ memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN); ++ params->bss_type = DOT11_BSSTYPE_ANY; ++ params->scan_type = 0; ++ params->nprobes = -1; ++ params->active_time = -1; ++ params->passive_time = -1; ++ params->home_time = -1; ++ params->channel_num = 0; ++ ++ params->nprobes = htod32(params->nprobes); ++ params->active_time = htod32(params->active_time); ++ params->passive_time = htod32(params->passive_time); ++ params->home_time = htod32(params->home_time); ++ if (ssid && ssid->SSID_len) ++ memcpy(¶ms->ssid, ssid, sizeof(wlc_ssid_t)); ++ ++ return err; ++} ++ ++static int ++wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action) ++{ ++ int params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params)); ++ wl_iscan_params_t *params; ++ int err = 0; ++ ++ if (ssid && ssid->SSID_len) { ++ params_size += sizeof(wlc_ssid_t); ++ } ++ params = (wl_iscan_params_t*)kmalloc(params_size, GFP_KERNEL); ++ if (params == NULL) { ++ return -ENOMEM; ++ } ++ memset(params, 0, params_size); ++ ASSERT(params_size < WLC_IOCTL_SMLEN); ++ ++ err = wl_iw_iscan_prep(¶ms->params, ssid); ++ ++ if (!err) { ++ params->version = htod32(ISCAN_REQ_VERSION); ++ params->action = htod16(action); ++ params->scan_duration = htod16(0); ++ ++ ++ (void) dev_iw_iovar_setbuf(iscan->dev, "iscan", params, params_size, ++ iscan->ioctlbuf, WLC_IOCTL_SMLEN); ++ } ++ ++ kfree(params); ++ return err; ++} ++ ++static uint32 ++wl_iw_iscan_get(iscan_info_t *iscan) ++{ ++ iscan_buf_t * buf; ++ iscan_buf_t * ptr; ++ wl_iscan_results_t * list_buf; ++ wl_iscan_results_t list; ++ wl_scan_results_t *results; ++ uint32 status; ++ ++ ++ if (iscan->list_cur) { ++ buf = iscan->list_cur; ++ iscan->list_cur = buf->next; ++ } ++ else { ++ buf = kmalloc(sizeof(iscan_buf_t), GFP_KERNEL); ++ if (!buf) ++ return WL_SCAN_RESULTS_ABORTED; ++ buf->next = NULL; ++ if (!iscan->list_hdr) ++ iscan->list_hdr = buf; ++ else { ++ ptr = iscan->list_hdr; ++ while (ptr->next) { ++ ptr = ptr->next; ++ } ++ ptr->next = buf; ++ } ++ } ++ memset(buf->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN); ++ list_buf = (wl_iscan_results_t*)buf->iscan_buf; ++ results = &list_buf->results; ++ results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE; ++ results->version = 0; ++ results->count = 0; ++ ++ memset(&list, 0, sizeof(list)); ++ list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN); ++ (void) dev_iw_iovar_getbuf( ++ iscan->dev, ++ "iscanresults", ++ &list, ++ WL_ISCAN_RESULTS_FIXED_SIZE, ++ buf->iscan_buf, ++ WLC_IW_ISCAN_MAXLEN); ++ results->buflen = dtoh32(results->buflen); ++ results->version = dtoh32(results->version); ++ results->count = dtoh32(results->count); ++ WL_TRACE(("results->count = %d\n", results->count)); ++ ++ WL_TRACE(("results->buflen = %d\n", results->buflen)); ++ status = dtoh32(list_buf->status); ++ return status; ++} ++ ++static void wl_iw_send_scan_complete(iscan_info_t *iscan) ++{ ++ union iwreq_data wrqu; ++ ++ memset(&wrqu, 0, sizeof(wrqu)); ++ ++ ++ wireless_send_event(iscan->dev, SIOCGIWSCAN, &wrqu, NULL); ++} ++ ++static int ++_iscan_sysioc_thread(void *data) ++{ ++ uint32 status; ++ iscan_info_t *iscan = (iscan_info_t *)data; ++ ++ DAEMONIZE("iscan_sysioc"); ++ ++ status = WL_SCAN_RESULTS_PARTIAL; ++ while (down_interruptible(&iscan->sysioc_sem) == 0) { ++ if (iscan->timer_on) { ++ del_timer(&iscan->timer); ++ iscan->timer_on = 0; ++ } ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ rtnl_lock(); ++#endif ++ status = wl_iw_iscan_get(iscan); ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ rtnl_unlock(); ++#endif ++ ++ switch (status) { ++ case WL_SCAN_RESULTS_PARTIAL: ++ WL_TRACE(("iscanresults incomplete\n")); ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ rtnl_lock(); ++#endif ++ ++ wl_iw_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE); ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ rtnl_unlock(); ++#endif ++ ++ iscan->timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms); ++ add_timer(&iscan->timer); ++ iscan->timer_on = 1; ++ break; ++ case WL_SCAN_RESULTS_SUCCESS: ++ WL_TRACE(("iscanresults complete\n")); ++ iscan->iscan_state = ISCAN_STATE_IDLE; ++ wl_iw_send_scan_complete(iscan); ++ break; ++ case WL_SCAN_RESULTS_PENDING: ++ WL_TRACE(("iscanresults pending\n")); ++ ++ iscan->timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms); ++ add_timer(&iscan->timer); ++ iscan->timer_on = 1; ++ break; ++ case WL_SCAN_RESULTS_ABORTED: ++ WL_TRACE(("iscanresults aborted\n")); ++ iscan->iscan_state = ISCAN_STATE_IDLE; ++ wl_iw_send_scan_complete(iscan); ++ break; ++ default: ++ WL_TRACE(("iscanresults returned unknown status %d\n", status)); ++ break; ++ } ++ } ++ complete_and_exit(&iscan->sysioc_exited, 0); ++} ++ ++int ++wl_iw_attach(struct net_device *dev, void * dhdp) ++{ ++ iscan_info_t *iscan = NULL; ++ ++ if (!dev) ++ return 0; ++ ++ iscan = kmalloc(sizeof(iscan_info_t), GFP_KERNEL); ++ if (!iscan) ++ return -ENOMEM; ++ memset(iscan, 0, sizeof(iscan_info_t)); ++ iscan->sysioc_pid = -1; ++ ++ g_iscan = iscan; ++ iscan->dev = dev; ++ iscan->iscan_state = ISCAN_STATE_IDLE; ++ ++ ++ ++ iscan->timer_ms = 2000; ++ init_timer(&iscan->timer); ++ iscan->timer.data = (ulong)iscan; ++ iscan->timer.function = wl_iw_timerfunc; ++ ++ sema_init(&iscan->sysioc_sem, 0); ++ init_completion(&iscan->sysioc_exited); ++ iscan->sysioc_pid = kernel_thread(_iscan_sysioc_thread, iscan, 0); ++ if (iscan->sysioc_pid < 0) ++ return -ENOMEM; ++ return 0; ++} ++ ++void wl_iw_detach(void) ++{ ++ iscan_buf_t *buf; ++ iscan_info_t *iscan = g_iscan; ++ if (!iscan) ++ return; ++ if (iscan->sysioc_pid >= 0) { ++ KILL_PROC(iscan->sysioc_pid, SIGTERM); ++ wait_for_completion(&iscan->sysioc_exited); ++ } ++ ++ while (iscan->list_hdr) { ++ buf = iscan->list_hdr->next; ++ kfree(iscan->list_hdr); ++ iscan->list_hdr = buf; ++ } ++ kfree(iscan); ++ g_iscan = NULL; ++} ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/wl_iw.h b/drivers/net/wireless/bcmdhd/wl_iw.h +new file mode 100644 +index 00000000..2afb5a68 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/wl_iw.h +@@ -0,0 +1,161 @@ ++/* ++ * Linux Wireless Extensions support ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wl_iw.h 291086 2011-10-21 01:17:24Z $ ++ */ ++ ++#ifndef _wl_iw_h_ ++#define _wl_iw_h_ ++ ++#include ++ ++#include ++#include ++#include ++ ++#define WL_SCAN_PARAMS_SSID_MAX 10 ++#define GET_SSID "SSID=" ++#define GET_CHANNEL "CH=" ++#define GET_NPROBE "NPROBE=" ++#define GET_ACTIVE_ASSOC_DWELL "ACTIVE=" ++#define GET_PASSIVE_ASSOC_DWELL "PASSIVE=" ++#define GET_HOME_DWELL "HOME=" ++#define GET_SCAN_TYPE "TYPE=" ++ ++#define BAND_GET_CMD "GETBAND" ++#define BAND_SET_CMD "SETBAND" ++#define DTIM_SKIP_GET_CMD "DTIMSKIPGET" ++#define DTIM_SKIP_SET_CMD "DTIMSKIPSET" ++#define SETSUSPEND_CMD "SETSUSPENDOPT" ++#define PNOSSIDCLR_SET_CMD "PNOSSIDCLR" ++ ++#define PNOSETUP_SET_CMD "PNOSETUP " ++#define PNOENABLE_SET_CMD "PNOFORCE" ++#define PNODEBUG_SET_CMD "PNODEBUG" ++#define TXPOWER_SET_CMD "TXPOWER" ++ ++#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] ++#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" ++ ++ ++typedef struct wl_iw_extra_params { ++ int target_channel; ++} wl_iw_extra_params_t; ++ ++struct cntry_locales_custom { ++ char iso_abbrev[WLC_CNTRY_BUF_SZ]; ++ char custom_locale[WLC_CNTRY_BUF_SZ]; ++ int32 custom_locale_rev; ++}; ++ ++ ++#define WL_IW_RSSI_MINVAL -200 ++#define WL_IW_RSSI_NO_SIGNAL -91 ++#define WL_IW_RSSI_VERY_LOW -80 ++#define WL_IW_RSSI_LOW -70 ++#define WL_IW_RSSI_GOOD -68 ++#define WL_IW_RSSI_VERY_GOOD -58 ++#define WL_IW_RSSI_EXCELLENT -57 ++#define WL_IW_RSSI_INVALID 0 ++#define MAX_WX_STRING 80 ++#define SSID_FMT_BUF_LEN ((4 * 32) + 1) ++#define isprint(c) bcm_isprint(c) ++#define WL_IW_SET_ACTIVE_SCAN (SIOCIWFIRSTPRIV+1) ++#define WL_IW_GET_RSSI (SIOCIWFIRSTPRIV+3) ++#define WL_IW_SET_PASSIVE_SCAN (SIOCIWFIRSTPRIV+5) ++#define WL_IW_GET_LINK_SPEED (SIOCIWFIRSTPRIV+7) ++#define WL_IW_GET_CURR_MACADDR (SIOCIWFIRSTPRIV+9) ++#define WL_IW_SET_STOP (SIOCIWFIRSTPRIV+11) ++#define WL_IW_SET_START (SIOCIWFIRSTPRIV+13) ++ ++#define G_SCAN_RESULTS 8*1024 ++#define WE_ADD_EVENT_FIX 0x80 ++#define G_WLAN_SET_ON 0 ++#define G_WLAN_SET_OFF 1 ++ ++ ++typedef struct wl_iw { ++ char nickname[IW_ESSID_MAX_SIZE]; ++ ++ struct iw_statistics wstats; ++ ++ int spy_num; ++ uint32 pwsec; ++ uint32 gwsec; ++ bool privacy_invoked; ++ struct ether_addr spy_addr[IW_MAX_SPY]; ++ struct iw_quality spy_qual[IW_MAX_SPY]; ++ void *wlinfo; ++} wl_iw_t; ++ ++struct wl_ctrl { ++ struct timer_list *timer; ++ struct net_device *dev; ++ long sysioc_pid; ++ struct semaphore sysioc_sem; ++ struct completion sysioc_exited; ++}; ++ ++ ++#if WIRELESS_EXT > 12 ++#include ++extern const struct iw_handler_def wl_iw_handler_def; ++#endif ++ ++extern int wl_iw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); ++extern void wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data); ++extern int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats); ++int wl_iw_attach(struct net_device *dev, void * dhdp); ++int wl_iw_send_priv_event(struct net_device *dev, char *flag); ++ ++void wl_iw_detach(void); ++ ++#define CSCAN_COMMAND "CSCAN " ++#define CSCAN_TLV_PREFIX 'S' ++#define CSCAN_TLV_VERSION 1 ++#define CSCAN_TLV_SUBVERSION 0 ++#define CSCAN_TLV_TYPE_SSID_IE 'S' ++#define CSCAN_TLV_TYPE_CHANNEL_IE 'C' ++#define CSCAN_TLV_TYPE_NPROBE_IE 'N' ++#define CSCAN_TLV_TYPE_ACTIVE_IE 'A' ++#define CSCAN_TLV_TYPE_PASSIVE_IE 'P' ++#define CSCAN_TLV_TYPE_HOME_IE 'H' ++#define CSCAN_TLV_TYPE_STYPE_IE 'T' ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) ++#define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \ ++ iwe_stream_add_event(info, stream, ends, iwe, extra) ++#define IWE_STREAM_ADD_VALUE(info, event, value, ends, iwe, event_len) \ ++ iwe_stream_add_value(info, event, value, ends, iwe, event_len) ++#define IWE_STREAM_ADD_POINT(info, stream, ends, iwe, extra) \ ++ iwe_stream_add_point(info, stream, ends, iwe, extra) ++#else ++#define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \ ++ iwe_stream_add_event(stream, ends, iwe, extra) ++#define IWE_STREAM_ADD_VALUE(info, event, value, ends, iwe, event_len) \ ++ iwe_stream_add_value(event, value, ends, iwe, event_len) ++#define IWE_STREAM_ADD_POINT(info, stream, ends, iwe, extra) \ ++ iwe_stream_add_point(stream, ends, iwe, extra) ++#endif ++ ++#endif +diff --git a/drivers/net/wireless/bcmdhd/wl_linux_mon.c b/drivers/net/wireless/bcmdhd/wl_linux_mon.c +new file mode 100644 +index 00000000..af258632 +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/wl_linux_mon.c +@@ -0,0 +1,422 @@ ++/* ++ * Broadcom Dongle Host Driver (DHD), Linux monitor network interface ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: dhd_linux_mon.c 280623 2011-08-30 14:49:39Z $ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++typedef enum monitor_states ++{ ++ MONITOR_STATE_DEINIT = 0x0, ++ MONITOR_STATE_INIT = 0x1, ++ MONITOR_STATE_INTERFACE_ADDED = 0x2, ++ MONITOR_STATE_INTERFACE_DELETED = 0x4 ++} monitor_states_t; ++int dhd_add_monitor(char *name, struct net_device **new_ndev); ++extern int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); ++int dhd_del_monitor(struct net_device *ndev); ++int dhd_monitor_init(void *dhd_pub); ++int dhd_monitor_uninit(void); ++ ++/** ++ * Local declarations and defintions (not exposed) ++ */ ++#ifndef DHD_MAX_IFS ++#define DHD_MAX_IFS 16 ++#endif ++#define MON_PRINT(format, ...) printk("DHD-MON: %s " format, __func__, ##__VA_ARGS__) ++#define MON_TRACE MON_PRINT ++ ++typedef struct monitor_interface { ++ int radiotap_enabled; ++ struct net_device* real_ndev; /* The real interface that the monitor is on */ ++ struct net_device* mon_ndev; ++} monitor_interface; ++ ++typedef struct dhd_linux_monitor { ++ void *dhd_pub; ++ monitor_states_t monitor_state; ++ monitor_interface mon_if[DHD_MAX_IFS]; ++ struct mutex lock; /* lock to protect mon_if */ ++} dhd_linux_monitor_t; ++ ++static dhd_linux_monitor_t g_monitor; ++ ++static struct net_device* lookup_real_netdev(char *name); ++static monitor_interface* ndev_to_monif(struct net_device *ndev); ++static int dhd_mon_if_open(struct net_device *ndev); ++static int dhd_mon_if_stop(struct net_device *ndev); ++static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev); ++static void dhd_mon_if_set_multicast_list(struct net_device *ndev); ++static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr); ++ ++static const struct net_device_ops dhd_mon_if_ops = { ++ .ndo_open = dhd_mon_if_open, ++ .ndo_stop = dhd_mon_if_stop, ++ .ndo_start_xmit = dhd_mon_if_subif_start_xmit, ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) ++ .ndo_set_rx_mode = dhd_mon_if_set_multicast_list, ++#else ++ .ndo_set_multicast_list = dhd_mon_if_set_multicast_list, ++#endif ++ .ndo_set_mac_address = dhd_mon_if_change_mac, ++}; ++ ++/** ++ * Local static function defintions ++ */ ++ ++/* Look up dhd's net device table to find a match (e.g. interface "eth0" is a match for "mon.eth0" ++ * "p2p-eth0-0" is a match for "mon.p2p-eth0-0") ++ */ ++static struct net_device* lookup_real_netdev(char *name) ++{ ++ struct net_device *ndev_found = NULL; ++ ++ int i; ++ int len = 0; ++ int last_name_len = 0; ++ struct net_device *ndev; ++ ++ /* We need to find interface "p2p-p2p-0" corresponding to monitor interface "mon-p2p-0", ++ * Once mon iface name reaches IFNAMSIZ, it is reset to p2p0-0 and corresponding mon ++ * iface would be mon-p2p0-0. ++ */ ++ for (i = 0; i < DHD_MAX_IFS; i++) { ++ ndev = dhd_idx2net(g_monitor.dhd_pub, i); ++ ++ /* Skip "p2p" and look for "-p2p0-x" in monitor interface name. If it ++ * it matches, then this netdev is the corresponding real_netdev. ++ */ ++ if (ndev && strstr(ndev->name, "p2p-p2p0")) { ++ len = strlen("p2p"); ++ } else { ++ /* if p2p- is not present, then the IFNAMSIZ have reached and name ++ * would have got reset. In this casse,look for p2p0-x in mon-p2p0-x ++ */ ++ len = 0; ++ } ++ if (ndev && strstr(name, (ndev->name + len))) { ++ if (strlen(ndev->name) > last_name_len) { ++ ndev_found = ndev; ++ last_name_len = strlen(ndev->name); ++ } ++ } ++ } ++ ++ return ndev_found; ++} ++ ++static monitor_interface* ndev_to_monif(struct net_device *ndev) ++{ ++ int i; ++ ++ for (i = 0; i < DHD_MAX_IFS; i++) { ++ if (g_monitor.mon_if[i].mon_ndev == ndev) ++ return &g_monitor.mon_if[i]; ++ } ++ ++ return NULL; ++} ++ ++static int dhd_mon_if_open(struct net_device *ndev) ++{ ++ int ret = 0; ++ ++ MON_PRINT("enter\n"); ++ return ret; ++} ++ ++static int dhd_mon_if_stop(struct net_device *ndev) ++{ ++ int ret = 0; ++ ++ MON_PRINT("enter\n"); ++ return ret; ++} ++ ++static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev) ++{ ++ int ret = 0; ++ int rtap_len; ++ int qos_len = 0; ++ int dot11_hdr_len = 24; ++ int snap_len = 6; ++ unsigned char *pdata; ++ unsigned short frame_ctl; ++ unsigned char src_mac_addr[6]; ++ unsigned char dst_mac_addr[6]; ++ struct ieee80211_hdr *dot11_hdr; ++ struct ieee80211_radiotap_header *rtap_hdr; ++ monitor_interface* mon_if; ++ ++ MON_PRINT("enter\n"); ++ ++ mon_if = ndev_to_monif(ndev); ++ if (mon_if == NULL || mon_if->real_ndev == NULL) { ++ MON_PRINT(" cannot find matched net dev, skip the packet\n"); ++ goto fail; ++ } ++ ++ if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) ++ goto fail; ++ ++ rtap_hdr = (struct ieee80211_radiotap_header *)skb->data; ++ if (unlikely(rtap_hdr->it_version)) ++ goto fail; ++ ++ rtap_len = ieee80211_get_radiotap_len(skb->data); ++ if (unlikely(skb->len < rtap_len)) ++ goto fail; ++ ++ MON_PRINT("radiotap len (should be 14): %d\n", rtap_len); ++ ++ /* Skip the ratio tap header */ ++ skb_pull(skb, rtap_len); ++ ++ dot11_hdr = (struct ieee80211_hdr *)skb->data; ++ frame_ctl = le16_to_cpu(dot11_hdr->frame_control); ++ /* Check if the QoS bit is set */ ++ if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { ++ /* Check if this ia a Wireless Distribution System (WDS) frame ++ * which has 4 MAC addresses ++ */ ++ if (dot11_hdr->frame_control & 0x0080) ++ qos_len = 2; ++ if ((dot11_hdr->frame_control & 0x0300) == 0x0300) ++ dot11_hdr_len += 6; ++ ++ memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr)); ++ memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr)); ++ ++ /* Skip the 802.11 header, QoS (if any) and SNAP, but leave spaces for ++ * for two MAC addresses ++ */ ++ skb_pull(skb, dot11_hdr_len + qos_len + snap_len - sizeof(src_mac_addr) * 2); ++ pdata = (unsigned char*)skb->data; ++ memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr)); ++ memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr, sizeof(src_mac_addr)); ++ PKTSETPRIO(skb, 0); ++ ++ MON_PRINT("if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); ++ ++ /* Use the real net device to transmit the packet */ ++ ret = dhd_start_xmit(skb, mon_if->real_ndev); ++ ++ return ret; ++ } ++fail: ++ dev_kfree_skb(skb); ++ return 0; ++} ++ ++static void dhd_mon_if_set_multicast_list(struct net_device *ndev) ++{ ++ monitor_interface* mon_if; ++ ++ mon_if = ndev_to_monif(ndev); ++ if (mon_if == NULL || mon_if->real_ndev == NULL) { ++ MON_PRINT(" cannot find matched net dev, skip the packet\n"); ++ } else { ++ MON_PRINT("enter, if name: %s, matched if name %s\n", ++ ndev->name, mon_if->real_ndev->name); ++ } ++} ++ ++static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr) ++{ ++ int ret = 0; ++ monitor_interface* mon_if; ++ ++ mon_if = ndev_to_monif(ndev); ++ if (mon_if == NULL || mon_if->real_ndev == NULL) { ++ MON_PRINT(" cannot find matched net dev, skip the packet\n"); ++ } else { ++ MON_PRINT("enter, if name: %s, matched if name %s\n", ++ ndev->name, mon_if->real_ndev->name); ++ } ++ return ret; ++} ++ ++/** ++ * Global function definitions (declared in dhd_linux_mon.h) ++ */ ++ ++int dhd_add_monitor(char *name, struct net_device **new_ndev) ++{ ++ int i; ++ int idx = -1; ++ int ret = 0; ++ struct net_device* ndev = NULL; ++ dhd_linux_monitor_t **dhd_mon; ++ ++ mutex_lock(&g_monitor.lock); ++ ++ MON_TRACE("enter, if name: %s\n", name); ++ if (!name || !new_ndev) { ++ MON_PRINT("invalid parameters\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * Find a vacancy ++ */ ++ for (i = 0; i < DHD_MAX_IFS; i++) ++ if (g_monitor.mon_if[i].mon_ndev == NULL) { ++ idx = i; ++ break; ++ } ++ if (idx == -1) { ++ MON_PRINT("exceeds maximum interfaces\n"); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ ndev = alloc_etherdev(sizeof(dhd_linux_monitor_t*)); ++ if (!ndev) { ++ MON_PRINT("failed to allocate memory\n"); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ndev->type = ARPHRD_IEEE80211_RADIOTAP; ++ strncpy(ndev->name, name, IFNAMSIZ); ++ ndev->name[IFNAMSIZ - 1] = 0; ++ ndev->netdev_ops = &dhd_mon_if_ops; ++ ++ ret = register_netdevice(ndev); ++ if (ret) { ++ MON_PRINT(" register_netdevice failed (%d)\n", ret); ++ goto out; ++ } ++ ++ *new_ndev = ndev; ++ g_monitor.mon_if[idx].radiotap_enabled = TRUE; ++ g_monitor.mon_if[idx].mon_ndev = ndev; ++ g_monitor.mon_if[idx].real_ndev = lookup_real_netdev(name); ++ dhd_mon = (dhd_linux_monitor_t **)netdev_priv(ndev); ++ *dhd_mon = &g_monitor; ++ g_monitor.monitor_state = MONITOR_STATE_INTERFACE_ADDED; ++ MON_PRINT("net device returned: 0x%p\n", ndev); ++ MON_PRINT("found a matched net device, name %s\n", g_monitor.mon_if[idx].real_ndev->name); ++ ++out: ++ if (ret && ndev) ++ free_netdev(ndev); ++ ++ mutex_unlock(&g_monitor.lock); ++ return ret; ++ ++} ++ ++int dhd_del_monitor(struct net_device *ndev) ++{ ++ int i; ++ bool rollback_lock = false; ++ if (!ndev) ++ return -EINVAL; ++ mutex_lock(&g_monitor.lock); ++ for (i = 0; i < DHD_MAX_IFS; i++) { ++ if (g_monitor.mon_if[i].mon_ndev == ndev || ++ g_monitor.mon_if[i].real_ndev == ndev) { ++ g_monitor.mon_if[i].real_ndev = NULL; ++ if (rtnl_is_locked()) { ++ rtnl_unlock(); ++ rollback_lock = true; ++ } ++ unregister_netdev(g_monitor.mon_if[i].mon_ndev); ++ free_netdev(g_monitor.mon_if[i].mon_ndev); ++ g_monitor.mon_if[i].mon_ndev = NULL; ++ g_monitor.monitor_state = MONITOR_STATE_INTERFACE_DELETED; ++ break; ++ } ++ } ++ if (rollback_lock) { ++ rtnl_lock(); ++ rollback_lock = false; ++ } ++ ++ if (g_monitor.monitor_state != ++ MONITOR_STATE_INTERFACE_DELETED) ++ MON_PRINT("interface not found in monitor IF array, is this a monitor IF? 0x%p\n", ++ ndev); ++ mutex_unlock(&g_monitor.lock); ++ ++ return 0; ++} ++ ++int dhd_monitor_init(void *dhd_pub) ++{ ++ if (g_monitor.monitor_state == MONITOR_STATE_DEINIT) { ++ g_monitor.dhd_pub = dhd_pub; ++ mutex_init(&g_monitor.lock); ++ g_monitor.monitor_state = MONITOR_STATE_INIT; ++ } ++ return 0; ++} ++ ++int dhd_monitor_uninit(void) ++{ ++ int i; ++ struct net_device *ndev; ++ bool rollback_lock = false; ++ mutex_lock(&g_monitor.lock); ++ if (g_monitor.monitor_state != MONITOR_STATE_DEINIT) { ++ for (i = 0; i < DHD_MAX_IFS; i++) { ++ ndev = g_monitor.mon_if[i].mon_ndev; ++ if (ndev) { ++ if (rtnl_is_locked()) { ++ rtnl_unlock(); ++ rollback_lock = true; ++ } ++ unregister_netdev(ndev); ++ free_netdev(ndev); ++ g_monitor.mon_if[i].real_ndev = NULL; ++ g_monitor.mon_if[i].mon_ndev = NULL; ++ if (rollback_lock) { ++ rtnl_lock(); ++ rollback_lock = false; ++ } ++ } ++ } ++ g_monitor.monitor_state = MONITOR_STATE_DEINIT; ++ } ++ mutex_unlock(&g_monitor.lock); ++ return 0; ++} +diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c +new file mode 100644 +index 00000000..18008e0b +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/wldev_common.c +@@ -0,0 +1,384 @@ ++/* ++ * Common function shared by Linux WEXT, cfg80211 and p2p drivers ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wldev_common.c,v 1.1.4.1.2.14 2011-02-09 01:40:07 $ ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define htod32(i) i ++#define htod16(i) i ++#define dtoh32(i) i ++#define dtoh16(i) i ++#define htodchanspec(i) i ++#define dtohchanspec(i) i ++ ++#define WLDEV_ERROR(args) \ ++ do { \ ++ printk(KERN_ERR "WLDEV-ERROR) %s : ", __func__); \ ++ printk args; \ ++ } while (0) ++ ++extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd); ++ ++s32 wldev_ioctl( ++ struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set) ++{ ++ s32 ret = 0; ++ struct wl_ioctl ioc; ++ ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ioc.cmd = cmd; ++ ioc.buf = arg; ++ ioc.len = len; ++ ioc.set = set; ++ ++ ret = dhd_ioctl_entry_local(dev, &ioc, cmd); ++ ++ return ret; ++} ++ ++/* Format a iovar buffer, not bsscfg indexed. The bsscfg index will be ++ * taken care of in dhd_ioctl_entry. Internal use only, not exposed to ++ * wl_iw, wl_cfg80211 and wl_cfgp2p ++ */ ++static s32 wldev_mkiovar( ++ s8 *iovar_name, s8 *param, s32 paramlen, ++ s8 *iovar_buf, u32 buflen) ++{ ++ s32 iolen = 0; ++ ++ iolen = bcm_mkiovar(iovar_name, param, paramlen, iovar_buf, buflen); ++ return iolen; ++} ++ ++s32 wldev_iovar_getbuf( ++ struct net_device *dev, s8 *iovar_name, ++ void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) ++{ ++ s32 ret = 0; ++ if (buf_sync) { ++ mutex_lock(buf_sync); ++ } ++ wldev_mkiovar(iovar_name, param, paramlen, buf, buflen); ++ ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE); ++ if (buf_sync) ++ mutex_unlock(buf_sync); ++ return ret; ++} ++ ++ ++s32 wldev_iovar_setbuf( ++ struct net_device *dev, s8 *iovar_name, ++ void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) ++{ ++ s32 ret = 0; ++ s32 iovar_len; ++ if (buf_sync) { ++ mutex_lock(buf_sync); ++ } ++ iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen); ++ if (iovar_len > 0) ++ ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE); ++ else ++ ret = BCME_BUFTOOSHORT; ++ if (buf_sync) ++ mutex_unlock(buf_sync); ++ return ret; ++} ++ ++s32 wldev_iovar_setint( ++ struct net_device *dev, s8 *iovar, s32 val) ++{ ++ s8 iovar_buf[WLC_IOCTL_SMLEN]; ++ ++ val = htod32(val); ++ memset(iovar_buf, 0, sizeof(iovar_buf)); ++ return wldev_iovar_setbuf(dev, iovar, &val, sizeof(val), iovar_buf, ++ sizeof(iovar_buf), NULL); ++} ++ ++ ++s32 wldev_iovar_getint( ++ struct net_device *dev, s8 *iovar, s32 *pval) ++{ ++ s8 iovar_buf[WLC_IOCTL_SMLEN]; ++ s32 err; ++ ++ memset(iovar_buf, 0, sizeof(iovar_buf)); ++ err = wldev_iovar_getbuf(dev, iovar, pval, sizeof(*pval), iovar_buf, ++ sizeof(iovar_buf), NULL); ++ if (err == 0) ++ { ++ memcpy(pval, iovar_buf, sizeof(*pval)); ++ *pval = dtoh32(*pval); ++ } ++ return err; ++} ++ ++/** Format a bsscfg indexed iovar buffer. The bsscfg index will be ++ * taken care of in dhd_ioctl_entry. Internal use only, not exposed to ++ * wl_iw, wl_cfg80211 and wl_cfgp2p ++ */ ++s32 wldev_mkiovar_bsscfg( ++ const s8 *iovar_name, s8 *param, s32 paramlen, ++ s8 *iovar_buf, s32 buflen, s32 bssidx) ++{ ++ const s8 *prefix = "bsscfg:"; ++ s8 *p; ++ u32 prefixlen; ++ u32 namelen; ++ u32 iolen; ++ ++ if (bssidx == 0) { ++ return wldev_mkiovar((s8*)iovar_name, (s8 *)param, paramlen, ++ (s8 *) iovar_buf, buflen); ++ } ++ ++ prefixlen = (u32) strlen(prefix); /* lengh of bsscfg prefix */ ++ namelen = (u32) strlen(iovar_name) + 1; /* lengh of iovar name + null */ ++ iolen = prefixlen + namelen + sizeof(u32) + paramlen; ++ ++ if (buflen < 0 || iolen > (u32)buflen) ++ { ++ WLDEV_ERROR(("%s: buffer is too short\n", __FUNCTION__)); ++ return BCME_BUFTOOSHORT; ++ } ++ ++ p = (s8 *)iovar_buf; ++ ++ /* copy prefix, no null */ ++ memcpy(p, prefix, prefixlen); ++ p += prefixlen; ++ ++ /* copy iovar name including null */ ++ memcpy(p, iovar_name, namelen); ++ p += namelen; ++ ++ /* bss config index as first param */ ++ bssidx = htod32(bssidx); ++ memcpy(p, &bssidx, sizeof(u32)); ++ p += sizeof(u32); ++ ++ /* parameter buffer follows */ ++ if (paramlen) ++ memcpy(p, param, paramlen); ++ ++ return iolen; ++ ++} ++ ++s32 wldev_iovar_getbuf_bsscfg( ++ struct net_device *dev, s8 *iovar_name, ++ void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) ++{ ++ s32 ret = 0; ++ if (buf_sync) { ++ mutex_lock(buf_sync); ++ } ++ ++ wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx); ++ ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE); ++ if (buf_sync) { ++ mutex_unlock(buf_sync); ++ } ++ return ret; ++ ++} ++ ++s32 wldev_iovar_setbuf_bsscfg( ++ struct net_device *dev, s8 *iovar_name, ++ void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) ++{ ++ s32 ret = 0; ++ s32 iovar_len; ++ if (buf_sync) { ++ mutex_lock(buf_sync); ++ } ++ iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx); ++ if (iovar_len > 0) ++ ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE); ++ else { ++ ret = BCME_BUFTOOSHORT; ++ } ++ ++ if (buf_sync) { ++ mutex_unlock(buf_sync); ++ } ++ return ret; ++} ++ ++s32 wldev_iovar_setint_bsscfg( ++ struct net_device *dev, s8 *iovar, s32 val, s32 bssidx) ++{ ++ s8 iovar_buf[WLC_IOCTL_SMLEN]; ++ ++ val = htod32(val); ++ memset(iovar_buf, 0, sizeof(iovar_buf)); ++ return wldev_iovar_setbuf_bsscfg(dev, iovar, &val, sizeof(val), iovar_buf, ++ sizeof(iovar_buf), bssidx, NULL); ++} ++ ++ ++s32 wldev_iovar_getint_bsscfg( ++ struct net_device *dev, s8 *iovar, s32 *pval, s32 bssidx) ++{ ++ s8 iovar_buf[WLC_IOCTL_SMLEN]; ++ s32 err; ++ ++ memset(iovar_buf, 0, sizeof(iovar_buf)); ++ err = wldev_iovar_getbuf_bsscfg(dev, iovar, pval, sizeof(*pval), iovar_buf, ++ sizeof(iovar_buf), bssidx, NULL); ++ if (err == 0) ++ { ++ memcpy(pval, iovar_buf, sizeof(*pval)); ++ *pval = dtoh32(*pval); ++ } ++ return err; ++} ++ ++int wldev_get_link_speed( ++ struct net_device *dev, int *plink_speed) ++{ ++ int error; ++ ++ if (!plink_speed) ++ return -ENOMEM; ++ error = wldev_ioctl(dev, WLC_GET_RATE, plink_speed, sizeof(int), 0); ++ if (unlikely(error)) ++ return error; ++ ++ /* Convert internal 500Kbps to Kbps */ ++ *plink_speed *= 500; ++ return error; ++} ++ ++int wldev_get_rssi( ++ struct net_device *dev, int *prssi) ++{ ++ scb_val_t scb_val; ++ int error; ++ ++ if (!prssi) ++ return -ENOMEM; ++ bzero(&scb_val, sizeof(scb_val_t)); ++ ++ error = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t), 0); ++ if (unlikely(error)) ++ return error; ++ ++ *prssi = dtoh32(scb_val.val); ++ return error; ++} ++ ++int wldev_get_ssid( ++ struct net_device *dev, wlc_ssid_t *pssid) ++{ ++ int error; ++ ++ if (!pssid) ++ return -ENOMEM; ++ error = wldev_ioctl(dev, WLC_GET_SSID, pssid, sizeof(wlc_ssid_t), 0); ++ if (unlikely(error)) ++ return error; ++ pssid->SSID_len = dtoh32(pssid->SSID_len); ++ return error; ++} ++ ++int wldev_get_band( ++ struct net_device *dev, uint *pband) ++{ ++ int error; ++ ++ error = wldev_ioctl(dev, WLC_GET_BAND, pband, sizeof(uint), 0); ++ return error; ++} ++ ++int wldev_set_band( ++ struct net_device *dev, uint band) ++{ ++ int error = -1; ++ ++ if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) { ++ error = wldev_ioctl(dev, WLC_SET_BAND, &band, sizeof(band), true); ++ if (!error) ++ dhd_bus_band_set(dev, band); ++ } ++ return error; ++} ++ ++int wldev_set_country( ++ struct net_device *dev, char *country_code, bool notify, bool user_enforced) ++{ ++ int error = -1; ++ wl_country_t cspec = {{0}, 0, {0}}; ++ scb_val_t scbval; ++ char smbuf[WLC_IOCTL_SMLEN]; ++ ++ if (!country_code) ++ return error; ++ ++ bzero(&scbval, sizeof(scb_val_t)); ++ error = wldev_iovar_getbuf(dev, "country", NULL, 0, &cspec, sizeof(cspec), NULL); ++ if (error < 0) { ++ WLDEV_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error)); ++ return error; ++ } ++ ++ if ((error < 0) || ++ (strncmp(country_code, cspec.ccode, WLC_CNTRY_BUF_SZ) != 0)) { ++ ++ if (user_enforced) { ++ bzero(&scbval, sizeof(scb_val_t)); ++ error = wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true); ++ if (error < 0) { ++ WLDEV_ERROR(("%s: set country failed due to Disassoc error %d\n", ++ __FUNCTION__, error)); ++ return error; ++ } ++ } ++ ++ cspec.rev = -1; ++ memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ); ++ memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); ++ get_customized_country_code((char *)&cspec.country_abbrev, &cspec); ++ error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec), ++ smbuf, sizeof(smbuf), NULL); ++ if (error < 0) { ++ WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n", ++ __FUNCTION__, country_code, cspec.ccode, cspec.rev)); ++ return error; ++ } ++ dhd_bus_country_set(dev, &cspec, notify); ++ WLDEV_ERROR(("%s: set country for %s as %s rev %d\n", ++ __FUNCTION__, country_code, cspec.ccode, cspec.rev)); ++ } ++ return 0; ++} +diff --git a/drivers/net/wireless/bcmdhd/wldev_common.h b/drivers/net/wireless/bcmdhd/wldev_common.h +new file mode 100644 +index 00000000..e63620fe +--- /dev/null ++++ b/drivers/net/wireless/bcmdhd/wldev_common.h +@@ -0,0 +1,112 @@ ++/* ++ * Common function shared by Linux WEXT, cfg80211 and p2p drivers ++ * ++ * Copyright (C) 1999-2012, Broadcom Corporation ++ * ++ * Unless you and Broadcom execute a separate written software license ++ * agreement governing use of this software, this software is licensed to you ++ * under the terms of the GNU General Public License version 2 (the "GPL"), ++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the ++ * following added to such license: ++ * ++ * As a special exception, the copyright holders of this software give you ++ * permission to link this software with independent modules, and to copy and ++ * distribute the resulting executable under terms of your choice, provided that ++ * you also meet, for each linked independent module, the terms and conditions of ++ * the license of that module. An independent module is a module which is not ++ * derived from this software. The special exception does not apply to any ++ * modifications of the software. ++ * ++ * Notwithstanding the above, under no circumstances may you combine this ++ * software in any way with any other Broadcom software provided under a license ++ * other than the GPL, without Broadcom's express prior written consent. ++ * ++ * $Id: wldev_common.h,v 1.1.4.1.2.14 2011-02-09 01:40:07 $ ++ */ ++#ifndef __WLDEV_COMMON_H__ ++#define __WLDEV_COMMON_H__ ++ ++#include ++ ++/* wl_dev_ioctl - get/set IOCTLs, will call net_device's do_ioctl (or ++ * netdev_ops->ndo_do_ioctl in new kernels) ++ * @dev: the net_device handle ++ */ ++s32 wldev_ioctl( ++ struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set); ++ ++/** Retrieve named IOVARs, this function calls wl_dev_ioctl with ++ * WLC_GET_VAR IOCTL code ++ */ ++s32 wldev_iovar_getbuf( ++ struct net_device *dev, s8 *iovar_name, ++ void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync); ++ ++/** Set named IOVARs, this function calls wl_dev_ioctl with ++ * WLC_SET_VAR IOCTL code ++ */ ++s32 wldev_iovar_setbuf( ++ struct net_device *dev, s8 *iovar_name, ++ void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync); ++ ++s32 wldev_iovar_setint( ++ struct net_device *dev, s8 *iovar, s32 val); ++ ++s32 wldev_iovar_getint( ++ struct net_device *dev, s8 *iovar, s32 *pval); ++ ++/** The following function can be implemented if there is a need for bsscfg ++ * indexed IOVARs ++ */ ++ ++s32 wldev_mkiovar_bsscfg( ++ const s8 *iovar_name, s8 *param, s32 paramlen, ++ s8 *iovar_buf, s32 buflen, s32 bssidx); ++ ++/** Retrieve named and bsscfg indexed IOVARs, this function calls wl_dev_ioctl with ++ * WLC_GET_VAR IOCTL code ++ */ ++s32 wldev_iovar_getbuf_bsscfg( ++ struct net_device *dev, s8 *iovar_name, void *param, s32 paramlen, ++ void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync); ++ ++/** Set named and bsscfg indexed IOVARs, this function calls wl_dev_ioctl with ++ * WLC_SET_VAR IOCTL code ++ */ ++s32 wldev_iovar_setbuf_bsscfg( ++ struct net_device *dev, s8 *iovar_name, void *param, s32 paramlen, ++ void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync); ++ ++s32 wldev_iovar_getint_bsscfg( ++ struct net_device *dev, s8 *iovar, s32 *pval, s32 bssidx); ++ ++s32 wldev_iovar_setint_bsscfg( ++ struct net_device *dev, s8 *iovar, s32 val, s32 bssidx); ++ ++extern void get_customized_country_code(char *country_iso_code, wl_country_t *cspec); ++extern void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec, bool notify); ++extern void dhd_bus_band_set(struct net_device *dev, uint band); ++extern int wldev_set_country(struct net_device *dev, char *country_code, bool notify, ++ bool user_enforced); ++extern int net_os_wake_lock(struct net_device *dev); ++extern int net_os_wake_unlock(struct net_device *dev); ++extern int net_os_wake_lock_timeout(struct net_device *dev); ++extern int net_os_wake_lock_timeout_enable(struct net_device *dev, int val); ++extern int net_os_set_dtim_skip(struct net_device *dev, int val); ++extern int net_os_set_suspend_disable(struct net_device *dev, int val); ++extern int net_os_set_suspend(struct net_device *dev, int val, int force); ++extern int wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, ++ int max, int *bytes_left); ++ ++/* Get the link speed from dongle, speed is in kpbs */ ++int wldev_get_link_speed(struct net_device *dev, int *plink_speed); ++ ++int wldev_get_rssi(struct net_device *dev, int *prssi); ++ ++int wldev_get_ssid(struct net_device *dev, wlc_ssid_t *pssid); ++ ++int wldev_get_band(struct net_device *dev, uint *pband); ++ ++int wldev_set_band(struct net_device *dev, uint band); ++ ++#endif /* __WLDEV_COMMON_H__ */ +diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig +index 99dc29f2..a86498ba 100644 +--- a/drivers/power/Kconfig ++++ b/drivers/power/Kconfig +@@ -190,6 +190,16 @@ config BATTERY_MAX17042 + multi-function devices that include fuel gauages that are compatible + with MAX17042. + ++config BATTERY_ANDROID ++ tristate "Battery driver for Android" ++ help ++ Say Y to enable generic support for battery charging according ++ to common Android policies. ++ This driver adds periodic battery level and health monitoring, ++ kernel log reporting and other debugging features, common board ++ battery file glue logic for battery/case temperature sensors, ++ etc. ++ + config BATTERY_Z2 + tristate "Z2 battery driver" + depends on I2C && MACH_ZIPIT2 +@@ -307,4 +317,12 @@ config AB8500_BATTERY_THERM_ON_BATCTRL + help + Say Y to enable battery temperature measurements using + thermistor connected on BATCTRL ADC. ++ ++config BATTERY_AK ++ tristate "Anyka Battery driver support" ++ depends on ARCH_AK39 ++ help ++ Say Y to enable support for the anyka battery from SOCLE. ++ ++ + endif # POWER_SUPPLY +diff --git a/drivers/power/Makefile b/drivers/power/Makefile +index b6b24341..07e3946a 100644 +--- a/drivers/power/Makefile ++++ b/drivers/power/Makefile +@@ -44,3 +44,5 @@ obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o + obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o + obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o + obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o ++obj-$(CONFIG_BATTERY_ANDROID) += android_battery.o ++obj-$(CONFIG_BATTERY_AK) += plat-anyka/ +diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c +new file mode 100644 +index 00000000..ee437d08 +--- /dev/null ++++ b/drivers/power/android_battery.c +@@ -0,0 +1,692 @@ ++/* ++ * android_battery.c ++ * Android Battery Driver ++ * ++ * Copyright (C) 2012 Google, Inc. ++ * Copyright (C) 2012 Samsung Electronics ++ * ++ * Based on work by himihee.seo@samsung.com, ms925.kim@samsung.com, and ++ * joshua.chang@samsung.com. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define FAST_POLL (1 * 60) ++#define SLOW_POLL (10 * 60) ++ ++struct android_bat_data { ++ struct android_bat_platform_data *pdata; ++ struct android_bat_callbacks callbacks; ++ ++ struct device *dev; ++ ++ struct power_supply psy_bat; ++ ++ struct wake_lock monitor_wake_lock; ++ struct wake_lock charger_wake_lock; ++ ++ int charge_source; ++ ++ int batt_temp; ++ int batt_current; ++ unsigned int batt_health; ++ unsigned int batt_vcell; ++ unsigned int batt_soc; ++ unsigned int charging_status; ++ bool recharging; ++ unsigned long charging_start_time; ++ ++ struct workqueue_struct *monitor_wqueue; ++ struct work_struct monitor_work; ++ struct work_struct charger_work; ++ ++ struct alarm monitor_alarm; ++ ktime_t last_poll; ++ ++ struct dentry *debugfs_entry; ++}; ++ ++static enum power_supply_property android_battery_props[] = { ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_HEALTH, ++ POWER_SUPPLY_PROP_PRESENT, ++ POWER_SUPPLY_PROP_TEMP, ++ POWER_SUPPLY_PROP_ONLINE, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_CAPACITY, ++ POWER_SUPPLY_PROP_TECHNOLOGY, ++ POWER_SUPPLY_PROP_CURRENT_NOW, ++}; ++ ++static DEFINE_MUTEX(android_bat_state_lock); ++ ++static void android_bat_update_data(struct android_bat_data *battery); ++static int android_bat_enable_charging(struct android_bat_data *battery, ++ bool enable); ++ ++static char *charge_source_str(int charge_source) ++{ ++ switch (charge_source) { ++ case CHARGE_SOURCE_NONE: ++ return "none"; ++ case CHARGE_SOURCE_AC: ++ return "ac"; ++ case CHARGE_SOURCE_USB: ++ return "usb"; ++ default: ++ break; ++ } ++ ++ return "?"; ++} ++ ++static int android_bat_get_property(struct power_supply *ps, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct android_bat_data *battery = ++ container_of(ps, struct android_bat_data, psy_bat); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_STATUS: ++ val->intval = battery->charging_status; ++ break; ++ case POWER_SUPPLY_PROP_HEALTH: ++ val->intval = battery->batt_health; ++ break; ++ case POWER_SUPPLY_PROP_PRESENT: ++ val->intval = 1; ++ break; ++ case POWER_SUPPLY_PROP_TEMP: ++ val->intval = battery->batt_temp; ++ break; ++ case POWER_SUPPLY_PROP_ONLINE: ++ val->intval = 1; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ android_bat_update_data(battery); ++ val->intval = battery->batt_vcell; ++ if (val->intval == -1) ++ return -EINVAL; ++ break; ++ case POWER_SUPPLY_PROP_CAPACITY: ++ val->intval = battery->batt_soc; ++ if (val->intval == -1) ++ return -EINVAL; ++ break; ++ case POWER_SUPPLY_PROP_TECHNOLOGY: ++ val->intval = POWER_SUPPLY_TECHNOLOGY_LION; ++ break; ++ case POWER_SUPPLY_PROP_CURRENT_NOW: ++ android_bat_update_data(battery); ++ val->intval = battery->batt_current; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static void android_bat_get_temp(struct android_bat_data *battery) ++{ ++ int batt_temp = 42; /* 4.2C */ ++ int health = battery->batt_health; ++ ++ if (battery->pdata->get_temperature) ++ battery->pdata->get_temperature(&batt_temp); ++ ++ if (battery->charge_source != CHARGE_SOURCE_NONE) { ++ if (batt_temp >= battery->pdata->temp_high_threshold) { ++ if (health != POWER_SUPPLY_HEALTH_OVERHEAT && ++ health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { ++ pr_info("battery overheat (%d>=%d), " \ ++ "charging unavailable\n", ++ batt_temp, ++ battery->pdata->temp_high_threshold); ++ battery->batt_health = ++ POWER_SUPPLY_HEALTH_OVERHEAT; ++ } ++ } else if (batt_temp <= battery->pdata->temp_high_recovery && ++ batt_temp >= battery->pdata->temp_low_recovery) { ++ if (health == POWER_SUPPLY_HEALTH_OVERHEAT || ++ health == POWER_SUPPLY_HEALTH_COLD) { ++ pr_info("battery recovery (%d,%d~%d)," \ ++ "charging available\n", ++ batt_temp, ++ battery->pdata->temp_low_recovery, ++ battery->pdata->temp_high_recovery); ++ battery->batt_health = ++ POWER_SUPPLY_HEALTH_GOOD; ++ } ++ } else if (batt_temp <= battery->pdata->temp_low_threshold) { ++ if (health != POWER_SUPPLY_HEALTH_COLD && ++ health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { ++ pr_info("battery cold (%d <= %d)," \ ++ "charging unavailable\n", ++ batt_temp, ++ battery->pdata->temp_low_threshold); ++ battery->batt_health = ++ POWER_SUPPLY_HEALTH_COLD; ++ } ++ } ++ } ++ ++ battery->batt_temp = batt_temp; ++} ++ ++/* ++ * android_bat_state_lock not held, may call back into ++ * android_bat_charge_source_changed. Gathering data here can be ++ * non-atomic; updating our state based on the data may need to be ++ * atomic. ++ */ ++ ++static void android_bat_update_data(struct android_bat_data *battery) ++{ ++ int ret; ++ int v; ++ ++ if (battery->pdata->poll_charge_source) ++ battery->charge_source = battery->pdata->poll_charge_source(); ++ ++ if (battery->pdata->get_voltage_now) { ++ ret = battery->pdata->get_voltage_now(); ++ battery->batt_vcell = ret >= 0 ? ret : 4242000; ++ } ++ ++ if (battery->pdata->get_capacity) { ++ ret = battery->pdata->get_capacity(); ++ battery->batt_soc = ret >= 0 ? ret : 42; ++ } ++ ++ if (battery->pdata->get_current_now) { ++ ret = battery->pdata->get_current_now(&v); ++ ++ if (!ret) ++ battery->batt_current = v; ++ } ++ ++ android_bat_get_temp(battery); ++} ++ ++static void android_bat_set_charge_time(struct android_bat_data *battery, ++ bool enable) ++{ ++ if (enable && !battery->charging_start_time) { ++ struct timespec cur_time; ++ ++ get_monotonic_boottime(&cur_time); ++ /* record start time for charge timeout timer */ ++ battery->charging_start_time = cur_time.tv_sec; ++ } else if (!enable) { ++ /* clear charge timeout timer */ ++ battery->charging_start_time = 0; ++ } ++} ++ ++static int android_bat_enable_charging(struct android_bat_data *battery, ++ bool enable) ++{ ++ if (enable && (battery->batt_health != POWER_SUPPLY_HEALTH_GOOD)) { ++ battery->charging_status = ++ POWER_SUPPLY_STATUS_NOT_CHARGING; ++ return -EPERM; ++ } ++ ++ if (enable) { ++ if (battery->pdata && battery->pdata->set_charging_current) ++ battery->pdata->set_charging_current ++ (battery->charge_source); ++ } ++ ++ if (battery->pdata && battery->pdata->set_charging_enable) ++ battery->pdata->set_charging_enable(enable); ++ ++ android_bat_set_charge_time(battery, enable); ++ pr_info("battery: enable=%d charger: %s\n", enable, ++ charge_source_str(battery->charge_source)); ++ return 0; ++} ++ ++static bool android_bat_charge_timeout(struct android_bat_data *battery, ++ unsigned long timeout) ++{ ++ struct timespec cur_time; ++ ++ if (!battery->charging_start_time) ++ return 0; ++ ++ get_monotonic_boottime(&cur_time); ++ pr_debug("%s: Start time: %ld, End time: %ld, current time: %ld\n", ++ __func__, battery->charging_start_time, ++ battery->charging_start_time + timeout, ++ cur_time.tv_sec); ++ return cur_time.tv_sec >= battery->charging_start_time + timeout; ++} ++ ++static void android_bat_charging_timer(struct android_bat_data *battery) ++{ ++ if (!battery->charging_start_time && ++ battery->charging_status == POWER_SUPPLY_STATUS_CHARGING) { ++ android_bat_enable_charging(battery, true); ++ battery->recharging = true; ++ pr_debug("%s: charge status charging but timer is expired\n", ++ __func__); ++ } else if (battery->charging_start_time == 0) { ++ pr_debug("%s: charging_start_time never initialized\n", ++ __func__); ++ return; ++ } ++ ++ if (android_bat_charge_timeout( ++ battery, ++ battery->recharging ? battery->pdata->recharging_time : ++ battery->pdata->full_charging_time)) { ++ android_bat_enable_charging(battery, false); ++ if (battery->batt_vcell > ++ battery->pdata->recharging_voltage && ++ battery->batt_soc == 100) ++ battery->charging_status = ++ POWER_SUPPLY_STATUS_FULL; ++ battery->recharging = false; ++ battery->charging_start_time = 0; ++ pr_info("battery: charging timer expired\n"); ++ } ++ ++ return; ++} ++ ++static void android_bat_charge_source_changed(struct android_bat_callbacks *ptr, ++ int charge_source) ++{ ++ struct android_bat_data *battery = ++ container_of(ptr, struct android_bat_data, callbacks); ++ ++ wake_lock(&battery->charger_wake_lock); ++ mutex_lock(&android_bat_state_lock); ++ battery->charge_source = charge_source; ++ ++ pr_info("battery: charge source type was changed: %s\n", ++ charge_source_str(battery->charge_source)); ++ ++ mutex_unlock(&android_bat_state_lock); ++ queue_work(battery->monitor_wqueue, &battery->charger_work); ++} ++ ++static void android_bat_set_full_status(struct android_bat_callbacks *ptr) ++{ ++ struct android_bat_data *battery = ++ container_of(ptr, struct android_bat_data, callbacks); ++ ++ mutex_lock(&android_bat_state_lock); ++ pr_info("battery: battery full\n"); ++ battery->charging_status = POWER_SUPPLY_STATUS_FULL; ++ android_bat_enable_charging(battery, false); ++ battery->recharging = false; ++ mutex_unlock(&android_bat_state_lock); ++ power_supply_changed(&battery->psy_bat); ++} ++ ++static void android_bat_charger_work(struct work_struct *work) ++{ ++ struct android_bat_data *battery = ++ container_of(work, struct android_bat_data, charger_work); ++ ++ mutex_lock(&android_bat_state_lock); ++ ++ switch (battery->charge_source) { ++ case CHARGE_SOURCE_NONE: ++ battery->charging_status = POWER_SUPPLY_STATUS_DISCHARGING; ++ android_bat_enable_charging(battery, false); ++ battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; ++ battery->recharging = false; ++ battery->charging_start_time = 0; ++ break; ++ case CHARGE_SOURCE_USB: ++ case CHARGE_SOURCE_AC: ++ /* ++ * If charging status indicates a charger was already ++ * connected prior to this and the status is something ++ * other than charging ("full" or "not-charging"), leave ++ * the status alone. ++ */ ++ if (battery->charging_status == ++ POWER_SUPPLY_STATUS_DISCHARGING || ++ battery->charging_status == POWER_SUPPLY_STATUS_UNKNOWN) ++ battery->charging_status = POWER_SUPPLY_STATUS_CHARGING; ++ ++ /* ++ * Don't re-enable charging if the battery is full and we ++ * are not actively re-charging it, or if "not-charging" ++ * status is set. ++ */ ++ if (!((battery->charging_status == POWER_SUPPLY_STATUS_FULL ++ && !battery->recharging) || battery->charging_status == ++ POWER_SUPPLY_STATUS_NOT_CHARGING)) ++ android_bat_enable_charging(battery, true); ++ ++ break; ++ default: ++ pr_err("%s: Invalid charger type\n", __func__); ++ break; ++ } ++ ++ mutex_unlock(&android_bat_state_lock); ++ wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); ++ power_supply_changed(&battery->psy_bat); ++} ++ ++ ++static void android_bat_monitor_set_alarm(struct android_bat_data *battery, ++ int seconds) ++{ ++ alarm_start(&battery->monitor_alarm, ++ ktime_add(battery->last_poll, ktime_set(seconds, 0))); ++} ++ ++static void android_bat_monitor_work(struct work_struct *work) ++{ ++ struct android_bat_data *battery = ++ container_of(work, struct android_bat_data, monitor_work); ++ struct timespec cur_time; ++ ++ wake_lock(&battery->monitor_wake_lock); ++ android_bat_update_data(battery); ++ mutex_lock(&android_bat_state_lock); ++ ++ switch (battery->charging_status) { ++ case POWER_SUPPLY_STATUS_FULL: ++ if (battery->batt_vcell < battery->pdata->recharging_voltage && ++ !battery->recharging) { ++ battery->recharging = true; ++ android_bat_enable_charging(battery, true); ++ pr_info("battery: start recharging, v=%d\n", ++ battery->batt_vcell/1000); ++ } ++ break; ++ case POWER_SUPPLY_STATUS_DISCHARGING: ++ break; ++ case POWER_SUPPLY_STATUS_CHARGING: ++ switch (battery->batt_health) { ++ case POWER_SUPPLY_HEALTH_OVERHEAT: ++ case POWER_SUPPLY_HEALTH_COLD: ++ case POWER_SUPPLY_HEALTH_OVERVOLTAGE: ++ case POWER_SUPPLY_HEALTH_DEAD: ++ case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: ++ battery->charging_status = ++ POWER_SUPPLY_STATUS_NOT_CHARGING; ++ android_bat_enable_charging(battery, false); ++ ++ pr_info("battery: Not charging, health=%d\n", ++ battery->batt_health); ++ break; ++ default: ++ break; ++ } ++ break; ++ case POWER_SUPPLY_STATUS_NOT_CHARGING: ++ if (battery->batt_health == POWER_SUPPLY_HEALTH_GOOD) { ++ pr_info("battery: battery health recovered\n"); ++ if (battery->charge_source != CHARGE_SOURCE_NONE) { ++ android_bat_enable_charging(battery, true); ++ battery->charging_status ++ = POWER_SUPPLY_STATUS_CHARGING; ++ } else { ++ battery->charging_status ++ = POWER_SUPPLY_STATUS_DISCHARGING; ++ } ++ } ++ break; ++ default: ++ pr_err("%s: Undefined battery status: %d\n", __func__, ++ battery->charging_status); ++ break; ++ } ++ ++ android_bat_charging_timer(battery); ++ get_monotonic_boottime(&cur_time); ++ pr_info("battery: l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", ++ battery->batt_soc, battery->batt_vcell/1000, ++ battery->batt_current, battery->batt_temp < 0 ? "-" : "", ++ abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), ++ battery->batt_health, battery->charging_status, ++ battery->recharging ? "r" : "", ++ battery->charging_start_time ? ++ cur_time.tv_sec - battery->charging_start_time : 0, ++ charge_source_str(battery->charge_source)); ++ mutex_unlock(&android_bat_state_lock); ++ power_supply_changed(&battery->psy_bat); ++ battery->last_poll = ktime_get_boottime(); ++ android_bat_monitor_set_alarm(battery, FAST_POLL); ++ wake_unlock(&battery->monitor_wake_lock); ++ return; ++} ++ ++static enum alarmtimer_restart android_bat_monitor_alarm( ++ struct alarm *alarm, ktime_t now) ++{ ++ struct android_bat_data *battery = ++ container_of(alarm, struct android_bat_data, monitor_alarm); ++ ++ wake_lock(&battery->monitor_wake_lock); ++ queue_work(battery->monitor_wqueue, &battery->monitor_work); ++ return ALARMTIMER_NORESTART; ++} ++ ++static int android_power_debug_dump(struct seq_file *s, void *unused) ++{ ++ struct android_bat_data *battery = s->private; ++ struct timespec cur_time; ++ ++ android_bat_update_data(battery); ++ get_monotonic_boottime(&cur_time); ++ mutex_lock(&android_bat_state_lock); ++ seq_printf(s, "l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", ++ battery->batt_soc, battery->batt_vcell/1000, ++ battery->batt_current, battery->batt_temp < 0 ? "-" : "", ++ abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), ++ battery->batt_health, battery->charging_status, ++ battery->recharging ? "r" : "", ++ battery->charging_start_time ? ++ cur_time.tv_sec - battery->charging_start_time : 0, ++ charge_source_str(battery->charge_source)); ++ mutex_unlock(&android_bat_state_lock); ++ return 0; ++} ++ ++static int android_power_debug_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, android_power_debug_dump, inode->i_private); ++} ++ ++static const struct file_operations android_power_debug_fops = { ++ .open = android_power_debug_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static __devinit int android_bat_probe(struct platform_device *pdev) ++{ ++ struct android_bat_platform_data *pdata = dev_get_platdata(&pdev->dev); ++ struct android_bat_data *battery; ++ int ret = 0; ++ ++ dev_info(&pdev->dev, "Android Battery Driver\n"); ++ battery = kzalloc(sizeof(*battery), GFP_KERNEL); ++ if (!battery) ++ return -ENOMEM; ++ ++ battery->pdata = pdata; ++ if (!battery->pdata) { ++ pr_err("%s : No platform data\n", __func__); ++ ret = -EINVAL; ++ goto err_pdata; ++ } ++ ++ battery->dev = &pdev->dev; ++ platform_set_drvdata(pdev, battery); ++ battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; ++ ++ battery->psy_bat.name = "android-battery", ++ battery->psy_bat.type = POWER_SUPPLY_TYPE_BATTERY, ++ battery->psy_bat.properties = android_battery_props, ++ battery->psy_bat.num_properties = ARRAY_SIZE(android_battery_props), ++ battery->psy_bat.get_property = android_bat_get_property, ++ ++ battery->batt_vcell = -1; ++ battery->batt_soc = -1; ++ ++ wake_lock_init(&battery->monitor_wake_lock, WAKE_LOCK_SUSPEND, ++ "android-battery-monitor"); ++ wake_lock_init(&battery->charger_wake_lock, WAKE_LOCK_SUSPEND, ++ "android-chargerdetect"); ++ ++ ret = power_supply_register(&pdev->dev, &battery->psy_bat); ++ if (ret) { ++ dev_err(battery->dev, "%s: failed to register psy_bat\n", ++ __func__); ++ goto err_psy_bat_reg; ++ } ++ ++ battery->monitor_wqueue = ++ alloc_workqueue(dev_name(&pdev->dev), WQ_FREEZABLE, 1); ++ if (!battery->monitor_wqueue) { ++ dev_err(battery->dev, "%s: fail to create workqueue\n", ++ __func__); ++ goto err_wq; ++ } ++ ++ INIT_WORK(&battery->monitor_work, android_bat_monitor_work); ++ INIT_WORK(&battery->charger_work, android_bat_charger_work); ++ ++ battery->callbacks.charge_source_changed = ++ android_bat_charge_source_changed; ++ battery->callbacks.battery_set_full = ++ android_bat_set_full_status; ++ if (battery->pdata && battery->pdata->register_callbacks) ++ battery->pdata->register_callbacks(&battery->callbacks); ++ ++ /* get initial charger status */ ++ if (battery->pdata->poll_charge_source) ++ battery->charge_source = battery->pdata->poll_charge_source(); ++ ++ wake_lock(&battery->charger_wake_lock); ++ queue_work(battery->monitor_wqueue, &battery->charger_work); ++ ++ wake_lock(&battery->monitor_wake_lock); ++ battery->last_poll = ktime_get_boottime(); ++ alarm_init(&battery->monitor_alarm, ALARM_BOOTTIME, ++ android_bat_monitor_alarm); ++ queue_work(battery->monitor_wqueue, &battery->monitor_work); ++ ++ battery->debugfs_entry = ++ debugfs_create_file("android-power", S_IRUGO, NULL, ++ battery, &android_power_debug_fops); ++ if (!battery->debugfs_entry) ++ pr_err("failed to create android-power debugfs entry\n"); ++ ++ return 0; ++ ++err_wq: ++ power_supply_unregister(&battery->psy_bat); ++err_psy_bat_reg: ++ wake_lock_destroy(&battery->monitor_wake_lock); ++ wake_lock_destroy(&battery->charger_wake_lock); ++err_pdata: ++ kfree(battery); ++ ++ return ret; ++} ++ ++static int __devexit android_bat_remove(struct platform_device *pdev) ++{ ++ struct android_bat_data *battery = platform_get_drvdata(pdev); ++ ++ alarm_cancel(&battery->monitor_alarm); ++ flush_workqueue(battery->monitor_wqueue); ++ destroy_workqueue(battery->monitor_wqueue); ++ power_supply_unregister(&battery->psy_bat); ++ wake_lock_destroy(&battery->monitor_wake_lock); ++ wake_lock_destroy(&battery->charger_wake_lock); ++ debugfs_remove(battery->debugfs_entry); ++ kfree(battery); ++ return 0; ++} ++ ++static int android_bat_suspend(struct device *dev) ++{ ++ struct android_bat_data *battery = dev_get_drvdata(dev); ++ ++ cancel_work_sync(&battery->monitor_work); ++ android_bat_monitor_set_alarm( ++ battery, ++ battery->charge_source == CHARGE_SOURCE_NONE ? ++ SLOW_POLL : FAST_POLL); ++ return 0; ++} ++ ++static void android_bat_resume(struct device *dev) ++{ ++ struct android_bat_data *battery = dev_get_drvdata(dev); ++ ++ android_bat_monitor_set_alarm(battery, FAST_POLL); ++ return; ++} ++ ++static const struct dev_pm_ops android_bat_pm_ops = { ++ .prepare = android_bat_suspend, ++ .complete = android_bat_resume, ++}; ++ ++static struct platform_driver android_bat_driver = { ++ .driver = { ++ .name = "android-battery", ++ .owner = THIS_MODULE, ++ .pm = &android_bat_pm_ops, ++ }, ++ .probe = android_bat_probe, ++ .remove = __devexit_p(android_bat_remove), ++}; ++ ++static int __init android_bat_init(void) ++{ ++ return platform_driver_register(&android_bat_driver); ++} ++ ++static void __exit android_bat_exit(void) ++{ ++ platform_driver_unregister(&android_bat_driver); ++} ++ ++late_initcall(android_bat_init); ++module_exit(android_bat_exit); ++ ++MODULE_DESCRIPTION("Android battery driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/power/plat-anyka/Makefile b/drivers/power/plat-anyka/Makefile +new file mode 100644 +index 00000000..b8f84617 +--- /dev/null ++++ b/drivers/power/plat-anyka/Makefile +@@ -0,0 +1,5 @@ ++# ++# Makefile for RTC class/drivers. ++# ++ ++obj-$(CONFIG_BATTERY_AK) += battery.o +diff --git a/drivers/power/plat-anyka/battery.c b/drivers/power/plat-anyka/battery.c +new file mode 100755 +index 00000000..60a13269 +--- /dev/null ++++ b/drivers/power/plat-anyka/battery.c +@@ -0,0 +1,1436 @@ ++/* ++ * @file battery.c ++ * @battery driver for ak chip ++ * @Copyright (C) 2010 Anyka (Guangzhou) Microelectronics Technology Co ++ * @author gao_wangsheng ++ * @date 2011-04 ++ * @version 2.0 ++*/ ++ ++/* ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++//#define BATTERY_GET_VOLTAGE ++ ++/* BATTERY DEFINE*/ ++#define UPDATE_DISCHARGE_DELAY (HZ * 6) /* delay 6s */ ++#define UPDATE_CHARGE_DELAY (HZ * 60) /* delay 60s */ ++#define UPDATE_VOLTAGE_DELAY (HZ * 1) /* delay 1s */ ++#define CHECK_PDOWN_DELAY 50 /* delay 50ms */ ++#define CHECK_READ_CNT 10 ++#define CHECK_PDOWN_CNT 5 ++#define CHARGE_FULL 1 ++#define CHARGE_NOT_FULL 0 ++ ++#define PK(fmt...) //printk(fmt) // debug and read ad4 voltage ++#define PK_SAMPLE(fmt...) //printk(fmt) //debug read voltage sample ++#define PK_CHARGE(fmt...) //printk(fmt) //debug charge ac and usb in ++#define PK_DISCHARGE(fmt...) //printk(fmt) //debug discharge ++#define PK_PDOWN(fmt...) //printk(fmt) //debug check power down ++#define get_bat_platform_data(x) ((x)->bat_ps.dev->parent->platform_data) ++ ++struct ak_bat{ ++ struct power_supply bat_ps; ++ struct power_supply ac_ps; ++ struct power_supply usb_ps; ++ struct read_voltage_sample rd_voltage; ++ struct mutex bat_lock; ++ struct timer_list timer; ++ int status; ++ int ac_online; ++ int usb_online; ++ int voltage; ++ int capacity; ++ int pdown_count; ++ int read_count; ++ atomic_t full_flag; ++}; ++ ++static struct delayed_work charge_work; ++static struct delayed_work discharge_work; ++static struct delayed_work voltage_work; ++static struct delayed_work usbirq_work; ++static struct delayed_work acirq_work; ++static struct delayed_work pdown_work; ++static struct delayed_work resume_work; ++static struct ak_bat bat_device; ++ ++static struct notifier_block ac_detect_nb; ++ ++ ++static void ak_bat_update(struct ak_bat *battery); ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief set battery full flag ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void bat_charge_full_timer(unsigned long bat_dev) ++{ ++ struct ak_bat *battery = (void *)bat_dev; ++ ++ if (POWER_SUPPLY_STATUS_CHARGING == battery->status) ++ { ++ atomic_set(&battery->full_flag, CHARGE_FULL); ++ } ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief check capacity ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void bat_check_charge_capacity(struct ak_bat *battery) ++{ ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ unsigned int delay = info->bat_mach_info.full_delay * 60 * 1000; //minute to ms ++ ++ if (POWER_SUPPLY_STATUS_CHARGING != battery->status) ++ { ++ return; ++ } ++ ++ if (atomic_read(&battery->full_flag) == CHARGE_FULL ++ && battery->capacity != info->bat_mach_info.full_capacity) ++ { ++ battery->capacity = info->bat_mach_info.full_capacity; ++ } ++ ++ if (atomic_read(&battery->full_flag) == CHARGE_NOT_FULL ++ && battery->capacity == info->bat_mach_info.full_capacity) ++ { ++ battery->capacity = info->bat_mach_info.full_capacity - 1; ++ ++ if (!timer_pending(&battery->timer)) ++ { ++ mod_timer(&battery->timer,jiffies + msecs_to_jiffies(delay)); ++ } ++ } ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief read battery voltage ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *info ++* @return voltage ++*/ ++int ak_bat_read_voltage(struct ak_bat_mach_info *info) ++{ ++ int voltage; ++ ++ PK("\n###############%s; \n",__func__); ++ ++ // read voltage sample ++ PK("############adc1_read_bat:\n"); ++ voltage = (int)adc1_read_bat(); ++ PK("ad4_voltage = %d:\n",voltage); ++ ++ voltage = voltage ++ * (info->bat_adc.up_resistance + info->bat_adc.dw_resistance) ++ / info->bat_adc.dw_resistance; ++ PK("bat_voltage = %d:\n",voltage); ++ ++ voltage += info->bat_adc.voltage_correct; // correct battery voltage ++ PK("correct_voltage = %d:\n",voltage); ++ ++ return voltage; ++ ++} ++EXPORT_SYMBOL(ak_bat_read_voltage); ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief read and set gpio interrupt ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] gpio pin ++* @return void ++*/ ++static void bat_set_int_inverse(unsigned int pin) ++{ ++ if (ak_gpio_getpin(pin) == AK_GPIO_OUT_HIGH) ++ { ++ ak_gpio_intpol(pin, AK_GPIO_INT_LOWLEVEL); ++ } ++ else ++ { ++ ak_gpio_intpol(pin, AK_GPIO_INT_HIGHLEVEL); ++ } ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief notify ac and usb state who interest in ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void bat_notify_ac_usb_plug(struct ak_bat *battery) ++{ ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ PK_CHARGE("##################%s:\n", __func__); ++ ++ if (info->usb_gpio.pindata.pin >= 0) ++ { ++ // usb and ac pin all exist ++ if (battery->usb_online) ++ { ++ power_notifier_call_chain(POWER_EVENT_AC_PLUGIN, NULL); ++ } ++ else ++ { ++ power_notifier_call_chain(POWER_EVENT_AC_PLUGOUT, NULL); ++ } ++ } ++ ++ else ++ { ++ // only ac exist ++ if (battery->ac_online) ++ { ++ power_notifier_call_chain(POWER_EVENT_AC_PLUGIN, NULL); ++ } ++ else ++ { ++ power_notifier_call_chain(POWER_EVENT_AC_PLUGOUT, NULL); ++ } ++ } ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief poweroff system when voltage down to setting level ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void bat_check_voltage_poweroff(struct ak_bat *battery ) ++{ ++ int voltage; ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ voltage = ak_bat_read_voltage(info); ++ if (voltage <= info->bat_mach_info.min_voltage) ++ { ++ PK_PDOWN("voltage=%d <= pdown_voltage=%d\n",voltage, ++ info->bat_mach_info.min_voltage); ++ schedule_delayed_work(&pdown_work,0); ++ } ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief switch work depend on status ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void switch_charge_discharge_work(struct ak_bat *battery) ++{ ++ if (POWER_SUPPLY_STATUS_CHARGING == battery->status) ++ { ++ cancel_delayed_work_sync(&discharge_work); ++ schedule_delayed_work(&charge_work,UPDATE_CHARGE_DELAY); ++ } ++ else ++ { ++ cancel_delayed_work_sync(&charge_work); ++ del_timer_sync(&battery->timer); ++ schedule_delayed_work(&discharge_work,UPDATE_DISCHARGE_DELAY); ++ } ++} ++ ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief return battery property to user space ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *psy... ++* @return int ++*/ ++static int bat_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct ak_bat *battery = container_of(psy, struct ak_bat, bat_ps); ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_STATUS: ++ val->intval = battery->status; ++ break; ++ case POWER_SUPPLY_PROP_TECHNOLOGY: ++ val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ val->intval = battery->voltage; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: ++ val->intval = info->bat_mach_info.max_voltage; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: ++ val->intval = info->bat_mach_info.min_voltage; ++ break; ++ case POWER_SUPPLY_PROP_TEMP: ++ val->intval = 25; ++ break; ++ case POWER_SUPPLY_PROP_PRESENT: ++ val->intval = 1; ++ break; ++ case POWER_SUPPLY_PROP_CAPACITY: ++ val->intval = battery->capacity; // real capacity ++ break; ++#if 0 ++ case POWER_SUPPLY_PROP_POWEROFF_CAP: ++ val->intval = info->bat_mach_info.poweroff_cap; //power off capacity value ++ break; ++ case POWER_SUPPLY_PROP_LOW_CAP: ++ val->intval = info->bat_mach_info.low_cap; //low warring capacity ++ break; ++ case POWER_SUPPLY_PROP_RECOVER_CAP: ++ val->intval = info->bat_mach_info.recover_cap; //recover capacity limit ++ break; ++ case POWER_SUPPLY_PROP_POWER_ON_VOLTAGE: ++ val->intval = info->bat_mach_info.power_on_voltage; //power on voltage ++ break; ++ case POWER_SUPPLY_PROP_CPOWER_ON_VOLTAGE: ++ val->intval = info->bat_mach_info.cpower_on_voltage; //low warring capacity ++ break; ++#endif ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief return usb property ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *psy... ++* @return void ++*/ ++static int usb_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ int ret = 0; ++ struct ak_bat *battery = container_of(psy, struct ak_bat, usb_ps); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_ONLINE: ++ val->intval = battery->usb_online; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief return ac property ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *psy... ++* @return void ++*/ ++static int ac_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ int ret = 0; ++ struct ak_bat *battery = container_of(psy, struct ak_bat, ac_ps); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_ONLINE: ++ val->intval = battery->ac_online; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++ ++static void ak_battery_external_power_changed(struct power_supply *bat_ps) ++{ ++ // NULL ++} ++ ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief power manage function ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *dev ++* @return int ++*/ ++#ifdef CONFIG_PM ++static int ak_battery_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ flush_scheduled_work(); ++ del_timer_sync(&bat_device.timer); ++ atomic_set(&bat_device.full_flag, CHARGE_NOT_FULL); ++ cancel_delayed_work_sync(&charge_work); ++ cancel_delayed_work_sync(&discharge_work); ++ cancel_delayed_work_sync(&voltage_work); ++ cancel_delayed_work_sync(&usbirq_work); ++ cancel_delayed_work_sync(&acirq_work); ++ cancel_delayed_work_sync(&pdown_work); ++ cancel_delayed_work_sync(&resume_work); ++ ++ return 0; ++} ++ ++static int ak_battery_resume(struct platform_device *dev) ++{ ++ ++ schedule_delayed_work(&resume_work,msecs_to_jiffies(1000)); ++ return 0; ++} ++#else ++#define ak_battery_suspend NULL ++#define ak_battery_resume NULL ++#endif ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief caculate capacity from discharge voltage ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery... ++* @return void ++*/ ++static void get_capacity_from_voltage(struct ak_bat *battery, ++ struct ak_bat_mach_info *info,int voltage) ++{ ++ int vtg = voltage; ++ int min_voltage; ++ int max_voltage; ++ ++ min_voltage = (POWER_SUPPLY_STATUS_CHARGING == battery->status) ++ ? info->bat_mach_info.charge_min_voltage ++ : info->bat_mach_info.min_voltage; ++ max_voltage = (POWER_SUPPLY_STATUS_DISCHARGING == battery->status ++ && atomic_read(&battery->full_flag) == CHARGE_FULL) ++ ? info->bat_mach_info.full_voltage ++ : info->bat_mach_info.max_voltage; ++ ++ if (vtg < min_voltage) ++ { ++ vtg = min_voltage; ++ } ++ ++ if (vtg > max_voltage) ++ { ++ vtg = max_voltage; ++ } ++ ++ ++ battery->capacity = (vtg - min_voltage) * info->bat_mach_info.full_capacity ++ / (max_voltage - min_voltage); ++ ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief init sample value ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void ak_init_sample_value(struct ak_bat *battery) ++{ ++ struct read_voltage_sample *rd_voltage = &(battery->rd_voltage); ++ ++ rd_voltage->index = 0; ++ rd_voltage->sum = 0; ++ rd_voltage->max = 0; ++ rd_voltage->min = rd_voltage->design_max * 2; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief caculate voltage ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery,*info ++* @return void ++*/ ++static void ak_update_voltage(struct ak_bat *battery, ++ struct ak_bat_mach_info *info) ++{ ++ int temp; ++ struct read_voltage_sample *rd_voltage = &(battery->rd_voltage); ++ ++ PK_SAMPLE("\n###############%s; \n",__func__); ++ ++ // if voltage_index in correct range ++ if ((rd_voltage->index >= rd_voltage->sample) ++ || (rd_voltage->index < 0)) ++ { ++ PK_SAMPLE("%s:error voltage sample index\n",__func__); ++ ak_init_sample_value(battery); ++ } ++ ++ // read voltage sample ++ temp = ak_bat_read_voltage(info); ++ ++ // save voltage max and min ++ if (temp > rd_voltage->max) ++ { ++ rd_voltage->max = temp; ++ } ++ ++ if (temp < rd_voltage->min) ++ { ++ rd_voltage->min = temp; ++ } ++ ++ PK_SAMPLE("read_voltage=%d\n",temp); ++ PK_SAMPLE("voltage_max=%d; voltage_min=%d;\n",rd_voltage->max,rd_voltage->min); ++ PK_SAMPLE("voltage_index=%d; voltage_sum=%d;\n",rd_voltage->index,rd_voltage->sum); ++ ++ rd_voltage->sum += temp; ++ ++ if (++rd_voltage->index == rd_voltage->sample) ++ { ++ rd_voltage->sum -= rd_voltage->min + rd_voltage->max; ++ rd_voltage->index -= 2; ++ battery->voltage =rd_voltage->sum / rd_voltage->index; ++ ++ PK_SAMPLE("=========bat_data.voltage = %d;=======\n",battery->voltage); ++ ak_init_sample_value(battery); ++ } ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief wrap for caculating voltage ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void bat_update_voltage(struct ak_bat *battery) ++{ ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ mutex_lock(&battery->bat_lock); ++ ak_update_voltage(battery,info); ++ mutex_unlock(&battery->bat_lock); ++} ++ ++static void update_voltage_immediately(struct ak_bat *battery, ++ struct ak_bat_mach_info *info) ++{ ++ int read_cnt; ++ ++ ak_init_sample_value(battery); ++ ++ for (read_cnt = 0; read_cnt < battery->rd_voltage.sample; read_cnt++) ++ { ++ ak_update_voltage(battery,info); ++ } ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief debug message ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void bat_print_battery_info(struct ak_bat *battery) ++{ ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ printk("battery:cap=%d; vol=%d; status=%s; full=%d; poweroff_cap=%d; low_cap=%d.\n", ++ battery->capacity, ++ battery->voltage, ++ (battery->status == POWER_SUPPLY_STATUS_CHARGING) ? "charge" : "discharge", ++ ak_gpio_getpin(info->full_gpio.pindata.pin), ++ info->bat_mach_info.poweroff_cap, ++ info->bat_mach_info.low_cap); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief check capacity flag ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void check_cap_full_flag(struct ak_bat *battery) ++{ ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ if (atomic_read(&battery->full_flag) == CHARGE_FULL) ++ { ++ if (battery->voltage < info->bat_mach_info.full_voltage) ++ { ++ atomic_set(&battery->full_flag, CHARGE_NOT_FULL); ++ } ++ } ++ else ++ { ++ if (battery->capacity == info->bat_mach_info.full_capacity) ++ { ++ atomic_set(&battery->full_flag, CHARGE_FULL); ++ } ++ } ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief caculate discharge capacity from discharge voltage ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void update_discharge_capacity(struct ak_bat *battery) ++{ ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ int old_capacity = battery->capacity; ++ ++ PK_DISCHARGE("###########%s:\n",__func__); ++ get_capacity_from_voltage(battery,info,battery->voltage); ++ check_cap_full_flag(battery); ++ ++ if (old_capacity != battery->capacity) ++ { ++ power_supply_changed(&battery->bat_ps); ++ } ++ ++ PK_DISCHARGE("voltage = %d;capacity = %d;\n", battery->voltage, battery->capacity); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief caculate capacity from charge voltage ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void update_charge_capacity(struct ak_bat *battery) ++{ ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ int old_capacity = battery->capacity; ++ ++ get_capacity_from_voltage(battery,info,battery->voltage); ++ bat_check_charge_capacity(battery); ++ ++ if (battery->capacity != old_capacity) ++ { ++ power_supply_changed(&battery->bat_ps); ++ } ++ ++ PK_CHARGE("voltage = %d; capacity = %d;\n", ++ battery->voltage,battery->capacity); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief caculate ac and usb status ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery, *info ++* @return void ++*/ ++static void ak_get_bat_status(struct ak_bat *battery ++ ,struct ak_bat_mach_info *info) ++{ ++ if (info->ac_gpio.is_detect_mode == BAT_CHARGE_GPIO_DETECT) { ++ if (info->ac_gpio.pindata.pin >= 0) ++ { ++ battery->ac_online = ++ (ak_gpio_getpin(info->ac_gpio.pindata.pin) == info->ac_gpio.active)? 1 : 0; ++ } ++ else ++ { ++ battery->ac_online = 0; ++ } ++ } ++ ++ if (info->usb_gpio.pindata.pin >= 0) ++ { ++ battery->usb_online = ++ (ak_gpio_getpin(info->usb_gpio.pindata.pin)== info->usb_gpio.active)? 1 : 0; ++ } ++ else ++ { ++ battery->usb_online = 0; ++ } ++ ++ // if (battery->ac_online || battery->usb_online) ++ if (battery->ac_online) ++ { ++ battery->status = POWER_SUPPLY_STATUS_CHARGING; ++ } ++ else ++ { ++ battery->status = POWER_SUPPLY_STATUS_DISCHARGING; ++ } ++ ++} ++ ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief quickly power off system ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void check_machine_power_off(struct ak_bat *battery) ++{ ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ if ((poweroff_enable == info->bat_mach_info.power_off) ++ && (POWER_SUPPLY_STATUS_DISCHARGING == battery->status) ++ && (battery->voltage <= info->bat_mach_info.min_voltage)) ++ { ++ printk("battery:voltage=%d,machine power off immediately!\n",battery->voltage); ++ machine_power_off(); ++ } ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief ac irq work ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *work ++* @return void ++*/ ++static void bat_acirq_work(struct work_struct *work) ++{ ++ struct ak_bat *battery = &bat_device; ++ ++ PK_CHARGE("##################%s:\n", __func__); ++ ak_bat_update(battery); ++ check_machine_power_off(battery); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief ac irq handler ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] irq, *battery ++* @return irqreturn_t ++*/ ++static irqreturn_t akbat_ac_irqhandler(int irq, void *handle) ++{ ++ struct ak_bat *battery = (struct ak_bat *)handle; ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ PK_CHARGE("##################%s:\n", __func__); ++ disable_irq_nosync(irq); ++ bat_set_int_inverse(ak_irq_to_gpio(irq)); ++ schedule_delayed_work(&acirq_work,msecs_to_jiffies(info->ac_gpio.delay)); ++ enable_irq(irq); ++ return IRQ_HANDLED; ++} ++ ++static int ac_detect_plugin(struct notifier_block *nb, ++ unsigned long val, void *data) ++{ ++ struct ak_bat *battery = &bat_device; ++ ++ if (val == ADDETECT_AC_PLUGIN) { ++ battery->ac_online = 1; ++ } ++ else if (val == ADDETECT_AC_PLUGOUT) { ++ battery->ac_online = 0; ++ } ++ ++ ak_bat_update(battery); ++ return 0; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief usb irq handler ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery, irq ++* @return irqreturn_t ++*/ ++static irqreturn_t akbat_usb_irqhandler(int irq, void *handle) ++{ ++ struct ak_bat *battery = (struct ak_bat *)handle; ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ PK_CHARGE("##################%s:\n", __func__); ++ disable_irq_nosync(irq); ++ bat_set_int_inverse(ak_irq_to_gpio(irq)); ++ schedule_delayed_work(&usbirq_work,msecs_to_jiffies(info->usb_gpio.delay)); ++ enable_irq(irq); ++ return IRQ_HANDLED; ++} ++ ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief update battery info ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void ak_bat_update(struct ak_bat *battery) ++{ ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ PK_CHARGE("##################%s:\n", __func__); ++ mutex_lock(&battery->bat_lock); ++ ++ ak_get_bat_status(battery,info); ++ update_voltage_immediately(battery,info); ++ get_capacity_from_voltage(battery,info,battery->voltage); ++ bat_check_charge_capacity(battery); ++ power_supply_changed(&battery->bat_ps); ++ power_supply_changed(&battery->ac_ps); ++ power_supply_changed(&battery->usb_ps); ++ ++ switch_charge_discharge_work(battery); ++ bat_notify_ac_usb_plug(battery); ++ ++ mutex_unlock(&battery->bat_lock); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief update charge info ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void ak_bat_update_charge(struct ak_bat *battery) ++{ ++ PK_CHARGE("##################%s:\n", __func__); ++ mutex_lock(&battery->bat_lock); ++ update_charge_capacity(battery); ++ mutex_unlock(&battery->bat_lock); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief update discharge info ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void ak_bat_update_discharge(struct ak_bat *battery) ++{ ++ PK_DISCHARGE("##################%s:\n", __func__); ++ mutex_lock(&battery->bat_lock); ++ update_discharge_capacity(battery); ++ mutex_unlock(&battery->bat_lock); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief charge work ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *work ++* @return void ++*/ ++static void bat_charge_work(struct work_struct *work) ++{ ++ ak_bat_update_charge(&bat_device); ++ schedule_delayed_work(&charge_work,UPDATE_CHARGE_DELAY); ++// schedule_delayed_work(&charge_work,HZ*6); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief discharge work ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *battery ++* @return void ++*/ ++static void bat_discharge_work(struct work_struct *work) ++{ ++ ak_bat_update_discharge(&bat_device); ++ schedule_delayed_work(&discharge_work,UPDATE_DISCHARGE_DELAY); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief voltage work ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *work ++* @return void ++*/ ++static void bat_voltage_work(struct work_struct *work) ++{ ++#ifdef BATTERY_GET_VOLTAGE ++ // 5 seconds to print battery info ++ static int print_time = 10; ++ if (--print_time < 0) { ++ print_time = 5; ++ bat_print_battery_info(&bat_device); ++ } ++#endif ++ bat_check_voltage_poweroff(&bat_device); ++ bat_update_voltage(&bat_device); ++ schedule_delayed_work(&voltage_work,UPDATE_VOLTAGE_DELAY); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief usb irq work ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *work ++* @return void ++*/ ++static void bat_usbirq_work(struct work_struct *work) ++{ ++ struct ak_bat *battery = &bat_device; ++ ++ PK_CHARGE("##################%s:\n", __func__); ++ ak_bat_update (battery); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief power down work ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *work ++* @return void ++*/ ++static void bat_pdown_work(struct work_struct *work) ++{ ++ int voltage; ++ struct ak_bat *battery = &bat_device; ++ struct ak_bat_mach_info *info = get_bat_platform_data(battery); ++ ++ PK_PDOWN("#############%s:\n",__func__); ++ if (battery->status != POWER_SUPPLY_STATUS_DISCHARGING) ++ { ++ return; ++ } ++ ++ if (battery->read_count++ < CHECK_READ_CNT) ++ { ++ PK_PDOWN("read_count=%d\n",battery->read_count); ++ voltage = ak_bat_read_voltage(info); ++ PK_PDOWN("voltage=%d\n",voltage); ++ ++ if (voltage <= info->bat_mach_info.min_voltage) ++ { ++ battery->pdown_count++; ++ } ++ ++ schedule_delayed_work(&pdown_work,msecs_to_jiffies(CHECK_PDOWN_DELAY)); ++ } ++ else ++ { ++ PK_PDOWN("pdown_count=%d\n",battery->pdown_count); ++ ++ if (battery->pdown_count >= CHECK_PDOWN_CNT) ++ { ++ mutex_lock(&battery->bat_lock); ++ printk(KERN_WARNING "Ak_battery:capacity=%d => 0!\n",battery->capacity); ++ battery->capacity = 0; ++ power_supply_changed(&battery->bat_ps); ++ cancel_delayed_work_sync(&voltage_work); ++ cancel_delayed_work_sync(&charge_work); ++ cancel_delayed_work_sync(&discharge_work); ++ mutex_unlock(&battery->bat_lock); ++ } ++ ++ battery->read_count = 0; ++ battery->pdown_count = 0; ++ } ++ ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief resume work ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *work ++* @return void ++*/ ++static void bat_resume_work(struct work_struct *work) ++{ ++ ak_bat_update(&bat_device); ++ schedule_delayed_work(&voltage_work,UPDATE_VOLTAGE_DELAY); ++} ++ ++static int battery_reboot_notify(struct notifier_block *nb, unsigned long code, ++ void *unused) ++{ ++ switch (code) { ++ case SYS_DOWN: ++ case SYS_HALT: ++ case SYS_POWER_OFF: ++ printk("%s:code=%ld, cap=%d, vol=%d, status=%s.\n", ++ __func__, ++ code, ++ bat_device.capacity, ++ bat_device.voltage, ++ bat_device.status==POWER_SUPPLY_STATUS_CHARGING ? "charging":"discharging"); ++ break; ++ } ++ ++ return NOTIFY_DONE; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief power supply property ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] void ++* @return void ++*/ ++static enum power_supply_property bat_power_props[] = { ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_TECHNOLOGY, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, ++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, ++ POWER_SUPPLY_PROP_TEMP, ++ POWER_SUPPLY_PROP_PRESENT, ++ POWER_SUPPLY_PROP_CAPACITY, ++#if 0 ++ POWER_SUPPLY_PROP_POWEROFF_CAP, ++ POWER_SUPPLY_PROP_LOW_CAP, ++ POWER_SUPPLY_PROP_RECOVER_CAP, ++ POWER_SUPPLY_PROP_POWER_ON_VOLTAGE, ++ POWER_SUPPLY_PROP_CPOWER_ON_VOLTAGE, ++#endif ++}; ++ ++static enum power_supply_property usb_power_props[] = { ++ POWER_SUPPLY_PROP_ONLINE, ++}; ++ ++static enum power_supply_property ac_power_props[] = { ++ POWER_SUPPLY_PROP_ONLINE, ++}; ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief battery device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] void ++* @return void ++*/ ++static struct ak_bat bat_device = { ++ .bat_ps= { ++ .name = "battery", ++ .type = POWER_SUPPLY_TYPE_BATTERY, ++ .properties = bat_power_props, ++ .num_properties = ARRAY_SIZE(bat_power_props), ++ .get_property = bat_get_property, ++ .external_power_changed = ak_battery_external_power_changed, ++ .use_for_apm = 1, ++ }, ++ ++ .ac_ps = { ++ .name = "ac", ++ .type = POWER_SUPPLY_TYPE_MAINS, ++ .properties = ac_power_props, ++ .num_properties = ARRAY_SIZE(ac_power_props), ++ .get_property = ac_get_property, ++ }, ++ ++ .usb_ps = { ++ .name = "usb", ++ .type = POWER_SUPPLY_TYPE_USB, ++ .properties = usb_power_props, ++ .num_properties = ARRAY_SIZE(usb_power_props), ++ .get_property = usb_get_property, ++ }, ++ ++ .status = POWER_SUPPLY_STATUS_CHARGING, ++ .ac_online = 0, ++ .usb_online = 0, ++ .read_count = 0, ++ .pdown_count = 0, ++ .full_flag = ATOMIC_INIT(CHARGE_NOT_FULL), ++}; ++ ++ ++static struct notifier_block battery_reboot_nb = { ++ .notifier_call = battery_reboot_notify, ++ .next = NULL, ++ .priority = INT_MIN ++}; ++ ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief probe battery ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *dev ++* @return fail or not ++*/ ++static int __devinit ak_battery_probe(struct platform_device *dev) ++{ ++ int ret = 0; ++ struct ak_bat_mach_info *info; ++ ++ info = (struct ak_bat_mach_info *)dev->dev.platform_data; ++ if (!info) ++ { ++ printk(KERN_ERR "%s:no platform data for battery\n",__func__); ++ ret = -EINVAL; ++ goto nodata_out; ++ } ++ ++ /* initialize hardware */ ++ mutex_init(&bat_device.bat_lock); ++ bat_device.rd_voltage.sample = info->bat_mach_info.voltage_sample; ++ bat_device.rd_voltage.design_max = info->bat_mach_info.max_voltage; ++ init_timer(&bat_device.timer); ++ bat_device.timer.function = bat_charge_full_timer; ++ bat_device.timer.data = (unsigned long) &bat_device; ++ ++ INIT_DELAYED_WORK(&charge_work,bat_charge_work); ++ INIT_DELAYED_WORK(&discharge_work,bat_discharge_work); ++ INIT_DELAYED_WORK(&voltage_work,bat_voltage_work); ++ INIT_DELAYED_WORK(&usbirq_work,bat_usbirq_work); ++ INIT_DELAYED_WORK(&pdown_work,bat_pdown_work); ++ INIT_DELAYED_WORK(&resume_work,bat_resume_work); ++ ++ ret = power_supply_register(&dev->dev, &bat_device.bat_ps); ++ if (ret != 0) ++ { ++ goto cancel_out; ++ } ++ ++ ret = power_supply_register(&dev->dev,&bat_device.usb_ps); ++ if (ret != 0) ++ { ++ goto free_bat_ps_out; ++ } ++ ++ ret = power_supply_register(&dev->dev,&bat_device.ac_ps); ++ if (ret != 0) ++ { ++ goto free_usb_ps_out; ++ } ++ ++ // use for charge full state ++ if (info->full_gpio.pindata.pin >= 0) ++ { ++ info->gpio_init(&info->full_gpio.pindata); ++ } ++ ++ if (info->ac_gpio.is_detect_mode == BAT_CHARGE_GPIO_DETECT) { ++ INIT_DELAYED_WORK(&acirq_work,bat_acirq_work); ++ // use for ac charge in irq ++ if (info->ac_gpio.irq >= 0) ++ { ++ info->gpio_init(&info->ac_gpio.pindata); ++ bat_set_int_inverse(info->ac_gpio.pindata.pin); ++ if (request_irq(info->ac_gpio.irq,akbat_ac_irqhandler,0,"ac_charge",&bat_device)) ++ { ++ printk(KERN_ERR "%s:Could not allocate IRQ %d\n", __func__,info->ac_gpio.irq); ++ ret = -EIO; ++ goto free_ac_ps_out; ++ } ++ } ++ } else if (info->ac_gpio.is_detect_mode == BAT_CHARGE_ADC_DETECT) { ++ memset(&ac_detect_nb, 0, sizeof(ac_detect_nb)); ++ ac_detect_nb.notifier_call = ac_detect_plugin; ++ addetect_register_client(&ac_detect_nb); ++ } ++ ++ // use for usb charge in irq ++ if (info->usb_gpio.irq >= 0) ++ { ++ info->gpio_init(&info->usb_gpio.pindata); ++ mdelay(100); ++ bat_set_int_inverse(info->usb_gpio.pindata.pin); ++ if (request_irq(info->usb_gpio.irq,akbat_usb_irqhandler,0,"usb_charge",&bat_device)) ++ { ++ printk(KERN_ERR "%s:Could not allocate IRQ %d\n", __func__,info->usb_gpio.irq); ++ ret = -EIO; ++ goto free_acirq_out; ++ } ++ } ++ ++ ak_bat_update(&bat_device); ++ schedule_delayed_work(&voltage_work,UPDATE_VOLTAGE_DELAY); ++ register_reboot_notifier(&battery_reboot_nb); ++ ++ bat_print_battery_info(&bat_device); ++ printk("AK Battery initialized\n"); ++ return ret; ++ ++free_acirq_out: ++ if (info->ac_gpio.irq > 0) ++ { ++ disable_irq(info->ac_gpio.irq); ++ free_irq(info->ac_gpio.irq, dev); ++ } ++free_ac_ps_out: ++ power_supply_unregister(&bat_device.ac_ps); ++free_usb_ps_out: ++ power_supply_unregister(&bat_device.usb_ps); ++free_bat_ps_out: ++ power_supply_unregister(&bat_device.bat_ps); ++cancel_out: ++ del_timer_sync(&bat_device.timer); ++ cancel_delayed_work_sync(&charge_work); ++ cancel_delayed_work_sync(&discharge_work); ++ cancel_delayed_work_sync(&voltage_work); ++ cancel_delayed_work_sync(&usbirq_work); ++ cancel_delayed_work_sync(&acirq_work); ++ cancel_delayed_work_sync(&pdown_work); ++ cancel_delayed_work_sync(&resume_work); ++ ++nodata_out: ++ printk(KERN_ERR "###########%s:ERR out##########\n",__func__); ++ return ret; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief remove battery ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] *dev ++* @return fail or not ++*/ ++static int __devexit ak_battery_remove(struct platform_device *dev) ++{ ++ struct ak_bat_mach_info *info; ++ ++ info = (struct ak_bat_mach_info *)dev->dev.platform_data; ++ if (!info) { ++ printk(KERN_ERR "no platform data for battery\n"); ++ return -EINVAL; ++ } ++ ++#ifdef CONFIG_AC_GPIO_DETECT ++ if (info->ac_gpio.irq > 0) ++ { ++ disable_irq(info->ac_gpio.irq); ++ free_irq(info->ac_gpio.irq, &bat_device); ++ } ++#elif defined(CONFIG_AC_AD_DETECT) ++ addetect_unregister_client(&ac_detect_nb); ++#endif ++ ++ if (info->usb_gpio.irq > 0) ++ { ++ disable_irq(info->usb_gpio.irq); ++ free_irq(info->usb_gpio.irq, &bat_device); ++ } ++ ++ del_timer_sync(&bat_device.timer); ++ cancel_delayed_work_sync(&charge_work); ++ cancel_delayed_work_sync(&discharge_work); ++ cancel_delayed_work_sync(&voltage_work); ++ cancel_delayed_work_sync(&usbirq_work); ++ cancel_delayed_work_sync(&acirq_work); ++ cancel_delayed_work_sync(&pdown_work); ++ cancel_delayed_work_sync(&resume_work); ++ ++ power_supply_unregister(&bat_device.bat_ps); ++ power_supply_unregister(&bat_device.ac_ps); ++ power_supply_unregister(&bat_device.usb_ps); ++ ++ unregister_reboot_notifier(&battery_reboot_nb); ++ return 0; ++} ++ ++static struct platform_driver ak_battery_driver = { ++ .driver = { ++ .name = "battery", ++ }, ++ .probe = ak_battery_probe, ++ .remove = __devexit_p(ak_battery_remove), ++ .suspend = ak_battery_suspend, ++ .resume = ak_battery_resume, ++}; ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief init battery ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] void ++* @return fail or not ++*/ ++static int __init ak_battery_init(void) ++{ ++ return platform_driver_register(&ak_battery_driver); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief exit battery ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-11-2 ++* @param[out] void ++* @param[in] void ++* @return void ++*/ ++static void __exit ak_battery_exit(void) ++{ ++ platform_driver_unregister(&ak_battery_driver); ++} ++ ++module_init(ak_battery_init); ++module_exit(ak_battery_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("gao_wangsheng "); ++MODULE_DESCRIPTION("ak battery driver"); +diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c +index 6ad61272..773fca15 100644 +--- a/drivers/power/power_supply_core.c ++++ b/drivers/power/power_supply_core.c +@@ -41,23 +41,40 @@ static int __power_supply_changed_work(struct device *dev, void *data) + + static void power_supply_changed_work(struct work_struct *work) + { ++ unsigned long flags; + struct power_supply *psy = container_of(work, struct power_supply, + changed_work); + + dev_dbg(psy->dev, "%s\n", __func__); + +- class_for_each_device(power_supply_class, NULL, psy, +- __power_supply_changed_work); ++ spin_lock_irqsave(&psy->changed_lock, flags); ++ if (psy->changed) { ++ psy->changed = false; ++ spin_unlock_irqrestore(&psy->changed_lock, flags); + +- power_supply_update_leds(psy); ++ class_for_each_device(power_supply_class, NULL, psy, ++ __power_supply_changed_work); + +- kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); ++ power_supply_update_leds(psy); ++ ++ kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); ++ spin_lock_irqsave(&psy->changed_lock, flags); ++ } ++ if (!psy->changed) ++ pm_relax(psy->dev); ++ spin_unlock_irqrestore(&psy->changed_lock, flags); + } + + void power_supply_changed(struct power_supply *psy) + { ++ unsigned long flags; ++ + dev_dbg(psy->dev, "%s\n", __func__); + ++ spin_lock_irqsave(&psy->changed_lock, flags); ++ psy->changed = true; ++ pm_stay_awake(psy->dev); ++ spin_unlock_irqrestore(&psy->changed_lock, flags); + schedule_work(&psy->changed_work); + } + EXPORT_SYMBOL_GPL(power_supply_changed); +@@ -197,6 +214,11 @@ int power_supply_register(struct device *parent, struct power_supply *psy) + if (rc) + goto device_add_failed; + ++ spin_lock_init(&psy->changed_lock); ++ rc = device_init_wakeup(dev, true); ++ if (rc) ++ goto wakeup_init_failed; ++ + rc = power_supply_create_triggers(psy); + if (rc) + goto create_triggers_failed; +@@ -206,6 +228,7 @@ int power_supply_register(struct device *parent, struct power_supply *psy) + goto success; + + create_triggers_failed: ++wakeup_init_failed: + device_del(dev); + kobject_set_name_failed: + device_add_failed: +diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c +index 4368e7d6..b20acfa9 100644 +--- a/drivers/power/power_supply_sysfs.c ++++ b/drivers/power/power_supply_sysfs.c +@@ -174,6 +174,10 @@ static struct device_attribute power_supply_attrs[] = { + POWER_SUPPLY_ATTR(time_to_full_avg), + POWER_SUPPLY_ATTR(type), + POWER_SUPPLY_ATTR(scope), ++ /* Local extensions */ ++ POWER_SUPPLY_ATTR(usb_hc), ++ POWER_SUPPLY_ATTR(usb_otg), ++ POWER_SUPPLY_ATTR(charge_enabled), + /* Properties of type `const char *' */ + POWER_SUPPLY_ATTR(model_name), + POWER_SUPPLY_ATTR(manufacturer), +diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c +index ce1694d1..fb56ec75 100644 +--- a/drivers/power/smb347-charger.c ++++ b/drivers/power/smb347-charger.c +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + /* + * Configuration registers. These are mirrored to volatile RAM and can be +@@ -38,14 +39,20 @@ + #define CFG_CURRENT_LIMIT_DC_MASK 0xf0 + #define CFG_CURRENT_LIMIT_DC_SHIFT 4 + #define CFG_CURRENT_LIMIT_USB_MASK 0x0f ++#define CFG_VARIOUS_FUNCTION 0x02 ++#define CFG_INPUT_SOURCE_PRIORITY BIT(2) + #define CFG_FLOAT_VOLTAGE 0x03 + #define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK 0xc0 ++#define CFG_FLOAT_VOLTAGE_MASK 0x3F + #define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT 6 ++#define CFG_CHARGE_CONTROL 0x04 ++#define CFG_AUTOMATIC_RECHARGE_DISABLE BIT(7) + #define CFG_STAT 0x05 + #define CFG_STAT_DISABLED BIT(5) + #define CFG_STAT_ACTIVE_HIGH BIT(7) + #define CFG_PIN 0x06 + #define CFG_PIN_EN_CTRL_MASK 0x60 ++#define CFG_PIN_USB_MODE_CTRL BIT(4) + #define CFG_PIN_EN_CTRL_ACTIVE_HIGH 0x40 + #define CFG_PIN_EN_CTRL_ACTIVE_LOW 0x60 + #define CFG_PIN_EN_APSD_IRQ BIT(1) +@@ -85,8 +92,12 @@ + #define CMD_A 0x30 + #define CMD_A_CHG_ENABLED BIT(1) + #define CMD_A_SUSPEND_ENABLED BIT(2) ++#define CMD_A_OTG_ENABLE BIT(4) + #define CMD_A_ALLOW_WRITE BIT(7) + #define CMD_B 0x31 ++#define CMD_B_POR BIT(7) ++#define CMD_B_USB59_MODE BIT(1) ++#define CMD_B_HC_MODE BIT(0) + #define CMD_C 0x33 + + /* Interrupt Status registers */ +@@ -108,6 +119,7 @@ + #define STAT_B 0x3c + #define STAT_C 0x3d + #define STAT_C_CHG_ENABLED BIT(0) ++#define STAT_C_CHG_STATUS BIT(5) + #define STAT_C_CHG_MASK 0x06 + #define STAT_C_CHG_SHIFT 1 + #define STAT_C_CHARGER_ERROR BIT(6) +@@ -135,6 +147,11 @@ struct smb347_charger { + bool mains_online; + bool usb_online; + bool charging_enabled; ++ unsigned int mains_current_limit; ++ bool usb_hc_mode; ++ bool usb_otg_enabled; ++ bool is_fully_charged; ++ int en_gpio; + struct dentry *dentry; + const struct smb347_charger_platform_data *pdata; + }; +@@ -315,9 +332,17 @@ static int smb347_charging_set(struct smb347_charger *smb, bool enable) + { + int ret = 0; + ++ if (enable && !smb->charging_enabled) ++ smb->is_fully_charged = false; ++ + if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) { +- dev_dbg(&smb->client->dev, +- "charging enable/disable in SW disabled\n"); ++ smb->charging_enabled = enable; ++ ++ if (smb->en_gpio) ++ gpio_set_value( ++ smb->en_gpio, ++ (smb->pdata->enable_control == ++ SMB347_CHG_ENABLE_PIN_ACTIVE_LOW) ^ enable); + return 0; + } + +@@ -424,9 +449,9 @@ static int smb347_set_current_limits(struct smb347_charger *smb) + if (ret < 0) + return ret; + +- if (smb->pdata->mains_current_limit) { ++ if (smb->mains_current_limit) { + val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), +- smb->pdata->mains_current_limit); ++ smb->mains_current_limit); + if (val < 0) + return val; + +@@ -473,6 +498,7 @@ static int smb347_set_voltage_limits(struct smb347_charger *smb) + val = clamp_val(val, 3500000, 4500000) - 3500000; + val /= 20000; + ++ ret &= ~CFG_FLOAT_VOLTAGE_MASK; + ret |= val; + } + +@@ -662,6 +688,168 @@ static int smb347_set_writable(struct smb347_charger *smb, bool writable) + return smb347_write(smb, CMD_A, ret); + } + ++static int smb347_irq_set(struct smb347_charger *smb, bool enable) ++{ ++ int ret; ++ ++ ret = smb347_set_writable(smb, true); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * Enable/disable interrupts for: ++ * - under voltage ++ * - termination current reached ++ * - charger error ++ */ ++ if (enable) { ++ ret = smb347_write(smb, CFG_FAULT_IRQ, CFG_FAULT_IRQ_DCIN_UV); ++ if (ret < 0) ++ goto fail; ++ ++ ret = smb347_write(smb, CFG_STATUS_IRQ, ++ CFG_STATUS_IRQ_TERMINATION_OR_TAPER); ++ if (ret < 0) ++ goto fail; ++ ++ ret = smb347_read(smb, CFG_PIN); ++ if (ret < 0) ++ goto fail; ++ ++ ret |= CFG_PIN_EN_CHARGER_ERROR; ++ ++ ret = smb347_write(smb, CFG_PIN, ret); ++ } else { ++ ret = smb347_write(smb, CFG_FAULT_IRQ, 0); ++ if (ret < 0) ++ goto fail; ++ ++ ret = smb347_write(smb, CFG_STATUS_IRQ, 0); ++ if (ret < 0) ++ goto fail; ++ ++ ret = smb347_read(smb, CFG_PIN); ++ if (ret < 0) ++ goto fail; ++ ++ ret &= ~CFG_PIN_EN_CHARGER_ERROR; ++ ++ ret = smb347_write(smb, CFG_PIN, ret); ++ } ++ ++fail: ++ smb347_set_writable(smb, false); ++ return ret; ++} ++ ++static inline int smb347_irq_enable(struct smb347_charger *smb) ++{ ++ return smb347_irq_set(smb, true); ++} ++ ++static inline int smb347_irq_disable(struct smb347_charger *smb) ++{ ++ return smb347_irq_set(smb, false); ++} ++ ++static irqreturn_t smb347_interrupt(int irq, void *data) ++{ ++ struct smb347_charger *smb = data; ++ int stat_c, t; ++ u8 irqstat[6]; ++ irqreturn_t ret = IRQ_NONE; ++ ++ t = i2c_smbus_read_i2c_block_data(smb->client, IRQSTAT_A, 6, irqstat); ++ if (t < 0) { ++ dev_warn(&smb->client->dev, ++ "reading IRQSTAT registers failed\n"); ++ return IRQ_NONE; ++ } ++ ++ stat_c = smb347_read(smb, STAT_C); ++ if (stat_c < 0) { ++ dev_warn(&smb->client->dev, "reading STAT_C failed\n"); ++ return IRQ_NONE; ++ } ++ ++ pr_debug("%s: stat c=%x irq a=%x b=%x c=%x d=%x e=%x f=%x\n", ++ __func__, stat_c, irqstat[0], irqstat[1], irqstat[2], ++ irqstat[3], irqstat[4], irqstat[5]); ++ ++ /* ++ * If we get charger error we report the error back to user and ++ * disable charging. ++ */ ++ if (stat_c & STAT_C_CHARGER_ERROR) { ++ dev_err(&smb->client->dev, ++ "error in charger, disabling charging\n"); ++ ++ smb347_charging_disable(smb); ++ power_supply_changed(&smb->battery); ++ ++ ret = IRQ_HANDLED; ++ } else if (((stat_c & STAT_C_CHG_STATUS) || ++ (irqstat[2] & (IRQSTAT_C_TERMINATION_IRQ | ++ IRQSTAT_C_TERMINATION_STAT))) && ++ !smb->is_fully_charged) { ++ dev_info(&smb->client->dev, "charge terminated\n"); ++ smb->is_fully_charged = true; ++ smb347_charging_disable(smb); ++ power_supply_changed(&smb->battery); ++ ret = IRQ_HANDLED; ++ } ++ ++ if (irqstat[2] & IRQSTAT_C_TAPER_IRQ) ++ ret = IRQ_HANDLED; ++ ++ /* ++ * If we got an under voltage interrupt it means that AC/USB input ++ * was disconnected. ++ */ ++ if (irqstat[4] & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) ++ ret = IRQ_HANDLED; ++ ++ if (smb347_update_status(smb) > 0) { ++ smb347_update_online(smb); ++ power_supply_changed(&smb->mains); ++ power_supply_changed(&smb->usb); ++ ret = IRQ_HANDLED; ++ } ++ ++ return ret; ++} ++ ++static int smb347_irq_init(struct smb347_charger *smb) ++{ ++ const struct smb347_charger_platform_data *pdata = smb->pdata; ++ int ret, irq = gpio_to_irq(pdata->irq_gpio); ++ ++ ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, smb->client->name); ++ if (ret < 0) ++ goto fail; ++ ++ ret = request_threaded_irq(irq, NULL, smb347_interrupt, ++ pdata->disable_stat_interrupts ? ++ IRQF_TRIGGER_RISING | IRQF_ONESHOT : ++ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ++ smb->client->name, smb); ++ if (ret < 0) ++ goto fail_gpio; ++ ++ ret = enable_irq_wake(irq); ++ if (ret) ++ pr_err("%s: failed to enable wake on irq %d\n", __func__, irq); ++ ++ smb->client->irq = irq; ++ return 0; ++ ++fail_gpio: ++ gpio_free(pdata->irq_gpio); ++fail: ++ smb->client->irq = 0; ++ return ret; ++} ++ + static int smb347_hw_init(struct smb347_charger *smb) + { + int ret; +@@ -686,9 +874,12 @@ static int smb347_hw_init(struct smb347_charger *smb) + if (ret < 0) + goto fail; + ++// HACK for Manta pre-alpha 0.2, TH_BATTERY not connected properly ++#if 0 // HACK + ret = smb347_set_temp_limits(smb); + if (ret < 0) + goto fail; ++#endif // HACK + + /* If USB charging is disabled we put the USB in suspend mode */ + if (!smb->pdata->use_usb) { +@@ -719,6 +910,19 @@ static int smb347_hw_init(struct smb347_charger *smb) + if (ret < 0) + goto fail; + ++ /* If configured by platform data, disable AUTOMATIC RECHARGE */ ++ if (smb->pdata->disable_automatic_recharge) { ++ ret = smb347_read(smb, CFG_CHARGE_CONTROL); ++ if (ret < 0) ++ goto fail; ++ ++ ret |= CFG_AUTOMATIC_RECHARGE_DISABLE; ++ ++ ret = smb347_write(smb, CFG_CHARGE_CONTROL, ret); ++ if (ret < 0) ++ goto fail; ++ } ++ + ret = smb347_read(smb, CFG_PIN); + if (ret < 0) + goto fail; +@@ -728,7 +932,7 @@ static int smb347_hw_init(struct smb347_charger *smb) + * command register unless pin control is specified in the platform + * data. + */ +- ret &= ~CFG_PIN_EN_CTRL_MASK; ++ ret &= ~(CFG_PIN_EN_CTRL_MASK | CFG_PIN_USB_MODE_CTRL); + + switch (smb->pdata->enable_control) { + case SMB347_CHG_ENABLE_SW: +@@ -742,6 +946,9 @@ static int smb347_hw_init(struct smb347_charger *smb) + break; + } + ++ if (smb->pdata->usb_mode_pin_ctrl) ++ ret |= CFG_PIN_USB_MODE_CTRL; ++ + /* Disable Automatic Power Source Detection (APSD) interrupt. */ + ret &= ~CFG_PIN_EN_APSD_IRQ; + +@@ -755,123 +962,27 @@ static int smb347_hw_init(struct smb347_charger *smb) + + ret = smb347_update_online(smb); + +-fail: +- smb347_set_writable(smb, false); +- return ret; +-} +- +-static irqreturn_t smb347_interrupt(int irq, void *data) +-{ +- struct smb347_charger *smb = data; +- int stat_c, irqstat_e, irqstat_c; +- irqreturn_t ret = IRQ_NONE; +- +- stat_c = smb347_read(smb, STAT_C); +- if (stat_c < 0) { +- dev_warn(&smb->client->dev, "reading STAT_C failed\n"); +- return IRQ_NONE; +- } +- +- irqstat_c = smb347_read(smb, IRQSTAT_C); +- if (irqstat_c < 0) { +- dev_warn(&smb->client->dev, "reading IRQSTAT_C failed\n"); +- return IRQ_NONE; +- } +- +- irqstat_e = smb347_read(smb, IRQSTAT_E); +- if (irqstat_e < 0) { +- dev_warn(&smb->client->dev, "reading IRQSTAT_E failed\n"); +- return IRQ_NONE; +- } +- +- /* +- * If we get charger error we report the error back to user and +- * disable charging. +- */ +- if (stat_c & STAT_C_CHARGER_ERROR) { +- dev_err(&smb->client->dev, +- "error in charger, disabling charging\n"); +- +- smb347_charging_disable(smb); +- power_supply_changed(&smb->battery); +- +- ret = IRQ_HANDLED; +- } +- +- /* +- * If we reached the termination current the battery is charged and +- * we can update the status now. Charging is automatically +- * disabled by the hardware. +- */ +- if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { +- if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) +- power_supply_changed(&smb->battery); +- ret = IRQ_HANDLED; +- } +- +- /* +- * If we got an under voltage interrupt it means that AC/USB input +- * was connected or disconnected. +- */ +- if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) { +- if (smb347_update_status(smb) > 0) { +- smb347_update_online(smb); +- power_supply_changed(&smb->mains); +- power_supply_changed(&smb->usb); +- } +- ret = IRQ_HANDLED; +- } +- +- return ret; +-} +- +-static int smb347_irq_set(struct smb347_charger *smb, bool enable) +-{ +- int ret; +- +- ret = smb347_set_writable(smb, true); +- if (ret < 0) +- return ret; +- +- /* +- * Enable/disable interrupts for: +- * - under voltage +- * - termination current reached +- * - charger error +- */ +- if (enable) { +- ret = smb347_write(smb, CFG_FAULT_IRQ, CFG_FAULT_IRQ_DCIN_UV); +- if (ret < 0) +- goto fail; +- +- ret = smb347_write(smb, CFG_STATUS_IRQ, +- CFG_STATUS_IRQ_TERMINATION_OR_TAPER); +- if (ret < 0) +- goto fail; +- +- ret = smb347_read(smb, CFG_PIN); ++ if ((smb->pdata->irq_gpio >= 0) && ++ !smb->pdata->disable_stat_interrupts) { ++ /* ++ * Configure the STAT output to be suitable for interrupts: ++ * disable all other output (except interrupts) and make it ++ * active low. ++ */ ++ ret = smb347_read(smb, CFG_STAT); + if (ret < 0) + goto fail; + +- ret |= CFG_PIN_EN_CHARGER_ERROR; ++ ret &= ~CFG_STAT_ACTIVE_HIGH; ++ ret |= CFG_STAT_DISABLED; + +- ret = smb347_write(smb, CFG_PIN, ret); +- } else { +- ret = smb347_write(smb, CFG_FAULT_IRQ, 0); ++ ret = smb347_write(smb, CFG_STAT, ret); + if (ret < 0) + goto fail; + +- ret = smb347_write(smb, CFG_STATUS_IRQ, 0); +- if (ret < 0) +- goto fail; +- +- ret = smb347_read(smb, CFG_PIN); ++ ret = smb347_irq_enable(smb); + if (ret < 0) + goto fail; +- +- ret &= ~CFG_PIN_EN_CHARGER_ERROR; +- +- ret = smb347_write(smb, CFG_PIN, ret); + } + + fail: +@@ -879,85 +990,90 @@ fail: + return ret; + } + +-static inline int smb347_irq_enable(struct smb347_charger *smb) ++static int smb347_mains_get_property(struct power_supply *psy, ++ enum power_supply_property prop, ++ union power_supply_propval *val) + { +- return smb347_irq_set(smb, true); +-} ++ struct smb347_charger *smb = ++ container_of(psy, struct smb347_charger, mains); + +-static inline int smb347_irq_disable(struct smb347_charger *smb) +-{ +- return smb347_irq_set(smb, false); ++ switch (prop) { ++ case POWER_SUPPLY_PROP_ONLINE: ++ val->intval = smb->mains_online; ++ return 0; ++ ++ case POWER_SUPPLY_PROP_CURRENT_MAX: ++ val->intval = smb->mains_current_limit; ++ return 0; ++ ++ default: ++ return -EINVAL; ++ } ++ return -EINVAL; + } + +-static int smb347_irq_init(struct smb347_charger *smb) ++static int smb347_mains_set_property(struct power_supply *psy, ++ enum power_supply_property prop, ++ const union power_supply_propval *val) + { +- const struct smb347_charger_platform_data *pdata = smb->pdata; +- int ret, irq = gpio_to_irq(pdata->irq_gpio); ++ struct smb347_charger *smb = ++ container_of(psy, struct smb347_charger, mains); ++ int ret; ++ bool oldval; + +- ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, smb->client->name); +- if (ret < 0) +- goto fail; ++ switch (prop) { ++ case POWER_SUPPLY_PROP_ONLINE: ++ oldval = smb->mains_online; + +- ret = request_threaded_irq(irq, NULL, smb347_interrupt, +- IRQF_TRIGGER_FALLING, smb->client->name, +- smb); +- if (ret < 0) +- goto fail_gpio; ++ smb->mains_online = val->intval; + +- ret = smb347_set_writable(smb, true); +- if (ret < 0) +- goto fail_irq; ++ smb347_set_writable(smb, true); + +- /* +- * Configure the STAT output to be suitable for interrupts: disable +- * all other output (except interrupts) and make it active low. +- */ +- ret = smb347_read(smb, CFG_STAT); +- if (ret < 0) +- goto fail_readonly; ++ ret = smb347_read(smb, CMD_A); ++ if (ret < 0) ++ return -EINVAL; + +- ret &= ~CFG_STAT_ACTIVE_HIGH; +- ret |= CFG_STAT_DISABLED; ++ ret &= ~CMD_A_SUSPEND_ENABLED; ++ if (val->intval) ++ ret |= CMD_A_SUSPEND_ENABLED; + +- ret = smb347_write(smb, CFG_STAT, ret); +- if (ret < 0) +- goto fail_readonly; ++ ret = smb347_write(smb, CMD_A, ret); + +- ret = smb347_irq_enable(smb); +- if (ret < 0) +- goto fail_readonly; ++ smb347_hw_init(smb); + +- smb347_set_writable(smb, false); +- smb->client->irq = irq; +- return 0; ++ smb347_set_writable(smb, false); + +-fail_readonly: +- smb347_set_writable(smb, false); +-fail_irq: +- free_irq(irq, smb); +-fail_gpio: +- gpio_free(pdata->irq_gpio); +-fail: +- smb->client->irq = 0; +- return ret; ++ if (smb->mains_online != oldval) ++ power_supply_changed(psy); ++ return 0; ++ case POWER_SUPPLY_PROP_CURRENT_MAX: ++ smb->mains_current_limit = val->intval; ++ smb347_hw_init(smb); ++ return 0; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return -EINVAL; + } + +-static int smb347_mains_get_property(struct power_supply *psy, +- enum power_supply_property prop, +- union power_supply_propval *val) ++static int smb347_mains_property_is_writeable(struct power_supply *psy, ++ enum power_supply_property prop) + { +- struct smb347_charger *smb = +- container_of(psy, struct smb347_charger, mains); +- +- if (prop == POWER_SUPPLY_PROP_ONLINE) { +- val->intval = smb->mains_online; +- return 0; ++ switch (prop) { ++ case POWER_SUPPLY_PROP_CURRENT_MAX: ++ return 1; ++ default: ++ break; + } +- return -EINVAL; ++ ++ return 0; + } + + static enum power_supply_property smb347_mains_properties[] = { + POWER_SUPPLY_PROP_ONLINE, ++ POWER_SUPPLY_PROP_CURRENT_MAX, + }; + + static int smb347_usb_get_property(struct power_supply *psy, +@@ -967,15 +1083,94 @@ static int smb347_usb_get_property(struct power_supply *psy, + struct smb347_charger *smb = + container_of(psy, struct smb347_charger, usb); + +- if (prop == POWER_SUPPLY_PROP_ONLINE) { ++ switch (prop) { ++ case POWER_SUPPLY_PROP_ONLINE: + val->intval = smb->usb_online; + return 0; ++ ++ case POWER_SUPPLY_PROP_USB_HC: ++ val->intval = smb->usb_hc_mode; ++ return 0; ++ ++ case POWER_SUPPLY_PROP_USB_OTG: ++ val->intval = smb->usb_otg_enabled; ++ return 0; ++ ++ default: ++ break; + } + return -EINVAL; + } + ++static int smb347_usb_set_property(struct power_supply *psy, ++ enum power_supply_property prop, ++ const union power_supply_propval *val) ++{ ++ int ret = -EINVAL; ++ struct smb347_charger *smb = ++ container_of(psy, struct smb347_charger, usb); ++ bool oldval; ++ ++ switch (prop) { ++ case POWER_SUPPLY_PROP_ONLINE: ++ oldval = smb->usb_online; ++ smb->usb_online = val->intval; ++ ++ if (smb->usb_online != oldval) ++ power_supply_changed(psy); ++ ret = 0; ++ break; ++ case POWER_SUPPLY_PROP_USB_HC: ++ smb347_set_writable(smb, true); ++ ret = smb347_write(smb, CMD_B, val->intval ? ++ CMD_B_HC_MODE : CMD_B_USB59_MODE); ++ smb347_set_writable(smb, false); ++ smb->usb_hc_mode = val->intval; ++ break; ++ ++ case POWER_SUPPLY_PROP_USB_OTG: ++ ret = smb347_read(smb, CMD_A); ++ ++ if (ret < 0) ++ return ret; ++ ++ if (val->intval) ++ ret |= CMD_A_OTG_ENABLE; ++ else ++ ret &= ~CMD_A_OTG_ENABLE; ++ ++ ret = smb347_write(smb, CMD_A, ret); ++ ++ if (ret >= 0) ++ smb->usb_otg_enabled = val->intval; ++ ++ break; ++ ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++static int smb347_usb_property_is_writeable(struct power_supply *psy, ++ enum power_supply_property prop) ++{ ++ switch (prop) { ++ case POWER_SUPPLY_PROP_USB_HC: ++ case POWER_SUPPLY_PROP_USB_OTG: ++ return 1; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ + static enum power_supply_property smb347_usb_properties[] = { + POWER_SUPPLY_PROP_ONLINE, ++ POWER_SUPPLY_PROP_USB_HC, ++ POWER_SUPPLY_PROP_USB_OTG, + }; + + static int smb347_battery_get_property(struct power_supply *psy, +@@ -991,16 +1186,25 @@ static int smb347_battery_get_property(struct power_supply *psy, + if (ret < 0) + return ret; + ++ if (ret > 0) { ++ smb347_update_online(smb); ++ power_supply_changed(&smb->mains); ++ power_supply_changed(&smb->usb); ++ } ++ + switch (prop) { + case POWER_SUPPLY_PROP_STATUS: + if (!smb347_is_online(smb)) { ++ smb->is_fully_charged = false; + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + } + if (smb347_charging_status(smb)) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else +- val->intval = POWER_SUPPLY_STATUS_FULL; ++ val->intval = smb->is_fully_charged ? ++ POWER_SUPPLY_STATUS_FULL : ++ POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + + case POWER_SUPPLY_PROP_CHARGE_TYPE: +@@ -1078,6 +1282,10 @@ static int smb347_battery_get_property(struct power_supply *psy, + val->intval = pdata->battery_info.charge_full_design; + break; + ++ case POWER_SUPPLY_PROP_CHARGE_ENABLED: ++ val->intval = smb->charging_enabled; ++ break; ++ + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = pdata->battery_info.name; + break; +@@ -1089,6 +1297,39 @@ static int smb347_battery_get_property(struct power_supply *psy, + return 0; + } + ++static int smb347_battery_set_property(struct power_supply *psy, ++ enum power_supply_property prop, ++ const union power_supply_propval *val) ++{ ++ int ret = -EINVAL; ++ struct smb347_charger *smb = ++ container_of(psy, struct smb347_charger, battery); ++ ++ switch (prop) { ++ case POWER_SUPPLY_PROP_CHARGE_ENABLED: ++ ret = smb347_charging_set(smb, val->intval); ++ break; ++ ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++static int smb347_battery_property_is_writeable(struct power_supply *psy, ++ enum power_supply_property prop) ++{ ++ switch (prop) { ++ case POWER_SUPPLY_PROP_CHARGE_ENABLED: ++ return 1; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ + static enum power_supply_property smb347_battery_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, +@@ -1098,6 +1339,7 @@ static enum power_supply_property smb347_battery_properties[] = { + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, ++ POWER_SUPPLY_PROP_CHARGE_ENABLED, + POWER_SUPPLY_PROP_MODEL_NAME, + }; + +@@ -1181,6 +1423,33 @@ static int smb347_probe(struct i2c_client *client, + smb->client = client; + smb->pdata = pdata; + ++ smb->mains_current_limit = smb->pdata->mains_current_limit; ++ ++ if (pdata->en_gpio) { ++ ret = gpio_request_one( ++ pdata->en_gpio, ++ smb->pdata->enable_control == ++ SMB347_CHG_ENABLE_PIN_ACTIVE_LOW ? ++ GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, ++ smb->client->name); ++ if (ret < 0) ++ dev_warn(dev, "failed to claim EN GPIO: %d\n", ret); ++ else ++ smb->en_gpio = pdata->en_gpio; ++ } ++ ++ ret = smb347_write(smb, CMD_B, CMD_B_POR); ++ if (ret < 0) ++ return ret; ++ ++ msleep(20); ++ ++ ret = smb347_read(smb, CMD_B); ++ if (ret < 0) { ++ dev_err(dev, "failed read after reset\n"); ++ return ret; ++ } ++ + ret = smb347_hw_init(smb); + if (ret < 0) + return ret; +@@ -1188,6 +1457,8 @@ static int smb347_probe(struct i2c_client *client, + smb->mains.name = "smb347-mains"; + smb->mains.type = POWER_SUPPLY_TYPE_MAINS; + smb->mains.get_property = smb347_mains_get_property; ++ smb->mains.set_property = smb347_mains_set_property; ++ smb->mains.property_is_writeable = smb347_mains_property_is_writeable; + smb->mains.properties = smb347_mains_properties; + smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties); + smb->mains.supplied_to = battery; +@@ -1196,6 +1467,8 @@ static int smb347_probe(struct i2c_client *client, + smb->usb.name = "smb347-usb"; + smb->usb.type = POWER_SUPPLY_TYPE_USB; + smb->usb.get_property = smb347_usb_get_property; ++ smb->usb.set_property = smb347_usb_set_property; ++ smb->usb.property_is_writeable = smb347_usb_property_is_writeable; + smb->usb.properties = smb347_usb_properties; + smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties); + smb->usb.supplied_to = battery; +@@ -1204,9 +1477,17 @@ static int smb347_probe(struct i2c_client *client, + smb->battery.name = "smb347-battery"; + smb->battery.type = POWER_SUPPLY_TYPE_BATTERY; + smb->battery.get_property = smb347_battery_get_property; ++ smb->battery.set_property = smb347_battery_set_property; ++ smb->battery.property_is_writeable = smb347_battery_property_is_writeable; + smb->battery.properties = smb347_battery_properties; + smb->battery.num_properties = ARRAY_SIZE(smb347_battery_properties); + ++ if (smb->pdata->supplied_to) { ++ smb->battery.supplied_to = smb->pdata->supplied_to; ++ smb->battery.num_supplicants = smb->pdata->num_supplicants; ++ smb->battery.external_power_changed = power_supply_changed; ++ } ++ + ret = power_supply_register(dev, &smb->mains); + if (ret < 0) + return ret; +@@ -1250,6 +1531,7 @@ static int smb347_remove(struct i2c_client *client) + + if (client->irq) { + smb347_irq_disable(smb); ++ disable_irq_wake(client->irq); + free_irq(client->irq, smb); + gpio_free(smb->pdata->irq_gpio); + } +@@ -1260,6 +1542,29 @@ static int smb347_remove(struct i2c_client *client) + return 0; + } + ++static int smb347_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ ++ if (client->irq) ++ disable_irq(client->irq); ++ return 0; ++} ++ ++static int smb347_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ ++ if (client->irq) ++ enable_irq(client->irq); ++ return 0; ++} ++ ++static const struct dev_pm_ops smb347_pm_ops = { ++ .suspend = smb347_suspend, ++ .resume = smb347_resume, ++}; ++ + static const struct i2c_device_id smb347_id[] = { + { "smb347", 0 }, + { } +@@ -1269,6 +1574,7 @@ MODULE_DEVICE_TABLE(i2c, smb347_id); + static struct i2c_driver smb347_driver = { + .driver = { + .name = "smb347", ++ .pm = &smb347_pm_ops, + }, + .probe = smb347_probe, + .remove = __devexit_p(smb347_remove), +diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig +index 8c8377d5..cef7eeba 100644 +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -1087,4 +1087,10 @@ config RTC_DRV_LOONGSON1 + This driver can also be built as a module. If so, the module + will be called rtc-ls1x. + ++config RTC_DRV_AK ++ tristate "AK RTC" ++ depends on ARCH_AK39 ++ help ++ If you say Y here you will get support for the onboard AK RTC. ++ + endif # RTC_CLASS +diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile +index 727ae778..aaa613a2 100644 +--- a/drivers/rtc/Makefile ++++ b/drivers/rtc/Makefile +@@ -111,3 +111,4 @@ obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o + obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o + obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o + obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o ++obj-$(CONFIG_RTC_DRV_AK) += plat-anyka/ +diff --git a/drivers/rtc/plat-anyka/Makefile b/drivers/rtc/plat-anyka/Makefile +new file mode 100644 +index 00000000..b18e7080 +--- /dev/null ++++ b/drivers/rtc/plat-anyka/Makefile +@@ -0,0 +1,5 @@ ++# ++# Makefile for RTC class/drivers. ++# ++ ++obj-$(CONFIG_RTC_DRV_AK) += rtc.o +diff --git a/drivers/rtc/plat-anyka/rtc.c b/drivers/rtc/plat-anyka/rtc.c +new file mode 100755 +index 00000000..976f791f +--- /dev/null ++++ b/drivers/rtc/plat-anyka/rtc.c +@@ -0,0 +1,655 @@ ++/** ++* @file drivers/rtc/rtc-ak.c ++* @brief ++* Copyright C 2011 Anyka CO.,LTD ++ ++* This program is free software; you can redistribute it and/or modify ++* it under the terms of the GNU General Public License version 2 as ++* published by the Free Software Foundation. ++* @author zhou wenyong ++* @date 2011-08-29 ++* @note ++* ++*/ ++/* ++ * drivers/rtc/rtc-ak.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * 2011-3-22 for AK zwy ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++ ++ ++//#define RTC_DEBUG ++#define REG32(_reg_) (*(volatile unsigned long *)(_reg_)) ++ ++ ++#undef PDEBUG /* undef it, just in case */ ++#ifdef RTC_DEBUG ++#ifdef __KERNEL__ ++/* This one if debugging is on, and kernel space */ ++#define PDEBUG(fmt, args...) printk( KERN_INFO fmt,## args) ++#else ++/* This one for user space */ ++#define PDEBUG(fmt, args...) fprintf(stderr, "%s %d: "fmt,__FILE__, __LINE__, ## args) ++#endif ++#else ++#define PDEBUG(fmt, args...) /* not debugging: nothing */ ++#endif ++ ++ ++static int ak_rtc_wakeup_enable(int en); ++static void ak_rtc_alarm_enable(void ); ++static void ak_rtc_alarm_disable(void ); ++ ++ ++/** ++* @brief ak_rtc_int_clear ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[in] void ++* @return void ++*/ ++static inline void ak_rtc_int_clear(void) ++{ ++ unsigned int regval; ++ ++ PDEBUG("Entering %s\n", __FUNCTION__); ++ regval = ak_rtc_read(AK_RTC_SETTING); ++ regval |= (1<<0); ++ ++ ak_rtc_write(AK_RTC_SETTING, regval); ++ ++} ++ ++ ++/** ++* @brief ak_rtc_wakeup_enable ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[in] en ++* @return int ++*/ ++static int ak_rtc_wakeup_enable(int en) ++{ ++ unsigned int val; ++ ++ PDEBUG("%s(): Entering...\n", __func__); ++ PDEBUG("%s: %s\n", __FUNCTION__, en ? "enable" : "disable"); ++ ++ /* read rtc wakeup setting and clear status */ ++ val = REG32(OTHER_WAKEUP_CTRL); ++ val &= ~RTC_WAKEUP_EN; ++ REG32(OTHER_WAKEUP_CTRL) = val; ++ ++ ++ if (en) ++ { ++ /* enable wakeup signal */ ++ val |= RTC_WAKEUP_EN ;//| RTC_WAKEUP_SIGNAL_LOWACTIVE); ++ REG32(OTHER_WAKEUP_CTRL) = val; ++ } ++ else ++ { ++ /* disable wakeup signal */ ++ /* val |= RTC_WAKEUP_SIGNAL_CLEAR; */ ++ /* ak_rtc_write(AK_WDT_TIMER1, val); */ ++ } ++ return 0; ++ ++} ++ ++ ++/** ++* @brief ak_rtc_gettime ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *dev ++* @param[out] *tm ++* @return int ++*/ ++static int ak_rtc_gettime(struct device *dev, struct rtc_time *tm) ++{ ++ unsigned long rtcset; ++ unsigned long rtc_time1; ++ unsigned long rtc_time2; ++ unsigned long rtc_time3; ++ ++ PDEBUG("Entering %s\n", __FUNCTION__); ++ rtcset = ak_rtc_read(AK_RTC_SETTING); ++ rtcset |= RTC_SETTING_REAL_TIME_RE; ++ ak_rtc_write(AK_RTC_SETTING, rtcset); ++ ++ rtc_time1 = ak_rtc_read(AK_RTC_REAL_TIME1); ++ rtc_time2 = ak_rtc_read(AK_RTC_REAL_TIME2); ++ rtc_time3 = ak_rtc_read(AK_RTC_REAL_TIME3); ++ ++ tm->tm_year = ((rtc_time3 >> 4) & 0x7F) - EPOCH_START_YEAR + RTC_START_YEAR; ++ tm->tm_mon = (rtc_time3 & 0xF) - 1; ++ tm->tm_mday = (rtc_time2 >> 5) & 0x1F; ++ tm->tm_hour = rtc_time2 & 0x1F; ++ tm->tm_min = (rtc_time1 >> 6) & 0x3F; ++ tm->tm_sec = rtc_time1 & 0x3F; ++ tm->tm_wday = (rtc_time2 >> 10) & 0x7; ++ tm->tm_isdst = -1; ++ if ((tm->tm_year < (RTC_START_YEAR - EPOCH_START_YEAR)) ++ || (tm->tm_year > (RTC_START_YEAR - EPOCH_START_YEAR + RTC_YEAR_COUNT))) { ++ printk("%s(): -----RTC Year must between (%d ~ %d), now tm->tm_year:%d--------------\n", ++ __func__, RTC_START_YEAR, (RTC_START_YEAR + RTC_YEAR_COUNT), tm->tm_year); ++ ++ tm->tm_year = 2011 - EPOCH_START_YEAR ; ++ tm->tm_mon = 0; ++ tm->tm_mday = 1; ++ tm->tm_hour = 0; ++ tm->tm_min = 0; ++ tm->tm_sec = 0; ++ tm->tm_wday = 6; ++ ++ } ++#if 0 ++ printk("Current RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n", ++ tm->tm_year+1900, tm->tm_mon + 1, tm->tm_mday, ++ tm->tm_hour, tm->tm_min, tm->tm_sec); ++#endif ++ return 0; ++} ++ ++ ++/** ++* @brief ak_rtc_settime ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *dev ++* @param[out] *tm ++* @return int ++*/ ++static int ak_rtc_settime(struct device *dev, struct rtc_time *tm) ++{ ++ unsigned long regval; ++ ++ PDEBUG("Entering %s\n", __FUNCTION__); ++ printk("%s: %d-%d-%d %d:%d:%d\n", __func__, tm->tm_year, tm->tm_mon + 1, ++ tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); ++ if ((tm->tm_year < (RTC_START_YEAR - EPOCH_START_YEAR)) ++ || (tm->tm_year > (RTC_START_YEAR - EPOCH_START_YEAR + RTC_YEAR_COUNT))) { ++ printk("%s(): RTC Year must between (%d ~ %d)\n", ++ __func__, RTC_START_YEAR, (RTC_START_YEAR + RTC_YEAR_COUNT)); ++ return -1; ++ } ++ ++ ak_rtc_write(AK_RTC_REAL_TIME1, (tm->tm_min << 6) | tm->tm_sec); ++ ak_rtc_write(AK_RTC_REAL_TIME2, (tm->tm_wday << 10) | (tm->tm_mday << 5) | tm->tm_hour); ++ ak_rtc_write(AK_RTC_REAL_TIME3, ((tm->tm_year + EPOCH_START_YEAR - RTC_START_YEAR) << 4) | (tm->tm_mon + 1)); ++ ++ regval = ak_rtc_read(AK_RTC_SETTING); ++ regval |= RTC_SETTING_REAL_TIME_WR; ++ ak_rtc_write(AK_RTC_SETTING, regval); ++ ++ while (ak_rtc_read(AK_RTC_SETTING) & RTC_SETTING_REAL_TIME_WR) ++ ; ++ ++ udelay(40); ++ ++ return 0; ++} ++ ++/** ++* @brief ak_rtc_getalarm ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *dev ++* @param[out] *wkalrm ++* @return int ++*/ ++static int ak_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) ++{ ++ unsigned long rtc_alarm1; ++ unsigned long rtc_alarm2; ++ unsigned long rtc_alarm3; ++ ++ PDEBUG("Entering %s\n", __FUNCTION__); ++ ++ rtc_alarm1 = ak_rtc_read(AK_RTC_ALARM_TIME1); ++ rtc_alarm2 = ak_rtc_read(AK_RTC_ALARM_TIME2); ++ rtc_alarm3 = ak_rtc_read(AK_RTC_ALARM_TIME3); ++ ++ PDEBUG("%x %x %x\n", rtc_alarm1, rtc_alarm2, rtc_alarm3); ++ ++ wkalrm->time.tm_sec = rtc_alarm1 & 0x3F; ++ wkalrm->time.tm_min = (rtc_alarm1 >> 6) & 0x3F; ++ wkalrm->time.tm_hour = rtc_alarm2 & 0x1F; ++ wkalrm->time.tm_mday = (rtc_alarm2 >> 5) & 0x1F; ++ wkalrm->time.tm_mon = (rtc_alarm3 & 0xF) - 1; ++ wkalrm->time.tm_year = ((rtc_alarm3 >> 4) & 0x7F) - EPOCH_START_YEAR + RTC_START_YEAR; ++ wkalrm->time.tm_isdst = -1; ++ ++ PDEBUG("%d-%d-%d %d:%d:%d",wkalrm->time.tm_year,wkalrm->time.tm_mon + 1,wkalrm->time.tm_mday, ++ wkalrm->time.tm_hour,wkalrm->time.tm_min ,wkalrm->time.tm_sec); ++ return 0; ++} ++ ++ ++/** ++* @brief ak_rtc_setalarm ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *dev ++* @param[out] *wkalrm ++* @return int ++*/ ++static int ak_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) ++{ ++ unsigned long rtc_alarm1; ++ unsigned long rtc_alarm2; ++ unsigned long rtc_alarm3; ++ ++ PDEBUG("Entering %s\n", __FUNCTION__); ++ rtc_alarm1 = ak_rtc_read(AK_RTC_ALARM_TIME1); ++ rtc_alarm2 = ak_rtc_read(AK_RTC_ALARM_TIME2); ++ rtc_alarm3 = ak_rtc_read(AK_RTC_ALARM_TIME3); ++ ++ PDEBUG("%u %u %u\n", rtc_alarm1, rtc_alarm2, rtc_alarm3); ++ PDEBUG("%d-%d-%d %d:%d:%d",wkalrm->time.tm_year,wkalrm->time.tm_mon + 1,wkalrm->time.tm_mday, ++ wkalrm->time.tm_hour,wkalrm->time.tm_min ,wkalrm->time.tm_sec); ++ ++ rtc_alarm1 &= ~(0xFFF); ++ rtc_alarm2 &= ~(0x3FF); ++ rtc_alarm3 &= ~(0x7FF); ++ ++ ++ rtc_alarm1 |= ((wkalrm->time.tm_min << 6) + wkalrm->time.tm_sec); ++ rtc_alarm2 |= ((wkalrm->time.tm_mday << 5) + wkalrm->time.tm_hour); ++ rtc_alarm3 |= (((wkalrm->time.tm_year + EPOCH_START_YEAR - RTC_START_YEAR) << 4) + (wkalrm->time.tm_mon + 1)); ++ ++ ak_rtc_write(AK_RTC_ALARM_TIME1, rtc_alarm1); ++ ak_rtc_write(AK_RTC_ALARM_TIME2, rtc_alarm2); ++ ak_rtc_write(AK_RTC_ALARM_TIME3, rtc_alarm3); ++ ++ PDEBUG("%u %u %u\n", rtc_alarm1, rtc_alarm2, rtc_alarm3); ++ ++ if (wkalrm->enabled == 1) ++ { ++ ak_rtc_wakeup_enable(1); ++ ak_rtc_alarm_enable(); ++ } ++ else ++ { ++ ak_rtc_alarm_disable(); ++ ak_rtc_wakeup_enable(0); ++ } ++ ++ return 0; ++} ++/** ++* @brief ak_rtc_alarm_enable ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[in] void ++* @return void ++*/ ++static void ak_rtc_alarm_enable(void ) ++{ ++ unsigned long rtc_alarm1; ++ unsigned long rtc_alarm2; ++ unsigned long rtc_alarm3; ++ ++ rtc_alarm1 = ak_rtc_read(AK_RTC_ALARM_TIME1); ++ rtc_alarm2 = ak_rtc_read(AK_RTC_ALARM_TIME2); ++ rtc_alarm3 = ak_rtc_read(AK_RTC_ALARM_TIME3); ++ ++ rtc_alarm1 |= (1<<13); ++ rtc_alarm2 |= (1<<13); ++ rtc_alarm3 |= (1<<13); ++ ++ ak_rtc_write(AK_RTC_ALARM_TIME1, rtc_alarm1); ++ ak_rtc_write(AK_RTC_ALARM_TIME2, rtc_alarm2); ++ ak_rtc_write(AK_RTC_ALARM_TIME3, rtc_alarm3); ++} ++ ++/** ++* @brief ak_rtc_alarm_disable ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[in] void ++* @return void ++*/ ++static void ak_rtc_alarm_disable(void ) ++{ ++ unsigned long rtc_alarm1; ++ unsigned long rtc_alarm2; ++ unsigned long rtc_alarm3; ++ ++ rtc_alarm1 = ak_rtc_read(AK_RTC_ALARM_TIME1); ++ rtc_alarm2 = ak_rtc_read(AK_RTC_ALARM_TIME2); ++ rtc_alarm3 = ak_rtc_read(AK_RTC_ALARM_TIME3); ++ ++ rtc_alarm1 &= ~(1<<13); ++ rtc_alarm2 &= ~(1<<13); ++ rtc_alarm3 &= ~(1<<13); ++ ++ ak_rtc_write(AK_RTC_ALARM_TIME1, rtc_alarm1); ++ ak_rtc_write(AK_RTC_ALARM_TIME2, rtc_alarm2); ++ ak_rtc_write(AK_RTC_ALARM_TIME3, rtc_alarm3); ++} ++ ++ ++/** ++* @brief ak_rtc_ioctl ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *dev ++* @param[in] cmd ++* @param[in] arg ++* @return int ++*/ ++static int ak_rtc_ioctl(struct device *dev, ++ unsigned int cmd, unsigned long arg) ++{ ++ //PDEBUG("Entering %s: cmd %d\n", __FUNCTION__, cmd); ++ unsigned int ret = -ENOIOCTLCMD; ++ ++ switch (cmd) { ++ case RTC_AIE_OFF: ++ PDEBUG("RTC_AIE_OFF\n"); ++ ak_rtc_alarm_disable(); ++ ak_rtc_wakeup_enable(0); ++ ++ ret = 0; ++ break; ++ case RTC_AIE_ON: ++ PDEBUG("RTC_AIE_ON\n"); ++ ak_rtc_wakeup_enable(1); ++ ak_rtc_alarm_enable(); ++ ret = 0; ++ break; ++ ++ case RTC_UIE_ON: ++ case RTC_UIE_OFF: ++ ret = -ENOTTY; ++ break; ++ case RTC_RD_TIME: ++ PDEBUG("Read Time\n"); ++ break; ++ case RTC_SET_TIME: ++ PDEBUG("Set Time\n"); ++ break; ++ case RTC_ALM_SET: ++ PDEBUG("Set alarm\n"); ++ break; ++ case RTC_ALM_READ: ++ PDEBUG("Read alarm\n"); ++ break; ++ case RTC_WKALM_SET: ++ PDEBUG("Set alarm\n"); ++ break; ++ case RTC_WKALM_RD: ++ PDEBUG("Read alarm\n"); ++ break; ++ default: ++ return -ENOTTY; ++ ++ } ++ ++ return ret; ++} ++ ++/** ++* @brief ak_rtc_proc ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *dev ++* @param[out] *seq ++* @return int ++*/ ++static int ak_rtc_proc(struct device *dev, struct seq_file *seq) ++{ ++ PDEBUG("Entering %s\n", __FUNCTION__); ++ return 0; ++} ++ ++/** ++* @brief ak_rtc_alarmirq ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[in] irq ++* @param[out] *id ++* @return irqreturn_t ++*/ ++static irqreturn_t ak_rtc_alarmirq(int irq, void *id) ++{ ++ struct rtc_device *rdev = id; ++ PDEBUG("%s(): Entering...\n", __func__); ++ ak_rtc_int_clear(); ++ rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++* @brief ak_rtc_open ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *dev ++* @return int ++*/ ++static int ak_rtc_open(struct device *dev) ++{ ++ PDEBUG("Entering %s\n", __FUNCTION__); ++ return 0; ++} ++ ++/** ++* @brief ak_rtc_release ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *dev ++* @return void ++*/ ++static void ak_rtc_release(struct device *dev) ++{ ++ PDEBUG("Entering %s\n", __FUNCTION__); ++ ++} ++ ++static const struct rtc_class_ops ak_rtc_ops = { ++ .open = ak_rtc_open, ++ .release = ak_rtc_release, ++ .ioctl = ak_rtc_ioctl, ++ .read_time = ak_rtc_gettime, ++ .set_time = ak_rtc_settime, ++ .read_alarm = ak_rtc_getalarm, ++ .set_alarm = ak_rtc_setalarm, ++ .proc = ak_rtc_proc, ++}; ++ ++/** ++* @brief ak_rtc_remove ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *dev ++* @return int ++*/ ++static int ak_rtc_remove(struct platform_device *dev) ++{ ++ struct rtc_device *rtc = platform_get_drvdata(dev); ++ PDEBUG("Entering %s\n", __FUNCTION__); ++ free_irq(IRQ_RTC_ALARM, rtc); ++ ++ platform_set_drvdata(dev, NULL); ++ rtc_device_unregister(rtc); ++ ++ return 0; ++} ++ ++/** ++* @brief ak_rtc_probe ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *pdev ++* @return int ++*/ ++static int ak_rtc_probe(struct platform_device *pdev) ++{ ++ struct rtc_device *rtc; ++ /* struct resource *res; */ ++ int ret; ++ ++ rtc = rtc_device_register("ak-rtc", &pdev->dev, &ak_rtc_ops, ++ THIS_MODULE); ++ ++ if (IS_ERR(rtc)) { ++ dev_err(&pdev->dev, "cannot attach rtc\n"); ++ ret = PTR_ERR(rtc); ++ ++ return -1; ++ } ++ ++ rtc->max_user_freq = 128; ++ ++ platform_set_drvdata(pdev, rtc); ++ ++ ak_rtc_wakeup_enable(0); ++ ++ ret = request_irq(IRQ_RTC_ALARM, ak_rtc_alarmirq, ++ IRQF_DISABLED, "ak-rtc alarm", rtc); ++ if (ret) { ++ printk("IRQ %d error %d\n", IRQ_RTC_ALARM, ret); ++ return ret; ++ } ++ ++ return 0; ++ ++} ++ ++#ifdef CONFIG_PM ++ ++ ++//static struct timespec ak_rtc_delta; ++/* RTC Power management control */ ++/** ++* @brief ak_rtc_suspend ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *pdev ++* @param[in] state ++* @return int ++*/ ++static int ak_rtc_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct rtc_time tm; ++ struct timespec time; ++ ++ time.tv_nsec = 0; ++ ++ /* calculate time delta for suspend */ ++ ++ ak_rtc_gettime(&pdev->dev, &tm); ++ rtc_tm_to_time(&tm, &time.tv_sec); ++// save_time_delta(&ak_rtc_delta, &time); ++ ++ return 0; ++} ++ ++/** ++* @brief ak_rtc_resume ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[out] *pdev ++* @return int ++*/ ++static int ak_rtc_resume(struct platform_device *pdev) ++{ ++ struct rtc_time tm; ++ struct timespec time; ++ ++ time.tv_nsec = 0; ++ ak_rtc_gettime(&pdev->dev, &tm); ++ rtc_tm_to_time(&tm, &time.tv_sec); ++// restore_time_delta(&ak_rtc_delta, &time); ++ ++ return 0; ++} ++#else ++#define ak_rtc_suspend NULL ++#define ak_rtc_resume NULL ++#endif ++ ++static struct platform_driver ak_rtcdrv = { ++ .probe = ak_rtc_probe, ++ .remove = ak_rtc_remove, ++ .suspend = ak_rtc_suspend, ++ .resume = ak_rtc_resume, ++ .driver = { ++ .name = "ak-rtc", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++/** ++* @brief ak_rtc_init ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[in] void ++* @return int __init ++*/ ++static int __init ak_rtc_init(void) ++{ ++ PDEBUG("RTC Init...\n"); ++ ++ ak_rtc_power(RTC_ON); ++ ++ if (test_rtc_inter_reg(AK_RTC_REAL_TIME1)<0) { ++ printk("Board has not RTC support. exit %s\n", __func__); ++ ak_rtc_power(RTC_OFF); ++ return -ENODEV; ++ } ++ ++ //printk("AK RTC, (c) 2010 ANYKA \n"); ++ return platform_driver_register(&ak_rtcdrv); ++} ++ ++/** ++* @brief ak_rtc_exit ++* @author zhou wenyong ++* @date 2011-08-29 ++* @param[in] void ++* @return void __exit ++*/ ++static void __exit ak_rtc_exit(void) ++{ ++ /* ++ When RTC is powered off, this bit(AK_RTC_CONF [24] ) should be set to 0 ++ */ ++ ak_rtc_power(RTC_OFF); ++ platform_driver_unregister(&ak_rtcdrv); ++} ++ ++module_init(ak_rtc_init); ++module_exit(ak_rtc_exit); ++ ++MODULE_DESCRIPTION("ANYKA AK RTC Driver"); ++MODULE_AUTHOR("anyka"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:ak-rtc"); +diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig +index 29684c81..362baecf 100644 +--- a/drivers/scsi/Kconfig ++++ b/drivers/scsi/Kconfig +@@ -265,7 +265,7 @@ config SCSI_SCAN_ASYNC + + config SCSI_WAIT_SCAN + tristate # No prompt here, this is an invisible symbol. +- default m ++ default n + depends on SCSI + depends on MODULES + # scsi_wait_scan is a loadable module which waits until all the async scans are +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 00c02403..0706d7a0 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -403,6 +403,14 @@ config SPI_NUC900 + help + SPI driver for Nuvoton NUC900 series ARM SoCs + ++config SPI_ANYKA ++ tristate "ANYKA SPI controller" ++ depends on SPI_MASTER && (ARCH_AK98 || ARCH_AK39) ++ select SPI_BITBANG ++ help ++ SPI driver for anyka ARM SoCs ++ ++ + # + # Add new SPI master controllers in alphabetical order above this line + # +@@ -454,6 +462,13 @@ config SPI_TLE62X0 + + endif # SPI_MASTER + ++# (slave support would go here) ++config SPI_ANYKA_SLAVE ++ tristate "ANYKA SPI slave controller " ++ depends on (ARCH_AK98 || ARCH_AK39) && !SPI_ANYKA ++ help ++ This is the SPI slave controller driver for anyka platform ++ + # (slave support would go here) + + endif # SPI +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index 9d75d219..f88966f0 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -62,4 +62,6 @@ obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o + obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o + obj-$(CONFIG_SPI_TXX9) += spi-txx9.o + obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o ++obj-$(CONFIG_SPI_ANYKA) += spi-anyka.o ++obj-$(CONFIG_SPI_ANYKA_SLAVE) += spi-anyka-slave.o + +diff --git a/drivers/spi/spi-anyka-slave.c b/drivers/spi/spi-anyka-slave.c +new file mode 100644 +index 00000000..ec17db3c +--- /dev/null ++++ b/drivers/spi/spi-anyka-slave.c +@@ -0,0 +1,1050 @@ ++/* ++ * spi_anyka_slave.c - Anyka SPI slave controller driver ++ * based on spi_anyka.c ++ * ++ * Copyright (C) Anyka 2012 ++ * Wangsheng Gao ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ */ ++ ++/* ++ * Note: ++ * ++ * Supports interrupt programmed transfers. ++ * ++ */ ++ ++ /* ++ * Usage: ++ * 1. set one device as master: ++ * 1. register spidev board info in platform file; ++ * 2. select spidev and anyka spi controler in menuconfig. ++ * 2. set one device as slave: select anyka spi slave controler in menuconfig ++ * 3. run make command in Documentation/spi/ directory ++ * 4. copy Documentation/spi/spi_master_test and Documentation/spi/spi_slave_test to Your_rootfs_dir ++ * 5. firstly run spi_slave_test in slave and then run spi_master_test in master ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++/** ++* @brief slave data: devices, lock, wait_queue, io resource... ++* @author gao wangsheng ++* @date 2012-10-16 ++* @param[out] void ++* @param[in] void ++* @param[in] void ++* @return void ++*/ ++struct spi_anyka_slave { ++ /* Driver model hookup */ ++ struct platform_device *pdev; ++ struct ak_spi_info *pdata; ++ struct cdev cdev; ++ struct fasync_struct *async_queue; /* asynchronous readers */ ++ ++ struct clk *clk; ++ ++ /* Lock */ ++ struct mutex slave_lock; ++ spinlock_t regs_lock; ++ ++ /* wait queue */ ++ wait_queue_head_t readq; ++ wait_queue_head_t writeq; ++ ++ /* read and write buffer */ ++ char *rdbuf, *rdend; ++ char *recvp, *readp; ++ int rdsize; ++ char *wrbuf, *wrend; ++ char *sentp, *writep; ++ int wrsize; ++ ++ /* Spi controler register addresses */ ++ void *ioarea; ++ void *regs; ++ int irq; ++ ++ /* flag */ ++ u8 mode; ++ u8 bits_per_word; ++ u32 max_speed_hz; ++ int openers; ++}; ++ ++static struct class *slave_class; ++static int slave_major; ++static int slave_minor = 0; ++#define SLAVE_MAX_MINOR (8) ++#define DFT_DIV (255) ++#define DFT_CON AK_SPICON_EN ++#define BUF_LEN (512) ++#define DCNT (0xffff) ++#define SET_CLK (1 << 0) ++#define SET_MODE (1 << 1) ++#define SPI_MODE_MASK (SPI_CPOL | SPI_CPHA ) ++#define SLAVE_MASK (AK_SPISTA_RXHFULL|AK_SPIINT_TIMEOUT |AK_SPIINT_RXFULL) ++ ++#define sdbug(fmt...) //printk(fmt) ++ ++static int akspi_slave_fasync(int fd, struct file *filp, int mode); ++ ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief How much space is free? ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *slave ++* @return free space in buffer ++*/ ++static int akspi_slave_space_free(struct spi_anyka_slave *slave) ++{ ++ if (slave->readp == slave->recvp){ ++ return slave->rdsize - 1; ++ } ++ return ((slave->readp + slave->rdsize - slave->recvp) % slave->rdsize) - 1; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief store data into buffer ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] slave->recvp ++* @param[in] *slave ++* @param[in] *buf ++* @param[in] count ++* @return How much data has been stored ++*/ ++static int akspi_slave_reveive_data(struct spi_anyka_slave *slave, ++ char *buf, size_t count) ++{ ++// sdbug("%s\n", __func__); ++ ++ /* ok, space is there, accept something */ ++ count = min(count, (size_t)akspi_slave_space_free(slave)); ++ if (slave->recvp >= slave->readp){ ++ /* to end-of-buf */ ++ count = min(count, (size_t)(slave->rdend - slave->recvp)); ++ } ++ else { ++ /* the write pointer has wrapped, fill up to rp-1 */ ++ count = min(count, (size_t)(slave->readp - slave->recvp - 1)); ++ } ++ ++ memcpy(slave->recvp, buf, count); ++ slave->recvp += count; ++ if (slave->recvp == slave->rdend){ ++ slave->recvp = slave->rdbuf; /* wrapped */ ++ } ++ ++ return count; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief Enable or disable irq ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *slave ++* @param[in] is enable? ++* @return void ++*/ ++static void akspi_slave_set_irq(struct spi_anyka_slave *slave, int enable) ++{ ++ u32 spiint = SLAVE_MASK; ++ if (enable) ++ iowrite32(spiint, slave->regs + AK_SPIINT); ++ else ++ iowrite32(0, slave->regs + AK_SPIINT); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief receive data int interupt ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *slave ++* @param[in] irq ++* @return irqreturn_t ++*/ ++static irqreturn_t akspi_slave_int(int irq, void *dev_id) ++{ ++ struct spi_anyka_slave *slave = (struct spi_anyka_slave *)dev_id; ++ u32 status, val; ++ u16 count; ++ int i; ++ ++ spin_lock(&slave->regs_lock); ++ status = ioread32(slave->regs + AK_SPISTA); ++ sdbug("%s: status=0x%08x\n", __func__, status); ++ ++ if (((status & AK_SPISTA_TIMEOUT) == AK_SPISTA_TIMEOUT) ++ && ((status & AK_SPISTA_RXEMP) != AK_SPISTA_RXEMP)) ++ { ++ char temp; ++ u16 cnt =0; ++ ++ count = ioread32(slave->regs + AK_SPICNT); ++ cnt = (DCNT - count)%4; ++ val = ioread32(slave->regs + AK_SPIIN); ++ sdbug("%s: AK_SPISTA_TIMEOUT, val=0x%08x, cnt=%u\n", __func__, val, cnt); ++ ++ for (i=0; i> i*8) & 0xff; ++ akspi_slave_reveive_data(slave, &temp, 1); ++ } ++ ++ iowrite32(DCNT, slave->regs + AK_SPICNT); ++ } ++ else if ((status & AK_SPISTA_RXFULL) == AK_SPISTA_RXFULL) ++ { ++ val = ioread32(slave->regs + AK_SPIIN); ++ sdbug("%s: AK_SPISTA_RXFULL, val=0x%08x\n", __func__, val); ++ akspi_slave_reveive_data(slave, (char *)&val, 4); ++ val = ioread32(slave->regs + AK_SPIIN); ++ sdbug("%s: AK_SPISTA_RXFULL, val=0x%08x\n", __func__, val); ++ akspi_slave_reveive_data(slave, (char *)&val, 4); ++ } ++ ++ else if ((status & AK_SPISTA_RXHFULL) == AK_SPISTA_RXHFULL) ++ { ++ val = ioread32(slave->regs + AK_SPIIN); ++ sdbug("%s: AK_SPISTA_RXHFULL, val=0x%08x\n", __func__, val); ++ akspi_slave_reveive_data(slave, (char *)&val, 4); ++ } ++ ++ wake_up_interruptible(&slave->readq); ++ if (slave->async_queue){ ++ kill_fasync(&slave->async_queue, SIGIO, POLL_IN); ++ } ++ spin_unlock(&slave->regs_lock); ++ return IRQ_HANDLED; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief prepare receive data from master ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *slave ++* @return void ++*/ ++static void akspi_slave_prepare_read(struct spi_anyka_slave *slave) ++{ ++ u32 val; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&slave->regs_lock, flags); ++ ++ val = ioread32(slave->regs + AK_SPICON); ++ val &= ~(AK_SPICON_ARRM); ++ val |= AK_SPICON_TGDM; ++ iowrite32(val, slave->regs + AK_SPICON); ++ iowrite32(DCNT, slave->regs + AK_SPICNT); ++ akspi_slave_set_irq(slave, 1); ++ ++ spin_unlock_irqrestore(&slave->regs_lock, flags); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief open slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *node ++* @param[in] *filp ++* @return fail or not ++*/ ++static int akspi_slave_open(struct inode *node, struct file *filp) ++{ ++ struct spi_anyka_slave *slave = container_of(node->i_cdev, struct spi_anyka_slave, cdev); ++ ++ if (mutex_lock_interruptible(&slave->slave_lock)){ ++ return -ERESTARTSYS; ++ } ++ ++ nonseekable_open(node, filp); ++ ++ if (!slave->rdbuf){ ++ /* Alloc memory for receive data */ ++ slave->rdbuf = kzalloc(BUF_LEN, GFP_KERNEL); ++ if (!slave->rdbuf){ ++ mutex_unlock(&slave->slave_lock); ++ printk("%s: %d\n", __func__, __LINE__); ++ return -ENOMEM; ++ } ++ akspi_slave_prepare_read(slave); ++ } ++ ++ slave->rdsize = BUF_LEN; ++ slave->rdend = slave->rdbuf + slave->rdsize; ++ slave->readp = slave->recvp = slave->rdbuf; /* rd and wr from the beginning */ ++ slave->openers++; ++ filp->private_data = slave; ++ mutex_unlock(&slave->slave_lock); ++ sdbug("%s: openers=%d\n", __func__, slave->openers); ++ return 0; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief relese slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *node ++* @param[in] *filp ++* @return fail or not ++*/ ++static int akspi_slave_release(struct inode *node, struct file *filp) ++{ ++ struct spi_anyka_slave *slave = container_of(node->i_cdev, struct spi_anyka_slave, cdev); ++ ++ akspi_slave_fasync(-1, filp, 0); ++ mutex_lock(&slave->slave_lock); ++ ++ slave->openers--; ++ if (!slave->openers){ ++ /* close spi controler */ ++ akspi_slave_set_irq(slave, 0); ++ kfree(slave->rdbuf); ++ slave->rdbuf = NULL; ++ } ++ ++ filp->private_data = NULL; ++ mutex_unlock(&slave->slave_lock); ++ sdbug("%s: openers=%d\n", __func__, slave->openers); ++ return 0; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief setup slave controler ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *slave ++* @param[in] *void ++* @return fail or not ++*/ ++static int akspi_slave_setmode(struct spi_anyka_slave *slave) ++{ ++ u16 spicon; ++ ++ sdbug("%s: set mode-------------.\n", __func__); ++ spin_lock(&slave->regs_lock); ++ spicon = ioread32(slave->regs + AK_SPICON); ++ ++ if (slave->mode & SPI_CPHA) ++ spicon |= AK_SPICON_CPHA; ++ else ++ spicon &= ~AK_SPICON_CPHA; ++ if (slave->mode & SPI_CPOL) ++ spicon |= AK_SPICON_CPOL; ++ else ++ spicon &= ~AK_SPICON_CPOL; ++ ++ iowrite32(spicon, slave->regs + AK_SPICON); ++ spin_unlock(&slave->regs_lock); ++ return 0; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief setup slave controler ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *slave ++* @param[in] *void ++* @return fail or not ++*/ ++static int akspi_slave_setclk(struct spi_anyka_slave *slave) ++{ ++ unsigned int div; ++ unsigned int hz = slave->max_speed_hz; ++ unsigned long clk = ak_get_asic_clk(); ++ u16 spicon; ++ ++ sdbug("%s: set clk-------------.\n", __func__); ++ spin_lock(&slave->regs_lock); ++ spicon = ioread32(slave->regs + AK_SPICON); ++ ++ div = clk / (hz*2) - 1; ++ if (div > 255){ ++ div = 255; ++ } ++ else if (div < 3){ ++ div = 3; ++ } ++ ++ spicon &=~(0xff << 8); ++ spicon |= div << 8; ++ iowrite32(spicon, slave->regs + AK_SPICON); ++ spin_unlock(&slave->regs_lock); ++ return 0; ++} ++ ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief ioctl slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *node ++* @param[in] *filp, cmd, arg ++* @return fail or not ++*/ ++static long akspi_slave_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ int retval = 0; ++ int err = 0; ++ u32 tmp; ++ struct spi_anyka_slave *slave; ++ ++ sdbug("%s: cmd=%u\n", __func__, cmd); ++ ++ /* Check type and command number */ ++ if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) ++ return -ENOTTY; ++ ++ /* Check access direction once here; don't repeat below. ++ * IOC_DIR is from the user perspective, while access_ok is ++ * from the kernel perspective; so they look reversed. ++ */ ++ if (_IOC_DIR(cmd) & _IOC_READ){ ++ err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); ++ } ++ ++ if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE){ ++ err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); ++ } ++ ++ if (err){ ++ return -EFAULT; ++ } ++ ++ slave = filp->private_data; ++ if (!slave){ ++ return -ESHUTDOWN; ++ } ++ ++ mutex_lock(&slave->slave_lock); ++ ++ switch (cmd){ ++ /* read requests */ ++ case SPI_IOC_RD_MODE: ++ retval = __put_user(slave->mode & SPI_MODE_MASK, (__u8 __user *)arg); ++ break; ++ case SPI_IOC_RD_LSB_FIRST: ++ retval = __put_user((slave->mode & SPI_LSB_FIRST) ? 1 : 0, (__u8 __user *)arg); ++ break; ++ case SPI_IOC_RD_BITS_PER_WORD: ++ retval = __put_user(slave->bits_per_word, (__u8 __user *)arg); ++ break; ++ case SPI_IOC_RD_MAX_SPEED_HZ: ++ retval = __put_user(slave->max_speed_hz, (__u32 __user *)arg); ++ break; ++ ++ /* write requests */ ++ case SPI_IOC_WR_MODE: ++ retval = __get_user(tmp, (u8 __user *)arg); ++ if (retval == 0) { ++ u8 save = slave->mode; ++ ++ if (tmp & ~SPI_MODE_MASK) { ++ retval = -EINVAL; ++ break; ++ } ++ ++ tmp |= slave->mode & ~SPI_MODE_MASK; ++ slave->mode = (u8)tmp; ++ retval = akspi_slave_setmode(slave); ++ if (retval < 0) ++ slave->mode = save; ++ else ++ dev_dbg(&slave->pdev->dev, "spi mode %02x\n", tmp); ++ } ++ break; ++ case SPI_IOC_WR_LSB_FIRST: ++ retval = -EINVAL; ++ break; ++ case SPI_IOC_WR_BITS_PER_WORD: ++ retval = -EINVAL; ++ break; ++ case SPI_IOC_WR_MAX_SPEED_HZ: ++ retval = __get_user(tmp, (__u32 __user *)arg); ++ if (retval == 0) { ++ u32 save = slave->max_speed_hz; ++ ++ slave->max_speed_hz = tmp; ++ retval = akspi_slave_setclk(slave); ++ if (retval < 0) ++ slave->max_speed_hz = save; ++ else ++ dev_dbg(&slave->pdev->dev, "%d Hz (max)\n", tmp); ++ } ++ break; ++ ++ default: ++ retval = -EINVAL; ++ break; ++ } ++ ++ mutex_unlock(&slave->slave_lock); ++ return retval; ++ } ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief read slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *buf ++* @param[in] *filp ++* @return read data count ++*/ ++static ssize_t akspi_slave_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) ++{ ++ struct spi_anyka_slave *slave = filp->private_data; ++ ++ if (mutex_lock_interruptible(&slave->slave_lock)){ ++ return -ERESTARTSYS; ++ } ++ ++ sdbug("%s: %d\n", __func__, __LINE__); ++ while (slave->recvp == slave->readp){ ++ mutex_unlock(&slave->slave_lock); ++ if (filp->f_flags & O_NONBLOCK){ ++ return -EAGAIN; ++ } ++ if (wait_event_interruptible(slave->readq, (slave->recvp != slave->readp))){ ++ return -ERESTARTSYS; ++ } ++ if (mutex_lock_interruptible(&slave->slave_lock)){ ++ return -ERESTARTSYS; ++ } ++ sdbug("%s: %d\n", __func__, __LINE__); ++ } ++ sdbug("%s: %d\n", __func__, __LINE__); ++ ++ if (slave->recvp > slave->readp) { ++ /* return the data */ ++ count = min(count, (size_t)(slave->recvp - slave->readp)); ++ } ++ else { ++ /* the write pointer has wrapped, return data up to end */ ++ count = min (count, (size_t)(slave->rdend - slave->readp)); ++ } ++ ++ if (copy_to_user(buf, slave->readp, count)) { ++ mutex_unlock(&slave->slave_lock); ++ return -EFAULT; ++ } ++ ++ slave->readp += count; ++ if (slave->readp == slave->rdend){ ++ slave->readp = slave->rdbuf; ++ } ++ ++ mutex_unlock(&slave->slave_lock); ++ sdbug("%s: %d\n", __func__, __LINE__); ++ return count; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief write slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *buf ++* @param[in] *filp, count, pos ++* @return write data count ++*/ ++static ssize_t akspi_slave_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) ++{ ++ struct spi_anyka_slave *slave = filp->private_data; ++ ++ sdbug("%s: %d\n", __func__, __LINE__); ++ ++ return count; ++ ++ if (mutex_lock_interruptible(&slave->slave_lock)){ ++ return -ERESTARTSYS; ++ } ++ ++ while (slave->sentp != slave->writep){ ++ mutex_unlock(&slave->slave_lock); ++ if (filp->f_flags & O_NONBLOCK){ ++ return -EAGAIN; ++ } ++ if (wait_event_interruptible(slave->writeq, slave->sentp != slave->writep)){ ++ return -ERESTARTSYS; ++ } ++ if (mutex_lock_interruptible(&slave->slave_lock)){ ++ return -ERESTARTSYS; ++ } ++ } ++ ++ /* spi slave has something to write */ ++ ++ mutex_unlock(&slave->slave_lock); ++ sdbug("%s: %d\n", __func__, __LINE__); ++ return count; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief poll slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *table ++* @param[in] *filp ++* @return fail or not ++*/ ++unsigned int akspi_slave_poll (struct file *filp, struct poll_table_struct *table) ++{ ++ struct spi_anyka_slave *slave = filp->private_data; ++ int mask = 0; ++ ++ sdbug("%s: %d\n", __func__, __LINE__); ++ mutex_lock(&slave->slave_lock); ++ poll_wait(filp, &slave->readq, table); ++ poll_wait(filp, &slave->writeq, table); ++ ++ if (slave->recvp != slave->readp){ ++ mask |= POLLIN | POLLRDNORM; /* readable */ ++ } ++ ++ if (slave->sentp != slave->writep){ ++ mask |= POLLOUT | POLLWRNORM; /* writable */ ++ } ++ mutex_unlock(&slave->slave_lock); ++ sdbug("%s: %d\n", __func__, __LINE__); ++ return mask; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief fasync slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *node ++* @param[in] *filp, fd, mode ++* @return fail or not ++*/ ++static int akspi_slave_fasync(int fd, struct file *filp, int mode) ++{ ++ struct spi_anyka_slave *slave = filp->private_data; ++ ++ return fasync_helper(fd, filp, mode, &slave->async_queue); ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief initialize slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *slave ++* @param[in] void ++* @return void ++*/ ++static void akspi_slave_initial_setup(struct spi_anyka_slave *slave) ++{ ++ u32 value = 0; ++ ++ sdbug("%s: %d\n", __func__, __LINE__); ++ clk_enable(slave->clk); ++ ++ spin_lock(&slave->regs_lock); ++ value = DFT_CON ; ++ iowrite32(value, slave->regs + AK_SPICON); ++ spin_unlock(&slave->regs_lock); ++ akspi_slave_setclk(slave); ++ akspi_slave_setmode(slave); ++ sdbug("value=%08x, reg=%08x\n",value, ioread32(slave->regs + AK_SPICON)); ++ akspi_slave_set_irq(slave, 0); ++ ++ if (slave->pdata) { ++ if (slave->pdata->gpio_setup){ ++ slave->pdata->gpio_setup(slave->pdata, 1); ++ } ++ } ++} ++ ++static const struct file_operations slave_ops = { ++ .owner = THIS_MODULE, ++ .open = akspi_slave_open, ++ .release = akspi_slave_release, ++ .unlocked_ioctl = akspi_slave_ioctl, ++ .read = akspi_slave_read, ++ .write = akspi_slave_write, ++ .poll = akspi_slave_poll, ++ .fasync = akspi_slave_fasync, ++}; ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief probe slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] slave ++* @param[in] *pdev ++* @param[in] *pdata ++* @return fail or not ++*/ ++static int ak_spi_slave_probe(struct platform_device *pdev) ++{ ++ struct ak_spi_info *pdata; ++ struct spi_anyka_slave *slave = NULL; ++ struct resource *res; ++ int err = 0; ++ ++ pdata = pdev->dev.platform_data; ++ if (pdata == NULL) ++ { ++ dev_err(&pdev->dev, "No platform data supplied\n"); ++ err = -ENOENT; ++ goto err_no_pdata; ++ } ++ ++ /* Allocate Slave with space for drv_data and null dma buffer */ ++ slave = kzalloc(sizeof(struct spi_anyka_slave), GFP_KERNEL); ++ if (!slave) { ++ dev_err(&pdev->dev, "cannot alloc mem\n"); ++ err = -ENOMEM; ++ goto err_nomem; ++ } ++ ++ slave->pdata = pdata; ++ slave->pdev = pdev; ++ cdev_init(&slave->cdev, &slave_ops); ++ slave->cdev.owner = THIS_MODULE; ++ ++ spin_lock_init(&slave->regs_lock); ++ mutex_init(&slave->slave_lock); ++ init_waitqueue_head(&slave->readq); ++ init_waitqueue_head(&slave->writeq); ++ slave->mode = SPI_MODE_0; ++ slave->bits_per_word = 8; ++ slave->max_speed_hz = 2147483647; ++ ++ /* get basic io resource and map it */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); ++ err = -ENOENT; ++ goto err_no_iores; ++ } ++ ++ slave->ioarea = request_mem_region(res->start, resource_size(res), pdev->name); ++ if (slave->ioarea == NULL) { ++ dev_err(&pdev->dev, "Cannot reserve region\n"); ++ err = -ENXIO; ++ goto err_no_iores; ++ } ++ ++ slave->regs = ioremap(res->start, resource_size(res)); ++ if (!slave->regs) { ++ err = -ENOMEM; ++ goto err_no_iomap; ++ } ++ printk(KERN_INFO "SPI-Slave:map regs = %08x\n", (int)slave->regs); ++ ++ /* Attach to IRQ */ ++ slave->irq = platform_get_irq(pdev, 0); ++ if (slave->irq < 0) { ++ dev_err(&pdev->dev, "No IRQ specified\n"); ++ err = -ENOENT; ++ goto err_no_irq; ++ } ++ printk(KERN_INFO "SPI-Slave:get irq = %04x\n", (int)slave->irq); ++ ++ err = request_irq(slave->irq, akspi_slave_int, IRQF_DISABLED, pdev->name, slave); ++ if (err < 0) { ++ dev_err(&pdev->dev, "can not get IRQ\n"); ++ goto err_no_irq; ++ } ++ printk(KERN_INFO "SPI-Slave: request IRQ: %04x\n", slave->irq); ++ ++ err = cdev_add(&slave->cdev, MKDEV(slave_major, slave_minor), 1); ++ if (err){ ++ dev_err(&pdev->dev, "cannot add cdev\n"); ++ err = -ENOMEM; ++ goto err_register; ++ } ++ printk(KERN_INFO "SPI-Slave: register with char device framework\n"); ++ ++ if (IS_ERR(device_create(slave_class, &pdev->dev, ++ MKDEV(slave_major, slave_minor), ++ slave, "spi_slave.%u", slave_minor))){ ++ dev_err(&pdev->dev, "cannot device_create\n"); ++ } ++ ++ slave->clk = clk_get(&pdev->dev, pdata->clk_name); ++ sdbug("%s: %lu \n", slave->clk->name, clk_get_rate(slave->clk)); ++ if (IS_ERR(slave->clk)) { ++ dev_err(&pdev->dev, "No clock for device\n"); ++ err = PTR_ERR(slave->clk); ++ goto err_no_clk; ++ } ++ ++ akspi_slave_initial_setup(slave); ++ ++ platform_set_drvdata(pdev, slave); ++ printk("Ak spi slave initialized!\n"); ++ return 0; ++ ++ err_no_clk: ++ device_destroy(slave_class, MKDEV(slave_major, slave_minor)); ++ cdev_del(&slave->cdev); ++ err_register: ++ free_irq(slave->irq, slave); ++ err_no_irq: ++ iounmap(slave->regs); ++ err_no_iomap: ++ release_resource(slave->ioarea); ++ kfree(slave->ioarea); ++ err_no_iores: ++ err_no_pdata: ++ err_nomem: ++ return err; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief remove slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *pdev ++* @param[in] *slave ++* @return void ++*/ ++static void __devexit ak_spi_slave_remove(struct platform_device *pdev) ++{ ++ struct spi_anyka_slave *slave = platform_get_drvdata(pdev); ++ int minor = MINOR(slave->cdev.dev); ++ ++ if (!slave) ++ return; ++ ++ platform_set_drvdata(pdev, NULL); ++ device_destroy(slave_class, MKDEV(slave_major, minor)); ++ cdev_del(&slave->cdev); ++ ++ /* Release IRQ */ ++ free_irq(slave->irq, slave); ++ iounmap(slave->regs); ++ release_resource(slave->ioarea); ++ kfree(slave->ioarea); ++ kfree(slave); ++ ++ return; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief suspend and resume slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] *dev ++* @param[in] state ++* @return fail or not ++*/ ++#ifdef CONFIG_PM ++static int ak_spi_slave_suspend(struct device *dev, pm_message_t state) ++{ ++ struct spi_anyka_slave *slave = dev_get_drvdata(dev); ++ ++ printk(KERN_ERR "%s: suspend\n", slave->pdev->name); ++ return 0; ++} ++ ++static int ak_spi_slave_resume(struct device *dev) ++{ ++ struct spi_anyka_slave *slave = dev_get_drvdata(dev); ++ ++ printk(KERN_ERR "%s: resume\n", slave->pdev->name); ++ return 0; ++} ++#else ++#define ak_spi_slave_suspend NULL ++#define ak_spi_slave_resume NULL ++#endif /* CONFIG_PM */ ++ ++static struct platform_driver akspi_slave_driver = { ++ .driver = { ++ .name = "akspi-spi", ++ .owner = THIS_MODULE, ++ .suspend = ak_spi_slave_suspend, ++ .resume = ak_spi_slave_resume, ++ }, ++ .remove = __exit_p(ak_spi_slave_remove), ++ .probe = ak_spi_slave_probe, ++}; ++ ++static ssize_t slave_show_version(struct class *cls, struct class_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "spi-1.0.01\n"); ++} ++static CLASS_ATTR(version, 0444, slave_show_version, NULL); ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief init slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] void ++* @param[in] void ++* @return fail or not ++*/ ++static int __init ak_spi_slave_init(void) ++{ ++ int ret; ++ dev_t dev; ++ ++ slave_class = class_create(THIS_MODULE, "spi_slave"); ++ if (IS_ERR(slave_class)){ ++ ret = PTR_ERR(slave_class); ++ printk("%s:class create fail!\n", __func__); ++ goto err; ++ } ++ ++ ret = class_create_file(slave_class, &class_attr_version); ++ if (ret){ ++ printk("%s:class create file fail!\n", __func__); ++ goto err_class; ++ } ++ ++ ret = alloc_chrdev_region(&dev, 0, SLAVE_MAX_MINOR, "spi_slave"); ++ if (ret){ ++ printk("%s:alloc chrdev fail!\n", __func__); ++ goto err_chrdev; ++ } ++ slave_major = MAJOR(dev); ++ ++ ret = platform_driver_register(&akspi_slave_driver); ++ if (ret){ ++ printk("%s:platform_driver_register fail!\n", __func__); ++ goto err_plat; ++ } ++ ++ return 0; ++ ++err_plat: ++ unregister_chrdev_region(dev, SLAVE_MAX_MINOR); ++err_chrdev: ++ class_remove_file(slave_class, &class_attr_version); ++err_class: ++ class_destroy(slave_class); ++err: ++ return ret; ++} ++ ++/** ++* @Copyright (C) Anyka 2012 ++* @brief exit slave device ++* @author Gao wangsheng ++* @email gao_wangsheng@anyka.oa ++* @date 2012-10-23 ++* @param[out] void ++* @param[in] void ++* @param[in] void ++* @return void ++*/ ++static void __exit ak_spi_slave_exit(void) ++{ ++ platform_driver_unregister(&akspi_slave_driver); ++ unregister_chrdev_region(MKDEV(slave_major, 0), SLAVE_MAX_MINOR); ++ class_remove_file(slave_class, &class_attr_version); ++ class_destroy(slave_class); ++} ++ ++MODULE_AUTHOR("Wangsheng Gao"); ++MODULE_DESCRIPTION("Anyka SPI Slave Contoller"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:AK-spi-slave"); ++module_init(ak_spi_slave_init); ++module_exit(ak_spi_slave_exit); +diff --git a/drivers/spi/spi-anyka.c b/drivers/spi/spi-anyka.c +new file mode 100644 +index 00000000..2e8c9f22 +--- /dev/null ++++ b/drivers/spi/spi-anyka.c +@@ -0,0 +1,1778 @@ ++/** ++* @file /driver/spi/spi_anyka.c ++* @brief AK On-chip SPI driver ++* Copyright C 2011 Anyka CO.,LTD ++* modify based on spi_s3c24xx.c ++* ++* Copyright (c) 2006 Ben Dooks ++* Copyright (c) 2006 Simtec Electronics ++* Ben Dooks ++* ++* This program is free software; you can redistribute it and/or modify ++* it under the terms of the GNU General Public License version 2 as ++* published by the Free Software Foundation. ++* @author zhou wenyong ++* @date 2011-08-19 ++* @note 2011-5-16 created ++* @note 2011-08-19 add more comments ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++//#define SPI_DEBUG ++ ++#define TMPDBG(fmt, args...) //printk( KERN_INFO fmt,## args) ++ ++#define TMPDEBUG(fmt, args...) //printk( KERN_INFO fmt,## args) ++ ++/* #define DEBUG */ ++#undef PDEBUG /* undef it, just in case */ ++#ifdef SPI_DEBUG ++# ifdef __KERNEL__ ++/* This one if debugging is on, and kernel space */ ++# define PDEBUG(fmt, args...) printk( KERN_INFO fmt,## args) ++# else ++/* This one for user space */ ++# define PDEBUG(fmt, args...) fprintf(stderr, "%s %d: "fmt,__FILE__, __LINE__, ## args) ++# endif ++#else ++# define PDEBUG(fmt, args...) /* not debugging: nothing */ ++#endif ++ ++ /*usually use for spi keyboard or spi mouse.*/ ++//#define SPI_CPU_MODE_USE_INTERRUPT ++ ++ ++/** ++ * ak_spi_devstate - per device data ++ * @hz: Last frequency calculated for @sppre field. ++ * @mode: Last mode setting for the @spcon field. ++ * @spcon: Value to write to the SPCON register. ++ * @sppre: Value to write to the SPIINT register. ++ */ ++struct ak_spi_devstate { ++ unsigned int hz; ++ u16 mode; ++ u32 spcon; ++ u8 spint; ++ u8 initialed; ++}; ++ ++struct ak_spi { ++ /* bitbang has to be first */ ++ struct spi_bitbang bitbang; ++ struct completion done; ++ struct spi_master *master; ++ struct spi_device *curdev; ++ struct device *dev; ++ struct ak_spi_info *pdata; ++ ++ void __iomem *regs; ++ struct clk *clk; ++ struct resource *ioarea; ++ int irq; ++ int len; /*need transfer len*/ ++ int count; /*have transferred len*/ ++ ++ u8 l2buf_tid; ++ u8 l2buf_rid; ++ ++ /* data buffers */ ++ unsigned char *tx; ++ unsigned char *rx; ++ int xfer_dir; ++ int xfer_mode; /*use for dma or cpu*/ ++}; ++ ++enum spi_xfer_dir { ++ SPI_DIR_TX, ++ SPI_DIR_RX, ++ SPI_DIR_TXRX, ++ SPI_DIR_XFER_NUM, ++}; ++ ++ ++#define TRANS_TIMEOUT (10000) ++#define MAX_XFER_LEN (8*1024) ++#define SPI_TRANS_TIMEOUT (5000) ++ ++#define DFT_CON (AK_SPICON_EN | AK_SPICON_MS) ++#define DFT_DIV (1) //5 /*127*/ ++#define DFT_BIT_PER_WORD (8) ++#define FORCE_CS (1 << 5) ++#define SPPIN_DEFAULT (0) ++ ++#if 1 ++/** ++* @brief print the value of registers related spi bus. ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[in] *hw ++* @return void ++*/ ++static void dbg_dumpregs(struct ak_spi *hw) ++{ ++ PDEBUG("\n"); ++ PDEBUG("CON: \t0x%x\n", ioread32(hw->regs + AK_SPICON)); ++ PDEBUG("STA: \t0x%x\n", ioread32(hw->regs + AK_SPISTA)); ++ PDEBUG("INT: \t0x%x\n", ioread32(hw->regs + AK_SPIINT)); ++ PDEBUG("CNT: \t0x%x\n", ioread32(hw->regs + AK_SPICNT)); ++ PDEBUG("DOUT: \t0x%x\n", ioread32(hw->regs + AK_SPIOUT)); ++ PDEBUG("DIN: \t0x%x\n", ioread32(hw->regs + AK_SPIIN)); ++} ++ ++static inline void dbg_dumpdata(struct ak_spi *hw, ++ void *data, int size) ++{ ++ int ii; ++ int dsize = (size +3)/4; ++ u32 *dptr = data; ++ ++ printk("xfer data (size:%d):", size); ++ ++ for(ii = 0; ii < dsize; ii++) { ++ if((ii%10) == 0) ++ printk("\n"); ++ ++ printk("%08x ", *(dptr + ii)); ++ } ++ printk("\n"); ++} ++#endif ++ ++ ++#define AKSPI_DATA_WIRE(mode) \ ++ (((mode & XFER_4DATAWIRE) == XFER_4DATAWIRE) ? \ ++ AKSPI_4DATAWIRE:((mode & XFER_2DATAWIRE) == XFER_2DATAWIRE) ? \ ++ AKSPI_2DATAWIRE : AKSPI_1DATAWIRE) ++ ++ ++#define SPI_L2_TXADDR(m) \ ++ ((m->bus_num == AKSPI_BUS_NUM1) ? ADDR_SPI1_TX : ADDR_SPI2_TX) ++ ++#define SPI_L2_RXADDR(m) \ ++ ((m->bus_num == AKSPI_BUS_NUM1) ? ADDR_SPI1_RX : ADDR_SPI2_RX) ++ ++ ++#define SPI_SHAREPIN(m) \ ++ ((m->bus_num == AKSPI_BUS_NUM1) ? ePIN_AS_SPI1 : ePIN_AS_SPI2) ++ ++#define SPI_RESET_NUM(m) \ ++ ((m->bus_num == AKSPI_BUS_NUM1) ? AK39_SRESET_SPI1 : AK39_SRESET_SPI2) ++ ++ ++static inline struct ak_spi *to_hw(struct spi_device *sdev) ++{ ++ return spi_master_get_devdata(sdev->master); ++} ++ ++/** ++* @brief hw_txbyte ++* TODO: send one bytes ++* @author lixinhai ++* @date 2013-03-19 ++* @param[in] *hw ++* @param[in] count ++* @return unsigned int ++*/ ++static inline unsigned int hw_txbyte(struct ak_spi *hw, int len) ++{ ++ u32 val = 0; ++ int i = 0; ++ ++ while (i < len) ++ { ++ val |= (hw->tx[hw->count+i] << i*8); ++ i++; ++ } ++ return val; ++} ++ ++/** ++* @brief hw_txdword ++* TODO: send double words ++* @author lixinhai ++* @date 2013-03-19 ++* @param[in] *hw ++* @param[in] count ++* @return unsigned int ++*/ ++static unsigned int hw_txdword(struct ak_spi *hw) ++{ ++ u32 val = 0; ++ int l = 0; ++ ++ l = (hw->len - hw->count) > 4 ? 4 : (hw->len - hw->count); ++ ++ val = hw_txbyte(hw, l); ++ ++ hw->count += l; ++ PDEBUG("[%08x] ", val); ++ return val; ++} ++ ++/** ++* @brief hw_rxbyte ++* TODO: recv one bytes ++* @author lixinhai ++* @date 2013-03-19 ++* @param[in] count ++* @return unsigned int ++*/ ++static inline void hw_rxbyte(struct ak_spi *hw, unsigned int val, int len) ++{ ++ int i = 0; ++ ++ while (i < len) ++ { ++ hw->rx[hw->count + i] = (val >> i*8) & 0xff; ++ i++; ++ } ++} ++ ++/** ++* @brief hw_rxbyte ++* TODO: double words ++* @author lixinhai ++* @date 2013-03-19 ++* @param[in] count ++* @return unsigned int ++*/ ++static void hw_rxdword(struct ak_spi *hw, unsigned int val) ++{ ++ int l = 0; ++ ++ l = (hw->len - hw->count) > 4 ? 4 : (hw->len - hw->count); ++ ++ hw_rxbyte(hw, val, l); ++ hw->count += l; ++ PDEBUG("[%08x] ", val); ++} ++ ++static inline u32 hw_remain_datalen(struct ak_spi *hw) ++{ ++ return (hw->len - hw->count); ++} ++ ++static inline bool ak_spi_use_dma(struct ak_spi *hw) ++{ ++ return (hw->xfer_mode == AKSPI_XFER_MODE_DMA); ++} ++ ++static inline int wait_for_spi_cnt_to_zero(struct ak_spi *hw, u32 timeout) ++{ ++ do { ++ if (readl(hw->regs + AK_SPICNT) == 0) ++ break; ++ udelay(1); ++ }while(timeout--); ++ ++ return (timeout > 0) ? 0 : -EBUSY; ++} ++ ++ ++static void ak_spi_gpio_setup(struct ak_spi *hw, int enable) ++{ ++ struct ak_spi_info *plat = hw->pdata; ++ ++ if(!plat) ++ return; ++ ++ if (enable) ++ { ++ int ii; ++ ak_group_config(SPI_SHAREPIN(hw->master)); ++ ++ for(ii=0; iinum_cs; ii++) { ++ if(ii == AKSPI_ONCHIP_CS) ++ continue; ++ ++ ak_setpin_as_gpio(plat->pin_cs[ii]); ++ ak_gpio_dircfg(plat->pin_cs[ii], AK_GPIO_DIR_OUTPUT); ++ ak_gpio_setpin(plat->pin_cs[ii], 1); ++ } ++ } ++} ++ ++static void ak_spi_set_cs(struct ak_spi *hw, int cs, int pol) ++{ ++ struct ak_spi_info *plat = hw->pdata; ++ ++ if(!plat) ++ return; ++ ++ BUG_ON(cs >= plat->num_cs); ++ ++ ak_gpio_setpin(plat->pin_cs[cs], pol); ++} ++ ++ ++ ++/** ++* @brief ak_spi_chipsel ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[out] *spi ++* @param[in] value ++* @return void ++*/ ++static void ak_spi_chipsel(struct spi_device *spi, int value) ++{ ++ struct ak_spi_devstate *cs = spi->controller_state; ++ struct ak_spi *hw = to_hw(spi); ++ unsigned int cspol = (spi->mode & SPI_CS_HIGH) ? 1 : 0; ++ ++ /* change the chipselect state and the state of the spi engine clock */ ++ switch (value) { ++ case BITBANG_CS_INACTIVE: ++ PDEBUG("BITBANG_CS_INACTIVE\n"); ++ ++ if(spi->chip_select == AKSPI_ONCHIP_CS) { ++ cs->spcon = ioread32(hw->regs + AK_SPICON); ++ cs->spcon &= ~FORCE_CS; ++ iowrite32(cs->spcon, hw->regs + AK_SPICON); ++ } ++ else ++ ak_spi_set_cs(hw, spi->chip_select, cspol^1); ++ ++ break; ++ case BITBANG_CS_ACTIVE: ++ PDEBUG("BITBANG_CS_ACTIVE"); ++ ++ if(spi->chip_select == AKSPI_ONCHIP_CS) { ++ cs->spcon = ioread32(hw->regs + AK_SPICON); ++ cs->spcon |= FORCE_CS; //by Shaohua ++ iowrite32(cs->spcon, hw->regs + AK_SPICON); ++ } ++ else ++ ak_spi_set_cs(hw, spi->chip_select, cspol); ++ ++ break; ++ default: ++ break; ++ } ++} ++ ++/** ++* @brief update the device's state ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[in] *spi ++* @param[in] *t ++* @return int ++*/ ++static int ak_spi_update_state(struct spi_device *spi, ++ struct spi_transfer *t) ++{ ++ struct ak_spi_devstate *cs = spi->controller_state; ++ unsigned int bpw; ++ unsigned int hz, typical_hz; ++ unsigned int div = 0; ++ unsigned long clk; ++ ++ bpw = t ? t->bits_per_word : spi->bits_per_word; ++ typical_hz = t ? t->speed_hz : spi->max_speed_hz; ++ ++ if (!bpw) ++ bpw = DFT_BIT_PER_WORD; ++ ++ if (bpw != DFT_BIT_PER_WORD) { ++ dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw); ++ return -EINVAL; ++ } ++ ++ /*spi mode change, config again*/ ++ if (spi->mode != cs->mode) { ++ cs->spcon &= ~(AK_SPICON_CPHA|AK_SPICON_CPOL); ++ ++ if ((spi->mode & SPI_CPHA) == SPI_CPHA) ++ cs->spcon |= AK_SPICON_CPHA; ++ ++ if ((spi->mode & SPI_CPOL) == SPI_CPOL) ++ cs->spcon |= AK_SPICON_CPOL; ++ ++ cs->mode = spi->mode; ++ } ++ ++ if (cs->hz != typical_hz) { ++ if (!typical_hz) ++ typical_hz = spi->max_speed_hz; ++ ++ clk = ak_get_asic_clk(); ++ div = clk / (typical_hz*2) - 1; ++ ++ if (div > 255) ++ div = 255; ++ ++ if (div < 0) ++ div = 0; ++ ++ hz = clk /((div+1)*2); ++ ++ /*when got clock greater than wanted clock, increase divider*/ ++ if((hz - typical_hz) > 0) ++ div++; ++ ++ printk("pre-scaler=%d (wanted %ldMhz, got %ldMhz)\n", ++ div, typical_hz/MHz, (clk / (2 * (div + 1)))/MHz); ++ ++ cs->hz = hz; ++ cs->spcon &= ~AK_SPICON_CLKDIV; ++ cs->spcon |= div << 8; ++ } ++ PDEBUG("spi new hz is %u, div is %u(%s).\n", cs->hz, div, div ?"change":"not change"); ++ return 0; ++} ++ ++/** ++* @brief setup one transfer ++* setup_transfer() changes clock and/or wordsize to match settings ++* for this transfer; zeroes restore defaults from spi_device. ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[in] *spi ++* @param[in] *t ++* @return int ++*/ ++static int ak_spi_setupxfer(struct spi_device *spi, ++ struct spi_transfer *t) ++{ ++ struct ak_spi_devstate *cs = spi->controller_state; ++ struct ak_spi *hw = to_hw(spi); ++ int ret=0; ++ ++ /*spi device not change and has been initilize, return.*/ ++ if(likely((cs->initialed == 1) && (spi == hw->curdev))) ++ return ret; ++ ++ cs->spcon = readl(hw->regs + AK_SPICON); ++ ++ ret = ak_spi_update_state(spi, t); ++ if (!ret) ++ { ++ /*Add code to fix the issue that the SPICON register ++ was deranged. Shaohua @2012-3-23 ++ */ ++ writel(cs->spcon, hw->regs + AK_SPICON); ++ printk("ak_spi_setupxfer,con:%08x.\n", cs->spcon); ++ cs->initialed = 1; ++ hw->curdev = spi; ++ } ++ ++ return ret; ++} ++ ++#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) ++ ++/** ++* @brief ak_spi_setup ++* updates the device mode and clocking records used by a ++* device's SPI controller; ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[in] *spi -- Master side proxy ++* @return int -- return 0 on success ++*/ ++static int ak_spi_setup(struct spi_device *spi) ++{ ++ struct ak_spi_devstate *cs = spi->controller_state; ++ struct ak_spi *hw = to_hw(spi); ++ int ret; ++ ++ printk("ak_spi setup the master.\n"); ++ ++ /* allocate settings on the first call */ ++ if (!cs) { ++ cs = kzalloc(sizeof(struct ak_spi_devstate), GFP_KERNEL); ++ if (!cs) { ++ dev_err(&spi->dev, "no memory for controller state\n"); ++ return -ENOMEM; ++ } ++ ++ cs->spcon = DFT_CON; ++ cs->hz = -1; ++ spi->controller_state = cs; ++ cs->initialed = 0; ++ } ++ ++ if (spi->mode & ~(hw->pdata->mode_bits)) { ++ dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", ++ spi->mode & ~(hw->pdata->mode_bits)); ++ return -EINVAL; ++ } ++ /* initialise the state from the device */ ++ ret = ak_spi_update_state(spi, NULL); ++ if (ret) ++ return ret; ++ ++ spin_lock(&hw->bitbang.lock); ++ if (!hw->bitbang.busy) { ++ hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); ++ /* need to ndelay for 0.5 clocktick ? */ ++ } ++ spin_unlock(&hw->bitbang.lock); ++ ++ return 0; ++} ++ ++/** ++* @brief ak_spi_cleanup ++* called on to free memory provided by spi_master ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[out] *spi ++* @return void ++*/ ++static void ak_spi_cleanup(struct spi_device *spi) ++{ ++ kfree(spi->controller_state); ++} ++ ++ ++/** ++* @brief enable the irq mask. ++* @author lixinhai ++* @date 2013-03-10 ++* @param[in] *hw :akspi master dev. ++* @param[in] imask: interrupt bit mask. ++* @return new mask. ++*/ ++static inline u32 enable_imask(struct ak_spi *hw, u32 imask) ++{ ++ u32 newmask; ++ ++ newmask = readl(hw->regs + AK_SPIINT); ++ newmask |= imask; ++ ++ writel(newmask, hw->regs + AK_SPIINT); ++ ++ return newmask; ++} ++ ++ ++/** ++* @brief disable the irq mask. ++* @author lixinhai ++* @date 2013-03-10 ++* @param[in] *hw :akspi master dev. ++* @param[in] imask: interrupt bit mask. ++* @return new mask. ++*/ ++static inline u32 disable_imask(struct ak_spi *hw, u32 imask) ++{ ++ u32 newmask; ++ ++ newmask = readl(hw->regs + AK_SPIINT); ++ newmask &= ~imask; ++ ++ writel(newmask, hw->regs + AK_SPIINT); ++ ++ return newmask; ++} ++ ++ ++/** ++* @brief configure the master register when start transfer data. ++* @author lixinhai ++* @date 2013-03-15 ++* @param[in] *hw :akspi master dev. ++* @param[in] t: a read/write buffer pair. ++* @return void ++*/ ++static void ak_spi_start_txrx(struct ak_spi *hw, struct spi_transfer *t) ++{ ++ u32 reg_value; ++ enum spi_xfer_dir dir = hw->xfer_dir; ++ ++ PDEBUG("the spi transfer mode is %s.\n", (dir == SPI_DIR_TXRX) ? ++ "txrx" : (dir == SPI_DIR_RX)? "rx":"tx"); ++ ++ reg_value = readl(hw->regs + AK_SPICON); ++ ++ switch(dir) { ++ case SPI_DIR_TX: ++ reg_value &= ~AK_SPICON_TGDM; ++ reg_value |= AK_SPICON_ARRM; ++ break; ++ case SPI_DIR_RX: ++ reg_value |= AK_SPICON_TGDM; ++ reg_value &= ~AK_SPICON_ARRM; ++ break; ++ case SPI_DIR_TXRX: ++ reg_value &= ~AK_SPICON_TGDM; ++ reg_value &= ~AK_SPICON_ARRM; ++ break; ++ default: ++ break; ++ } ++ ++ /*configure the data wire*/ ++ reg_value &= ~AK_SPICON_WIRE; ++ reg_value |= AKSPI_DATA_WIRE(t->xfer_mode); ++ ++ writel(reg_value, hw->regs + AK_SPICON); ++} ++ ++ ++/** ++* @brief configure the master register when stop transfer data. ++* @author lixinhai ++* @date 2013-03-15 ++* @param[in] *hw :akspi master dev. ++* @param[in] t: a read/write buffer pair. ++* @return void ++*/ ++static void ak_spi_stop_txrx(struct ak_spi *hw, struct spi_transfer *t) ++{ ++ u32 reg_value; ++ ++ reg_value = readl(hw->regs + AK_SPICON); ++ reg_value &= ~AK_SPICON_WIRE; ++ writel(reg_value, hw->regs + AK_SPICON); ++} ++ ++ ++/** ++* @brief spi write data function by l2dma/l2cpu mode. ++* @author lixinhai ++* @date 2013-03-15 ++* @param[in] *hw :akspi master dev. ++* @param[in] buf: transfer data pointer. ++* @param[in] count: transfer data length. ++* @return transfer success result 0, otherwise result a negative value ++*/ ++static int spi_dma_write(struct ak_spi *hw, unsigned char *buf, int count) ++{ ++ int ret = 0; ++ bool flags = false; ++ u32 val; ++ dma_addr_t phyaddr = 0; ++ ++ init_completion(&hw->done); ++ enable_imask(hw, AK_SPIINT_TRANSF); ++ ++ val = AK_SPIEXTX_BUFEN|AK_SPIEXTX_DMAEN; ++ iowrite32(val, hw->regs + AK_SPIEXTX); ++ iowrite32(count, hw->regs + AK_SPICNT); ++ ++ /*use for dma mode: greater than 256 bytes, align 4bytes of buf addr, ++ align 64 bytes of data count*/ ++ if((count < 256) || ((unsigned long)buf & 0x3) || (count & (64 - 1))) { ++ l2_combuf_cpu((unsigned long)buf, hw->l2buf_tid, count, MEM2BUF); ++ ++ } else { ++ phyaddr = dma_map_single(hw->dev, buf, count, DMA_TO_DEVICE); ++ if (phyaddr == 0) { ++ printk("tx dma_map_single error!\n"); ++ ret = -EINVAL; ++ goto wr_exit; ++ } ++ ++ //start l2 dma transmit ++ l2_combuf_dma(phyaddr, hw->l2buf_tid, count, MEM2BUF, AK_FALSE); ++ flags = true; ++ } ++ ++ ret = wait_for_completion_timeout(&hw->done, msecs_to_jiffies(SPI_TRANS_TIMEOUT)); ++ if(ret <= 0) { ++ printk("wait for spi transfer interrupt timeout(%s).\n", __func__); ++ dbg_dumpregs(hw); ++ ret = -EINVAL; ++ goto xfer_fail; ++ } ++ ++ if (flags && (l2_combuf_wait_dma_finish(hw->l2buf_tid) == AK_FALSE)) { ++ printk("%s: l2_combuf_wait_dma_finish failed!\n", __func__); ++ ret = -EINVAL; ++ goto xfer_fail; ++ } ++ ++ ret = wait_for_spi_cnt_to_zero(hw, TRANS_TIMEOUT); ++ if(ret) { ++ printk("%s: wait_for_spi_cnt_to_zero failed!\n", __func__); ++ ret = -EINVAL; ++ goto xfer_fail; ++ } ++ ++ ret = count; ++xfer_fail: ++ if(phyaddr) ++ dma_unmap_single(hw->dev, phyaddr, count, DMA_TO_DEVICE); ++ ++wr_exit: ++ //disable l2 dma ++ iowrite32(0, hw->regs + AK_SPIEXTX); ++ l2_clr_status(hw->l2buf_tid); ++ return ret; ++} ++ ++ ++/** ++* @brief spi read data function by l2dma/l2cpu mode. ++* @author lixinhai ++* @date 2013-03-15 ++* @param[in] *hw :akspi master dev. ++* @param[in] buf: transfer data pointer. ++* @param[in] count: transfer data length. ++* @return transfer success result 0, otherwise result a negative value ++*/ ++static int spi_dma_read(struct ak_spi *hw, unsigned char *buf, int count) ++{ ++ int ret = 0; ++ bool flags = false; ++ u32 val; ++ dma_addr_t phyaddr = 0; ++ ++ //prepare spi read ++ init_completion(&hw->done); ++ enable_imask(hw, AK_SPIINT_TRANSF); ++ ++ val = AK_SPIEXTX_BUFEN|AK_SPIEXTX_DMAEN; ++ iowrite32(val, hw->regs + AK_SPIEXRX); ++ iowrite32(count, hw->regs + AK_SPICNT); ++ ++ if(count < 256 || ((unsigned long)buf & 0x3) || (count & (64 - 1))) { ++ l2_combuf_cpu((unsigned long)buf, hw->l2buf_rid, count, BUF2MEM); ++ } ++ else { ++ phyaddr = dma_map_single(hw->dev, buf, count, DMA_FROM_DEVICE); ++ if (phyaddr == 0) { ++ printk("tx dma_map_single error!\n"); ++ ret = -EINVAL; ++ goto rd_exit; ++ } ++ ++ //start L2 dma ++ l2_combuf_dma(phyaddr, hw->l2buf_rid, count, BUF2MEM, AK_FALSE); ++ flags = true; ++ } ++ ++ ret = wait_for_completion_timeout(&hw->done, msecs_to_jiffies(SPI_TRANS_TIMEOUT)); ++ if(ret <= 0) { ++ printk("wait for spi transfer interrupt timeout(%s).\n", __func__); ++ dbg_dumpregs(hw); ++ ret = -EINVAL; ++ goto xfer_fail; ++ } ++ ++ //wait L2 dma finish, if need frac dma,start frac dma ++ if (flags && l2_combuf_wait_dma_finish(hw->l2buf_rid) == AK_FALSE) { ++ ret = -EINVAL; ++ goto xfer_fail; ++ } ++ ++ /*wait for spi count register value to zero.*/ ++ ret = wait_for_spi_cnt_to_zero(hw, TRANS_TIMEOUT); ++ if(ret) { ++ printk("%s: wait for spi count to zero failed!\n", __func__); ++ ret = -EINVAL; ++ goto xfer_fail; ++ } ++ ++ ret = count; ++xfer_fail: ++ if(phyaddr) ++ dma_unmap_single(hw->dev, phyaddr, count, DMA_FROM_DEVICE); ++ ++rd_exit: ++ //disable l2 dma ++ iowrite32(0, hw->regs + AK_SPIEXRX); ++ l2_clr_status(hw->l2buf_rid); ++ return ret; ++} ++ ++ ++/** ++* @brief spi transfer function by l2dma/l2cpu mode. ++* @author lixinhai ++* @date 2013-03-15 ++* @param[in] *hw :akspi master dev. ++* @param[in] tx: send data pointer. ++* @param[in] rx: receive data pointer. ++* @param[in] count: transfer data length. ++* @return transfer success result 0, otherwise result a negative value ++*/ ++static int spi_dma_duplex(struct ak_spi *hw, ++ unsigned char *tx, unsigned char *rx, int count) ++{ ++ int ret = 0; ++ bool flags = false; ++ dma_addr_t tx_phyaddr, rx_phyaddr; ++ u32 val; ++ ++ init_completion(&hw->done); ++ enable_imask(hw, AK_SPIINT_TRANSF); ++ ++ val = AK_SPIEXTX_BUFEN|AK_SPIEXTX_DMAEN; ++ iowrite32(val, hw->regs + AK_SPIEXTX); ++ ++ val = AK_SPIEXRX_BUFEN|AK_SPIEXRX_DMAEN; ++ iowrite32(val, hw->regs + AK_SPIEXRX); ++ ++ iowrite32(count, hw->regs + AK_SPICNT); ++ ++ if((count < 512) || ((unsigned long)tx & 0x3)) { ++ //printk("write: l2_combuf_cpu\n"); ++ l2_combuf_cpu((unsigned long)tx, hw->l2buf_tid, count, MEM2BUF); ++ l2_combuf_cpu((unsigned long)rx, hw->l2buf_rid, count, BUF2MEM); ++ } ++ else { ++ tx_phyaddr = dma_map_single(hw->dev, tx, count, DMA_TO_DEVICE); ++ if (tx_phyaddr == 0) { ++ printk("tx dma_map_single error!\n"); ++ ret = -EINVAL; ++ goto wr_fail; ++ } ++ ++ //start l2 dma transmit ++ l2_combuf_dma(tx_phyaddr, hw->l2buf_tid, count, MEM2BUF, AK_FALSE); ++ ++ rx_phyaddr = dma_map_single(hw->dev, rx, count, DMA_FROM_DEVICE); ++ if (rx_phyaddr == 0) { ++ printk("tx dma_map_single error!\n"); ++ ret = -EINVAL; ++ goto rd_fail; ++ } ++ ++ //start L2 dma ++ l2_combuf_dma(rx_phyaddr, hw->l2buf_rid, count, BUF2MEM, AK_FALSE); ++ } ++ ret = wait_for_completion_timeout(&hw->done, msecs_to_jiffies(SPI_TRANS_TIMEOUT)); ++ if(ret <= 0) { ++ printk("wait for spi transfer interrupt timeout(%s).\n", __func__); ++ dbg_dumpregs(hw); ++ ret = -EINVAL; ++ goto xfer_fail; ++ } ++ ++ if (flags && ((AK_FALSE == l2_combuf_wait_dma_finish(hw->l2buf_tid)) || ++ (AK_FALSE == l2_combuf_wait_dma_finish(hw->l2buf_rid)))) ++ { ++ printk("%s: l2_combuf_wait_dma_finish failed!\n", __func__); ++ ret = -EINVAL; ++ goto xfer_fail; ++ } ++ ++ ret = wait_for_spi_cnt_to_zero(hw, TRANS_TIMEOUT); ++ if(ret) { ++ printk("%s: wait for spi count to zero failed!\n", __func__); ++ ret = -EINVAL; ++ goto xfer_fail; ++ } ++ ++ ret = 0; ++ ++xfer_fail: ++ dma_unmap_single(hw->dev, rx_phyaddr, count, DMA_FROM_DEVICE); ++ ++rd_fail: ++ dma_unmap_single(hw->dev, tx_phyaddr, count, DMA_TO_DEVICE); ++ ++wr_fail: ++ //disable l2 dma ++ iowrite32(0, hw->regs + AK_SPIEXTX); ++ iowrite32(0, hw->regs + AK_SPIEXRX); ++ l2_clr_status(hw->l2buf_tid); ++ l2_clr_status(hw->l2buf_rid); ++ return ret; ++} ++ ++ ++/** ++* @brief spi transfer function by l2dma/l2cpu mode, actual worker ++* spi_dma_read()/spi_dma_write() to be call. ++* @author lixinhai ++* @date 2013-03-15 ++* @param[in] *hw :akspi master dev. ++* @param[in] dir: transfer direction. ++* @return transfer success result 0, otherwise result a negative value ++*/ ++static int ak_spi_dma_txrx(struct ak_spi *hw, struct spi_transfer *t) ++{ ++ int ret = 0; ++ u32 retlen; ++ u32 count = t->len; ++ ++ hw->tx = (unsigned char*)t->tx_buf; ++ hw->rx = t->rx_buf; ++ hw->l2buf_tid = hw->l2buf_rid = BUF_NULL; ++ PDEBUG("start the spi dma transfer.\n"); ++ ++ switch(hw->xfer_dir) { ++ case SPI_DIR_TXRX: ++ { ++ //alloc L2 buffer ++ hw->l2buf_tid = l2_alloc(SPI_L2_TXADDR(hw->master)); ++ hw->l2buf_rid = l2_alloc(SPI_L2_RXADDR(hw->master)); ++ ++ if ((BUF_NULL == hw->l2buf_tid) || (BUF_NULL == hw->l2buf_rid)) ++ { ++ printk("%s: l2_alloc failed!\n", __func__); ++ ret = -EBUSY; ++ goto txrx_ret; ++ } ++ ++ while(count > 0) { ++ hw->count = 0; ++ hw->len = (count > MAX_XFER_LEN) ? MAX_XFER_LEN : count; ++ ++ retlen = spi_dma_duplex(hw, hw->tx, hw->rx, hw->len); ++ if(unlikely(retlen < 0)) { ++ printk("spi master transfer data error!\n"); ++ ret = -EBUSY; ++ goto txrx_ret; ++ } ++ hw->tx += retlen; ++ hw->rx += retlen; ++ count -= retlen; ++ } ++ break; ++ } ++ case SPI_DIR_TX: ++ { ++ //alloc L2 buffer ++ hw->l2buf_tid = l2_alloc(SPI_L2_TXADDR(hw->master)); ++ if (unlikely(BUF_NULL == hw->l2buf_tid)) { ++ printk("%s: l2_alloc failed!\n", __func__); ++ return -EBUSY; ++ } ++ ++ while(count > 0) { ++ hw->count = 0; ++ hw->len = (count > MAX_XFER_LEN) ? MAX_XFER_LEN : count; ++ ++ retlen = spi_dma_write(hw, hw->tx + hw->count, hw->len); ++ if(unlikely(retlen < 0)) { ++ printk("spi master read data error!\n"); ++ ret = -EBUSY; ++ goto txrx_ret; ++ } ++ hw->tx += retlen; ++ count -= retlen; ++ } ++ break; ++ } ++ case SPI_DIR_RX: ++ { ++ //alloc L2 buffer ++ hw->l2buf_rid = l2_alloc(SPI_L2_RXADDR(hw->master)); ++ if (unlikely(BUF_NULL == hw->l2buf_rid)) ++ { ++ return -EBUSY; ++ } ++ ++ while(count > 0) { ++ hw->count = 0; ++ hw->len = (count > MAX_XFER_LEN) ? MAX_XFER_LEN : count; ++ ++ retlen = spi_dma_read(hw, hw->rx, hw->len); ++ if(unlikely(retlen < 0)) { ++ printk("spi master read data error!\n"); ++ ret = -EBUSY; ++ goto txrx_ret; ++ } ++ hw->rx += retlen; ++ count -= retlen; ++ } ++ break; ++ } ++ } ++ PDEBUG("finish the spi dma transfer.\n"); ++txrx_ret: ++ if(hw->l2buf_tid != BUF_NULL) ++ l2_free(SPI_L2_TXADDR(hw->master)); ++ if(hw->l2buf_rid != BUF_NULL) ++ l2_free(SPI_L2_RXADDR(hw->master)); ++ ++ return ret ? ret : t->len; ++} ++ ++ ++/** ++* @brief spi read/write data function by cpu mode, ++* @author lixinhai ++* @date 2013-03-15 ++* @param[in] *hw :akspi master dev. ++* @param[in] t: a read/write buffer pair. ++* @return transfer success result 0, otherwise result a negative value ++*/ ++static int ak_cpu_duplex(struct spi_device *spi, struct spi_transfer *t) ++{ ++ struct ak_spi *hw = to_hw(spi); ++ u32 tran_4_nbr = hw->len/4; ++ u32 frac_nbr = hw->len%4; ++ u32 status, val; ++ u32 off_set_read = 0, off_set_write = 0; ++ const u8 *buff_tx; ++ u8 *buff_rx; ++ int i = 0, j; ++ u32 to_read = 0, to_write = 0; //, to = 0; ++ ++ hw->tx = (unsigned char*)t->tx_buf; ++ hw->rx = t->rx_buf; ++ ++ buff_tx = hw->tx; ++ buff_rx = hw->rx; ++ ++ PDEBUG("duplex transfer,tran_4_nbr:%u frac_nbr:%u\n", tran_4_nbr, frac_nbr); ++ ++ if (hw->len >= MAX_XFER_LEN) ++ { ++ printk("Too much to be read and send...\n"); ++ return -EINVAL; ++ } ++ ++ //set data count, and the the master will rise clk ++ writel(hw->len, hw->regs + AK_SPICNT); ++ ++ while(1) { ++ //write 4 bytes first, and then read 4 bytes ++ if (iregs + AK_SPISTA); ++ if ((status & AK_SPISTA_TXHEMP) == AK_SPISTA_TXHEMP) { ++ PDEBUG("TX HEMP...\n"); ++ break; ++ } else { ++ if(to_write++ < TRANS_TIMEOUT) ++ udelay(10); ++ else { ++ PDEBUG("master transfer timeout...\n"); ++ goto SPI_TRANS_FAIL; ++ } ++ } ++ } ++ iowrite32(*(volatile u32 *)(buff_tx + off_set_write), hw->regs + AK_SPIOUT); ++ off_set_write += 4; ++ i++; ++ } ++ //write not finished ++ else if (off_set_write < hw->len) { ++ PDEBUG("write frac...\n"); ++ to_write = 0; ++ val = 0; ++ if (frac_nbr != 0) { ++ while(1) { ++ status = ioread32(hw->regs + AK_SPISTA); ++ if ((status & AK_SPISTA_TXHEMP) == AK_SPISTA_TXHEMP) ++ break; ++ if (to_write++ < TRANS_TIMEOUT) ++ udelay(10); ++ else { ++ printk("SPI master write timeout...\n"); ++ goto SPI_TRANS_FAIL; ++ } ++ } ++ ++ for (j=0; jregs + AK_SPIOUT); ++ off_set_write += frac_nbr; ++ } ++ } ++ ++ //read ++ status = ioread32(hw->regs + AK_SPISTA); ++ ++ if ((status & AK_SPISTA_TRANSF) == AK_SPISTA_TRANSF) { ++ if (status & AK_SPISTA_RXFULL) { ++ val = ioread32(hw->regs + AK_SPIIN); ++ *(volatile u32 *)(buff_rx + off_set_read) = val; ++ off_set_read += 4; ++ ++ val = ioread32(hw->regs + AK_SPIIN); ++ *(volatile u32 *)(buff_rx + off_set_read) = val; ++ off_set_read += 4; ++ ++ } else if (status & AK_SPISTA_RXHFULL) { ++ ++ val = ioread32(hw->regs + AK_SPIIN); ++ *(volatile u32 *)(buff_rx + off_set_read) = val; ++ off_set_read += 4; ++ } ++ if (frac_nbr != 0) { ++ PDEBUG("read frac...\n"); ++ val = ioread32(hw->regs + AK_SPIIN); ++ ++ for (j=0; j> j*8) & 0xff; ++ PDEBUG("%x ", *(buff_rx+off_set_read+j)); ++ } ++ } ++ break; ++ } else { ++ if ( (status & AK_SPISTA_RXHFULL) == AK_SPISTA_RXHFULL) { ++ val = ioread32(hw->regs + AK_SPIIN); ++ *(volatile u32 *)(buff_rx + off_set_read) = val; ++ PDEBUG("rx hfull .. %x\n", val); ++ PDEBUG("[0]%x [1]%x [2]%x [3]%x\n", buff_rx[0], buff_rx[1], buff_rx[2], buff_rx[3]); ++ off_set_read += 4; ++ } else { ++ if (to_read++ < TRANS_TIMEOUT) ++ udelay(10); ++ else { ++ PDEBUG("master read timeout...\n"); ++ goto SPI_READ_TIMEOUT; ++ } ++ } ++ } ++ } ++ ++ return hw->len; ++ ++SPI_TRANS_FAIL: ++SPI_READ_TIMEOUT: ++ return off_set_read > off_set_write ? off_set_read: off_set_write; ++ ++} ++ ++ ++#if defined(SPI_CPU_MODE_USE_INTERRUPT) ++static int pio_imasks[SPI_DIR_XFER_NUM] = { ++ AK_SPIINT_TRANSF | AK_SPIINT_TXHEMP, ++ AK_SPIINT_TRANSF | AK_SPIINT_RXHFULL, ++ AK_SPIINT_TXHEMP | AK_SPIINT_RXHFULL, ++}; ++ ++ ++/** ++* @brief spi transfer function by cpu mode. ++* @author lixinhai ++* @date 2013-03-15 ++* @param[in] *hw :akspi master dev. ++* @param[in] dir: transfer direction. ++* @return transfer success result 0, otherwise result a negative value ++*/ ++static int ak_spi_pio_txrx(struct ak_spi *hw, struct spi_transfer *t) ++{ ++ int ret; ++ ++ init_completion(&hw->done); ++ ++ writel(hw->len, hw->regs + AK_SPICNT); ++ enable_imask(hw, pio_imasks[hw->xfer_dir]); ++ ++ ret = wait_for_completion_timeout(&hw->done, msecs_to_jiffies(SPI_TRANS_TIMEOUT)); ++ if(ret <= 0) { ++ printk("wait for spi transfer interrupt timeout(%s).\n", __func__); ++ dbg_dumpregs(hw); ++ return -EINVAL; ++ } ++ ++ return hw->count; ++} ++#else ++ ++ ++/** ++* @brief ak37_spi_writeCPU ++* send message function with CPU mode and polling mode ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[in] *spi ++* @param[in] *t ++* @return int ++*/ ++static int spi_pio_write(struct ak_spi *hw, unsigned char *buf, int count) ++{ ++ u32 status; ++ u32 to = 0; ++ ++ PDEBUG("ak spi write by cpu mode\n"); ++ if (count > 64*1024) ++ { ++ printk("too much to be send...\n"); ++ return -EINVAL; ++ } ++ //set data count, and the the master will rise clk ++ writel(count, hw->regs + AK_SPICNT); ++ ++ while(hw_remain_datalen(hw) > 0) { ++ status = readl(hw->regs + AK_SPISTA); ++ if ((status & AK_SPISTA_TXHEMP) == AK_SPISTA_TXHEMP) { ++ writel(hw_txdword(hw), hw->regs + AK_SPIOUT); ++ }else { ++ if (to++ > 10 * 1000000) { ++ printk("master write data timeout.\n"); ++ return hw->count; ++ } ++ } ++ } ++ ++ //wait transfer finish ++ while(1) { ++ status = readl(hw->regs + AK_SPISTA); ++ if ((status & AK_SPISTA_TRANSF) == AK_SPISTA_TRANSF) ++ break; ++ ++ if (to++ > 10 * 1000000) { ++ printk("wait for write data finish timeout..\n"); ++ return hw->count; ++ } ++ } ++ ++ if (hw_remain_datalen(hw) > 0) ++ printk("write wasn't finished.\n"); ++ ++ return hw->count; ++} ++ ++/** ++* @brief spi_pio_read ++* receiving message function with CPU mode and polling mode ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[in] *spi ++* @param[in] *t ++* @return int ++*/ ++static int spi_pio_read(struct ak_spi *hw, unsigned char *buf, int count) ++{ ++ u32 status; ++ u32 to=0; ++ ++ PDEBUG("ak spi read by cpu mode\n"); ++ if (count >= 64*1024) ++ { ++ printk("too much to be read...\n"); ++ return -EINVAL; ++ } ++ ++ //set data count, and the the master will rise clk ++ writel(count, hw->regs + AK_SPICNT); ++ ++ while(1) { ++ status = readl(hw->regs + AK_SPISTA); ++ ++ if((status & AK_SPISTA_TRANSF) == AK_SPISTA_TRANSF) ++ { ++ if(status & AK_SPISTA_RXFULL) { ++ hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); ++ hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); ++ }else if (status & AK_SPISTA_RXHFULL) { ++ hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); ++ } ++ ++ if (hw_remain_datalen(hw) > 0) { ++ hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); ++ } ++ break; ++ } else { ++ if((status & AK_SPISTA_RXHFULL) == AK_SPISTA_RXHFULL) { ++ hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); ++ } ++ else { ++ if (to++ > 10 * 1000000) { ++ PDEBUG("master read timeout.\n"); ++ return hw->count; ++ } ++ } ++ } ++ } ++ if (hw_remain_datalen(hw) > 0) ++ printk("read wasn't finished.\n"); ++ ++ return hw->count; ++} ++ ++ ++/** ++* @brief spi transfer function by cpu mode. ++* @author lixinhai ++* @date 2013-03-15 ++* @param[in] *hw :akspi master dev. ++* @param[in] dir: transfer direction. ++* @return transfer success result 0, otherwise result a negative value ++*/ ++static int ak_spi_pio_txrx(struct ak_spi *hw, struct spi_transfer *t) ++{ ++ int retlen; ++ u32 count = t->len; ++ ++ hw->tx = (unsigned char*)t->tx_buf; ++ hw->rx = t->rx_buf; ++ PDEBUG("start the spi dma transfer.\n"); ++ ++ switch(hw->xfer_dir) { ++ case SPI_DIR_TX: ++ { ++ while(count > 0) { ++ hw->count = 0; ++ hw->len = (count > MAX_XFER_LEN) ? MAX_XFER_LEN : count; ++ ++ retlen = spi_pio_write(hw, hw->tx, hw->len); ++ if(unlikely(retlen < 0)) { ++ printk("spi master transfer data error!\n"); ++ goto txrx_ret; ++ } ++ hw->tx += retlen; ++ count -= retlen; ++ ++ } ++ break; ++ } ++ case SPI_DIR_RX: ++ { ++ while(count > 0) { ++ hw->count = 0; ++ hw->len = (count > MAX_XFER_LEN) ? MAX_XFER_LEN : count; ++ ++ retlen = spi_pio_read(hw, hw->rx, hw->len); ++ if(unlikely(retlen < 0)) { ++ printk("spi master transfer data error!\n"); ++ goto txrx_ret; ++ } ++ hw->rx += retlen; ++ count -= retlen; ++ } ++ break; ++ } ++ } ++ PDEBUG("finish the spi dma transfer.\n"); ++ ++txrx_ret: ++ return (retlen<0) ? retlen : t->len; ++} ++ ++#endif ++ ++/** ++* @brief transfer a message ++* call proper function to complete the transefer ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[in] *spi ++* @param[in] *t ++* @return int ++*/ ++static int ak_spi_txrx(struct spi_device *spi, struct spi_transfer *t) ++{ ++ int ret; ++ struct ak_spi *hw = to_hw(spi); ++ ++ TMPDEBUG("txrx: tx %p, rx %p, len %d\n", ++ t->tx_buf, t->rx_buf, t->len); ++ ++ dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", ++ t->tx_buf, t->rx_buf, t->len); ++ ++ hw->xfer_mode = t->len < 256 ? AKSPI_XFER_MODE_CPU : AKSPI_XFER_MODE_DMA; ++ ++ hw->xfer_dir = (t->tx_buf && t->rx_buf) ? SPI_DIR_TXRX : ++ t->tx_buf ? SPI_DIR_TX : SPI_DIR_RX; ++ ++ ak_spi_start_txrx(hw, t); ++ ++ ak_drv_module_protect(DRV_MODULE_SPI); ++ ++ if(hw->xfer_dir == SPI_DIR_TXRX) { ++ //printk(KERN_WARNING "this spi master driver no support duplex now!\n"); ++ ret = ak_cpu_duplex(spi, t); ++ ak_drv_module_unprotect(DRV_MODULE_SPI); ++ return ret; ++ } ++ ++ if(ak_spi_use_dma(hw)) { ++ ret = ak_spi_dma_txrx(hw, t); ++ } else { ++ ret = ak_spi_pio_txrx(hw, t); ++ } ++ ++ ak_drv_module_unprotect(DRV_MODULE_SPI); ++ ak_spi_stop_txrx(hw, t); ++ ++ return ret; ++} ++ ++/** ++* @brief ak_spi_irq ++* TODO: used by interrupt mode ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[in] irq ++* @param[out] *dev ++* @return irqreturn_t ++*/ ++static irqreturn_t ak_spi_irq(int irq, void *dev) ++{ ++ struct ak_spi *hw = dev; ++ unsigned int status; ++ ++ status = readl(hw->regs + AK_SPISTA); ++ ++ PDEBUG("spi interrupt: status register value is %08x\n", status); ++ ++ if(ak_spi_use_dma(hw)) { ++ if((status & AK_SPISTA_TRANSF) == AK_SPISTA_TRANSF ) { ++ PDEBUG("spi transfer data have been finish.\n"); ++ ++ //printk("--->status:%d, cnt:%d\n", status, readl(hw->regs + AK_SPICNT)); ++ disable_imask(hw, AK_SPIINT_TRANSF); ++ complete(&hw->done); ++ } ++ } else { ++ switch(hw->xfer_dir) { ++ case SPI_DIR_RX: ++ if(status & (AK_SPISTA_RXHFULL | AK_SPISTA_TRANSF)) { ++ PDEBUG("spi recv buffer half full or data transfer finish.\n"); ++ ++ if(status & (AK_SPISTA_RXFULL|AK_SPISTA_TRANSF)) ++ hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); ++ hw_rxdword(hw, readl(hw->regs + AK_SPIIN)); ++ ++ if (hw->count >= hw->len) { ++ disable_imask(hw, AK_SPIINT_RXHFULL|AK_SPIINT_TRANSF); ++ complete(&hw->done); ++ } ++ } ++ break; ++ case SPI_DIR_TX: ++ if(status & (AK_SPISTA_TXHEMP | AK_SPISTA_TRANSF)) { ++ PDEBUG("spi send buffer half empty or data transfer finish.\n"); ++ ++ if (hw->count < hw->len) { ++ if((status & AK_SPISTA_TXEMP) && ((hw->count - hw->len)>4)) ++ writel(hw_txdword(hw), hw->regs + AK_SPIOUT); ++ ++ writel(hw_txdword(hw), hw->regs + AK_SPIOUT); ++ } else { ++ disable_imask(hw, AK_SPIINT_TXHEMP|AK_SPIINT_TRANSF); ++ complete(&hw->done); ++ } ++ } ++ break; ++ case SPI_DIR_TXRX : ++ default: ++ BUG(); ++ break; ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++ ++/** ++* @brief ak_spi_initialsetup ++* set up gpio and spi master ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[out] *hw ++* @return void ++*/ ++static void ak_spi_initialsetup(struct ak_spi *hw) ++{ ++ /* for the moment, permanently enable the clock */ ++ TMPDEBUG("Entering %s\n", __FUNCTION__); ++ ++ BUG_ON(hw->master && hw->master->bus_num >= AKSPI_MAX_BUS_NUM); ++ ++ ak_soft_reset(SPI_RESET_NUM(hw->master)); ++ clk_enable(hw->clk); ++ ++ /* program defaults into the registers */ ++ writel(DFT_DIV<<8 | DFT_CON | (1<<1),hw->regs + AK_SPICON); ++ ++ writel(0, hw->regs + AK_SPIINT); ++ ++ ak_spi_gpio_setup(hw, 1); ++ ++ printk("akpi regs: SPICON:%08x, SPISTA:%08x, SPIINT:%08x.\n", ++ ioread32(hw->regs + AK_SPICON), ++ ioread32(hw->regs + AK_SPISTA), ++ ioread32(hw->regs + AK_SPIINT)); ++ ++} ++ ++/** ++* @brief ak_spi_probe ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[out] *pdev ++* @return int __init ++*/ ++static int __init ak_spi_probe(struct platform_device *pdev) ++{ ++ struct ak_spi_info *pdata; ++ struct ak_spi *hw; ++ struct spi_master *master; ++ struct resource *res; ++ int err = 0; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(struct ak_spi)); ++ if (master == NULL) ++ { ++ dev_err(&pdev->dev, "No memory for spi_master\n"); ++ err = -ENOMEM; ++ goto err_nomem; ++ } ++ ++ hw = spi_master_get_devdata(master); ++ memset(hw, 0, sizeof(struct ak_spi)); ++ ++ hw->master = spi_master_get(master); ++ hw->pdata = pdata = pdev->dev.platform_data; ++ hw->dev = &pdev->dev; ++ hw->xfer_mode = pdata->xfer_mode; ++ ++ if (pdata == NULL) ++ { ++ dev_err(&pdev->dev, "No platform data supplied\n"); ++ err = -ENOENT; ++ goto err_no_pdata; ++ } ++ ++ platform_set_drvdata(pdev, hw); ++ init_completion(&hw->done); ++ ++ /* setup the master state. */ ++ /* the spi->mode bits understood by this driver: */ ++ master->mode_bits = hw->pdata->mode_bits; ++ ++ master->num_chipselect = hw->pdata->num_cs; ++ master->bus_num = pdata->bus_num; ++ ++ /* setup the state for the bitbang driver */ ++ ++ hw->bitbang.master = hw->master; ++ hw->bitbang.setup_transfer = ak_spi_setupxfer; ++ hw->bitbang.chipselect = ak_spi_chipsel; ++ hw->bitbang.txrx_bufs = ak_spi_txrx; ++ ++ hw->master->setup = ak_spi_setup; ++ hw->master->cleanup = ak_spi_cleanup; ++ ++ dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); ++ ++ /* find and map our resources */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); ++ err = -ENOENT; ++ goto err_no_iores; ++ } ++ ++ hw->ioarea = request_mem_region(res->start, resource_size(res), ++ pdev->name); ++ ++ if (hw->ioarea == NULL) { ++ dev_err(&pdev->dev, "Cannot reserve region\n"); ++ err = -ENXIO; ++ goto err_no_iores; ++ } ++ ++ hw->regs = ioremap(res->start, resource_size(res)); ++ if (hw->regs == NULL) { ++ dev_err(&pdev->dev, "Cannot map IO\n"); ++ err = -ENXIO; ++ goto err_no_iomap; ++ } ++ ++ hw->irq = platform_get_irq(pdev, 0); ++ if (hw->irq < 0) { ++ dev_err(&pdev->dev, "No IRQ specified\n"); ++ err = -ENOENT; ++ goto err_no_irq; ++ } ++ ++ err = request_irq(hw->irq, ak_spi_irq, 0, pdev->name, hw); ++ if (err) { ++ dev_err(&pdev->dev, "Cannot claim IRQ\n"); ++ goto err_no_irq; ++ } ++ ++ hw->clk = clk_get(&pdev->dev, pdata->clk_name); ++ if (IS_ERR(hw->clk)) { ++ dev_err(&pdev->dev, "No clock for device\n"); ++ err = PTR_ERR(hw->clk); ++ goto err_no_clk; ++ } ++ PDEBUG("%s: %luMhz \n", hw->clk->name, clk_get_rate(hw->clk)/MHz); ++ ++ /* setup any gpio we can */ ++ ++ ak_spi_initialsetup(hw); ++ ++ /* register our spi controller */ ++ ++ err = spi_bitbang_start(&hw->bitbang); ++ if (err) { ++ dev_err(&pdev->dev, "Failed to register SPI master\n"); ++ goto err_register; ++ } ++ ++ printk("akspi master initialize success, use for %s mode.\n", ++ ak_spi_use_dma(hw)?"DMA":"PIO"); ++ ++ return 0; ++ ++ err_register: ++ //if (hw->set_cs == ak_spi_gpiocs) ++ // gpio_free(pdata->pin_cs); ++ ++ clk_disable(hw->clk); ++ clk_put(hw->clk); ++ ++ err_no_clk: ++ free_irq(hw->irq, hw); ++ ++ err_no_irq: ++ iounmap(hw->regs); ++ ++ err_no_iomap: ++ release_resource(hw->ioarea); ++ ++ err_no_iores: ++ err_no_pdata: ++ spi_master_put(hw->master); ++ ++ err_nomem: ++ return err; ++} ++ ++/** ++* @brief ak_spi_remove ++* free allocated resources while remove ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[out] *dev ++* @return int __exit ++*/ ++static int __exit ak_spi_remove(struct platform_device *dev) ++{ ++ struct ak_spi *hw = platform_get_drvdata(dev); ++ ++ platform_set_drvdata(dev, NULL); ++ ++ spi_unregister_master(hw->master); ++ ++ clk_disable(hw->clk); ++ clk_put(hw->clk); ++ ++ free_irq(hw->irq, hw); ++ iounmap(hw->regs); ++ ++ release_resource(hw->ioarea); ++ ++ spi_master_put(hw->master); ++ return 0; ++} ++ ++ ++#ifdef CONFIG_PM ++ ++/** ++* @brief ak_spi_suspend ++* suspend, disable the clock to the SPI ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[in] *dev ++* @return int ++*/ ++static int ak_spi_suspend(struct device *dev) ++{ ++ struct ak_spi *hw = platform_get_drvdata(to_platform_device(dev)); ++ ++ ak_spi_gpio_setup(hw, 0); ++ ++ clk_disable(hw->clk); ++ return 0; ++} ++ ++/** ++* @brief ak_spi_resume ++* resume, initialize the spi master ++* @author zhou wenyong ++* @date 2011-08-19 ++* @param[out] *dev ++* @return int ++*/ ++static int ak_spi_resume(struct device *dev) ++{ ++ struct ak_spi *hw = platform_get_drvdata(to_platform_device(dev)); ++ ++ ak_spi_initialsetup(hw); ++ return 0; ++} ++ ++static struct dev_pm_ops ak_spi_pmops = { ++ .suspend = ak_spi_suspend, ++ .resume = ak_spi_resume, ++}; ++ ++#define AK_SPI_PMOPS &ak_spi_pmops ++#else ++#define AK_SPI_PMOPS NULL ++#endif /* CONFIG_PM */ ++ ++MODULE_ALIAS("platform:ak-spi"); ++static struct platform_driver ak_spi_driver = { ++ .remove = __exit_p(ak_spi_remove), ++ .driver = { ++ .name = "ak-spi", ++ .owner = THIS_MODULE, ++ .pm = AK_SPI_PMOPS, ++ }, ++}; ++ ++/** ++* @brief ak_spi_init ++* init, register this driver ++* @author zhou wenyong ++* @date 2011-08-19 ++* @return int __init ++*/ ++static int __init ak_spi_init(void) ++{ ++ printk("AK SPI Driver, (c) 2012 ANYKA\n"); ++ return platform_driver_probe(&ak_spi_driver, ak_spi_probe); ++} ++ ++/** ++* @brief ak_spi_exit ++* exit, unregister this driver ++* @author zhou wenyong ++* @date 2011-08-19 ++* @return void __exit ++*/ ++static void __exit ak_spi_exit(void) ++{ ++ platform_driver_unregister(&ak_spi_driver); ++} ++ ++module_init(ak_spi_init); ++module_exit(ak_spi_exit); ++ ++MODULE_DESCRIPTION("AK On-Chip SPI Driver"); ++MODULE_AUTHOR("ANYKA"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c +index aef59b1a..b6f31bc1 100644 +--- a/drivers/spi/spi-bitbang.c ++++ b/drivers/spi/spi-bitbang.c +@@ -348,8 +348,6 @@ static void bitbang_work(struct work_struct *work) + + if (!cs_change) + continue; +- if (t->transfer_list.next == &m->transfers) +- break; + + /* sometimes a short mid-message deselect of the chip + * may be needed to terminate a mode or command +@@ -357,6 +355,9 @@ static void bitbang_work(struct work_struct *work) + ndelay(nsecs); + bitbang->chipselect(spi, BITBANG_CS_INACTIVE); + ndelay(nsecs); ++ ++ if (t->transfer_list.next == &m->transfers) ++ break; + } + + m->status = status; +diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig +index eb1dee26..43d17c27 100644 +--- a/drivers/staging/android/Kconfig ++++ b/drivers/staging/android/Kconfig +@@ -38,6 +38,20 @@ config ANDROID_RAM_CONSOLE + select ANDROID_PERSISTENT_RAM + default n + ++config PERSISTENT_TRACER ++ bool "Persistent function tracer" ++ depends on HAVE_FUNCTION_TRACER ++ select FUNCTION_TRACER ++ select ANDROID_PERSISTENT_RAM ++ help ++ persistent_trace traces function calls into a persistent ram ++ buffer that can be decoded and dumped after reboot through ++ /sys/kernel/debug/persistent_trace. It can be used to ++ determine what function was last called before a reset or ++ panic. ++ ++ If unsure, say N. ++ + config ANDROID_TIMED_OUTPUT + bool "Timed output class driver" + default y +@@ -53,33 +67,25 @@ config ANDROID_LOW_MEMORY_KILLER + ---help--- + Register processes to be killed when memory is low + ++config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES ++ bool "Android Low Memory Killer: detect oom_adj values" ++ depends on ANDROID_LOW_MEMORY_KILLER ++ default y ++ ---help--- ++ Detect oom_adj values written to ++ /sys/module/lowmemorykiller/parameters/adj and convert them ++ to oom_score_adj values. ++ + source "drivers/staging/android/switch/Kconfig" + +-config ANDROID_INTF_ALARM ++config ANDROID_INTF_ALARM_DEV + bool "Android alarm driver" + depends on RTC_CLASS + default n + help + Provides non-wakeup and rtc backed wakeup alarms based on rtc or + elapsed realtime, and a non-wakeup alarm on the monotonic clock. +- Also provides an interface to set the wall time which must be used +- for elapsed realtime to work. +- +-config ANDROID_INTF_ALARM_DEV +- bool "Android alarm device" +- depends on ANDROID_INTF_ALARM +- default y +- help +- Exports the alarm interface to user-space. +- +-config ANDROID_ALARM_OLDDRV_COMPAT +- bool "Android Alarm compatability with old drivers" +- depends on ANDROID_INTF_ALARM +- default n +- help +- Provides preprocessor alias to aid compatability with +- older out-of-tree drivers that use the Android Alarm +- in-kernel API. This will be removed eventually. ++ Also exports the alarm interface to user-space. + + endif # if ANDROID + +diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile +index 9b6c9ed9..8769e325 100644 +--- a/drivers/staging/android/Makefile ++++ b/drivers/staging/android/Makefile +@@ -1,3 +1,5 @@ ++ccflags-y += -I$(src) # needed for trace events ++ + obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o + obj-$(CONFIG_ASHMEM) += ashmem.o + obj-$(CONFIG_ANDROID_LOGGER) += logger.o +@@ -7,5 +9,7 @@ obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o + obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o + obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o + obj-$(CONFIG_ANDROID_SWITCH) += switch/ +-obj-$(CONFIG_ANDROID_INTF_ALARM) += alarm.o + obj-$(CONFIG_ANDROID_INTF_ALARM_DEV) += alarm-dev.o ++obj-$(CONFIG_PERSISTENT_TRACER) += trace_persistent.o ++ ++CFLAGS_REMOVE_trace_persistent.o = -pg +diff --git a/drivers/staging/android/TODO b/drivers/staging/android/TODO +deleted file mode 100644 +index b15fb0d6..00000000 +--- a/drivers/staging/android/TODO ++++ /dev/null +@@ -1,10 +0,0 @@ +-TODO: +- - checkpatch.pl cleanups +- - sparse fixes +- - rename files to be not so "generic" +- - make sure things build as modules properly +- - add proper arch dependencies as needed +- - audit userspace interfaces to make sure they are sane +- +-Please send patches to Greg Kroah-Hartman and Cc: +-Brian Swetland +diff --git a/drivers/staging/android/alarm-dev.c b/drivers/staging/android/alarm-dev.c +index 03efb34c..e001fe58 100644 +--- a/drivers/staging/android/alarm-dev.c ++++ b/drivers/staging/android/alarm-dev.c +@@ -22,24 +22,14 @@ + #include + #include + #include ++#include ++#include + #include "android_alarm.h" + +-/* XXX - Hack out wakelocks, while they are out of tree */ +-struct wake_lock { +- int i; +-}; +-#define wake_lock(x) +-#define wake_lock_timeout(x, y) +-#define wake_unlock(x) +-#define WAKE_LOCK_SUSPEND 0 +-#define wake_lock_init(x, y, z) ((x)->i = 1) +-#define wake_lock_destroy(x) +- + #define ANDROID_ALARM_PRINT_INFO (1U << 0) + #define ANDROID_ALARM_PRINT_IO (1U << 1) + #define ANDROID_ALARM_PRINT_INT (1U << 2) + +- + static int debug_mask = ANDROID_ALARM_PRINT_INFO; + module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +@@ -66,7 +56,53 @@ static uint32_t alarm_pending; + static uint32_t alarm_enabled; + static uint32_t wait_pending; + +-static struct android_alarm alarms[ANDROID_ALARM_TYPE_COUNT]; ++struct devalarm { ++ union { ++ struct hrtimer hrt; ++ struct alarm alrm; ++ } u; ++ enum android_alarm_type type; ++}; ++ ++static struct devalarm alarms[ANDROID_ALARM_TYPE_COUNT]; ++ ++ ++static int is_wakeup(enum android_alarm_type type) ++{ ++ if (type == ANDROID_ALARM_RTC_WAKEUP || ++ type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP) ++ return 1; ++ return 0; ++} ++ ++ ++static void devalarm_start(struct devalarm *alrm, ktime_t exp) ++{ ++ if (is_wakeup(alrm->type)) ++ alarm_start(&alrm->u.alrm, exp); ++ else ++ hrtimer_start(&alrm->u.hrt, exp, HRTIMER_MODE_ABS); ++} ++ ++ ++static int devalarm_try_to_cancel(struct devalarm *alrm) ++{ ++ int ret; ++ if (is_wakeup(alrm->type)) ++ ret = alarm_try_to_cancel(&alrm->u.alrm); ++ else ++ ret = hrtimer_try_to_cancel(&alrm->u.hrt); ++ return ret; ++} ++ ++static void devalarm_cancel(struct devalarm *alrm) ++{ ++ if (is_wakeup(alrm->type)) ++ alarm_cancel(&alrm->u.alrm); ++ else ++ hrtimer_cancel(&alrm->u.hrt); ++} ++ + + static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + { +@@ -75,6 +111,8 @@ static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + struct timespec new_alarm_time; + struct timespec new_rtc_time; + struct timespec tmp_time; ++ struct rtc_time new_rtc_tm; ++ struct rtc_device *rtc_dev; + enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd); + uint32_t alarm_type_mask = 1U << alarm_type; + +@@ -101,7 +139,7 @@ static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + case ANDROID_ALARM_CLEAR(0): + spin_lock_irqsave(&alarm_slock, flags); + pr_alarm(IO, "alarm %d clear\n", alarm_type); +- android_alarm_try_to_cancel(&alarms[alarm_type]); ++ devalarm_try_to_cancel(&alarms[alarm_type]); + if (alarm_pending) { + alarm_pending &= ~alarm_type_mask; + if (!alarm_pending && !wait_pending) +@@ -132,8 +170,7 @@ from_old_alarm_set: + pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type, + new_alarm_time.tv_sec, new_alarm_time.tv_nsec); + alarm_enabled |= alarm_type_mask; +- android_alarm_start_range(&alarms[alarm_type], +- timespec_to_ktime(new_alarm_time), ++ devalarm_start(&alarms[alarm_type], + timespec_to_ktime(new_alarm_time)); + spin_unlock_irqrestore(&alarm_slock, flags); + if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0) +@@ -163,7 +200,13 @@ from_old_alarm_set: + rv = -EFAULT; + goto err1; + } +- rv = android_alarm_set_rtc(new_rtc_time); ++ rtc_time_to_tm(new_rtc_time.tv_sec, &new_rtc_tm); ++ rtc_dev = alarmtimer_get_rtcdev(); ++ rv = do_settimeofday(&new_rtc_time); ++ if (rv < 0) ++ goto err1; ++ if (rtc_dev) ++ rv = rtc_set_time(rtc_dev, &new_rtc_tm); + spin_lock_irqsave(&alarm_slock, flags); + alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; + wake_up(&alarm_wait_queue); +@@ -179,8 +222,7 @@ from_old_alarm_set: + break; + case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP: + case ANDROID_ALARM_ELAPSED_REALTIME: +- tmp_time = +- ktime_to_timespec(alarm_get_elapsed_realtime()); ++ get_monotonic_boottime(&tmp_time); + break; + case ANDROID_ALARM_TYPE_COUNT: + case ANDROID_ALARM_SYSTEMTIME: +@@ -224,7 +266,7 @@ static int alarm_release(struct inode *inode, struct file *file) + alarm_enabled &= ~alarm_type_mask; + } + spin_unlock_irqrestore(&alarm_slock, flags); +- android_alarm_cancel(&alarms[i]); ++ devalarm_cancel(&alarms[i]); + spin_lock_irqsave(&alarm_slock, flags); + } + if (alarm_pending | wait_pending) { +@@ -241,12 +283,12 @@ static int alarm_release(struct inode *inode, struct file *file) + return 0; + } + +-static void alarm_triggered(struct android_alarm *alarm) ++static void devalarm_triggered(struct devalarm *alarm) + { + unsigned long flags; + uint32_t alarm_type_mask = 1U << alarm->type; + +- pr_alarm(INT, "alarm_triggered type %d\n", alarm->type); ++ pr_alarm(INT, "devalarm_triggered type %d\n", alarm->type); + spin_lock_irqsave(&alarm_slock, flags); + if (alarm_enabled & alarm_type_mask) { + wake_lock_timeout(&alarm_wake_lock, 5 * HZ); +@@ -257,6 +299,25 @@ static void alarm_triggered(struct android_alarm *alarm) + spin_unlock_irqrestore(&alarm_slock, flags); + } + ++ ++static enum hrtimer_restart devalarm_hrthandler(struct hrtimer *hrt) ++{ ++ struct devalarm *devalrm = container_of(hrt, struct devalarm, u.hrt); ++ ++ devalarm_triggered(devalrm); ++ return HRTIMER_NORESTART; ++} ++ ++static enum alarmtimer_restart devalarm_alarmhandler(struct alarm *alrm, ++ ktime_t now) ++{ ++ struct devalarm *devalrm = container_of(alrm, struct devalarm, u.alrm); ++ ++ devalarm_triggered(devalrm); ++ return ALARMTIMER_NORESTART; ++} ++ ++ + static const struct file_operations alarm_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = alarm_ioctl, +@@ -279,8 +340,23 @@ static int __init alarm_dev_init(void) + if (err) + return err; + +- for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) +- android_alarm_init(&alarms[i], i, alarm_triggered); ++ alarm_init(&alarms[ANDROID_ALARM_RTC_WAKEUP].u.alrm, ++ ALARM_REALTIME, devalarm_alarmhandler); ++ hrtimer_init(&alarms[ANDROID_ALARM_RTC].u.hrt, ++ CLOCK_REALTIME, HRTIMER_MODE_ABS); ++ alarm_init(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].u.alrm, ++ ALARM_BOOTTIME, devalarm_alarmhandler); ++ hrtimer_init(&alarms[ANDROID_ALARM_ELAPSED_REALTIME].u.hrt, ++ CLOCK_BOOTTIME, HRTIMER_MODE_ABS); ++ hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].u.hrt, ++ CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ++ ++ for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { ++ alarms[i].type = i; ++ if (!is_wakeup(i)) ++ alarms[i].u.hrt.function = devalarm_hrthandler; ++ } ++ + wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm"); + + return 0; +diff --git a/drivers/staging/android/alarm.c b/drivers/staging/android/alarm.c +deleted file mode 100644 +index c68950b9..00000000 +--- a/drivers/staging/android/alarm.c ++++ /dev/null +@@ -1,601 +0,0 @@ +-/* drivers/rtc/alarm.c +- * +- * Copyright (C) 2007-2009 Google, Inc. +- * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "android_alarm.h" +- +-/* XXX - Hack out wakelocks, while they are out of tree */ +-struct wake_lock { +- int i; +-}; +-#define wake_lock(x) +-#define wake_lock_timeout(x, y) +-#define wake_unlock(x) +-#define WAKE_LOCK_SUSPEND 0 +-#define wake_lock_init(x, y, z) ((x)->i = 1) +-#define wake_lock_destroy(x) +- +-#define ANDROID_ALARM_PRINT_ERROR (1U << 0) +-#define ANDROID_ALARM_PRINT_INIT_STATUS (1U << 1) +-#define ANDROID_ALARM_PRINT_TSET (1U << 2) +-#define ANDROID_ALARM_PRINT_CALL (1U << 3) +-#define ANDROID_ALARM_PRINT_SUSPEND (1U << 4) +-#define ANDROID_ALARM_PRINT_INT (1U << 5) +-#define ANDROID_ALARM_PRINT_FLOW (1U << 6) +- +-static int debug_mask = ANDROID_ALARM_PRINT_ERROR | \ +- ANDROID_ALARM_PRINT_INIT_STATUS; +-module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); +- +-#define pr_alarm(debug_level_mask, args...) \ +- do { \ +- if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \ +- pr_info(args); \ +- } \ +- } while (0) +- +-#define ANDROID_ALARM_WAKEUP_MASK ( \ +- ANDROID_ALARM_RTC_WAKEUP_MASK | \ +- ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) +- +-/* support old usespace code */ +-#define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */ +-#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t) +- +-struct alarm_queue { +- struct rb_root alarms; +- struct rb_node *first; +- struct hrtimer timer; +- ktime_t delta; +- bool stopped; +- ktime_t stopped_time; +-}; +- +-static struct rtc_device *alarm_rtc_dev; +-static DEFINE_SPINLOCK(alarm_slock); +-static DEFINE_MUTEX(alarm_setrtc_mutex); +-static struct wake_lock alarm_rtc_wake_lock; +-static struct platform_device *alarm_platform_dev; +-struct alarm_queue alarms[ANDROID_ALARM_TYPE_COUNT]; +-static bool suspended; +- +-static void update_timer_locked(struct alarm_queue *base, bool head_removed) +-{ +- struct android_alarm *alarm; +- bool is_wakeup = base == &alarms[ANDROID_ALARM_RTC_WAKEUP] || +- base == &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]; +- +- if (base->stopped) { +- pr_alarm(FLOW, "changed alarm while setting the wall time\n"); +- return; +- } +- +- if (is_wakeup && !suspended && head_removed) +- wake_unlock(&alarm_rtc_wake_lock); +- +- if (!base->first) +- return; +- +- alarm = container_of(base->first, struct android_alarm, node); +- +- pr_alarm(FLOW, "selected alarm, type %d, func %pF at %lld\n", +- alarm->type, alarm->function, ktime_to_ns(alarm->expires)); +- +- if (is_wakeup && suspended) { +- pr_alarm(FLOW, "changed alarm while suspened\n"); +- wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ); +- return; +- } +- +- hrtimer_try_to_cancel(&base->timer); +- base->timer.node.expires = ktime_add(base->delta, alarm->expires); +- base->timer._softexpires = ktime_add(base->delta, alarm->softexpires); +- hrtimer_start_expires(&base->timer, HRTIMER_MODE_ABS); +-} +- +-static void alarm_enqueue_locked(struct android_alarm *alarm) +-{ +- struct alarm_queue *base = &alarms[alarm->type]; +- struct rb_node **link = &base->alarms.rb_node; +- struct rb_node *parent = NULL; +- struct android_alarm *entry; +- int leftmost = 1; +- bool was_first = false; +- +- pr_alarm(FLOW, "added alarm, type %d, func %pF at %lld\n", +- alarm->type, alarm->function, ktime_to_ns(alarm->expires)); +- +- if (base->first == &alarm->node) { +- base->first = rb_next(&alarm->node); +- was_first = true; +- } +- if (!RB_EMPTY_NODE(&alarm->node)) { +- rb_erase(&alarm->node, &base->alarms); +- RB_CLEAR_NODE(&alarm->node); +- } +- +- while (*link) { +- parent = *link; +- entry = rb_entry(parent, struct android_alarm, node); +- /* +- * We dont care about collisions. Nodes with +- * the same expiry time stay together. +- */ +- if (alarm->expires.tv64 < entry->expires.tv64) { +- link = &(*link)->rb_left; +- } else { +- link = &(*link)->rb_right; +- leftmost = 0; +- } +- } +- if (leftmost) +- base->first = &alarm->node; +- if (leftmost || was_first) +- update_timer_locked(base, was_first); +- +- rb_link_node(&alarm->node, parent, link); +- rb_insert_color(&alarm->node, &base->alarms); +-} +- +-/** +- * android_alarm_init - initialize an alarm +- * @alarm: the alarm to be initialized +- * @type: the alarm type to be used +- * @function: alarm callback function +- */ +-void android_alarm_init(struct android_alarm *alarm, +- enum android_alarm_type type, void (*function)(struct android_alarm *)) +-{ +- RB_CLEAR_NODE(&alarm->node); +- alarm->type = type; +- alarm->function = function; +- +- pr_alarm(FLOW, "created alarm, type %d, func %pF\n", type, function); +-} +- +- +-/** +- * android_alarm_start_range - (re)start an alarm +- * @alarm: the alarm to be added +- * @start: earliest expiry time +- * @end: expiry time +- */ +-void android_alarm_start_range(struct android_alarm *alarm, ktime_t start, +- ktime_t end) +-{ +- unsigned long flags; +- +- spin_lock_irqsave(&alarm_slock, flags); +- alarm->softexpires = start; +- alarm->expires = end; +- alarm_enqueue_locked(alarm); +- spin_unlock_irqrestore(&alarm_slock, flags); +-} +- +-/** +- * android_alarm_try_to_cancel - try to deactivate an alarm +- * @alarm: alarm to stop +- * +- * Returns: +- * 0 when the alarm was not active +- * 1 when the alarm was active +- * -1 when the alarm may currently be excuting the callback function and +- * cannot be stopped (it may also be inactive) +- */ +-int android_alarm_try_to_cancel(struct android_alarm *alarm) +-{ +- struct alarm_queue *base = &alarms[alarm->type]; +- unsigned long flags; +- bool first = false; +- int ret = 0; +- +- spin_lock_irqsave(&alarm_slock, flags); +- if (!RB_EMPTY_NODE(&alarm->node)) { +- pr_alarm(FLOW, "canceled alarm, type %d, func %pF at %lld\n", +- alarm->type, alarm->function, +- ktime_to_ns(alarm->expires)); +- ret = 1; +- if (base->first == &alarm->node) { +- base->first = rb_next(&alarm->node); +- first = true; +- } +- rb_erase(&alarm->node, &base->alarms); +- RB_CLEAR_NODE(&alarm->node); +- if (first) +- update_timer_locked(base, true); +- } else +- pr_alarm(FLOW, "tried to cancel alarm, type %d, func %pF\n", +- alarm->type, alarm->function); +- spin_unlock_irqrestore(&alarm_slock, flags); +- if (!ret && hrtimer_callback_running(&base->timer)) +- ret = -1; +- return ret; +-} +- +-/** +- * android_alarm_cancel - cancel an alarm and wait for the handler to finish. +- * @alarm: the alarm to be cancelled +- * +- * Returns: +- * 0 when the alarm was not active +- * 1 when the alarm was active +- */ +-int android_alarm_cancel(struct android_alarm *alarm) +-{ +- for (;;) { +- int ret = android_alarm_try_to_cancel(alarm); +- if (ret >= 0) +- return ret; +- cpu_relax(); +- } +-} +- +-/** +- * alarm_set_rtc - set the kernel and rtc walltime +- * @new_time: timespec value containing the new time +- */ +-int android_alarm_set_rtc(struct timespec new_time) +-{ +- int i; +- int ret; +- unsigned long flags; +- struct rtc_time rtc_new_rtc_time; +- struct timespec tmp_time; +- +- rtc_time_to_tm(new_time.tv_sec, &rtc_new_rtc_time); +- +- pr_alarm(TSET, "set rtc %ld %ld - rtc %02d:%02d:%02d %02d/%02d/%04d\n", +- new_time.tv_sec, new_time.tv_nsec, +- rtc_new_rtc_time.tm_hour, rtc_new_rtc_time.tm_min, +- rtc_new_rtc_time.tm_sec, rtc_new_rtc_time.tm_mon + 1, +- rtc_new_rtc_time.tm_mday, +- rtc_new_rtc_time.tm_year + 1900); +- +- mutex_lock(&alarm_setrtc_mutex); +- spin_lock_irqsave(&alarm_slock, flags); +- wake_lock(&alarm_rtc_wake_lock); +- getnstimeofday(&tmp_time); +- for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { +- hrtimer_try_to_cancel(&alarms[i].timer); +- alarms[i].stopped = true; +- alarms[i].stopped_time = timespec_to_ktime(tmp_time); +- } +- alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta = +- alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta = +- ktime_sub(alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta, +- timespec_to_ktime(timespec_sub(tmp_time, new_time))); +- spin_unlock_irqrestore(&alarm_slock, flags); +- ret = do_settimeofday(&new_time); +- spin_lock_irqsave(&alarm_slock, flags); +- for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { +- alarms[i].stopped = false; +- update_timer_locked(&alarms[i], false); +- } +- spin_unlock_irqrestore(&alarm_slock, flags); +- if (ret < 0) { +- pr_alarm(ERROR, "alarm_set_rtc: Failed to set time\n"); +- goto err; +- } +- if (!alarm_rtc_dev) { +- pr_alarm(ERROR, +- "alarm_set_rtc: no RTC, time will be lost on reboot\n"); +- goto err; +- } +- ret = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time); +- if (ret < 0) +- pr_alarm(ERROR, "alarm_set_rtc: " +- "Failed to set RTC, time will be lost on reboot\n"); +-err: +- wake_unlock(&alarm_rtc_wake_lock); +- mutex_unlock(&alarm_setrtc_mutex); +- return ret; +-} +- +-/** +- * alarm_get_elapsed_realtime - get the elapsed real time in ktime_t format +- * +- * returns the time in ktime_t format +- */ +-ktime_t alarm_get_elapsed_realtime(void) +-{ +- ktime_t now; +- unsigned long flags; +- struct alarm_queue *base = &alarms[ANDROID_ALARM_ELAPSED_REALTIME]; +- +- spin_lock_irqsave(&alarm_slock, flags); +- now = base->stopped ? base->stopped_time : ktime_get_real(); +- now = ktime_sub(now, base->delta); +- spin_unlock_irqrestore(&alarm_slock, flags); +- return now; +-} +- +-static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer) +-{ +- struct alarm_queue *base; +- struct android_alarm *alarm; +- unsigned long flags; +- ktime_t now; +- +- spin_lock_irqsave(&alarm_slock, flags); +- +- base = container_of(timer, struct alarm_queue, timer); +- now = base->stopped ? base->stopped_time : hrtimer_cb_get_time(timer); +- now = ktime_sub(now, base->delta); +- +- pr_alarm(INT, "alarm_timer_triggered type %ld at %lld\n", +- base - alarms, ktime_to_ns(now)); +- +- while (base->first) { +- alarm = container_of(base->first, struct android_alarm, node); +- if (alarm->softexpires.tv64 > now.tv64) { +- pr_alarm(FLOW, "don't call alarm, %pF, %lld (s %lld)\n", +- alarm->function, ktime_to_ns(alarm->expires), +- ktime_to_ns(alarm->softexpires)); +- break; +- } +- base->first = rb_next(&alarm->node); +- rb_erase(&alarm->node, &base->alarms); +- RB_CLEAR_NODE(&alarm->node); +- pr_alarm(CALL, "call alarm, type %d, func %pF, %lld (s %lld)\n", +- alarm->type, alarm->function, +- ktime_to_ns(alarm->expires), +- ktime_to_ns(alarm->softexpires)); +- spin_unlock_irqrestore(&alarm_slock, flags); +- alarm->function(alarm); +- spin_lock_irqsave(&alarm_slock, flags); +- } +- if (!base->first) +- pr_alarm(FLOW, "no more alarms of type %ld\n", base - alarms); +- update_timer_locked(base, true); +- spin_unlock_irqrestore(&alarm_slock, flags); +- return HRTIMER_NORESTART; +-} +- +-static void alarm_triggered_func(void *p) +-{ +- struct rtc_device *rtc = alarm_rtc_dev; +- if (!(rtc->irq_data & RTC_AF)) +- return; +- pr_alarm(INT, "rtc alarm triggered\n"); +- wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ); +-} +- +-static int alarm_suspend(struct platform_device *pdev, pm_message_t state) +-{ +- int err = 0; +- unsigned long flags; +- struct rtc_wkalrm rtc_alarm; +- struct rtc_time rtc_current_rtc_time; +- unsigned long rtc_current_time; +- unsigned long rtc_alarm_time; +- struct timespec rtc_delta; +- struct timespec wall_time; +- struct alarm_queue *wakeup_queue = NULL; +- struct alarm_queue *tmp_queue = NULL; +- +- pr_alarm(SUSPEND, "alarm_suspend(%p, %d)\n", pdev, state.event); +- +- spin_lock_irqsave(&alarm_slock, flags); +- suspended = true; +- spin_unlock_irqrestore(&alarm_slock, flags); +- +- hrtimer_cancel(&alarms[ANDROID_ALARM_RTC_WAKEUP].timer); +- hrtimer_cancel(&alarms[ +- ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].timer); +- +- tmp_queue = &alarms[ANDROID_ALARM_RTC_WAKEUP]; +- if (tmp_queue->first) +- wakeup_queue = tmp_queue; +- tmp_queue = &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]; +- if (tmp_queue->first && (!wakeup_queue || +- hrtimer_get_expires(&tmp_queue->timer).tv64 < +- hrtimer_get_expires(&wakeup_queue->timer).tv64)) +- wakeup_queue = tmp_queue; +- if (wakeup_queue) { +- rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); +- getnstimeofday(&wall_time); +- rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time); +- set_normalized_timespec(&rtc_delta, +- wall_time.tv_sec - rtc_current_time, +- wall_time.tv_nsec); +- +- rtc_alarm_time = timespec_sub(ktime_to_timespec( +- hrtimer_get_expires(&wakeup_queue->timer)), +- rtc_delta).tv_sec; +- +- rtc_time_to_tm(rtc_alarm_time, &rtc_alarm.time); +- rtc_alarm.enabled = 1; +- rtc_set_alarm(alarm_rtc_dev, &rtc_alarm); +- rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); +- rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time); +- pr_alarm(SUSPEND, +- "rtc alarm set at %ld, now %ld, rtc delta %ld.%09ld\n", +- rtc_alarm_time, rtc_current_time, +- rtc_delta.tv_sec, rtc_delta.tv_nsec); +- if (rtc_current_time + 1 >= rtc_alarm_time) { +- pr_alarm(SUSPEND, "alarm about to go off\n"); +- memset(&rtc_alarm, 0, sizeof(rtc_alarm)); +- rtc_alarm.enabled = 0; +- rtc_set_alarm(alarm_rtc_dev, &rtc_alarm); +- +- spin_lock_irqsave(&alarm_slock, flags); +- suspended = false; +- wake_lock_timeout(&alarm_rtc_wake_lock, 2 * HZ); +- update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP], +- false); +- update_timer_locked(&alarms[ +- ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP], false); +- err = -EBUSY; +- spin_unlock_irqrestore(&alarm_slock, flags); +- } +- } +- return err; +-} +- +-static int alarm_resume(struct platform_device *pdev) +-{ +- struct rtc_wkalrm alarm; +- unsigned long flags; +- +- pr_alarm(SUSPEND, "alarm_resume(%p)\n", pdev); +- +- memset(&alarm, 0, sizeof(alarm)); +- alarm.enabled = 0; +- rtc_set_alarm(alarm_rtc_dev, &alarm); +- +- spin_lock_irqsave(&alarm_slock, flags); +- suspended = false; +- update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP], false); +- update_timer_locked(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP], +- false); +- spin_unlock_irqrestore(&alarm_slock, flags); +- +- return 0; +-} +- +-static struct rtc_task alarm_rtc_task = { +- .func = alarm_triggered_func +-}; +- +-static int rtc_alarm_add_device(struct device *dev, +- struct class_interface *class_intf) +-{ +- int err; +- struct rtc_device *rtc = to_rtc_device(dev); +- +- mutex_lock(&alarm_setrtc_mutex); +- +- if (alarm_rtc_dev) { +- err = -EBUSY; +- goto err1; +- } +- +- alarm_platform_dev = +- platform_device_register_simple("alarm", -1, NULL, 0); +- if (IS_ERR(alarm_platform_dev)) { +- err = PTR_ERR(alarm_platform_dev); +- goto err2; +- } +- err = rtc_irq_register(rtc, &alarm_rtc_task); +- if (err) +- goto err3; +- alarm_rtc_dev = rtc; +- pr_alarm(INIT_STATUS, "using rtc device, %s, for alarms", rtc->name); +- mutex_unlock(&alarm_setrtc_mutex); +- +- return 0; +- +-err3: +- platform_device_unregister(alarm_platform_dev); +-err2: +-err1: +- mutex_unlock(&alarm_setrtc_mutex); +- return err; +-} +- +-static void rtc_alarm_remove_device(struct device *dev, +- struct class_interface *class_intf) +-{ +- if (dev == &alarm_rtc_dev->dev) { +- pr_alarm(INIT_STATUS, "lost rtc device for alarms"); +- rtc_irq_unregister(alarm_rtc_dev, &alarm_rtc_task); +- platform_device_unregister(alarm_platform_dev); +- alarm_rtc_dev = NULL; +- } +-} +- +-static struct class_interface rtc_alarm_interface = { +- .add_dev = &rtc_alarm_add_device, +- .remove_dev = &rtc_alarm_remove_device, +-}; +- +-static struct platform_driver alarm_driver = { +- .suspend = alarm_suspend, +- .resume = alarm_resume, +- .driver = { +- .name = "alarm" +- } +-}; +- +-static int __init alarm_late_init(void) +-{ +- unsigned long flags; +- struct timespec tmp_time, system_time; +- +- /* this needs to run after the rtc is read at boot */ +- spin_lock_irqsave(&alarm_slock, flags); +- /* We read the current rtc and system time so we can later calulate +- * elasped realtime to be (boot_systemtime + rtc - boot_rtc) == +- * (rtc - (boot_rtc - boot_systemtime)) +- */ +- getnstimeofday(&tmp_time); +- ktime_get_ts(&system_time); +- alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta = +- alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta = +- timespec_to_ktime(timespec_sub(tmp_time, system_time)); +- +- spin_unlock_irqrestore(&alarm_slock, flags); +- return 0; +-} +- +-static int __init alarm_driver_init(void) +-{ +- int err; +- int i; +- +- for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { +- hrtimer_init(&alarms[i].timer, +- CLOCK_REALTIME, HRTIMER_MODE_ABS); +- alarms[i].timer.function = alarm_timer_triggered; +- } +- hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].timer, +- CLOCK_MONOTONIC, HRTIMER_MODE_ABS); +- alarms[ANDROID_ALARM_SYSTEMTIME].timer.function = alarm_timer_triggered; +- err = platform_driver_register(&alarm_driver); +- if (err < 0) +- goto err1; +- wake_lock_init(&alarm_rtc_wake_lock, WAKE_LOCK_SUSPEND, "alarm_rtc"); +- rtc_alarm_interface.class = rtc_class; +- err = class_interface_register(&rtc_alarm_interface); +- if (err < 0) +- goto err2; +- +- return 0; +- +-err2: +- wake_lock_destroy(&alarm_rtc_wake_lock); +- platform_driver_unregister(&alarm_driver); +-err1: +- return err; +-} +- +-static void __exit alarm_exit(void) +-{ +- class_interface_unregister(&rtc_alarm_interface); +- wake_lock_destroy(&alarm_rtc_wake_lock); +- platform_driver_unregister(&alarm_driver); +-} +- +-late_initcall(alarm_late_init); +-module_init(alarm_driver_init); +-module_exit(alarm_exit); +- +diff --git a/drivers/staging/android/android_alarm.h b/drivers/staging/android/android_alarm.h +index 6eecbde2..d0cafd63 100644 +--- a/drivers/staging/android/android_alarm.h ++++ b/drivers/staging/android/android_alarm.h +@@ -33,65 +33,6 @@ enum android_alarm_type { + /* ANDROID_ALARM_TIME_CHANGE = 16 */ + }; + +-#ifdef __KERNEL__ +- +-#include +-#include +- +-/* +- * The alarm interface is similar to the hrtimer interface but adds support +- * for wakeup from suspend. It also adds an elapsed realtime clock that can +- * be used for periodic timers that need to keep runing while the system is +- * suspended and not be disrupted when the wall time is set. +- */ +- +-/** +- * struct alarm - the basic alarm structure +- * @node: red black tree node for time ordered insertion +- * @type: alarm type. rtc/elapsed-realtime/systemtime, wakeup/non-wakeup. +- * @softexpires: the absolute earliest expiry time of the alarm. +- * @expires: the absolute expiry time. +- * @function: alarm expiry callback function +- * +- * The alarm structure must be initialized by alarm_init() +- * +- */ +- +-struct android_alarm { +- struct rb_node node; +- enum android_alarm_type type; +- ktime_t softexpires; +- ktime_t expires; +- void (*function)(struct android_alarm *); +-}; +- +-void android_alarm_init(struct android_alarm *alarm, +- enum android_alarm_type type, void (*function)(struct android_alarm *)); +-void android_alarm_start_range(struct android_alarm *alarm, ktime_t start, +- ktime_t end); +-int android_alarm_try_to_cancel(struct android_alarm *alarm); +-int android_alarm_cancel(struct android_alarm *alarm); +-ktime_t alarm_get_elapsed_realtime(void); +- +-/* set rtc while preserving elapsed realtime */ +-int android_alarm_set_rtc(const struct timespec ts); +- +-#ifdef CONFIG_ANDROID_ALARM_OLDDRV_COMPAT +-/* +- * Some older drivers depend on the old API, +- * so provide compatability macros for now. +- */ +-#define alarm android_alarm +-#define alarm_init(x, y, z) android_alarm_init(x, y, z) +-#define alarm_start_range(x, y, z) android_alarm_start_range(x, y, z) +-#define alarm_try_to_cancel(x) android_alarm_try_to_cancel(x) +-#define alarm_cancel(x) android_alarm_cancel(x) +-#define alarm_set_rtc(x) android_alarm_set_rtc(x) +-#endif +- +- +-#endif +- + enum android_alarm_return_flags { + ANDROID_ALARM_RTC_WAKEUP_MASK = 1U << ANDROID_ALARM_RTC_WAKEUP, + ANDROID_ALARM_RTC_MASK = 1U << ANDROID_ALARM_RTC, +diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c +index 9f1f27e7..bda20bf1 100644 +--- a/drivers/staging/android/ashmem.c ++++ b/drivers/staging/android/ashmem.c +@@ -314,21 +314,13 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma) + } + get_file(asma->file); + +- /* +- * XXX - Reworked to use shmem_zero_setup() instead of +- * shmem_set_file while we're in staging. -jstultz +- */ +- if (vma->vm_flags & VM_SHARED) { +- ret = shmem_zero_setup(vma); +- if (ret) { +- fput(asma->file); +- goto out; +- } ++ if (vma->vm_flags & VM_SHARED) ++ shmem_set_file(vma, asma->file); ++ else { ++ if (vma->vm_file) ++ fput(vma->vm_file); ++ vma->vm_file = asma->file; + } +- +- if (vma->vm_file) +- fput(vma->vm_file); +- vma->vm_file = asma->file; + vma->vm_flags |= VM_CAN_NONLINEAR; + + out: +diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c +index 223639a7..a7a5f2ae 100644 +--- a/drivers/staging/android/binder.c ++++ b/drivers/staging/android/binder.c +@@ -33,10 +33,12 @@ + #include + #include + #include ++#include + + #include "binder.h" ++#include "binder_trace.h" + +-static DEFINE_MUTEX(binder_lock); ++static DEFINE_MUTEX(binder_main_lock); + static DEFINE_MUTEX(binder_deferred_lock); + static DEFINE_MUTEX(binder_mmap_lock); + +@@ -499,6 +501,19 @@ out_unlock: + return -EBADF; + } + ++static inline void binder_lock(const char *tag) ++{ ++ trace_binder_lock(tag); ++ mutex_lock(&binder_main_lock); ++ trace_binder_locked(tag); ++} ++ ++static inline void binder_unlock(const char *tag) ++{ ++ trace_binder_unlock(tag); ++ mutex_unlock(&binder_main_lock); ++} ++ + static void binder_set_nice(long nice) + { + long min_nice; +@@ -625,6 +640,8 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, + if (end <= start) + return 0; + ++ trace_binder_update_page_range(proc, allocate, start, end); ++ + if (vma) + mm = NULL; + else +@@ -1473,6 +1490,10 @@ static void binder_transaction(struct binder_proc *proc, + return_error = BR_DEAD_REPLY; + goto err_dead_binder; + } ++ if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) { ++ return_error = BR_FAILED_REPLY; ++ goto err_invalid_target_handle; ++ } + if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { + struct binder_transaction *tmp; + tmp = thread->transaction_stack; +@@ -1549,6 +1570,9 @@ static void binder_transaction(struct binder_proc *proc, + t->code = tr->code; + t->flags = tr->flags; + t->priority = task_nice(current); ++ ++ trace_binder_transaction(reply, t, target_node); ++ + t->buffer = binder_alloc_buf(target_proc, tr->data_size, + tr->offsets_size, !reply && (t->flags & TF_ONE_WAY)); + if (t->buffer == NULL) { +@@ -1559,6 +1583,7 @@ static void binder_transaction(struct binder_proc *proc, + t->buffer->debug_id = t->debug_id; + t->buffer->transaction = t; + t->buffer->target_node = target_node; ++ trace_binder_transaction_alloc_buf(t->buffer); + if (target_node) + binder_inc_node(target_node, 1, 0, NULL); + +@@ -1618,6 +1643,10 @@ static void binder_transaction(struct binder_proc *proc, + fp->cookie, node->cookie); + goto err_binder_get_ref_for_node_failed; + } ++ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { ++ return_error = BR_FAILED_REPLY; ++ goto err_binder_get_ref_for_node_failed; ++ } + ref = binder_get_ref_for_node(target_proc, node); + if (ref == NULL) { + return_error = BR_FAILED_REPLY; +@@ -1631,6 +1660,7 @@ static void binder_transaction(struct binder_proc *proc, + binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, + &thread->todo); + ++ trace_binder_transaction_node_to_ref(t, node, ref); + binder_debug(BINDER_DEBUG_TRANSACTION, + " node %d u%p -> ref %d desc %d\n", + node->debug_id, node->ptr, ref->debug_id, +@@ -1647,6 +1677,10 @@ static void binder_transaction(struct binder_proc *proc, + return_error = BR_FAILED_REPLY; + goto err_binder_get_ref_failed; + } ++ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { ++ return_error = BR_FAILED_REPLY; ++ goto err_binder_get_ref_failed; ++ } + if (ref->node->proc == target_proc) { + if (fp->type == BINDER_TYPE_HANDLE) + fp->type = BINDER_TYPE_BINDER; +@@ -1655,6 +1689,7 @@ static void binder_transaction(struct binder_proc *proc, + fp->binder = ref->node->ptr; + fp->cookie = ref->node->cookie; + binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL); ++ trace_binder_transaction_ref_to_node(t, ref); + binder_debug(BINDER_DEBUG_TRANSACTION, + " ref %d desc %d -> node %d u%p\n", + ref->debug_id, ref->desc, ref->node->debug_id, +@@ -1668,6 +1703,8 @@ static void binder_transaction(struct binder_proc *proc, + } + fp->handle = new_ref->desc; + binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL); ++ trace_binder_transaction_ref_to_ref(t, ref, ++ new_ref); + binder_debug(BINDER_DEBUG_TRANSACTION, + " ref %d desc %d -> ref %d desc %d (node %d)\n", + ref->debug_id, ref->desc, new_ref->debug_id, +@@ -1700,6 +1737,11 @@ static void binder_transaction(struct binder_proc *proc, + return_error = BR_FAILED_REPLY; + goto err_fget_failed; + } ++ if (security_binder_transfer_file(proc->tsk, target_proc->tsk, file) < 0) { ++ fput(file); ++ return_error = BR_FAILED_REPLY; ++ goto err_get_unused_fd_failed; ++ } + target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC); + if (target_fd < 0) { + fput(file); +@@ -1707,6 +1749,7 @@ static void binder_transaction(struct binder_proc *proc, + goto err_get_unused_fd_failed; + } + task_fd_install(target_proc, target_fd, file); ++ trace_binder_transaction_fd(t, fp->handle, target_fd); + binder_debug(BINDER_DEBUG_TRANSACTION, + " fd %ld -> %d\n", fp->handle, target_fd); + /* TODO: fput? */ +@@ -1755,6 +1798,7 @@ err_binder_new_node_failed: + err_bad_object_type: + err_bad_offset: + err_copy_data_failed: ++ trace_binder_transaction_failed_buffer_release(t->buffer); + binder_transaction_buffer_release(target_proc, t->buffer, offp); + t->buffer->transaction = NULL; + binder_free_buf(target_proc, t->buffer); +@@ -1800,6 +1844,7 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, + if (get_user(cmd, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); ++ trace_binder_command(cmd); + if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) { + binder_stats.bc[_IOC_NR(cmd)]++; + proc->stats.bc[_IOC_NR(cmd)]++; +@@ -1969,6 +2014,7 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, + else + list_move_tail(buffer->target_node->async_todo.next, &thread->todo); + } ++ trace_binder_transaction_buffer_release(buffer); + binder_transaction_buffer_release(proc, buffer, NULL); + binder_free_buf(proc, buffer); + break; +@@ -2177,6 +2223,7 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, + void binder_stat_br(struct binder_proc *proc, struct binder_thread *thread, + uint32_t cmd) + { ++ trace_binder_return(cmd); + if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.br)) { + binder_stats.br[_IOC_NR(cmd)]++; + proc->stats.br[_IOC_NR(cmd)]++; +@@ -2223,6 +2270,7 @@ retry: + if (put_user(thread->return_error2, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); ++ binder_stat_br(proc, thread, thread->return_error2); + if (ptr == end) + goto done; + thread->return_error2 = BR_OK; +@@ -2230,6 +2278,7 @@ retry: + if (put_user(thread->return_error, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); ++ binder_stat_br(proc, thread, thread->return_error); + thread->return_error = BR_OK; + goto done; + } +@@ -2238,7 +2287,12 @@ retry: + thread->looper |= BINDER_LOOPER_STATE_WAITING; + if (wait_for_proc_work) + proc->ready_threads++; +- mutex_unlock(&binder_lock); ++ ++ binder_unlock(__func__); ++ ++ trace_binder_wait_for_work(wait_for_proc_work, ++ !!thread->transaction_stack, ++ !list_empty(&thread->todo)); + if (wait_for_proc_work) { + if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | + BINDER_LOOPER_STATE_ENTERED))) { +@@ -2262,7 +2316,9 @@ retry: + } else + ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread)); + } +- mutex_lock(&binder_lock); ++ ++ binder_lock(__func__); ++ + if (wait_for_proc_work) + proc->ready_threads--; + thread->looper &= ~BINDER_LOOPER_STATE_WAITING; +@@ -2385,6 +2441,7 @@ retry: + if (put_user(death->cookie, (void * __user *)ptr)) + return -EFAULT; + ptr += sizeof(void *); ++ binder_stat_br(proc, thread, cmd); + binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION, + "binder: %d:%d %s %p\n", + proc->pid, thread->pid, +@@ -2452,6 +2509,7 @@ retry: + return -EFAULT; + ptr += sizeof(tr); + ++ trace_binder_transaction_received(t); + binder_stat_br(proc, thread, cmd); + binder_debug(BINDER_DEBUG_TRANSACTION, + "binder: %d:%d %s %d %d:%d, cmd %d" +@@ -2492,6 +2550,7 @@ done: + proc->pid, thread->pid); + if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)) + return -EFAULT; ++ binder_stat_br(proc, thread, BR_SPAWN_LOOPER); + } + return 0; + } +@@ -2628,12 +2687,14 @@ static unsigned int binder_poll(struct file *filp, + struct binder_thread *thread = NULL; + int wait_for_proc_work; + +- mutex_lock(&binder_lock); ++ binder_lock(__func__); ++ + thread = binder_get_thread(proc); + + wait_for_proc_work = thread->transaction_stack == NULL && + list_empty(&thread->todo) && thread->return_error == BR_OK; +- mutex_unlock(&binder_lock); ++ ++ binder_unlock(__func__); + + if (wait_for_proc_work) { + if (binder_has_proc_work(proc, thread)) +@@ -2661,11 +2722,13 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) + + /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/ + ++ trace_binder_ioctl(cmd, arg); ++ + ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); + if (ret) +- return ret; ++ goto err_unlocked; + +- mutex_lock(&binder_lock); ++ binder_lock(__func__); + thread = binder_get_thread(proc); + if (thread == NULL) { + ret = -ENOMEM; +@@ -2690,6 +2753,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) + + if (bwr.write_size > 0) { + ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed); ++ trace_binder_write_done(ret); + if (ret < 0) { + bwr.read_consumed = 0; + if (copy_to_user(ubuf, &bwr, sizeof(bwr))) +@@ -2699,6 +2763,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) + } + if (bwr.read_size > 0) { + ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); ++ trace_binder_read_done(ret); + if (!list_empty(&proc->todo)) + wake_up_interruptible(&proc->wait); + if (ret < 0) { +@@ -2729,6 +2794,9 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) + ret = -EBUSY; + goto err; + } ++ ret = security_binder_set_context_mgr(proc->tsk); ++ if (ret < 0) ++ goto err; + if (binder_context_mgr_uid != -1) { + if (binder_context_mgr_uid != current->cred->euid) { + printk(KERN_ERR "binder: BINDER_SET_" +@@ -2774,10 +2842,12 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) + err: + if (thread) + thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN; +- mutex_unlock(&binder_lock); ++ binder_unlock(__func__); + wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); + if (ret && ret != -ERESTARTSYS) + printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret); ++err_unlocked: ++ trace_binder_ioctl_done(ret); + return ret; + } + +@@ -2920,13 +2990,16 @@ static int binder_open(struct inode *nodp, struct file *filp) + INIT_LIST_HEAD(&proc->todo); + init_waitqueue_head(&proc->wait); + proc->default_priority = task_nice(current); +- mutex_lock(&binder_lock); ++ ++ binder_lock(__func__); ++ + binder_stats_created(BINDER_STAT_PROC); + hlist_add_head(&proc->proc_node, &binder_procs); + proc->pid = current->group_leader->pid; + INIT_LIST_HEAD(&proc->delivered_death); + filp->private_data = proc; +- mutex_unlock(&binder_lock); ++ ++ binder_unlock(__func__); + + if (binder_debugfs_dir_entry_proc) { + char strbuf[11]; +@@ -3108,7 +3181,7 @@ static void binder_deferred_func(struct work_struct *work) + + int defer; + do { +- mutex_lock(&binder_lock); ++ binder_lock(__func__); + mutex_lock(&binder_deferred_lock); + if (!hlist_empty(&binder_deferred_list)) { + proc = hlist_entry(binder_deferred_list.first, +@@ -3135,7 +3208,7 @@ static void binder_deferred_func(struct work_struct *work) + if (defer & BINDER_DEFERRED_RELEASE) + binder_deferred_release(proc); /* frees proc */ + +- mutex_unlock(&binder_lock); ++ binder_unlock(__func__); + if (files) + put_files_struct(files); + } while (proc); +@@ -3476,7 +3549,7 @@ static int binder_state_show(struct seq_file *m, void *unused) + int do_lock = !binder_debug_no_lock; + + if (do_lock) +- mutex_lock(&binder_lock); ++ binder_lock(__func__); + + seq_puts(m, "binder state:\n"); + +@@ -3488,7 +3561,7 @@ static int binder_state_show(struct seq_file *m, void *unused) + hlist_for_each_entry(proc, pos, &binder_procs, proc_node) + print_binder_proc(m, proc, 1); + if (do_lock) +- mutex_unlock(&binder_lock); ++ binder_unlock(__func__); + return 0; + } + +@@ -3499,7 +3572,7 @@ static int binder_stats_show(struct seq_file *m, void *unused) + int do_lock = !binder_debug_no_lock; + + if (do_lock) +- mutex_lock(&binder_lock); ++ binder_lock(__func__); + + seq_puts(m, "binder stats:\n"); + +@@ -3508,7 +3581,7 @@ static int binder_stats_show(struct seq_file *m, void *unused) + hlist_for_each_entry(proc, pos, &binder_procs, proc_node) + print_binder_proc_stats(m, proc); + if (do_lock) +- mutex_unlock(&binder_lock); ++ binder_unlock(__func__); + return 0; + } + +@@ -3519,13 +3592,13 @@ static int binder_transactions_show(struct seq_file *m, void *unused) + int do_lock = !binder_debug_no_lock; + + if (do_lock) +- mutex_lock(&binder_lock); ++ binder_lock(__func__); + + seq_puts(m, "binder transactions:\n"); + hlist_for_each_entry(proc, pos, &binder_procs, proc_node) + print_binder_proc(m, proc, 0); + if (do_lock) +- mutex_unlock(&binder_lock); ++ binder_unlock(__func__); + return 0; + } + +@@ -3535,11 +3608,11 @@ static int binder_proc_show(struct seq_file *m, void *unused) + int do_lock = !binder_debug_no_lock; + + if (do_lock) +- mutex_lock(&binder_lock); ++ binder_lock(__func__); + seq_puts(m, "binder proc state:\n"); + print_binder_proc(m, proc, 1); + if (do_lock) +- mutex_unlock(&binder_lock); ++ binder_unlock(__func__); + return 0; + } + +@@ -3634,4 +3707,7 @@ static int __init binder_init(void) + + device_initcall(binder_init); + ++#define CREATE_TRACE_POINTS ++#include "binder_trace.h" ++ + MODULE_LICENSE("GPL v2"); +diff --git a/drivers/staging/android/binder_trace.h b/drivers/staging/android/binder_trace.h +new file mode 100644 +index 00000000..82a567c2 +--- /dev/null ++++ b/drivers/staging/android/binder_trace.h +@@ -0,0 +1,327 @@ ++/* ++ * Copyright (C) 2012 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM binder ++ ++#if !defined(_BINDER_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _BINDER_TRACE_H ++ ++#include ++ ++struct binder_buffer; ++struct binder_node; ++struct binder_proc; ++struct binder_ref; ++struct binder_thread; ++struct binder_transaction; ++ ++TRACE_EVENT(binder_ioctl, ++ TP_PROTO(unsigned int cmd, unsigned long arg), ++ TP_ARGS(cmd, arg), ++ ++ TP_STRUCT__entry( ++ __field(unsigned int, cmd) ++ __field(unsigned long, arg) ++ ), ++ TP_fast_assign( ++ __entry->cmd = cmd; ++ __entry->arg = arg; ++ ), ++ TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg) ++); ++ ++DECLARE_EVENT_CLASS(binder_lock_class, ++ TP_PROTO(const char *tag), ++ TP_ARGS(tag), ++ TP_STRUCT__entry( ++ __field(const char *, tag) ++ ), ++ TP_fast_assign( ++ __entry->tag = tag; ++ ), ++ TP_printk("tag=%s", __entry->tag) ++); ++ ++#define DEFINE_BINDER_LOCK_EVENT(name) \ ++DEFINE_EVENT(binder_lock_class, name, \ ++ TP_PROTO(const char *func), \ ++ TP_ARGS(func)) ++ ++DEFINE_BINDER_LOCK_EVENT(binder_lock); ++DEFINE_BINDER_LOCK_EVENT(binder_locked); ++DEFINE_BINDER_LOCK_EVENT(binder_unlock); ++ ++DECLARE_EVENT_CLASS(binder_function_return_class, ++ TP_PROTO(int ret), ++ TP_ARGS(ret), ++ TP_STRUCT__entry( ++ __field(int, ret) ++ ), ++ TP_fast_assign( ++ __entry->ret = ret; ++ ), ++ TP_printk("ret=%d", __entry->ret) ++); ++ ++#define DEFINE_BINDER_FUNCTION_RETURN_EVENT(name) \ ++DEFINE_EVENT(binder_function_return_class, name, \ ++ TP_PROTO(int ret), \ ++ TP_ARGS(ret)) ++ ++DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done); ++DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_write_done); ++DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_read_done); ++ ++TRACE_EVENT(binder_wait_for_work, ++ TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo), ++ TP_ARGS(proc_work, transaction_stack, thread_todo), ++ ++ TP_STRUCT__entry( ++ __field(bool, proc_work) ++ __field(bool, transaction_stack) ++ __field(bool, thread_todo) ++ ), ++ TP_fast_assign( ++ __entry->proc_work = proc_work; ++ __entry->transaction_stack = transaction_stack; ++ __entry->thread_todo = thread_todo; ++ ), ++ TP_printk("proc_work=%d transaction_stack=%d thread_todo=%d", ++ __entry->proc_work, __entry->transaction_stack, ++ __entry->thread_todo) ++); ++ ++TRACE_EVENT(binder_transaction, ++ TP_PROTO(bool reply, struct binder_transaction *t, ++ struct binder_node *target_node), ++ TP_ARGS(reply, t, target_node), ++ TP_STRUCT__entry( ++ __field(int, debug_id) ++ __field(int, target_node) ++ __field(int, to_proc) ++ __field(int, to_thread) ++ __field(int, reply) ++ __field(unsigned int, code) ++ __field(unsigned int, flags) ++ ), ++ TP_fast_assign( ++ __entry->debug_id = t->debug_id; ++ __entry->target_node = target_node ? target_node->debug_id : 0; ++ __entry->to_proc = t->to_proc->pid; ++ __entry->to_thread = t->to_thread ? t->to_thread->pid : 0; ++ __entry->reply = reply; ++ __entry->code = t->code; ++ __entry->flags = t->flags; ++ ), ++ TP_printk("transaction=%d dest_node=%d dest_proc=%d dest_thread=%d reply=%d flags=0x%x code=0x%x", ++ __entry->debug_id, __entry->target_node, ++ __entry->to_proc, __entry->to_thread, ++ __entry->reply, __entry->flags, __entry->code) ++); ++ ++TRACE_EVENT(binder_transaction_received, ++ TP_PROTO(struct binder_transaction *t), ++ TP_ARGS(t), ++ ++ TP_STRUCT__entry( ++ __field(int, debug_id) ++ ), ++ TP_fast_assign( ++ __entry->debug_id = t->debug_id; ++ ), ++ TP_printk("transaction=%d", __entry->debug_id) ++); ++ ++TRACE_EVENT(binder_transaction_node_to_ref, ++ TP_PROTO(struct binder_transaction *t, struct binder_node *node, ++ struct binder_ref *ref), ++ TP_ARGS(t, node, ref), ++ ++ TP_STRUCT__entry( ++ __field(int, debug_id) ++ __field(int, node_debug_id) ++ __field(void __user *, node_ptr) ++ __field(int, ref_debug_id) ++ __field(uint32_t, ref_desc) ++ ), ++ TP_fast_assign( ++ __entry->debug_id = t->debug_id; ++ __entry->node_debug_id = node->debug_id; ++ __entry->node_ptr = node->ptr; ++ __entry->ref_debug_id = ref->debug_id; ++ __entry->ref_desc = ref->desc; ++ ), ++ TP_printk("transaction=%d node=%d src_ptr=0x%p ==> dest_ref=%d dest_desc=%d", ++ __entry->debug_id, __entry->node_debug_id, __entry->node_ptr, ++ __entry->ref_debug_id, __entry->ref_desc) ++); ++ ++TRACE_EVENT(binder_transaction_ref_to_node, ++ TP_PROTO(struct binder_transaction *t, struct binder_ref *ref), ++ TP_ARGS(t, ref), ++ ++ TP_STRUCT__entry( ++ __field(int, debug_id) ++ __field(int, ref_debug_id) ++ __field(uint32_t, ref_desc) ++ __field(int, node_debug_id) ++ __field(void __user *, node_ptr) ++ ), ++ TP_fast_assign( ++ __entry->debug_id = t->debug_id; ++ __entry->ref_debug_id = ref->debug_id; ++ __entry->ref_desc = ref->desc; ++ __entry->node_debug_id = ref->node->debug_id; ++ __entry->node_ptr = ref->node->ptr; ++ ), ++ TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%p", ++ __entry->debug_id, __entry->node_debug_id, ++ __entry->ref_debug_id, __entry->ref_desc, __entry->node_ptr) ++); ++ ++TRACE_EVENT(binder_transaction_ref_to_ref, ++ TP_PROTO(struct binder_transaction *t, struct binder_ref *src_ref, ++ struct binder_ref *dest_ref), ++ TP_ARGS(t, src_ref, dest_ref), ++ ++ TP_STRUCT__entry( ++ __field(int, debug_id) ++ __field(int, node_debug_id) ++ __field(int, src_ref_debug_id) ++ __field(uint32_t, src_ref_desc) ++ __field(int, dest_ref_debug_id) ++ __field(uint32_t, dest_ref_desc) ++ ), ++ TP_fast_assign( ++ __entry->debug_id = t->debug_id; ++ __entry->node_debug_id = src_ref->node->debug_id; ++ __entry->src_ref_debug_id = src_ref->debug_id; ++ __entry->src_ref_desc = src_ref->desc; ++ __entry->dest_ref_debug_id = dest_ref->debug_id; ++ __entry->dest_ref_desc = dest_ref->desc; ++ ), ++ TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ref=%d dest_desc=%d", ++ __entry->debug_id, __entry->node_debug_id, ++ __entry->src_ref_debug_id, __entry->src_ref_desc, ++ __entry->dest_ref_debug_id, __entry->dest_ref_desc) ++); ++ ++TRACE_EVENT(binder_transaction_fd, ++ TP_PROTO(struct binder_transaction *t, int src_fd, int dest_fd), ++ TP_ARGS(t, src_fd, dest_fd), ++ ++ TP_STRUCT__entry( ++ __field(int, debug_id) ++ __field(int, src_fd) ++ __field(int, dest_fd) ++ ), ++ TP_fast_assign( ++ __entry->debug_id = t->debug_id; ++ __entry->src_fd = src_fd; ++ __entry->dest_fd = dest_fd; ++ ), ++ TP_printk("transaction=%d src_fd=%d ==> dest_fd=%d", ++ __entry->debug_id, __entry->src_fd, __entry->dest_fd) ++); ++ ++DECLARE_EVENT_CLASS(binder_buffer_class, ++ TP_PROTO(struct binder_buffer *buf), ++ TP_ARGS(buf), ++ TP_STRUCT__entry( ++ __field(int, debug_id) ++ __field(size_t, data_size) ++ __field(size_t, offsets_size) ++ ), ++ TP_fast_assign( ++ __entry->debug_id = buf->debug_id; ++ __entry->data_size = buf->data_size; ++ __entry->offsets_size = buf->offsets_size; ++ ), ++ TP_printk("transaction=%d data_size=%zd offsets_size=%zd", ++ __entry->debug_id, __entry->data_size, __entry->offsets_size) ++); ++ ++DEFINE_EVENT(binder_buffer_class, binder_transaction_alloc_buf, ++ TP_PROTO(struct binder_buffer *buffer), ++ TP_ARGS(buffer)); ++ ++DEFINE_EVENT(binder_buffer_class, binder_transaction_buffer_release, ++ TP_PROTO(struct binder_buffer *buffer), ++ TP_ARGS(buffer)); ++ ++DEFINE_EVENT(binder_buffer_class, binder_transaction_failed_buffer_release, ++ TP_PROTO(struct binder_buffer *buffer), ++ TP_ARGS(buffer)); ++ ++TRACE_EVENT(binder_update_page_range, ++ TP_PROTO(struct binder_proc *proc, bool allocate, ++ void *start, void *end), ++ TP_ARGS(proc, allocate, start, end), ++ TP_STRUCT__entry( ++ __field(int, proc) ++ __field(bool, allocate) ++ __field(size_t, offset) ++ __field(size_t, size) ++ ), ++ TP_fast_assign( ++ __entry->proc = proc->pid; ++ __entry->allocate = allocate; ++ __entry->offset = start - proc->buffer; ++ __entry->size = end - start; ++ ), ++ TP_printk("proc=%d allocate=%d offset=%zu size=%zu", ++ __entry->proc, __entry->allocate, ++ __entry->offset, __entry->size) ++); ++ ++TRACE_EVENT(binder_command, ++ TP_PROTO(uint32_t cmd), ++ TP_ARGS(cmd), ++ TP_STRUCT__entry( ++ __field(uint32_t, cmd) ++ ), ++ TP_fast_assign( ++ __entry->cmd = cmd; ++ ), ++ TP_printk("cmd=0x%x %s", ++ __entry->cmd, ++ _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_command_strings) ? ++ binder_command_strings[_IOC_NR(__entry->cmd)] : ++ "unknown") ++); ++ ++TRACE_EVENT(binder_return, ++ TP_PROTO(uint32_t cmd), ++ TP_ARGS(cmd), ++ TP_STRUCT__entry( ++ __field(uint32_t, cmd) ++ ), ++ TP_fast_assign( ++ __entry->cmd = cmd; ++ ), ++ TP_printk("cmd=0x%x %s", ++ __entry->cmd, ++ _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_return_strings) ? ++ binder_return_strings[_IOC_NR(__entry->cmd)] : ++ "unknown") ++); ++ ++#endif /* _BINDER_TRACE_H */ ++ ++#undef TRACE_INCLUDE_PATH ++#undef TRACE_INCLUDE_FILE ++#define TRACE_INCLUDE_PATH . ++#define TRACE_INCLUDE_FILE binder_trace ++#include +diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c +index ea69b6a7..3813c743 100644 +--- a/drivers/staging/android/logger.c ++++ b/drivers/staging/android/logger.c +@@ -57,6 +57,8 @@ struct logger_reader { + struct logger_log *log; /* associated log */ + struct list_head list; /* entry in logger_log's list */ + size_t r_off; /* current read head offset */ ++ bool r_all; /* reader can read all entries */ ++ int r_ver; /* reader ABI version */ + }; + + /* logger_offset - returns index 'n' into the log via (optimized) modulus */ +@@ -90,8 +92,29 @@ static inline struct logger_log *file_get_log(struct file *file) + } + + /* +- * get_entry_len - Grabs the length of the payload of the next entry starting +- * from 'off'. ++ * get_entry_header - returns a pointer to the logger_entry header within ++ * 'log' starting at offset 'off'. A temporary logger_entry 'scratch' must ++ * be provided. Typically the return value will be a pointer within ++ * 'logger->buf'. However, a pointer to 'scratch' may be returned if ++ * the log entry spans the end and beginning of the circular buffer. ++ */ ++static struct logger_entry *get_entry_header(struct logger_log *log, ++ size_t off, struct logger_entry *scratch) ++{ ++ size_t len = min(sizeof(struct logger_entry), log->size - off); ++ if (len != sizeof(struct logger_entry)) { ++ memcpy(((void *) scratch), log->buffer + off, len); ++ memcpy(((void *) scratch) + len, log->buffer, ++ sizeof(struct logger_entry) - len); ++ return scratch; ++ } ++ ++ return (struct logger_entry *) (log->buffer + off); ++} ++ ++/* ++ * get_entry_msg_len - Grabs the length of the message of the entry ++ * starting from from 'off'. + * + * An entry length is 2 bytes (16 bits) in host endian order. + * In the log, the length does not include the size of the log entry structure. +@@ -99,20 +122,45 @@ static inline struct logger_log *file_get_log(struct file *file) + * + * Caller needs to hold log->mutex. + */ +-static __u32 get_entry_len(struct logger_log *log, size_t off) ++static __u32 get_entry_msg_len(struct logger_log *log, size_t off) + { +- __u16 val; ++ struct logger_entry scratch; ++ struct logger_entry *entry; + +- /* copy 2 bytes from buffer, in memcpy order, */ +- /* handling possible wrap at end of buffer */ ++ entry = get_entry_header(log, off, &scratch); ++ return entry->len; ++} + +- ((__u8 *)&val)[0] = log->buffer[off]; +- if (likely(off+1 < log->size)) +- ((__u8 *)&val)[1] = log->buffer[off+1]; ++static size_t get_user_hdr_len(int ver) ++{ ++ if (ver < 2) ++ return sizeof(struct user_logger_entry_compat); + else +- ((__u8 *)&val)[1] = log->buffer[0]; ++ return sizeof(struct logger_entry); ++} + +- return sizeof(struct logger_entry) + val; ++static ssize_t copy_header_to_user(int ver, struct logger_entry *entry, ++ char __user *buf) ++{ ++ void *hdr; ++ size_t hdr_len; ++ struct user_logger_entry_compat v1; ++ ++ if (ver < 2) { ++ v1.len = entry->len; ++ v1.__pad = 0; ++ v1.pid = entry->pid; ++ v1.tid = entry->tid; ++ v1.sec = entry->sec; ++ v1.nsec = entry->nsec; ++ hdr = &v1; ++ hdr_len = sizeof(struct user_logger_entry_compat); ++ } else { ++ hdr = entry; ++ hdr_len = sizeof(struct logger_entry); ++ } ++ ++ return copy_to_user(buf, hdr, hdr_len); + } + + /* +@@ -126,15 +174,31 @@ static ssize_t do_read_log_to_user(struct logger_log *log, + char __user *buf, + size_t count) + { ++ struct logger_entry scratch; ++ struct logger_entry *entry; + size_t len; ++ size_t msg_start; + + /* +- * We read from the log in two disjoint operations. First, we read from +- * the current read head offset up to 'count' bytes or to the end of ++ * First, copy the header to userspace, using the version of ++ * the header requested ++ */ ++ entry = get_entry_header(log, reader->r_off, &scratch); ++ if (copy_header_to_user(reader->r_ver, entry, buf)) ++ return -EFAULT; ++ ++ count -= get_user_hdr_len(reader->r_ver); ++ buf += get_user_hdr_len(reader->r_ver); ++ msg_start = logger_offset(log, ++ reader->r_off + sizeof(struct logger_entry)); ++ ++ /* ++ * We read from the msg in two disjoint operations. First, we read from ++ * the current msg head offset up to 'count' bytes or to the end of + * the log, whichever comes first. + */ +- len = min(count, log->size - reader->r_off); +- if (copy_to_user(buf, log->buffer + reader->r_off, len)) ++ len = min(count, log->size - msg_start); ++ if (copy_to_user(buf, log->buffer + msg_start, len)) + return -EFAULT; + + /* +@@ -145,9 +209,34 @@ static ssize_t do_read_log_to_user(struct logger_log *log, + if (copy_to_user(buf + len, log->buffer, count - len)) + return -EFAULT; + +- reader->r_off = logger_offset(log, reader->r_off + count); ++ reader->r_off = logger_offset(log, reader->r_off + ++ sizeof(struct logger_entry) + count); + +- return count; ++ return count + get_user_hdr_len(reader->r_ver); ++} ++ ++/* ++ * get_next_entry_by_uid - Starting at 'off', returns an offset into ++ * 'log->buffer' which contains the first entry readable by 'euid' ++ */ ++static size_t get_next_entry_by_uid(struct logger_log *log, ++ size_t off, uid_t euid) ++{ ++ while (off != log->w_off) { ++ struct logger_entry *entry; ++ struct logger_entry scratch; ++ size_t next_len; ++ ++ entry = get_entry_header(log, off, &scratch); ++ ++ if (entry->euid == euid) ++ return off; ++ ++ next_len = sizeof(struct logger_entry) + entry->len; ++ off = logger_offset(log, off + next_len); ++ } ++ ++ return off; + } + + /* +@@ -159,7 +248,7 @@ static ssize_t do_read_log_to_user(struct logger_log *log, + * - If there are no log entries to read, blocks until log is written to + * - Atomically reads exactly one log entry + * +- * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read ++ * Will set errno to EINVAL if read + * buffer is insufficient to hold next entry. + */ + static ssize_t logger_read(struct file *file, char __user *buf, +@@ -200,6 +289,10 @@ start: + + mutex_lock(&log->mutex); + ++ if (!reader->r_all) ++ reader->r_off = get_next_entry_by_uid(log, ++ reader->r_off, current_euid()); ++ + /* is there still something to read or did we race? */ + if (unlikely(log->w_off == reader->r_off)) { + mutex_unlock(&log->mutex); +@@ -207,7 +300,8 @@ start: + } + + /* get the size of the next entry */ +- ret = get_entry_len(log, reader->r_off); ++ ret = get_user_hdr_len(reader->r_ver) + ++ get_entry_msg_len(log, reader->r_off); + if (count < ret) { + ret = -EINVAL; + goto out; +@@ -233,7 +327,8 @@ static size_t get_next_entry(struct logger_log *log, size_t off, size_t len) + size_t count = 0; + + do { +- size_t nr = get_entry_len(log, off); ++ size_t nr = sizeof(struct logger_entry) + ++ get_entry_msg_len(log, off); + off = logger_offset(log, off + nr); + count += nr; + } while (count < len); +@@ -363,7 +458,9 @@ ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov, + header.tid = current->pid; + header.sec = now.tv_sec; + header.nsec = now.tv_nsec; ++ header.euid = current_euid(); + header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD); ++ header.hdr_size = sizeof(struct logger_entry); + + /* null writes succeed, return zero */ + if (unlikely(!header.len)) +@@ -436,6 +533,10 @@ static int logger_open(struct inode *inode, struct file *file) + return -ENOMEM; + + reader->log = log; ++ reader->r_ver = 1; ++ reader->r_all = in_egroup_p(inode->i_gid) || ++ capable(CAP_SYSLOG); ++ + INIT_LIST_HEAD(&reader->list); + + mutex_lock(&log->mutex); +@@ -495,6 +596,10 @@ static unsigned int logger_poll(struct file *file, poll_table *wait) + poll_wait(file, &log->wq, wait); + + mutex_lock(&log->mutex); ++ if (!reader->r_all) ++ reader->r_off = get_next_entry_by_uid(log, ++ reader->r_off, current_euid()); ++ + if (log->w_off != reader->r_off) + ret |= POLLIN | POLLRDNORM; + mutex_unlock(&log->mutex); +@@ -502,11 +607,25 @@ static unsigned int logger_poll(struct file *file, poll_table *wait) + return ret; + } + ++static long logger_set_version(struct logger_reader *reader, void __user *arg) ++{ ++ int version; ++ if (copy_from_user(&version, arg, sizeof(int))) ++ return -EFAULT; ++ ++ if ((version < 1) || (version > 2)) ++ return -EINVAL; ++ ++ reader->r_ver = version; ++ return 0; ++} ++ + static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + { + struct logger_log *log = file_get_log(file); + struct logger_reader *reader; +- long ret = -ENOTTY; ++ long ret = -EINVAL; ++ void __user *argp = (void __user *) arg; + + mutex_lock(&log->mutex); + +@@ -531,8 +650,14 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + break; + } + reader = file->private_data; ++ ++ if (!reader->r_all) ++ reader->r_off = get_next_entry_by_uid(log, ++ reader->r_off, current_euid()); ++ + if (log->w_off != reader->r_off) +- ret = get_entry_len(log, reader->r_off); ++ ret = get_user_hdr_len(reader->r_ver) + ++ get_entry_msg_len(log, reader->r_off); + else + ret = 0; + break; +@@ -541,11 +666,32 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + ret = -EBADF; + break; + } ++ if (!(in_egroup_p(file->f_dentry->d_inode->i_gid) || ++ capable(CAP_SYSLOG))) { ++ ret = -EPERM; ++ break; ++ } + list_for_each_entry(reader, &log->readers, list) + reader->r_off = log->w_off; + log->head = log->w_off; + ret = 0; + break; ++ case LOGGER_GET_VERSION: ++ if (!(file->f_mode & FMODE_READ)) { ++ ret = -EBADF; ++ break; ++ } ++ reader = file->private_data; ++ ret = reader->r_ver; ++ break; ++ case LOGGER_SET_VERSION: ++ if (!(file->f_mode & FMODE_READ)) { ++ ret = -EBADF; ++ break; ++ } ++ reader = file->private_data; ++ ret = logger_set_version(reader, argp); ++ break; + } + + mutex_unlock(&log->mutex); +@@ -566,8 +712,8 @@ static const struct file_operations logger_fops = { + + /* + * Defines a log structure with name 'NAME' and a size of 'SIZE' bytes, which +- * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, and less than +- * LONG_MAX minus LOGGER_ENTRY_MAX_LEN. ++ * must be a power of two, and greater than ++ * (LOGGER_ENTRY_MAX_PAYLOAD + sizeof(struct logger_entry)). + */ + #define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \ + static unsigned char _buf_ ## VAR[SIZE]; \ +diff --git a/drivers/staging/android/logger.h b/drivers/staging/android/logger.h +index 2cb06e9d..3f612a3b 100644 +--- a/drivers/staging/android/logger.h ++++ b/drivers/staging/android/logger.h +@@ -20,7 +20,12 @@ + #include + #include + +-struct logger_entry { ++/* ++ * The userspace structure for version 1 of the logger_entry ABI. ++ * This structure is returned to userspace unless the caller requests ++ * an upgrade to a newer ABI version. ++ */ ++struct user_logger_entry_compat { + __u16 len; /* length of the payload */ + __u16 __pad; /* no matter what, we get 2 bytes of padding */ + __s32 pid; /* generating process's pid */ +@@ -30,14 +35,28 @@ struct logger_entry { + char msg[0]; /* the entry's payload */ + }; + ++/* ++ * The structure for version 2 of the logger_entry ABI. ++ * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION) ++ * is called with version >= 2 ++ */ ++struct logger_entry { ++ __u16 len; /* length of the payload */ ++ __u16 hdr_size; /* sizeof(struct logger_entry_v2) */ ++ __s32 pid; /* generating process's pid */ ++ __s32 tid; /* generating process's tid */ ++ __s32 sec; /* seconds since Epoch */ ++ __s32 nsec; /* nanoseconds */ ++ uid_t euid; /* effective UID of logger */ ++ char msg[0]; /* the entry's payload */ ++}; ++ + #define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */ + #define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */ + #define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */ + #define LOGGER_LOG_MAIN "log_main" /* everything else */ + +-#define LOGGER_ENTRY_MAX_LEN (4*1024) +-#define LOGGER_ENTRY_MAX_PAYLOAD \ +- (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) ++#define LOGGER_ENTRY_MAX_PAYLOAD 4076 + + #define __LOGGERIO 0xAE + +@@ -45,5 +64,7 @@ struct logger_entry { + #define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ + #define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ + #define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ ++#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ ++#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ + + #endif /* _LINUX_LOGGER_H */ +diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c +index b91e4bc3..12f0a13a 100644 +--- a/drivers/staging/android/lowmemorykiller.c ++++ b/drivers/staging/android/lowmemorykiller.c +@@ -35,11 +35,11 @@ + #include + #include + #include ++#include + #include +-#include + #include + +-static uint32_t lowmem_debug_level = 2; ++static uint32_t lowmem_debug_level = 1; + static int lowmem_adj[6] = { + 0, + 1, +@@ -74,7 +74,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) + int selected_tasksize = 0; + int selected_oom_score_adj; + int array_size = ARRAY_SIZE(lowmem_adj); +- int other_free = global_page_state(NR_FREE_PAGES); ++ int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages; + int other_file = global_page_state(NR_FILE_PAGES) - + global_page_state(NR_SHMEM); + +@@ -175,9 +175,94 @@ static void __exit lowmem_exit(void) + unregister_shrinker(&lowmem_shrinker); + } + ++#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES ++static int lowmem_oom_adj_to_oom_score_adj(int oom_adj) ++{ ++ if (oom_adj == OOM_ADJUST_MAX) ++ return OOM_SCORE_ADJ_MAX; ++ else ++ return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE; ++} ++ ++static void lowmem_autodetect_oom_adj_values(void) ++{ ++ int i; ++ int oom_adj; ++ int oom_score_adj; ++ int array_size = ARRAY_SIZE(lowmem_adj); ++ ++ if (lowmem_adj_size < array_size) ++ array_size = lowmem_adj_size; ++ ++ if (array_size <= 0) ++ return; ++ ++ oom_adj = lowmem_adj[array_size - 1]; ++ if (oom_adj > OOM_ADJUST_MAX) ++ return; ++ ++ oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj); ++ if (oom_score_adj <= OOM_ADJUST_MAX) ++ return; ++ ++ lowmem_print(1, "lowmem_shrink: convert oom_adj to oom_score_adj:\n"); ++ for (i = 0; i < array_size; i++) { ++ oom_adj = lowmem_adj[i]; ++ oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj); ++ lowmem_adj[i] = oom_score_adj; ++ lowmem_print(1, "oom_adj %d => oom_score_adj %d\n", ++ oom_adj, oom_score_adj); ++ } ++} ++ ++static int lowmem_adj_array_set(const char *val, const struct kernel_param *kp) ++{ ++ int ret; ++ ++ ret = param_array_ops.set(val, kp); ++ ++ /* HACK: Autodetect oom_adj values in lowmem_adj array */ ++ lowmem_autodetect_oom_adj_values(); ++ ++ return ret; ++} ++ ++static int lowmem_adj_array_get(char *buffer, const struct kernel_param *kp) ++{ ++ return param_array_ops.get(buffer, kp); ++} ++ ++static void lowmem_adj_array_free(void *arg) ++{ ++ param_array_ops.free(arg); ++} ++ ++static struct kernel_param_ops lowmem_adj_array_ops = { ++ .set = lowmem_adj_array_set, ++ .get = lowmem_adj_array_get, ++ .free = lowmem_adj_array_free, ++}; ++ ++static const struct kparam_array __param_arr_adj = { ++ .max = ARRAY_SIZE(lowmem_adj), ++ .num = &lowmem_adj_size, ++ .ops = ¶m_ops_int, ++ .elemsize = sizeof(lowmem_adj[0]), ++ .elem = lowmem_adj, ++}; ++#endif ++ + module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); ++#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES ++__module_param_call(MODULE_PARAM_PREFIX, adj, ++ &lowmem_adj_array_ops, ++ .arr = &__param_arr_adj, ++ S_IRUGO | S_IWUSR, -1); ++__MODULE_PARM_TYPE(adj, "array of int"); ++#else + module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size, + S_IRUGO | S_IWUSR); ++#endif + module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, + S_IRUGO | S_IWUSR); + module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR); +diff --git a/drivers/staging/android/persistent_ram.c b/drivers/staging/android/persistent_ram.c +index 3d986ce5..e9e08fd0 100644 +--- a/drivers/staging/android/persistent_ram.c ++++ b/drivers/staging/android/persistent_ram.c +@@ -20,10 +20,10 @@ + #include + #include + #include ++#include + #include + #include + #include +-#include "persistent_ram.h" + + struct persistent_ram_buffer { + uint32_t sig; +@@ -34,7 +34,7 @@ struct persistent_ram_buffer { + + #define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */ + +-static __initdata LIST_HEAD(persistent_ram_list); ++static __devinitdata LIST_HEAD(persistent_ram_list); + + static inline size_t buffer_size(struct persistent_ram_zone *prz) + { +@@ -173,7 +173,7 @@ static void persistent_ram_ecc_old(struct persistent_ram_zone *prz) + } + + static int persistent_ram_init_ecc(struct persistent_ram_zone *prz, +- size_t buffer_size) ++ size_t buffer_size, struct persistent_ram *ram) + { + int numerr; + struct persistent_ram_buffer *buffer = prz->buffer; +@@ -182,12 +182,13 @@ static int persistent_ram_init_ecc(struct persistent_ram_zone *prz, + if (!prz->ecc) + return 0; + +- prz->ecc_block_size = 128; +- prz->ecc_size = 16; +- prz->ecc_symsize = 8; +- prz->ecc_poly = 0x11d; ++ prz->ecc_block_size = ram->ecc_block_size ?: 128; ++ prz->ecc_size = ram->ecc_size ?: 16; ++ prz->ecc_symsize = ram->ecc_symsize ?: 8; ++ prz->ecc_poly = ram->ecc_poly ?: 0x11d; + +- ecc_blocks = DIV_ROUND_UP(prz->buffer_size, prz->ecc_block_size); ++ ecc_blocks = DIV_ROUND_UP(prz->buffer_size - prz->ecc_size, ++ prz->ecc_block_size + prz->ecc_size); + prz->buffer_size -= (ecc_blocks + 1) * prz->ecc_size; + + if (prz->buffer_size > buffer_size) { +@@ -249,7 +250,7 @@ static void notrace persistent_ram_update(struct persistent_ram_zone *prz, + persistent_ram_update_ecc(prz, start, count); + } + +-static void __init ++static void __devinit + persistent_ram_save_old(struct persistent_ram_zone *prz) + { + struct persistent_ram_buffer *buffer = prz->buffer; +@@ -356,8 +357,8 @@ static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size, + return 0; + } + +-static int __init persistent_ram_buffer_init(const char *name, +- struct persistent_ram_zone *prz) ++static int __devinit persistent_ram_buffer_init(const char *name, ++ struct persistent_ram_zone *prz, struct persistent_ram **ramp) + { + int i; + struct persistent_ram *ram; +@@ -368,9 +369,11 @@ static int __init persistent_ram_buffer_init(const char *name, + start = ram->start; + for (i = 0; i < ram->num_descs; i++) { + desc = &ram->descs[i]; +- if (!strcmp(desc->name, name)) ++ if (!strcmp(desc->name, name)) { ++ *ramp = ram; + return persistent_ram_buffer_map(start, + desc->size, prz); ++ } + start += desc->size; + } + } +@@ -378,9 +381,10 @@ static int __init persistent_ram_buffer_init(const char *name, + return -EINVAL; + } + +-static __init ++static __devinit + struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) + { ++ struct persistent_ram *ram; + struct persistent_ram_zone *prz; + int ret = -ENOMEM; + +@@ -392,14 +396,14 @@ struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) + + INIT_LIST_HEAD(&prz->node); + +- ret = persistent_ram_buffer_init(dev_name(dev), prz); ++ ret = persistent_ram_buffer_init(dev_name(dev), prz, &ram); + if (ret) { + pr_err("persistent_ram: failed to initialize buffer\n"); + goto err; + } + + prz->ecc = ecc; +- ret = persistent_ram_init_ecc(prz, prz->buffer_size); ++ ret = persistent_ram_init_ecc(prz, prz->buffer_size, ram); + if (ret) + goto err; + +@@ -407,11 +411,11 @@ struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) + if (buffer_size(prz) > prz->buffer_size || + buffer_start(prz) > buffer_size(prz)) + pr_info("persistent_ram: found existing invalid buffer," +- " size %ld, start %ld\n", ++ " size %zu, start %zu\n", + buffer_size(prz), buffer_start(prz)); + else { + pr_info("persistent_ram: found existing buffer," +- " size %ld, start %ld\n", ++ " size %zu, start %zu\n", + buffer_size(prz), buffer_start(prz)); + persistent_ram_save_old(prz); + } +@@ -430,7 +434,7 @@ err: + return ERR_PTR(ret); + } + +-struct persistent_ram_zone * __init ++struct persistent_ram_zone * __devinit + persistent_ram_init_ringbuffer(struct device *dev, bool ecc) + { + return __persistent_ram_init(dev, ecc); +diff --git a/drivers/staging/android/ram_console.c b/drivers/staging/android/ram_console.c +index ce140ffc..cf0f8fb4 100644 +--- a/drivers/staging/android/ram_console.c ++++ b/drivers/staging/android/ram_console.c +@@ -16,12 +16,12 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + #include +-#include "persistent_ram.h" + #include "ram_console.h" + + static struct persistent_ram_zone *ram_console_zone; +@@ -50,7 +50,7 @@ void ram_console_enable_console(int enabled) + ram_console.flags &= ~CON_ENABLED; + } + +-static int __init ram_console_probe(struct platform_device *pdev) ++static int __devinit ram_console_probe(struct platform_device *pdev) + { + struct ram_console_platform_data *pdata = pdev->dev.platform_data; + struct persistent_ram_zone *prz; +@@ -78,11 +78,12 @@ static struct platform_driver ram_console_driver = { + .driver = { + .name = "ram_console", + }, ++ .probe = ram_console_probe, + }; + + static int __init ram_console_module_init(void) + { +- return platform_driver_probe(&ram_console_driver, ram_console_probe); ++ return platform_driver_register(&ram_console_driver); + } + + #ifndef CONFIG_PRINTK +@@ -100,9 +101,6 @@ static ssize_t ram_console_read_old(struct file *file, char __user *buf, + char *str; + int ret; + +- if (dmesg_restrict && !capable(CAP_SYSLOG)) +- return -EPERM; +- + /* Main last_kmsg log */ + if (pos < old_log_size) { + count = min(len, (size_t)(old_log_size - pos)); +diff --git a/drivers/staging/android/trace_persistent.c b/drivers/staging/android/trace_persistent.c +new file mode 100644 +index 00000000..7c3e9499 +--- /dev/null ++++ b/drivers/staging/android/trace_persistent.c +@@ -0,0 +1,242 @@ ++/* ++ * Copyright (C) 2012 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../../../kernel/trace/trace.h" ++ ++struct persistent_trace_record { ++ unsigned long ip; ++ unsigned long parent_ip; ++}; ++ ++#define REC_SIZE sizeof(struct persistent_trace_record) ++ ++static struct persistent_ram_zone *persistent_trace; ++ ++static int persistent_trace_enabled; ++ ++static struct trace_array *persistent_trace_array; ++ ++static struct ftrace_ops trace_ops; ++ ++static int persistent_tracer_init(struct trace_array *tr) ++{ ++ persistent_trace_array = tr; ++ tr->cpu = get_cpu(); ++ put_cpu(); ++ ++ tracing_start_cmdline_record(); ++ ++ persistent_trace_enabled = 0; ++ smp_wmb(); ++ ++ register_ftrace_function(&trace_ops); ++ ++ smp_wmb(); ++ persistent_trace_enabled = 1; ++ ++ return 0; ++} ++ ++static void persistent_trace_reset(struct trace_array *tr) ++{ ++ persistent_trace_enabled = 0; ++ smp_wmb(); ++ ++ unregister_ftrace_function(&trace_ops); ++ ++ tracing_stop_cmdline_record(); ++} ++ ++static void persistent_trace_start(struct trace_array *tr) ++{ ++ tracing_reset_online_cpus(tr); ++} ++ ++static void persistent_trace_call(unsigned long ip, unsigned long parent_ip) ++{ ++ struct trace_array *tr = persistent_trace_array; ++ struct trace_array_cpu *data; ++ long disabled; ++ struct persistent_trace_record rec; ++ unsigned long flags; ++ int cpu; ++ ++ smp_rmb(); ++ if (unlikely(!persistent_trace_enabled)) ++ return; ++ ++ if (unlikely(oops_in_progress)) ++ return; ++ ++ /* ++ * Need to use raw, since this must be called before the ++ * recursive protection is performed. ++ */ ++ local_irq_save(flags); ++ cpu = raw_smp_processor_id(); ++ data = tr->data[cpu]; ++ disabled = atomic_inc_return(&data->disabled); ++ ++ if (likely(disabled == 1)) { ++ rec.ip = ip; ++ rec.parent_ip = parent_ip; ++ rec.ip |= cpu; ++ persistent_ram_write(persistent_trace, &rec, sizeof(rec)); ++ } ++ ++ atomic_dec(&data->disabled); ++ local_irq_restore(flags); ++} ++ ++static struct ftrace_ops trace_ops __read_mostly = { ++ .func = persistent_trace_call, ++ .flags = FTRACE_OPS_FL_GLOBAL, ++}; ++ ++static struct tracer persistent_tracer __read_mostly = { ++ .name = "persistent", ++ .init = persistent_tracer_init, ++ .reset = persistent_trace_reset, ++ .start = persistent_trace_start, ++ .wait_pipe = poll_wait_pipe, ++}; ++ ++struct persistent_trace_seq_data { ++ const void *ptr; ++ size_t off; ++ size_t size; ++}; ++ ++void *persistent_trace_seq_start(struct seq_file *s, loff_t *pos) ++{ ++ struct persistent_trace_seq_data *data; ++ ++ data = kzalloc(sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return NULL; ++ ++ data->ptr = persistent_ram_old(persistent_trace); ++ data->size = persistent_ram_old_size(persistent_trace); ++ data->off = data->size % REC_SIZE; ++ ++ data->off += *pos * REC_SIZE; ++ ++ if (data->off + REC_SIZE > data->size) { ++ kfree(data); ++ return NULL; ++ } ++ ++ return data; ++ ++} ++void persistent_trace_seq_stop(struct seq_file *s, void *v) ++{ ++ kfree(v); ++} ++ ++void *persistent_trace_seq_next(struct seq_file *s, void *v, loff_t *pos) ++{ ++ struct persistent_trace_seq_data *data = v; ++ ++ data->off += REC_SIZE; ++ ++ if (data->off + REC_SIZE > data->size) ++ return NULL; ++ ++ (*pos)++; ++ ++ return data; ++} ++ ++int persistent_trace_seq_show(struct seq_file *s, void *v) ++{ ++ struct persistent_trace_seq_data *data = v; ++ struct persistent_trace_record *rec; ++ ++ rec = (struct persistent_trace_record *)(data->ptr + data->off); ++ ++ seq_printf(s, "%ld %08lx %08lx %pf <- %pF\n", ++ rec->ip & 3, rec->ip, rec->parent_ip, ++ (void *)rec->ip, (void *)rec->parent_ip); ++ ++ return 0; ++} ++ ++static const struct seq_operations persistent_trace_seq_ops = { ++ .start = persistent_trace_seq_start, ++ .next = persistent_trace_seq_next, ++ .stop = persistent_trace_seq_stop, ++ .show = persistent_trace_seq_show, ++}; ++ ++static int persistent_trace_old_open(struct inode *inode, struct file *file) ++{ ++ return seq_open(file, &persistent_trace_seq_ops); ++} ++ ++static const struct file_operations persistent_trace_old_fops = { ++ .open = persistent_trace_old_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++ ++static int __devinit persistent_trace_probe(struct platform_device *pdev) ++{ ++ struct dentry *d; ++ int ret; ++ ++ persistent_trace = persistent_ram_init_ringbuffer(&pdev->dev, false); ++ if (IS_ERR(persistent_trace)) { ++ pr_err("persistent_trace: failed to init ringbuffer: %ld\n", ++ PTR_ERR(persistent_trace)); ++ return PTR_ERR(persistent_trace); ++ } ++ ++ ret = register_tracer(&persistent_tracer); ++ if (ret) ++ pr_err("persistent_trace: failed to register tracer"); ++ ++ if (persistent_ram_old_size(persistent_trace) > 0) { ++ d = debugfs_create_file("persistent_trace", S_IRUGO, NULL, ++ NULL, &persistent_trace_old_fops); ++ if (IS_ERR_OR_NULL(d)) ++ pr_err("persistent_trace: failed to create old file\n"); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver persistent_trace_driver = { ++ .probe = persistent_trace_probe, ++ .driver = { ++ .name = "persistent_trace", ++ }, ++}; ++ ++static int __init persistent_trace_init(void) ++{ ++ return platform_driver_register(&persistent_trace_driver); ++} ++core_initcall(persistent_trace_init); +diff --git a/drivers/staging/iio/industrialio-event.c b/drivers/staging/iio/industrialio-event.c +index 5fdf739e..68ef8264 100644 +--- a/drivers/staging/iio/industrialio-event.c ++++ b/drivers/staging/iio/industrialio-event.c +@@ -35,6 +35,7 @@ + */ + struct iio_event_interface { + wait_queue_head_t wait; ++ struct mutex read_lock; + DECLARE_KFIFO(det_events, struct iio_event_data, 16); + + struct list_head dev_attr_list; +@@ -96,14 +97,16 @@ static ssize_t iio_event_chrdev_read(struct file *filep, + if (count < sizeof(struct iio_event_data)) + return -EINVAL; + +- spin_lock(&ev_int->wait.lock); ++ if (mutex_lock_interruptible(&ev_int->read_lock)) ++ return -ERESTARTSYS; ++ + if (kfifo_is_empty(&ev_int->det_events)) { + if (filep->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto error_unlock; + } + /* Blocking on device; waiting for something to be there */ +- ret = wait_event_interruptible_locked(ev_int->wait, ++ ret = wait_event_interruptible(ev_int->wait, + !kfifo_is_empty(&ev_int->det_events)); + if (ret) + goto error_unlock; +@@ -113,7 +116,7 @@ static ssize_t iio_event_chrdev_read(struct file *filep, + ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied); + + error_unlock: +- spin_unlock(&ev_int->wait.lock); ++ mutex_unlock(&ev_int->read_lock); + + return ret ? ret : copied; + } +@@ -376,6 +379,7 @@ static void iio_setup_ev_int(struct iio_event_interface *ev_int) + { + INIT_KFIFO(ev_int->det_events); + init_waitqueue_head(&ev_int->wait); ++ mutex_init(&ev_int->read_lock); + } + + static const char *iio_event_group_name = "events"; +@@ -437,6 +441,7 @@ int iio_device_register_eventset(struct iio_dev *indio_dev) + + error_free_setup_event_lines: + __iio_remove_event_config_attrs(indio_dev); ++ mutex_destroy(&indio_dev->event_interface->read_lock); + kfree(indio_dev->event_interface); + error_ret: + +@@ -449,5 +454,6 @@ void iio_device_unregister_eventset(struct iio_dev *indio_dev) + return; + __iio_remove_event_config_attrs(indio_dev); + kfree(indio_dev->event_interface->group.attrs); ++ mutex_destroy(&indio_dev->event_interface->read_lock); + kfree(indio_dev->event_interface); + } +diff --git a/drivers/switch/Kconfig b/drivers/switch/Kconfig +new file mode 100644 +index 00000000..52385914 +--- /dev/null ++++ b/drivers/switch/Kconfig +@@ -0,0 +1,15 @@ ++menuconfig SWITCH ++ tristate "Switch class support" ++ help ++ Say Y here to enable switch class support. This allows ++ monitoring switches by userspace via sysfs and uevent. ++ ++if SWITCH ++ ++config SWITCH_GPIO ++ tristate "GPIO Swith support" ++ depends on GENERIC_GPIO ++ help ++ Say Y here to enable GPIO based switch support. ++ ++endif # SWITCH +diff --git a/drivers/switch/Makefile b/drivers/switch/Makefile +new file mode 100644 +index 00000000..f7606ed4 +--- /dev/null ++++ b/drivers/switch/Makefile +@@ -0,0 +1,4 @@ ++# Switch Class Driver ++obj-$(CONFIG_SWITCH) += switch_class.o ++obj-$(CONFIG_SWITCH_GPIO) += switch_gpio.o ++ +diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c +new file mode 100644 +index 00000000..e05fc259 +--- /dev/null ++++ b/drivers/switch/switch_class.c +@@ -0,0 +1,174 @@ ++/* ++ * drivers/switch/switch_class.c ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct class *switch_class; ++static atomic_t device_count; ++ ++static ssize_t state_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct switch_dev *sdev = (struct switch_dev *) ++ dev_get_drvdata(dev); ++ ++ if (sdev->print_state) { ++ int ret = sdev->print_state(sdev, buf); ++ if (ret >= 0) ++ return ret; ++ } ++ return sprintf(buf, "%d\n", sdev->state); ++} ++ ++static ssize_t name_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct switch_dev *sdev = (struct switch_dev *) ++ dev_get_drvdata(dev); ++ ++ if (sdev->print_name) { ++ int ret = sdev->print_name(sdev, buf); ++ if (ret >= 0) ++ return ret; ++ } ++ return sprintf(buf, "%s\n", sdev->name); ++} ++ ++static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL); ++static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL); ++ ++void switch_set_state(struct switch_dev *sdev, int state) ++{ ++ char name_buf[120]; ++ char state_buf[120]; ++ char *prop_buf; ++ char *envp[3]; ++ int env_offset = 0; ++ int length; ++ ++ if (sdev->state != state) { ++ sdev->state = state; ++ ++ prop_buf = (char *)get_zeroed_page(GFP_KERNEL); ++ if (prop_buf) { ++ length = name_show(sdev->dev, NULL, prop_buf); ++ if (length > 0) { ++ if (prop_buf[length - 1] == '\n') ++ prop_buf[length - 1] = 0; ++ snprintf(name_buf, sizeof(name_buf), ++ "SWITCH_NAME=%s", prop_buf); ++ envp[env_offset++] = name_buf; ++ } ++ length = state_show(sdev->dev, NULL, prop_buf); ++ if (length > 0) { ++ if (prop_buf[length - 1] == '\n') ++ prop_buf[length - 1] = 0; ++ snprintf(state_buf, sizeof(state_buf), ++ "SWITCH_STATE=%s", prop_buf); ++ envp[env_offset++] = state_buf; ++ } ++ envp[env_offset] = NULL; ++ kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp); ++ free_page((unsigned long)prop_buf); ++ } else { ++ printk(KERN_ERR "out of memory in switch_set_state\n"); ++ kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE); ++ } ++ } ++} ++EXPORT_SYMBOL_GPL(switch_set_state); ++ ++static int create_switch_class(void) ++{ ++ if (!switch_class) { ++ switch_class = class_create(THIS_MODULE, "switch"); ++ if (IS_ERR(switch_class)) ++ return PTR_ERR(switch_class); ++ atomic_set(&device_count, 0); ++ } ++ ++ return 0; ++} ++ ++int switch_dev_register(struct switch_dev *sdev) ++{ ++ int ret; ++ ++ if (!switch_class) { ++ ret = create_switch_class(); ++ if (ret < 0) ++ return ret; ++ } ++ ++ sdev->index = atomic_inc_return(&device_count); ++ sdev->dev = device_create(switch_class, NULL, ++ MKDEV(0, sdev->index), NULL, sdev->name); ++ if (IS_ERR(sdev->dev)) ++ return PTR_ERR(sdev->dev); ++ ++ ret = device_create_file(sdev->dev, &dev_attr_state); ++ if (ret < 0) ++ goto err_create_file_1; ++ ret = device_create_file(sdev->dev, &dev_attr_name); ++ if (ret < 0) ++ goto err_create_file_2; ++ ++ dev_set_drvdata(sdev->dev, sdev); ++ sdev->state = 0; ++ return 0; ++ ++err_create_file_2: ++ device_remove_file(sdev->dev, &dev_attr_state); ++err_create_file_1: ++ device_destroy(switch_class, MKDEV(0, sdev->index)); ++ printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(switch_dev_register); ++ ++void switch_dev_unregister(struct switch_dev *sdev) ++{ ++ device_remove_file(sdev->dev, &dev_attr_name); ++ device_remove_file(sdev->dev, &dev_attr_state); ++ device_destroy(switch_class, MKDEV(0, sdev->index)); ++ dev_set_drvdata(sdev->dev, NULL); ++} ++EXPORT_SYMBOL_GPL(switch_dev_unregister); ++ ++static int __init switch_class_init(void) ++{ ++ return create_switch_class(); ++} ++ ++static void __exit switch_class_exit(void) ++{ ++ class_destroy(switch_class); ++} ++ ++module_init(switch_class_init); ++module_exit(switch_class_exit); ++ ++MODULE_AUTHOR("Mike Lockwood "); ++MODULE_DESCRIPTION("Switch class driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/switch/switch_gpio.c b/drivers/switch/switch_gpio.c +new file mode 100644 +index 00000000..7e9faa21 +--- /dev/null ++++ b/drivers/switch/switch_gpio.c +@@ -0,0 +1,172 @@ ++/* ++ * drivers/switch/switch_gpio.c ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct gpio_switch_data { ++ struct switch_dev sdev; ++ unsigned gpio; ++ const char *name_on; ++ const char *name_off; ++ const char *state_on; ++ const char *state_off; ++ int irq; ++ struct work_struct work; ++}; ++ ++static void gpio_switch_work(struct work_struct *work) ++{ ++ int state; ++ struct gpio_switch_data *data = ++ container_of(work, struct gpio_switch_data, work); ++ ++ state = gpio_get_value(data->gpio); ++ switch_set_state(&data->sdev, state); ++} ++ ++static irqreturn_t gpio_irq_handler(int irq, void *dev_id) ++{ ++ struct gpio_switch_data *switch_data = ++ (struct gpio_switch_data *)dev_id; ++ ++ schedule_work(&switch_data->work); ++ return IRQ_HANDLED; ++} ++ ++static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf) ++{ ++ struct gpio_switch_data *switch_data = ++ container_of(sdev, struct gpio_switch_data, sdev); ++ const char *state; ++ if (switch_get_state(sdev)) ++ state = switch_data->state_on; ++ else ++ state = switch_data->state_off; ++ ++ if (state) ++ return sprintf(buf, "%s\n", state); ++ return -1; ++} ++ ++static int gpio_switch_probe(struct platform_device *pdev) ++{ ++ struct gpio_switch_platform_data *pdata = pdev->dev.platform_data; ++ struct gpio_switch_data *switch_data; ++ int ret = 0; ++ ++ if (!pdata) ++ return -EBUSY; ++ ++ switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL); ++ if (!switch_data) ++ return -ENOMEM; ++ ++ switch_data->sdev.name = pdata->name; ++ switch_data->gpio = pdata->gpio; ++ switch_data->name_on = pdata->name_on; ++ switch_data->name_off = pdata->name_off; ++ switch_data->state_on = pdata->state_on; ++ switch_data->state_off = pdata->state_off; ++ switch_data->sdev.print_state = switch_gpio_print_state; ++ ++ ret = switch_dev_register(&switch_data->sdev); ++ if (ret < 0) ++ goto err_switch_dev_register; ++ ++ ret = gpio_request(switch_data->gpio, pdev->name); ++ if (ret < 0) ++ goto err_request_gpio; ++ ++ ret = gpio_direction_input(switch_data->gpio); ++ if (ret < 0) ++ goto err_set_gpio_input; ++ ++ INIT_WORK(&switch_data->work, gpio_switch_work); ++ ++ switch_data->irq = gpio_to_irq(switch_data->gpio); ++ if (switch_data->irq < 0) { ++ ret = switch_data->irq; ++ goto err_detect_irq_num_failed; ++ } ++ ++ ret = request_irq(switch_data->irq, gpio_irq_handler, ++ IRQF_TRIGGER_LOW, pdev->name, switch_data); ++ if (ret < 0) ++ goto err_request_irq; ++ ++ /* Perform initial detection */ ++ gpio_switch_work(&switch_data->work); ++ ++ return 0; ++ ++err_request_irq: ++err_detect_irq_num_failed: ++err_set_gpio_input: ++ gpio_free(switch_data->gpio); ++err_request_gpio: ++ switch_dev_unregister(&switch_data->sdev); ++err_switch_dev_register: ++ kfree(switch_data); ++ ++ return ret; ++} ++ ++static int __devexit gpio_switch_remove(struct platform_device *pdev) ++{ ++ struct gpio_switch_data *switch_data = platform_get_drvdata(pdev); ++ ++ cancel_work_sync(&switch_data->work); ++ gpio_free(switch_data->gpio); ++ switch_dev_unregister(&switch_data->sdev); ++ kfree(switch_data); ++ ++ return 0; ++} ++ ++static struct platform_driver gpio_switch_driver = { ++ .probe = gpio_switch_probe, ++ .remove = __devexit_p(gpio_switch_remove), ++ .driver = { ++ .name = "switch-gpio", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init gpio_switch_init(void) ++{ ++ return platform_driver_register(&gpio_switch_driver); ++} ++ ++static void __exit gpio_switch_exit(void) ++{ ++ platform_driver_unregister(&gpio_switch_driver); ++} ++ ++module_init(gpio_switch_init); ++module_exit(gpio_switch_exit); ++ ++MODULE_AUTHOR("Mike Lockwood "); ++MODULE_DESCRIPTION("GPIO Switch driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig +index 070b442c..296b412f 100644 +--- a/drivers/tty/serial/Kconfig ++++ b/drivers/tty/serial/Kconfig +@@ -1360,4 +1360,34 @@ config SERIAL_EFM32_UART_CONSOLE + depends on SERIAL_EFM32_UART=y + select SERIAL_CORE_CONSOLE + ++config SERIAL_AK39_UART ++ tristate "AK39xx serial port support" ++ select SERIAL_CORE ++ depends on ARCH_AK39 ++ help ++ AK98 Uart support. ++ ++config SERIAL_AK39_CONSOLE ++ bool "Console on AK39 serial port" ++ depends on SERIAL_AK39_UART ++ select SERIAL_CORE_CONSOLE ++ help ++ Say Y here if you want enable console support on AK39xx serial port. ++ ++config SERIAL_GPIO_UART ++ tristate "GPIO serial port support" ++ select SERIAL_CORE ++ depends on ARCH_AK39 ++ default n ++ help ++ AK39 GPIO simulate UART support. ++ ++config SERIAL_GPIO_CONSOLE ++ bool "Console on GPIO serial port" ++ depends on SERIAL_GPIO_UART ++ select SERIAL_CORE_CONSOLE ++ help ++ AK39 GPIO simulate UART console support. ++ ++ + endmenu +diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile +index 7257c5d8..e0d7f32f 100644 +--- a/drivers/tty/serial/Makefile ++++ b/drivers/tty/serial/Makefile +@@ -79,3 +79,6 @@ obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o + obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o + obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o + obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o ++obj-$(CONFIG_SERIAL_AK39_UART) += ak39_uart.o ++obj-$(CONFIG_SERIAL_GPIO_UART) += gpio_uart.o ++ +diff --git a/drivers/tty/serial/ak39_uart.c b/drivers/tty/serial/ak39_uart.c +new file mode 100755 +index 00000000..08401cec +--- /dev/null ++++ b/drivers/tty/serial/ak39_uart.c +@@ -0,0 +1,1242 @@ ++/* ++ * driver/tty/serial/ak39_uart.c ++ */ ++ ++#if defined(CONFIG_SERIAL_AK39_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ++#define SUPPORT_SYSRQ ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "ak39_uart.h" ++ ++extern void printch(char); ++extern void printascii(const char *); ++ ++ ++#if 0 ++#define dbg(x...) printk(x) ++#else ++#define dbg(x...) do {} while(0) ++#endif ++ ++/* UART name and device definitions */ ++#define NR_PORTS 2 ++ ++#define AK39_SERIAL_NAME "ttySAK" ++#define AK39_SERIAL_MAJOR 204 ++#define AK39_SERIAL_MINOR 64 ++ ++ ++#ifdef CONFIG_SERIAL_AK39_CONSOLE ++ ++static struct console ak39_serial_console; ++ ++#define AK39_SERIAL_CONSOLE &ak39_serial_console ++#else ++#define AK39_SERIAL_CONSOLE NULL ++#endif ++ ++struct ak39_uart_port { ++ char *name; ++ struct uart_port port; ++ ++ unsigned char __iomem *rxfifo_base; ++ unsigned char __iomem *txfifo_base; ++ ++ unsigned int rxfifo_offset; ++ unsigned int nbr_to_read; ++ unsigned int timeout_cnt; ++ ++ unsigned char claimed; ++ struct clk *clk; ++#ifdef CONFIG_CPU_FREQ ++ struct notifier_block freq_transition; ++#endif ++}; ++ ++/* macros to change one thing to another */ ++#define tx_enabled(port) ((port)->unused[0]) ++#define rx_enabled(port) ((port)->unused[1]) ++ ++static int uart_intevent_decode(unsigned long status, unsigned int maskbit, unsigned int statusbit) ++{ ++ if ((status & 1<port.membase + UART_CONF2); ++ uart_reg &= ~mask; ++ __raw_writel(uart_reg, ourport->port.membase + UART_CONF2); ++} ++ ++static inline void uart_subint_enable(struct ak39_uart_port *ourport, unsigned long unmask) ++{ ++ unsigned long uart_reg; ++ ++ /* enable tx_end interrupt */ ++ uart_reg = __raw_readl(ourport->port.membase + UART_CONF2); ++ uart_reg |= unmask; ++ __raw_writel(uart_reg, ourport->port.membase + UART_CONF2); ++} ++ ++static void uart_subint_clear(struct ak39_uart_port *ourport, unsigned int subint) ++{ ++ unsigned long uart_reg; ++ ++ switch (subint) { ++ ++ case TX_THR_INT: ++ uart_reg = __raw_readl(ourport->port.membase + UART_CONF2); ++ uart_reg |= (1<port.membase + UART_CONF2); ++ break; ++ ++ case RX_THR_INT: ++ uart_reg = __raw_readl(ourport->port.membase + UART_CONF2); ++ uart_reg &= AKUART_INT_MASK; ++ uart_reg |= (1<port.membase + UART_CONF2); ++ break; ++ ++ case RECVDATA_ERR_INT: ++ uart_reg = __raw_readl(ourport->port.membase + UART_CONF2); ++ uart_reg &= AKUART_INT_MASK; ++ uart_reg |= (0x1 << subint); ++ __raw_writel(uart_reg, ourport->port.membase + UART_CONF2); ++ break; ++ ++ case RX_TIMEOUT: ++ uart_reg = __raw_readl(ourport->port.membase + UART_CONF2); ++ uart_reg |= (0x1 << subint); ++ uart_reg &= ~( 0x1<<3 ); ++ __raw_writel(uart_reg, ourport->port.membase + UART_CONF2); ++ ++ /* start to receive data */ ++ uart_reg = __raw_readl(ourport->port.membase + BUF_THRESHOLD); ++ uart_reg |= (1 << 31); ++ __raw_writel(uart_reg, ourport->port.membase + BUF_THRESHOLD); ++ break; ++ ++ default: ++ printk(KERN_ERR "ak39xx 9xx 9xx 9xx 9xx 9xx 9xx 9xx 9xx kown subint type: %d\n", subint); ++ break; ++ } ++ ++ return; ++} ++ ++static inline struct ak39_uart_port *to_ourport(struct uart_port *port) ++{ ++ return container_of(port, struct ak39_uart_port, port); ++} ++ ++static inline void uart_txend_interrupt(struct ak39_uart_port *ourport, unsigned short status) ++{ ++ unsigned long uart_reg; ++ ++ /*handle Tx_end end interrupt */ ++ uart_reg = __raw_readl(ourport->port.membase + UART_CONF2); ++ switch(status) ++ { ++ case ENABLE: ++ uart_reg |= (UARTN_CONFIG2_TX_END_INT_EN); ++ break; ++ ++ case DISABLE: ++ uart_reg &= ~(UARTN_CONFIG2_TX_END_INT_EN); ++ break; ++ ++ default: ++ break; ++ } ++ __raw_writel(uart_reg, ourport->port.membase + UART_CONF2); ++} ++ ++/* clear a UARTn buffer status flag */ ++static inline void clear_uart_buf_status(struct ak39_uart_port *ourport, unsigned short status) ++{ ++ unsigned long regval; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ regval = __raw_readl(AK_VA_L2CTRL + 0x8C); ++ switch(status) ++ { ++ case RX_STATUS: ++ regval |= (0x1 << (17 + ourport->port.line * 2)); ++ break; ++ ++ case TX_STATUS: ++ regval |= (0x1 << (16 + ourport->port.line * 2)); ++ break; ++ ++ default: ++ break; ++ } ++ __raw_writel(regval, AK_VA_L2CTRL + 0x8C); ++ ++ local_irq_restore(flags); ++} ++ ++/* clear TX and RX internal status */ ++static inline void clear_internal_status(struct ak39_uart_port *ourport, unsigned short status) ++{ ++ unsigned long regval; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ regval = __raw_readl(ourport->port.membase + UART_CONF1); ++ switch(status) ++ { ++ case RX_STATUS: ++ __raw_writel(regval | (0x1 << 29), ourport->port.membase + UART_CONF1); ++ break; ++ ++ case TX_STATUS: ++ __raw_writel(regval | (0x1 << 28), ourport->port.membase + UART_CONF1); ++ break; ++ ++ default: ++ break; ++ } ++ ++ local_irq_restore(flags); ++} ++ ++/* clear TX_th and RX_th count interrupt */ ++static inline void clear_Int_status(struct ak39_uart_port *ourport, unsigned short status) ++{ ++ unsigned long regval; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ regval = __raw_readl(ourport->port.membase + BUF_THRESHOLD); ++ switch(status) ++ { ++ case RX_STATUS: ++ __raw_writel(regval | (0x1 << 5), ourport->port.membase + BUF_THRESHOLD); ++ break; ++ ++ case TX_STATUS: ++ __raw_writel(regval | (0x1 << 11), ourport->port.membase + BUF_THRESHOLD); ++ break; ++ ++ default: ++ break; ++ } ++ ++ local_irq_restore(flags); ++} ++ ++ ++/* enable/disable interrupt of RX_th */ ++static inline void uart_Rx_interrupt(struct ak39_uart_port *ourport, unsigned short status) ++{ ++ unsigned long regval; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ regval = __raw_readl(ourport->port.membase + UART_CONF2); ++ if(status) ++ __raw_writel(regval | (0x1 << RX_INTTERUPT), ourport->port.membase + UART_CONF2); ++ else ++ __raw_writel(regval & ~(0x1 << RX_INTTERUPT), ourport->port.membase + UART_CONF2); ++ ++ local_irq_restore(flags); ++} ++ ++static inline int uart_hwport_init(struct ak39_uart_port *ourport) ++{ ++ /* set share pin to UARTn, and disable pull-up */ ++ switch (ourport->port.line) { ++ case 0: ++ ak_group_config(ePIN_AS_UART1); ++ ++ /* clear tx/rx buffer status flag */ ++ rL2_CONBUF8_15 |= (0x3 << 16); ++ /* enable buffer status bit may be changed */ ++ rL2_FRACDMAADDR |= (0x1 << 29); ++ break; ++ ++ case 1: ++ /* set share pin */ ++ ak_group_config(ePIN_AS_UART2); ++ ++ rL2_CONBUF8_15 |= (0x3 << 18); ++ rL2_FRACDMAADDR |= (0x1 << 29); ++ break; ++ ++ default: ++ printk(KERN_ERR "unknown uart port\n"); ++ return -1; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int uart_enable_clock(struct ak39_uart_port *ourport, int enable) ++{ ++ unsigned long regval; ++ ++ regval = __raw_readl(AK_VA_SYSCTRL + 0x1C); ++ ++ switch (ourport->port.line) { ++ case 0: ++ if (enable) ++ regval &= ~(1 << 7); ++ else ++ regval |= (1 << 7); ++ ++ case 1: ++ if (enable) ++ regval &= ~(1 << 8); ++ else ++ regval |= (1 << 8); ++ break; ++ ++ default: ++ printk(KERN_ERR "unknown uart port\n"); ++ return -1; ++ } ++ __raw_writel(regval, AK_VA_SYSCTRL + 0x1C); ++ ++ return 0; ++} ++ ++/* power management control */ ++static void ak39_serial_pm(struct uart_port *port, unsigned int level, unsigned int old) ++{ ++ switch (level) { ++ case 3: /* disable */ ++ //dbg("%s: enterring pm level: %d\n", __FUNCTION__, level); ++ break; ++ ++ case 0: /* enable */ ++ //dbg("%s: enterring pm level: %d\n", __FUNCTION__, level); ++ break; ++ ++ default: ++ dbg(KERN_ERR "ak39xx serial: unknown pm %d\n", level); ++ break; ++ } ++} ++ ++/* is tx fifo empty */ ++static unsigned int ak39_serial_tx_empty(struct uart_port *port) ++{ ++ unsigned long uart_reg; ++ ++ uart_reg = __raw_readl(port->membase + UART_CONF2); ++ ++ if (uart_reg & (1 << TXFIFO_EMPTY)) ++ return 1; ++ ++ return 0; ++} ++ ++/* no modem control lines */ ++static unsigned int ak39_serial_get_mctrl(struct uart_port *port) ++{ ++ /* FIXME */ ++ dbg("%s\n", __FUNCTION__); ++ return 0; ++} ++ ++static void ak39_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++ /* todo - possibly remove AFC and do manual CTS */ ++ dbg("%s\n", __FUNCTION__); ++} ++ ++ ++static void ak39_serial_start_tx(struct uart_port *port) ++{ ++ struct ak39_uart_port *ourport = to_ourport(port); ++ ++ dbg("%s\n", __FUNCTION__); ++ ++ if (!tx_enabled(port)) ++ { ++ uart_txend_interrupt(ourport, ENABLE); ++ tx_enabled(port) = 1; ++ } ++} ++ ++static void ak39_serial_stop_tx(struct uart_port *port) ++{ ++ struct ak39_uart_port *ourport = to_ourport(port); ++ ++ dbg("%s\n", __FUNCTION__); ++ ++ if (tx_enabled(port)) ++ { ++ uart_txend_interrupt(ourport, DISABLE); ++ tx_enabled(port) = 0; ++ } ++} ++ ++static void ak39_serial_stop_rx(struct uart_port *port) ++{ ++ struct ak39_uart_port *ourport = to_ourport(port); ++ ++ dbg("%s\n", __FUNCTION__); ++ ++ if (rx_enabled(port)) ++ { ++ uart_Rx_interrupt(ourport, DISABLE); ++ rx_enabled(port) = 0; ++ } ++} ++ ++static void ak39_serial_enable_ms(struct uart_port *port) ++{ ++ dbg("%s\n", __FUNCTION__); ++} ++ ++static void ak39_serial_break_ctl(struct uart_port *port, int break_state) ++{ ++ dbg("%s\n", __FUNCTION__); ++} ++ ++static irqreturn_t ak39_uart_irqhandler(int irq, void *dev_id) ++{ ++ struct ak39_uart_port *ourport = dev_id; ++ struct uart_port *port = &ourport->port; ++ struct circ_buf *xmit = &port->state->xmit; //&port->info->xmit; ++ struct tty_struct *tty = port->state->port.tty; //port->info->tty; ++ unsigned int flag= TTY_NORMAL; ++ ++ unsigned char __iomem *pbuf; ++ unsigned char *pxmitbuf; ++ unsigned long uart_status; ++ unsigned int rxcount = 0; ++ unsigned char ch = 0; ++ unsigned int i; ++ int txcount , tx_tail; ++ unsigned int l2_offset = 0; ++ unsigned long regval; ++ ++ uart_status = __raw_readl(ourport->port.membase + UART_CONF2); ++ ++ /* clear R_err interrupt */ ++ if ( uart_intevent_decode(uart_status, RECVDATA_ERR_INT_ENABLE, RECVDATA_ERR_INT) ) ++ { ++ uart_subint_clear(ourport, RECVDATA_ERR_INT); ++ } ++ ++ if ( uart_intevent_decode(uart_status, TX_END_INTERRUPT, TX_END_STATUS) ) ++ { ++ /* if there is not anything more to transmit, or the uart is now ++ * stopped, disable the uart and exit ++ */ ++ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) ++ { ++ ak39_serial_stop_tx(port); ++ goto rx_irq; ++ } ++ ++ txcount = uart_circ_chars_pending(xmit); ++ ++ if(txcount > 32) ++ txcount = 32; ++ pbuf = ourport->txfifo_base; ++ pxmitbuf = xmit->buf; ++ ++ /* clear a uartx buffer status */ ++ clear_uart_buf_status(ourport, TX_STATUS); ++ ++ /* clear the tx internal status */ ++ clear_internal_status(ourport, TX_STATUS); ++ ++ __raw_writel(0x0, ourport->txfifo_base + 0x3C); ++ ++ l2_offset = 0; ++ tx_tail = xmit->tail; ++ regval = 0; ++ for(i = 0; i < txcount; i++) ++ { ++ regval |= pxmitbuf[tx_tail]<<((i & 3) * 8 ); ++ if((i & 3) == 3) ++ { ++ __raw_writel(regval, pbuf + l2_offset); ++ l2_offset = l2_offset + 4; ++ regval = 0; ++ } ++ tx_tail = (tx_tail + 1) & (UART_XMIT_SIZE - 1); ++ port->icount.tx += 1; ++ } ++ if(i & 3) ++ { ++ __raw_writel(regval, pbuf + l2_offset); ++ } ++ ++ regval = (__raw_readl(ourport->port.membase + UART_CONF2)&(~UARTN_CONFIG2_TX_BYT_CNT_MASK)) | (UARTN_CONFIG2_TX_BYT_CNT(txcount)) | (UARTN_CONFIG2_TX_BYT_CNT_VLD); ++ __raw_writel(regval, ourport->port.membase + UART_CONF2); ++ xmit->tail = tx_tail; ++ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(port); ++ ++ if (uart_circ_empty(xmit)) ++ ak39_serial_stop_tx(port); ++ } ++ ++rx_irq: ++ ++ /* rx threshold interrupt */ ++ if ( uart_intevent_decode(uart_status, RX_THR_INT_ENABLE, RX_THR_INT) || ++ uart_intevent_decode(uart_status, RX_TIMEOUT_INT_ENABLE, RX_TIMEOUT)) ++ { ++ if ( uart_intevent_decode(uart_status, RX_THR_INT_ENABLE, RX_THR_INT)) ++ uart_subint_clear(ourport, RX_THR_INT); ++ else { ++ uart_subint_clear(ourport, RX_TIMEOUT); ++ } ++ ++ while(__raw_readl(ourport->port.membase + UART_CONF2) & (UARTN_CONFIG2_MEM_RDY)); ++ ++ ourport->nbr_to_read = (__raw_readl(ourport->port.membase + DATA_CONF)>>13) & 0x7f; ++ ++ if (ourport->nbr_to_read != ourport->rxfifo_offset) { ++ l2_offset = ourport->rxfifo_offset; ++ pbuf = ourport->rxfifo_base + l2_offset; ++ ++ /* copy data */ ++ if (ourport->nbr_to_read > l2_offset) { ++ rxcount = ourport->nbr_to_read - l2_offset; ++ for (i=0; irxfifo_base; ++ for (i=0; i < ourport->nbr_to_read; i++) { ++ ch = __raw_readb(pbuf + i); ++ uart_insert_char(port, 0, 0, ch, flag); ++ } ++ } ++ ourport->rxfifo_offset = ourport->nbr_to_read; ++ } ++ ++ tty_flip_buffer_push(tty); ++ } ++ ++ return IRQ_HANDLED; ++ ++} ++ ++static void ak39_serial_shutdown(struct uart_port *port) ++{ ++ struct ak39_uart_port *ourport = to_ourport(port); ++ unsigned int uart_reg = 0; ++ ++ /* ++ * 1st, free irq. ++ * 2nd, disable/mask hw uart setting. ++ * 3rd, close uart clock. ++ */ ++ ++ /* mask all interrupt */ ++ __raw_writel(0, ourport->port.membase + UART_CONF2); ++ ++ uart_reg = __raw_readl(ourport->port.membase + 0xc); ++ uart_reg &= (~(1<<5)); ++ __raw_writel(uart_reg, ourport->port.membase + 0xc); ++ uart_reg = __raw_readl(ourport->port.membase + 0x0); ++ uart_reg &= (~(1<<29)); ++ __raw_writel(uart_reg, ourport->port.membase + 0x0); ++ /* clear uartx interrupt */ ++ uart_reg &= (~1<<21); ++ __raw_writel(uart_reg, ourport->port.membase + 0x0); ++ ++ /* clear uartn TX/RX buf status flag and call ak_setpin_as_gpio() */ ++ switch(ourport->port.line) { ++ case 0: ++ rL2_CONBUF8_15 |= (0x3<<16); ++ break; ++ case 1: ++ rL2_CONBUF8_15 |= (0x3<<18); ++ break; ++ } ++ free_irq(port->irq, ourport); ++ uart_enable_clock(ourport, 0); ++} ++ ++/* ++ * 1, setup gpio. ++ * 2, enable clock. ++ * 3, request irq and setting up uart control. ++ * 4, enable subirq. ++ */ ++static int ak39_serial_startup(struct uart_port *port) ++{ ++ struct ak39_uart_port *ourport = to_ourport(port); ++ unsigned long uart_reg; ++ int ret; ++ ++ if ( rx_enabled(port) && tx_enabled(port)) ++ return 0; ++ ++ /* enable uart clock */ ++ uart_enable_clock(ourport, 1); ++ ++ /* set share pin to UARTn */ ++ uart_hwport_init(ourport); ++ ++ //clear L2 Buffer ++ clear_uart_buf_status(ourport, RX_STATUS); ++ ++ uart_reg = UARTN_CONFIG1_RTS_EN_BY_CIRCUIT | UARTN_CONFIG1_EN |UARTN_CONFIG1_RX_STA_CLR|UARTN_CONFIG1_TX_STA_CLR|UARTN_CONFIG1_TIMEOUT_EN; ++ __raw_writel(uart_reg, ourport->port.membase + UART_CONF1); ++ ++ /* mask all interrupt */ ++ __raw_writel(0, ourport->port.membase + UART_CONF2); ++ ++ /* ++ * config stop bit and timeout value ++ * set timeout = 32, stop bit = 1; ++ */ ++ uart_reg = (0x1f << 16); ++ __raw_writel(uart_reg, ourport->port.membase + UART_STOPBIT_TIMEOUT); ++ ++ ++ /* ++ * set threshold to 32bytes ++ * set RX_th_cfg_h = 0, set RX_th_cfg_l = 31 ++ */ ++ uart_reg = __raw_readl(ourport->port.membase + DATA_CONF); ++ uart_reg &= ~UARTN_RX_TH_CFG_H_MASK; ++ __raw_writel(uart_reg, ourport->port.membase + DATA_CONF); ++ uart_reg = __raw_readl(ourport->port.membase + BUF_THRESHOLD); ++ uart_reg &= ~(UARTN_RX_TH_CFG_L_MASK); ++ uart_reg |= UARTN_RX_TH_CFG_L(0x1F); /* 32 Bytes */ ++ //uart_reg |= (1 << 11); ++ __raw_writel(uart_reg, ourport->port.membase + BUF_THRESHOLD); ++ ++ clear_internal_status(ourport, RX_STATUS); ++ ++ /* ourport->rxfifo_offset = 0; */ ++ ourport->rxfifo_offset = 0; ++ ++ /* to clear RX_th count interrupt */ ++ uart_reg = __raw_readl(ourport->port.membase + BUF_THRESHOLD); ++ uart_reg |= (UARTN_RX_TH_CLR); ++ __raw_writel(uart_reg, ourport->port.membase + BUF_THRESHOLD); ++ udelay(10); ++ uart_reg = __raw_readl(ourport->port.membase + BUF_THRESHOLD); ++ uart_reg &= ~(UARTN_RX_TH_CLR); ++ __raw_writel(uart_reg, ourport->port.membase + BUF_THRESHOLD); ++ udelay(10); ++ uart_reg = __raw_readl(ourport->port.membase + BUF_THRESHOLD); ++ uart_reg |= (UARTN_RX_START); ++ __raw_writel(uart_reg, ourport->port.membase + BUF_THRESHOLD); ++ ++ ++ /* ++ * enable timeout, rx mem_rdy and rx_th tx_end interrupt ++ */ ++ uart_reg = (UARTN_CONFIG2_RX_TH_INT_EN|UARTN_CONFIG2_RX_BUF_FULL_INT_EN|UARTN_CONFIG2_TIMEOUT_INT_EN|UARTN_CONFIG2_R_ERR_INT_EN); ++ __raw_writel(uart_reg, ourport->port.membase + UART_CONF2); ++ ++ rx_enabled(port) = 1; ++ tx_enabled(port) = 0; ++ ++ //ourport->rxfifo_offset =0; ++ ++ /* register interrupt */ ++ ret = request_irq(port->irq, ak39_uart_irqhandler, IRQF_DISABLED, ourport->name, ourport); ++ if (ret) { ++ printk(KERN_ERR "can't request irq %d for %s\n", port->irq, ourport->name); ++ goto startup_err; ++ } ++ return 0; ++ ++startup_err: ++ ak39_serial_shutdown(port); ++ return ret; ++} ++ ++static void ak39_serial_set_termios(struct uart_port *port, ++ struct ktermios *termios, struct ktermios *old) ++{ ++ struct ak39_uart_port *ourport = to_ourport(port); ++ unsigned int baud; ++ unsigned long flags; ++ unsigned long regval; ++ unsigned long asic_clk; ++ ++ asic_clk = ak_get_asic_clk(); ++ ++ termios->c_cflag &= ~(HUPCL | CMSPAR); ++ termios->c_cflag |= CLOCAL; ++ ++ /* ++ * Ask the core to calculate the divisor for us. ++ * min: 2.4kbps, max: 2.4Mbps ++ */ ++ baud = uart_get_baud_rate(port, termios, old, 2400, 115200*20); ++ ++ spin_lock_irqsave(&port->lock, flags); ++ ++ /* baudrate setting */ ++ regval = __raw_readl(port->membase + UART_CONF1); ++ regval &= ~(0xffff); ++ regval &= ~(0x1 << 22); ++ regval |= ((asic_clk / baud - 1) & 0xffff); ++ regval |= (1 << 28) | (1 << 29); ++ ++ if (asic_clk % baud) ++ regval |= (0x1 << 22); ++ ++ ourport->rxfifo_offset = 0; ++ ++ /* flow control setting */ ++ if(port->line != 0) ++ { ++ if((termios->c_cflag & CRTSCTS)) /* directly */ ++ { ++ switch (port->line) { ++ case 1: ++ AK_GPIO_UART1_FLOW(1); ++ break; ++ } ++ regval &= ~(1<<18|1<<19); ++ } ++ else /* inversly */ ++ { ++ switch(port->line) { ++ case 1: ++ ak_setpin_as_gpio(6); ++ ak_setpin_as_gpio(7); ++ break; ++ } ++ regval |= (1<<18|1<<19); ++ } ++ } ++ ++ /* parity setting */ ++ if (termios->c_cflag & PARENB) { ++ if (termios->c_cflag & PARODD) ++ regval |= (0x2 << 25); /* odd parity */ ++ else ++ regval |= (0x3 << 25); /* evnt parity*/ ++ } ++ __raw_writel(regval, port->membase + UART_CONF1); ++ ++ /* ++ * Update the per-port timeout. ++ */ ++ uart_update_timeout(port, termios->c_cflag, baud); ++ ++ /* ++ * Which character status flags should we ignore? ++ */ ++ port->ignore_status_mask = 0; ++ ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static const char *ak39_serial_type(struct uart_port *port) ++{ ++ switch (port->type) ++ { ++ case PORT_AK39: ++ return "AK39"; ++ default: ++ return NULL; ++ } ++} ++ ++static void ak39_serial_release_port(struct uart_port *port) ++{ ++ dbg("%s\n", __FUNCTION__); ++} ++ ++static int ak39_serial_request_port(struct uart_port *port) ++{ ++ dbg("%s\n", __FUNCTION__); ++ return 0; ++} ++ ++static void ak39_serial_config_port(struct uart_port *port, int flags) ++{ ++ struct ak39_uart_port *ourport = to_ourport(port); ++ ++ port->type = PORT_AK39; ++ ourport->rxfifo_offset = 0; ++} ++ ++/* ++ * verify the new serial_struct (for TIOCSSERIAL). ++ */ ++static int ++ak39_serial_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ dbg("%s\n", __FUNCTION__); ++ ++ return 0; ++} ++ ++ ++static struct uart_ops ak39_serial_ops = { ++ .pm = ak39_serial_pm, ++ .tx_empty = ak39_serial_tx_empty, ++ .get_mctrl = ak39_serial_get_mctrl, ++ .set_mctrl = ak39_serial_set_mctrl, ++ .stop_tx = ak39_serial_stop_tx, ++ .start_tx = ak39_serial_start_tx, ++ .stop_rx = ak39_serial_stop_rx, ++ .enable_ms = ak39_serial_enable_ms, ++ .break_ctl = ak39_serial_break_ctl, ++ .startup = ak39_serial_startup, ++ .shutdown = ak39_serial_shutdown, ++ .set_termios = ak39_serial_set_termios, ++ .type = ak39_serial_type, ++ .release_port = ak39_serial_release_port, ++ .request_port = ak39_serial_request_port, ++ .config_port = ak39_serial_config_port, ++ .verify_port = ak39_serial_verify_port, ++}; ++ ++ ++static struct ak39_uart_port ak39_serial_ports[NR_PORTS] = { ++ [0] = { ++ .name = "uart0", ++ .rxfifo_base = AK39_UART0_RXBUF_BASE, ++ .txfifo_base = AK39_UART0_TXBUF_BASE, ++ .port = { ++ .lock = __SPIN_LOCK_UNLOCKED(ak39_serial_ports[0].port.lock), ++ .iotype = UPIO_MEM, ++ .mapbase = AK39_UART0_PA_BASE, ++ .membase = AK39_UART0_BASE, ++ .irq = IRQ_UART0, ++ .uartclk = 0, ++ .fifosize = 64, ++ .ops = &ak39_serial_ops, ++ .flags = UPF_BOOT_AUTOCONF, ++ .line = 0, ++ }, ++ #ifdef CONFIG_CPU_FREQ ++ .freq_transition = { ++ .priority = INT_MAX -1, ++ }, ++ #endif ++ }, ++ [1] = { ++ .name = "uart1", ++ .rxfifo_base = AK39_UART1_RXBUF_BASE, ++ .txfifo_base = AK39_UART1_TXBUF_BASE, ++ .port = { ++ .lock = __SPIN_LOCK_UNLOCKED(ak39_serial_ports[1].port.lock), ++ .iotype = UPIO_MEM, ++ .mapbase = AK39_UART1_PA_BASE, ++ .membase = AK39_UART1_BASE, ++ .irq = IRQ_UART1, ++ .uartclk = 0, ++ .fifosize = 64, ++ .ops = &ak39_serial_ops, ++ .flags = UPF_BOOT_AUTOCONF, ++ .line = 1, ++ }, ++ }, ++}; ++ ++static struct uart_driver ak39_uart_drv = { ++ .owner = THIS_MODULE, ++ .dev_name = AK39_SERIAL_NAME, ++ .driver_name = AK39_SERIAL_NAME, ++ .nr = NR_PORTS, ++ .major = AK39_SERIAL_MAJOR, ++ .minor = AK39_SERIAL_MINOR, ++ .cons = AK39_SERIAL_CONSOLE, ++}; ++ ++#ifdef CONFIG_CPU_FREQ ++ ++static int ak39_serial_cpufreq_transition(struct notifier_block *nb, ++ unsigned long val, void *data) ++{ ++ struct ak39_uart_port *port; ++ struct uart_port *uport; ++ ++ port = container_of(nb, struct ak39_uart_port, freq_transition); ++ uport = &port->port; ++ ++ if(val == CPUFREQ_PRECHANGE){ ++ /* we should really shut the port down whilst the ++ * frequency change is in progress. */ ++ } ++ else if(val == CPUFREQ_POSTCHANGE) { ++ ++ struct ktermios *termios; ++ struct tty_struct *tty; ++ ++ if (uport->state == NULL) ++ goto exit; ++ ++ tty = uport->state->port.tty; ++ if (tty == NULL) ++ goto exit; ++ ++ termios = tty->termios; ++ if (termios == NULL) { ++ printk(KERN_WARNING "%s: no termios?\n", __func__); ++ goto exit; ++ } ++ ++ ak39_serial_set_termios(uport, termios, NULL); ++ } ++exit: ++ return 0; ++ ++} ++ ++static inline int ak39_serial_cpufreq_register(struct ak39_uart_port *port) ++{ ++ port->freq_transition.notifier_call = ak39_serial_cpufreq_transition; ++ ++ return cpufreq_register_notifier(&port->freq_transition, ++ CPUFREQ_TRANSITION_NOTIFIER); ++} ++ ++static inline void ak39_serial_cpufreq_unregister(struct ak39_uart_port *port) ++{ ++ cpufreq_unregister_notifier(&port->freq_transition, ++ CPUFREQ_TRANSITION_NOTIFIER); ++} ++#else ++static inline int ak39_serial_cpufreq_register(struct ak39_uart_port *port) ++{ ++ return 0; ++} ++ ++static inline void ak39_serial_cpufreq_unregister(struct ak39_uart_port *port) ++{ ++} ++ ++#endif ++ ++ ++/* ak39_serial_init_port ++ * ++ * initialise a single serial port from the platform device given ++ */ ++static int ak39_serial_init_port(struct ak39_uart_port *ourport, ++ struct platform_device *platdev) ++{ ++ struct uart_port *port = &ourport->port; ++ ++ if (platdev == NULL) ++ return -ENODEV; ++ ++ /* setup info for port */ ++ port->dev = &platdev->dev; ++ ++ ourport->clk = clk_get(port->dev, "asic_clk"); ++ ++ return 0; ++} ++ ++static int ak39_serial_probe(struct platform_device *dev) ++{ ++ struct ak39_uart_port *ourport; ++ int ret = 0; ++ ++ ourport = &ak39_serial_ports[dev->id]; ++ ++ ret = ak39_serial_init_port(ourport, dev); ++ if (ret < 0) ++ goto probe_err; ++ ++ uart_add_one_port(&ak39_uart_drv, &ourport->port); ++ ++ platform_set_drvdata(dev, &ourport->port); ++ ++ ret = ak39_serial_cpufreq_register(ourport); ++ if(ret < 0) ++ dev_err(&dev->dev, "faild to add cpufreq notifier\n"); ++ return 0; ++ ++probe_err: ++ return ret; ++} ++ ++static int ak39_serial_remove(struct platform_device *dev) ++{ ++ struct uart_port *port = (struct uart_port *)dev_get_drvdata(&dev->dev); ++ ++ if (port) { ++ ak39_serial_cpufreq_unregister(to_ourport(port)); ++ uart_remove_one_port(&ak39_uart_drv, port); ++ } ++ return 0; ++} ++ ++/* UART power management code */ ++ ++#ifdef CONFIG_PM ++ ++static int ak39_serial_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct uart_port *port = (struct uart_port *)dev_get_drvdata(&dev->dev); ++ ++ if (port) ++ uart_suspend_port(&ak39_uart_drv, port); ++ return 0; ++} ++ ++static int ak39_serial_resume(struct platform_device *dev) ++{ ++ struct uart_port *port = (struct uart_port *)dev_get_drvdata(&dev->dev); ++ ++ if (port) ++ uart_resume_port(&ak39_uart_drv, port); ++ return 0; ++} ++#else ++#define ak39_serial_suspend NULL ++#define ak39_serial_resume NULL ++#endif ++ ++static struct platform_driver ak39_serial_drv = { ++ .probe = ak39_serial_probe, ++ .remove = ak39_serial_remove, ++ .suspend = ak39_serial_suspend, ++ .resume = ak39_serial_resume, ++ .driver = { ++ .name = "ak39-uart", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++ ++/* module initialisation code */ ++static int __init ak39_serial_modinit(void) ++{ ++ int ret; ++ ++ printk("AK39xx uart driver init, (c) 2013 ANYKA\n"); ++ ++ //register ++ ret = uart_register_driver(&ak39_uart_drv); ++ if (ret < 0) { ++ printk(KERN_ERR "failed to register UART driver\n"); ++ return -1; ++ } ++ ++ platform_driver_register(&ak39_serial_drv); ++ ++ return 0; ++} ++ ++static void __exit ak39_serial_modexit(void) ++{ ++ platform_driver_unregister(&ak39_serial_drv); ++ ++ uart_unregister_driver(&ak39_uart_drv); ++} ++ ++module_init(ak39_serial_modinit); ++module_exit(ak39_serial_modexit); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("anyka"); ++MODULE_DESCRIPTION("Anyka Serial port driver"); ++ ++ ++/************* Console code ************/ ++#ifdef CONFIG_SERIAL_AK39_CONSOLE ++ ++static struct uart_port *cons_uart; ++ ++static inline void ak39_uart_putchar(struct ak39_uart_port *ourport, unsigned char ch) ++{ ++ unsigned long regval; ++ ++ /* clear the tx internal status */ ++ clear_internal_status(ourport, TX_STATUS); ++ ++ /* clear a uartx buffer status */ ++ clear_uart_buf_status(ourport, TX_STATUS); ++ ++ /*to inform the buf is full*/ ++ __raw_writel(ch, ourport->txfifo_base); ++ __raw_writel(0x0, ourport->txfifo_base + 0x3C); ++ ++ /* to clear TX_th count interrupt */ ++ clear_Int_status(ourport, TX_STATUS); ++ ++ /* start to transmit */ ++ regval = __raw_readl(ourport->port.membase + UART_CONF2); ++ regval &= AKUART_INT_MASK; ++ regval |= (0x1<<4) | (0x1<<16); ++ __raw_writel(regval, ourport->port.membase + UART_CONF2); ++ ++ ++ /* wait for tx end */ ++ while (!(__raw_readl(ourport->port.membase + UART_CONF2) & (1 << TX_END_STATUS))) ++ ; ++} ++ ++static inline void ak39_wait_for_txend(struct ak39_uart_port *ourport) ++{ ++ unsigned int timeout = 10000; ++ ++ /* ++ * Wait up to 10ms for the character(s) to be sent ++ */ ++ while (!(__raw_readl(ourport->port.membase + UART_CONF2) & (1 << TX_END_STATUS))) { ++ if (--timeout == 0) ++ break; ++ udelay(1); ++ } ++} ++ ++static void ++ak39_serial_console_putchar(struct uart_port *port, int ch) ++{ ++ struct ak39_uart_port *ourport = to_ourport(port); ++ ++ ak39_wait_for_txend(ourport); ++ ++ ak39_uart_putchar(ourport, ch); ++} ++ ++static void ++ak39_serial_console_write(struct console *co, const char *s, unsigned int count) ++{ ++ uart_console_write(cons_uart, s, count, ak39_serial_console_putchar); ++} ++ ++static void __init ++ak39_serial_get_options(struct uart_port *port, int *baud, int *parity, int *bits) ++{ ++ ++#if 0 ++ unsigned long regval; ++ struct clk *clk; ++ ++ *bits = 8; ++ ++ regval = __raw_readl(port->membase + UART_CONF1); ++ ++ if (regval & 0x1<<26) { ++ if (regval & 0x1<<25) ++ *parity = 'e'; ++ else ++ *parity = 'o'; ++ } ++ else ++ *parity = 'n'; ++ ++ clk = clk_get(port->dev, "asic_clk"); ++ if (!IS_ERR(clk) && clk != NULL) ++ *baud = clk_get_rate(clk) / ((regval & 0xFFFF) + 1); ++ ++ printk("calculated baudrate: %d\n", *baud); ++#endif ++} ++ ++ ++static int __init ++ak39_serial_console_setup(struct console *co, char *options) ++{ ++ struct uart_port *port; ++ int baud = 115200; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++ ++ dbg("ak39_serial_console_setup: co=%p (%d), %s\n", co, co->index, options); ++ ++ port = &ak39_serial_ports[co->index].port; ++ ++ /* is this a valid port */ ++ ++ if (co->index == -1 || co->index >= NR_PORTS) ++ co->index = 0; ++ ++ dbg("ak39_serial_console_setup: port=%p (%d)\n", port, co->index); ++ ++ cons_uart = port; ++ ++ /* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ else ++ ak39_serial_get_options(port, &baud, &parity, &bits); ++ ++ dbg("ak39_serial_console_setup: baud %d\n", baud); ++ ++ return uart_set_options(port, co, baud, parity, bits, flow); ++} ++ ++ ++static struct console ak39_serial_console = { ++ .name = AK39_SERIAL_NAME, ++ .device = uart_console_device, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++ .write = ak39_serial_console_write, ++ .setup = ak39_serial_console_setup ++}; ++ ++ ++/* ak39_serial_initconsole ++ * ++ * initialise the console from one of the uart drivers ++*/ ++static int ak39_serial_initconsole(void) ++{ ++ printk("AK39 console driver initial\n"); ++ ++ ak39_serial_console.data = &ak39_uart_drv; ++ ++ register_console(&ak39_serial_console); ++ ++ return 0; ++} ++ ++console_initcall(ak39_serial_initconsole); ++ ++#endif /* CONFIG_SERIAL_AK39_CONSOLE */ ++ +diff --git a/drivers/tty/serial/ak39_uart.h b/drivers/tty/serial/ak39_uart.h +new file mode 100755 +index 00000000..20f5b76d +--- /dev/null ++++ b/drivers/tty/serial/ak39_uart.h +@@ -0,0 +1,125 @@ ++#ifndef __AK39_UART_H_ ++#define __AK39_UART_H_ ++ ++#define AK39_UART0_TXBUF_BASE REG_VA_ADDR(AK_VA_L2MEM, 0x1000) ++#define AK39_UART0_RXBUF_BASE REG_VA_ADDR(AK_VA_L2MEM, 0x1080) ++#define AK39_UART1_TXBUF_BASE REG_VA_ADDR(AK_VA_L2MEM, 0x1100) ++#define AK39_UART1_RXBUF_BASE REG_VA_ADDR(AK_VA_L2MEM, 0x1180) ++ ++#define AK39_UART0_BASE REG_VA_ADDR(AK_VA_UART, 0x0000) ++#define AK39_UART1_BASE REG_VA_ADDR(AK_VA_UART, 0x8000) ++ ++#define AK39_UART0_PA_BASE REG_PA_ADDR(AK_PA_UART, 0x0000) ++#define AK39_UART1_PA_BASE REG_PA_ADDR(AK_PA_UART, 0x8000) ++ ++ ++#define UART_CONF1 0x00 ++#define UART_CONF2 0x04 ++#define DATA_CONF 0x08 ++#define BUF_THRESHOLD 0x0C ++#define UART_RXBUF 0x10 ++#define UART_STOPBIT_TIMEOUT (0x18) ++ ++#define RX_THR_INT_ENABLE (28) ++#define TX_END_INTERRUPT (27) ++#define TXBUF_EMP_INT_ENABLE (24) ++#define RECVDATA_ERR_INT_ENABLE (23) ++#define RX_TIMEOUT_INT_ENABLE (22) ++#define MEM_RDY_INT_ENABLE (21) ++#define TX_THR_INT (31) ++#define RX_THR_INT (30) ++#define TX_END_STATUS (19) ++#define RX_OV (18) ++#define MEM_RDY_INT (17) ++#define TX_BYT_CNT_VLD (16) ++#define RECVDATA_ERR_INT (3) ++#define RX_TIMEOUT (2) ++#define RXBUF_FULL (1) ++#define TXFIFO_EMPTY (0) ++ ++#define TX_INTTERUPT (29) ++#define RX_INTTERUPT (28) ++ ++#define TX_STATUS (1) ++#define RX_STATUS (0) ++#define DISABLE (0) ++#define ENABLE (1) ++#define AKUART_INT_MASK 0x3FE00000 ++#define UART_RX_FIFO_SIZE 128 ++ ++// Configuration Register 1 of UARTn ++#define UARTN_CONFIG1_DIV_CNT(cnt) ((cnt) & 0xffff) ++#define UARTN_CONFIG1_UTD_INVERSELY (1 << 16) ++#define UARTN_CONFIG1_URD_INVERSELY (1 << 17) ++#define UARTN_CONFIG1_CTS_INVERSELY (1 << 18) ++#define UARTN_CONFIG1_RTS_INVERSELY (1 << 19) ++#define UARTN_CONFIG1_RTS_EN_BY_CIRCUIT (1 << 20) ++#define UARTN_CONFIG1_EN (1 << 21) ++#define UARTN_CONFIG1_DIV_ADJ_EN (1 << 22) ++#define UARTN_CONFIG1_TIMEOUT_EN (1 << 23) ++#define UARTN_CONFIG1_PAR_EVEN (1 << 25) ++#define UARTN_CONFIG1_PAR_EN (1 << 26) ++#define UARTN_CONFIG1_ENDIAN_BIG (1 << 27) ++#define UARTN_CONFIG1_TX_STA_CLR (1 << 28) ++#define UARTN_CONFIG1_RX_STA_CLR (1 << 29) ++#define UARTN_RX_ADDR_CLR (1 << 30) ++#define UARTN_TX_ADDR_CLR (1 << 31) ++ ++// Configuration Register 2 of UARTn ++#define UARTN_CONFIG2_TX_FIFO_EMPTY (1 << 0) // read only ++#define UARTN_CONFIG2_RX_BUF_FULL (1 << 1) // write clear ++#define UARTN_CONFIG2_TIMEOUT (1 << 2) // write clear ++#define UARTN_CONFIG2_R_ERR (1 << 3) // write clear ++#define UARTN_CONFIG2_TX_BYT_CNT(cnt) (cnt << 4) ++#define UARTN_CONFIG2_TX_BYT_CNT_MASK (0xfff << 4) ++#define UARTN_CONFIG2_TX_BYT_CNT_VLD (1 << 16) // auto clear ++#define UARTN_CONFIG2_MEM_RDY (1 << 17) // read only ++ ++#define UARTN_CONFIG2_TX_END (1 << 19) // read only ++ ++#define UARTN_CONFIG2_RX_BUF_FULL_INT_EN (1 << 21) ++#define UARTN_CONFIG2_TIMEOUT_INT_EN (1 << 22) ++#define UARTN_CONFIG2_R_ERR_INT_EN (1 << 23) ++#define UARTN_CONFIG2_TX_BUF_EMP_INT_EN (1 << 24) ++ ++#define UARTN_CONFIG2_TX_END_INT_EN (1 << 27) ++#define UARTN_CONFIG2_RX_TH_INT_EN (1 << 28) ++#define UARTN_CONFIG2_TX_TH_INT_EN (1 << 29) ++ ++#define UARTN_CONFIG2_RX_TH_STA (1 << 30) // write clear ++#define UARTN_CONFIG2_TX_TH_STA (1 << 31) // write clear ++ ++// Data Configuration Register of UARTn ++#define UARTN_DATACONFIG_TX_BYT_SUM(rval) ((rval) & 0x1FFF) ++#define UARTN_DATACONFIG_RX_ADR(rval) (((rval) >> 13) & 0x7F) ++#define UARTN_DATACONFIG_TX_ADR(rval) (((rval) >> 20) & 0x1F) ++#define UARTN_RX_TH_CFG_H_MASK (0x7f >> 25) ++#define UARTN_RX_TH_CFG_H(value) (((value & 0x7f) >> 25)) ++ ++ ++// TX RX Data Threshold Resgister ++#define UARTN_RX_TH_CFG_L_MASK (0x1f) ++#define UARTN_RX_TH_CFG_L(value) (value) ++ ++#define UARTN_RX_TH_CLR (1 << 5) ++#define UARTN_TX_TH_CFG(value) (((value) & 0x1f) << 6) ++#define UARTN_TX_TH_CLR (1 << 11) ++#define UARTN_RX_TH_CNT(rvalue) (((rvalue) >> 12) & 0xFFF) // read only ++#define UARTN_TX_TH_CNT(rvalue) (((rvalue) >> 24) & 0x1F) // read only ++#define UARTN_BFIFO_BYTE_NUM(rvalue) (((rvalue) >> 29) & 0x03) // read only ++#define UARTN_RX_START (1 <<31) ++ ++ ++//Stop Bit Timeout Configuration Register ++ ++#define UARTN_BIT_CFG(value) ((value) & 0x1ff) ++#define UARTN_TIME_OUT_CFG(value) (((value) >> 16) & 0x0ffff) ++ ++ ++#undef REG_VA_VAL ++#define REG_VA_VAL(base_addr, offset) (*(volatile unsigned long *)((base_addr) + (offset))) ++ ++#define rL2_FRACDMAADDR REG_VA_VAL(AK_VA_L2CTRL, 0x84) ++#define rL2_CONBUF8_15 REG_VA_VAL(AK_VA_L2CTRL, 0x8C) ++ ++#endif /* end __AK39_UART_H_ */ +diff --git a/drivers/tty/serial/gpio_uart.c b/drivers/tty/serial/gpio_uart.c +new file mode 100644 +index 00000000..534b0191 +--- /dev/null ++++ b/drivers/tty/serial/gpio_uart.c +@@ -0,0 +1,393 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define TIMER_CLOCK (12000000) /* Hz */ ++#define GPIO_SERIAL_NAME ("ttySAK") ++#define GPIO_SERIAL_NR_PORTS (1) ++#define FAKE_PORT_MEMBASE ((unsigned char __iomem*)(0xffffffff)) ++#define FAKE_PORT_MAPBASE ((resource_size_t)(0xffffffff)) ++ ++struct gpio_uart { ++ int baud; ++ int parity; ++ int bits; ++ int flow; ++ ++ int timer_count; ++ int correction; /* correction coefficient */ ++ ++ struct ak_pwm_timer *timer; ++ struct completion comp; ++ struct gpio_info tx_pin; ++}; ++ ++static struct gpio_uart guart = { ++ /* UART configure: 115200/75, 38400/80, 19200/80, 9600/80 */ ++ .baud = 115200, ++ .parity = 'n', ++ .bits = 8, ++ .flow = 'n', ++ .correction = 55, ++ ++ /* TX GPIO pin configure */ ++ .tx_pin = { ++ .pin = AK_GPIO_2, ++ .dir = AK_GPIO_DIR_OUTPUT, ++ .pullup = AK_PULLUP_DISABLE, ++ .pulldown = -1, ++ .value = AK_GPIO_OUT_HIGH, ++ .int_pol = -1, ++ }, ++}; ++ ++#ifdef CONFIG_SERIAL_CORE_CONSOLE ++#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) ++#else ++#define uart_console(port) (0) ++#endif ++ ++#ifdef CONFIG_SERIAL_GPIO_CONSOLE ++static struct uart_port port; ++static struct uart_driver gpio_uart_driver; ++static void gpio_putchar(unsigned char value); ++ ++ ++static void gpio_uart_console_write(struct console *co, const char *s, unsigned count) ++{ ++ unsigned int i; ++ const char *ts = s; ++ ++ for (i = 0; i < count; i++, ts++) { ++ if (*ts == '\n') ++ gpio_putchar('\r'); ++ gpio_putchar(*ts); ++ } ++} ++ ++static struct tty_driver *gpio_uart_console_device(struct console *co, int *index) ++{ ++ struct uart_driver *p = co->data; ++ ++ *index = co->index; ++ return p->tty_driver; ++} ++ ++static int gpio_uart_console_setup(struct console *co, char *options) ++{ ++ int baud; ++ int bits; ++ int parity; ++ int flow; ++ ++ ak_gpio_set(&guart.tx_pin); ++ guart.timer_count = TIMER_CLOCK / guart.baud - guart.correction; ++ port.mapbase = FAKE_PORT_MAPBASE; ++ port.membase = FAKE_PORT_MEMBASE; ++ ++ if (options) { ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ } else { ++ baud = guart.baud; ++ bits = guart.bits; ++ parity = guart.parity; ++ flow = guart.flow; ++ } ++ ++ return uart_set_options(&port, co, baud, parity, bits, flow); ++} ++ ++static struct console gpio_serial_console = { ++ .name = GPIO_SERIAL_NAME, ++ .write = gpio_uart_console_write, ++ .device = gpio_uart_console_device, ++ .setup = gpio_uart_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++ .data = &gpio_uart_driver, ++}; ++ ++static int __init gpio_serial_console_init(void) ++{ ++ guart.timer = ak_timer_request(AK_PWM_TIMER2, 0, NULL); ++ ++ register_console(&gpio_serial_console); ++ return 0; ++} ++console_initcall(gpio_serial_console_init); ++ ++#define GPIO_SERIAL_CONSOLE &gpio_serial_console ++#else ++#define GPIO_SERIAL_CONSOLE NULL ++#endif ++#if 0 ++static void gpio_delay(int count) ++{ ++#if 0 ++ int delay = 1000*1000/115200; ++ udelay(delay); ++ return; ++#endif ++ ++ REG32(guart.membase) = (count << 0); ++ ++ REG32(guart.membase+0x04) = (1 << 30); ++ REG32(guart.membase+0x04) |= (1 << 24); ++ REG32(guart.membase+0x04) |= (1 << 29) | (1 << 28); ++ ++ while(!(REG32(guart.membase+0x04) & (1 << 27))) ++ ; ++} ++#else ++ ++static void gpio_delay(int count) ++{ ++ init_completion(&guart.comp); ++ ++ ak_timer_config(guart.timer, count, 0); ++ ak_timer_enable_sync(guart.timer); ++} ++ ++#endif ++ ++static void gpio_putchar(unsigned char value) ++{ ++ int count = 8; ++ ++ /* send start bit */ ++ ak_gpio_setpin(guart.tx_pin.pin, AK_GPIO_OUT_LOW); ++ gpio_delay(guart.timer_count); ++ ++ /* send data, no parity */ ++ while(count--) { ++ ak_gpio_setpin(guart.tx_pin.pin, (value & 0x1)); ++ gpio_delay(guart.timer_count); ++ value >>= 1; ++ } ++ ++ /* send stop bit */ ++ ak_gpio_setpin(guart.tx_pin.pin, AK_GPIO_OUT_HIGH); ++ gpio_delay(guart.timer_count); ++} ++ ++static void gpio_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) ++{ ++ switch (state) { ++ case 3: ++ break; ++ case 0: ++ break; ++ default: ++ break; ++ } ++} ++ ++static unsigned int gpio_uart_tx_empty(struct uart_port *port) ++{ ++ return 1; ++} ++ ++static unsigned int gpio_uart_get_mctrl(struct uart_port *port) ++{ ++ return 0; ++} ++ ++static void gpio_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++} ++ ++static void gpio_uart_stop_tx(struct uart_port *port) ++{ ++} ++ ++static void gpio_uart_start_tx(struct uart_port *port) ++{ ++ struct uart_state *state = port->state; ++ struct circ_buf *circ = &state->xmit; ++ int txcount = uart_circ_chars_pending(circ); ++ unsigned char value = 0; ++ int i; ++ ++ for (i = 0; i < txcount; i++) { ++ value = circ->buf[circ->tail]; ++ gpio_putchar(value); ++ circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1); ++ } ++} ++ ++static void gpio_uart_stop_rx(struct uart_port *port) ++{ ++} ++ ++static void gpio_uart_enable_ms(struct uart_port *port) ++{ ++} ++ ++static void gpio_uart_break_ctl(struct uart_port *port, int break_state) ++{ ++} ++ ++static int gpio_uart_startup(struct uart_port *port) ++{ ++ return 0; ++} ++ ++static void gpio_uart_shutdown(struct uart_port *port) ++{ ++} ++ ++static void gpio_uart_set_termios(struct uart_port *port, ++ struct ktermios *termios, struct ktermios *old) ++{ ++} ++ ++static const char *gpio_uart_type(struct uart_port *port) ++{ ++ if (port->type == PORT_GPIO) ++ return "GPIO"; ++ else ++ return NULL; ++} ++ ++static void gpio_uart_release_port(struct uart_port *port) ++{ ++} ++ ++static int gpio_uart_request_port(struct uart_port *port) ++{ ++ return 0; ++} ++ ++static void gpio_uart_config_port(struct uart_port *port, int flags) ++{ ++ port->type = PORT_GPIO; ++} ++ ++static int gpio_uart_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ return 0; ++} ++ ++static struct uart_ops gpio_uart_ops = { ++ .pm = gpio_uart_pm, ++ .tx_empty = gpio_uart_tx_empty, ++ .get_mctrl = gpio_uart_get_mctrl, ++ .set_mctrl = gpio_uart_set_mctrl, ++ .stop_tx = gpio_uart_stop_tx, ++ .start_tx = gpio_uart_start_tx, ++ .stop_rx = gpio_uart_stop_rx, ++ .enable_ms = gpio_uart_enable_ms, ++ .break_ctl = gpio_uart_break_ctl, ++ .startup = gpio_uart_startup, ++ .shutdown = gpio_uart_shutdown, ++ .set_termios = gpio_uart_set_termios, ++ .type = gpio_uart_type, ++ .release_port = gpio_uart_release_port, ++ .request_port = gpio_uart_request_port, ++ .config_port = gpio_uart_config_port, ++ .verify_port = gpio_uart_verify_port, ++}; ++ ++static struct uart_port port = { ++ .iotype = UPIO_MEM, ++ .ops = &gpio_uart_ops, ++ .flags = UPF_BOOT_AUTOCONF, ++ .line = 0, ++ .cons = GPIO_SERIAL_CONSOLE, ++}; ++ ++static struct uart_driver gpio_uart_driver = { ++ .owner = THIS_MODULE, ++ .driver_name = "gpio-uart", ++ .dev_name = GPIO_SERIAL_NAME, ++ .major = 0, ++ .minor = 0, ++ .nr = GPIO_SERIAL_NR_PORTS, ++ .cons = GPIO_SERIAL_CONSOLE, ++}; ++ ++static int gpio_uart_probe(struct platform_device *pdev) ++{ ++ int ret; ++ ++ if (!uart_console(&port)) { ++ port.membase = FAKE_PORT_MEMBASE; ++ port.mapbase = FAKE_PORT_MAPBASE; ++ guart.timer_count = TIMER_CLOCK / guart.baud - guart.correction; ++ ak_gpio_set(&guart.tx_pin); ++ guart.timer = ak_timer_request(AK_PWM_TIMER2, 0, NULL); ++ } ++ ++ ret = uart_register_driver(&gpio_uart_driver); ++ if (ret) { ++ printk("UART register driver failed\n"); ++ return ret; ++ } ++ ++ port.dev = &pdev->dev; ++ ret = uart_add_one_port(&gpio_uart_driver, &port); ++ if (ret) { ++ printk("Add gpio uart port failed\n"); ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, &port); ++ ++ printk("ak gpio uart initialize success.\n"); ++ return 0; ++} ++ ++static int gpio_uart_remove(struct platform_device *pdev) ++{ ++ platform_set_drvdata(pdev, NULL); ++ uart_remove_one_port(&gpio_uart_driver, &port); ++ uart_unregister_driver(&gpio_uart_driver); ++ return 0; ++} ++ ++static int gpio_uart_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ return 0; ++} ++ ++static int gpio_uart_resume(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++static struct platform_driver gpio_uart_pdrv = { ++ .probe = gpio_uart_probe, ++ .remove = gpio_uart_remove, ++ .suspend = gpio_uart_suspend, ++ .resume = gpio_uart_resume, ++ .driver = { ++ .name = "gpio-uart", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init gpio_uart_init(void) ++{ ++ printk("AK GPIO UART Driver (c) 2013 ANYKA.\n"); ++ return platform_driver_register(&gpio_uart_pdrv); ++} ++ ++static void __exit gpio_uart_exit(void) ++{ ++ platform_driver_unregister(&gpio_uart_pdrv); ++} ++module_init(gpio_uart_init); ++module_exit(gpio_uart_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("J.C.Zhong "); ++MODULE_DESCRIPTION("GPIO simulate UART driver"); +diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c +index d8b0aee3..c49f3bf6 100644 +--- a/drivers/tty/serial/samsung.c ++++ b/drivers/tty/serial/samsung.c +@@ -427,6 +427,8 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) + if (ourport->tx_claimed) { + if (!s3c24xx_serial_has_interrupt_mask(port)) + free_irq(ourport->tx_irq, ourport); ++ else ++ free_irq(port->irq, ourport); + tx_enabled(port) = 0; + ourport->tx_claimed = 0; + } +@@ -434,6 +436,8 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) + if (ourport->rx_claimed) { + if (!s3c24xx_serial_has_interrupt_mask(port)) + free_irq(ourport->rx_irq, ourport); ++ /* else already freed above as the s3c64xx_serial_startup() ++ * will have set both tx_claimed and rx_claimed */ + ourport->rx_claimed = 0; + rx_enabled(port) = 0; + } +diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c +index 246b823c..a4895dcc 100644 +--- a/drivers/tty/serial/serial_core.c ++++ b/drivers/tty/serial/serial_core.c +@@ -94,6 +94,9 @@ static void __uart_start(struct tty_struct *tty) + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; + ++ if (port->ops->wake_peer) ++ port->ops->wake_peer(port); ++ + if (!uart_circ_empty(&state->xmit) && state->xmit.buf && + !tty->stopped && !tty->hw_stopped) + port->ops->start_tx(port); +diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig +index 6f3ea9bb..19b1ab55 100644 +--- a/drivers/uio/Kconfig ++++ b/drivers/uio/Kconfig +@@ -111,4 +111,12 @@ config UIO_PRUSS + To compile this driver as a module, choose M here: the module + will be called uio_pruss. + ++config UIO_VCODEC ++ tristate "Anyka Video Codec support" ++ help ++ This driver supports Video HW Codec on Anyka SoC. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called uio_video_codec. ++ + endif +diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile +index d4dd9a55..9da3159e 100644 +--- a/drivers/uio/Makefile ++++ b/drivers/uio/Makefile +@@ -7,3 +7,4 @@ obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o + obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o + obj-$(CONFIG_UIO_NETX) += uio_netx.o + obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o ++obj-$(CONFIG_UIO_VCODEC) += uio_video_codec.o +\ No newline at end of file +diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c +index a783d533..c3612cf3 100644 +--- a/drivers/uio/uio.c ++++ b/drivers/uio/uio.c +@@ -503,6 +503,19 @@ static int uio_release(struct inode *inode, struct file *filep) + return ret; + } + ++static long uio_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) ++{ ++ struct uio_listener *listener = filep->private_data; ++ struct uio_device *idev = listener->dev; ++ int ret; ++ ++ ret = EINVAL; ++ if (idev->info->ioctl) ++ ret = idev->info->ioctl(idev->info, cmd, arg); ++ ++ return ret; ++} ++ + static unsigned int uio_poll(struct file *filep, poll_table *wait) + { + struct uio_listener *listener = filep->private_data; +@@ -721,6 +734,7 @@ static const struct file_operations uio_fops = { + .poll = uio_poll, + .fasync = uio_fasync, + .llseek = noop_llseek, ++ .unlocked_ioctl = uio_ioctl, + }; + + static int uio_major_init(void) +diff --git a/drivers/uio/uio_video_codec.c b/drivers/uio/uio_video_codec.c +new file mode 100644 +index 00000000..10431f88 +--- /dev/null ++++ b/drivers/uio/uio_video_codec.c +@@ -0,0 +1,344 @@ ++/** ++ * drivers/uio/uio_video_codec.c ++ * ++ * Userspace I/O driver for anyka soc video hardware codec. ++ * Based on uio_pdrv.c by Uwe Kleine-Koenig, ++ * ++ * Jacky Lau ++ * 2011-07-05 ++ * ++ * Copyright (C) 2011 by Anyka Inc. ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define DRIVER_NAME "uio_vcodec" ++ ++#define init_MUTEX(sem) sema_init(sem, 1) ++#define init_MUTEX_LOCKED(sem) sema_init(sem, 0) ++ ++/* IRQs of hw codec */ ++const unsigned VIDEO_IRQ_MASK = (1 << IRQ_VIDEO_ENCODER); ++const int MASK_BITS_NUM = sizeof(VIDEO_IRQ_MASK) * 8; ++ ++/* platform data of this driver */ ++struct uio_platdata { ++ struct uio_info *uioinfo; ++ struct semaphore vcodec_sem; ++ unsigned int open_count; ++}; ++ ++/** ++ * @brief Handle hw codec irq ++ * @author Jacky Lau ++ * @date 2011-07-05 ++ * @param [in] irq : irq id ++ * @param [in] dev_id : platform data ++ * @return irqreturn_t : return handle result ++ * @retval IRQ_HANDLE : irq handled successful. ++ */ ++static irqreturn_t uio_vcodec_irq_handler(int irq, void *dev_id) ++{ ++ struct uio_platdata *pdata = dev_id; ++ int i; ++ ++ for (i = 0; i < MASK_BITS_NUM; i++) ++ { ++ if ((1 << i) & VIDEO_IRQ_MASK) ++ disable_irq_nosync(i); ++ } ++ ++ up (&(pdata->vcodec_sem)); ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * @brief Handle hw codec ioctl ++ * @author Jacky Lau ++ * @date 2011-07-05 ++ * @param [in] uioinfo : information of uio driver ++ * @param [in] cmd : ioctl request code ++ * @param [in] arg : argument ++ * @return int : return 0 when handle successful, otherwise return negative ++ * @retval 0 : handled successful. ++ * @retval <0 : handle failed. ++ */ ++static int uio_vcodec_ioctl(struct uio_info *uioinfo, unsigned int cmd, unsigned long arg) ++{ ++ struct uio_platdata *pdata = uioinfo->priv; ++ int err; ++ ++ switch (cmd) { ++ case AKUIO_SYSREG_WRITE: ++ { ++ struct akuio_sysreg_write_t reg_write; ++ ++ if (copy_from_user(®_write, (void __user *)arg, sizeof(struct akuio_sysreg_write_t))) ++ return -EFAULT; ++ ++ sys_ctrl_reg_set(reg_write.paddr, reg_write.mask, reg_write.val); ++ ++ err = 0; ++ } ++ break; ++ ++ case AKUIO_WAIT_IRQ: ++ { ++ int i; ++ ++ for (i = 0; i < MASK_BITS_NUM; i++) ++ { ++ if ((1 << i) & VIDEO_IRQ_MASK) ++ enable_irq(i); ++ } ++ ++ down (&pdata->vcodec_sem); ++ err = 0; ++ } ++ break; ++ ++ case AKUIO_INVALIDATE_L2CACHE: ++ l2cache_invalidate (); ++ flush_cache_all(); ++ err = 0; ++ break; ++ ++ case AKUIO_INVALIDATE_L1CACHE: ++// flush_cache_all(); ++ err = 0; ++ break; ++ ++ default: ++ err = -EINVAL; ++ break; ++ }; ++ ++ return err; ++} ++ ++/** ++ * @brief When application open the akuio device, request all irq of the hw codec and disable the irq immediately ++ * @author Jacky Lau ++ * @date 2011-07-05 ++ * @param [in] uioinfo : information of uio driver ++ * @param [in] inode : inode of the device ++ * @return int : return 0 when handle successful, otherwise return negative ++ * @retval 0 : handled successful. ++ * @retval <0 : handle failed. ++ */ ++static int uio_vcodec_open(struct uio_info *uioinfo, struct inode *inode) ++{ ++ struct uio_platdata *pdata = uioinfo->priv; ++ int i; ++ int ret = 0; ++ ++ //Add code here to make sure uio0 can be multi-opened. ++ if( pdata->open_count++ > 0 ) { ++ DBG("uio0 has opened! open_count=%d\n", pdata->open_count ); ++ return 0; ++ } ++ ++ init_MUTEX_LOCKED(&(pdata->vcodec_sem)); ++ ++ for (i = 0; i < MASK_BITS_NUM; i++) ++ { ++ if ((1 << i) & VIDEO_IRQ_MASK) { ++ ret = request_irq(i, uio_vcodec_irq_handler, IRQF_DISABLED, "VIDEO HW CODEC", pdata); ++ disable_irq_nosync(i); ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * @brief When application close the akuio device, free all irq of the hw codec ++ * @author Jacky Lau ++ * @date 2011-07-05 ++ * @param [in] uioinfo : information of uio driver ++ * @param [in] inode : inode of the device ++ * @return int : return 0 when handle successful, otherwise return negative ++ * @retval 0 : handled successful. ++ * @retval <0 : handle failed. ++ */ ++static int uio_vcodec_release(struct uio_info *uioinfo, struct inode *inode) ++{ ++ struct uio_platdata *pdata = uioinfo->priv; ++ int i; ++ ++ //Add code here to make sure uio0 can be multi-opened. ++ if(--pdata->open_count != 0) { ++ DBG("uio0 does's closed to 0! open_count=%d\n", pdata->open_count ); ++ return 0; ++ } ++ ++ for (i = 0; i < MASK_BITS_NUM; i++) ++ { ++ if ((1 << i) & VIDEO_IRQ_MASK) ++ free_irq(i, pdata); ++ } ++ ++ return 0; ++} ++ ++/** ++ * @brief Init the device which was probed ++ * @author Jacky Lau ++ * @date 2011-07-05 ++ * @param [in] pdev : the device definition ++ * @return int : return 0 when handle successful, otherwise return negative ++ * @retval 0 : handled successful. ++ * @retval <0 : handle failed. ++ */ ++static int uio_vcodec_probe(struct platform_device *pdev) ++{ ++ struct uio_info *uioinfo = pdev->dev.platform_data; ++ struct uio_platdata *pdata; ++ struct uio_mem *uiomem; ++ int ret = -ENODEV; ++ int i; ++ ++ if (!uioinfo || !uioinfo->name || !uioinfo->version) { ++ dev_dbg(&pdev->dev, "%s: err_uioinfo\n", __func__); ++ goto err_uioinfo; ++ } ++ ++ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) { ++ ret = -ENOMEM; ++ dev_dbg(&pdev->dev, "%s: err_alloc_pdata\n", __func__); ++ goto err_alloc_pdata; ++ } ++ ++ pdata->uioinfo = uioinfo; ++ ++ uiomem = &uioinfo->mem[0]; ++ ++ for (i = 0; i < pdev->num_resources; ++i) { ++ struct resource *r = &pdev->resource[i]; ++ if (r->flags != IORESOURCE_MEM) ++ continue; ++ ++ if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) { ++ dev_warn(&pdev->dev, "device has more than " ++ __stringify(MAX_UIO_MAPS) ++ " I/O memory resources.\n"); ++ break; ++ } ++ ++ uiomem->memtype = UIO_MEM_PHYS; ++ uiomem->addr = r->start; ++ uiomem->size = r->end - r->start + 1; ++ ++uiomem; ++ } ++ ++ while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) { ++ uiomem->size = 0; ++ ++uiomem; ++ } ++ ++ /* open count */ ++ pdata->open_count = 0; ++ ++ /* irq */ ++ pdata->uioinfo->irq = UIO_IRQ_CUSTOM; ++ ++ /* file handle */ ++ pdata->uioinfo->open = uio_vcodec_open; ++ pdata->uioinfo->release = uio_vcodec_release; ++ pdata->uioinfo->ioctl = uio_vcodec_ioctl; ++ ++ pdata->uioinfo->priv = pdata; ++ ++ ret = uio_register_device(&pdev->dev, pdata->uioinfo); ++ ++ if (ret) { ++ kfree(pdata); ++err_alloc_pdata: ++err_uioinfo: ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, pdata); ++ ++ return 0; ++} ++ ++/** ++ * @brief De-init the device which will be removed ++ * @author Jacky Lau ++ * @date 2011-07-05 ++ * @param [in] pdev : the device definition ++ * @return int : return 0 when handle successful, otherwise return negative ++ * @retval 0 : handled successful. ++ * @retval <0 : handle failed. ++ */ ++static int uio_vcodec_remove(struct platform_device *pdev) ++{ ++ struct uio_platdata *pdata = platform_get_drvdata(pdev); ++ ++ uio_unregister_device(pdata->uioinfo); ++ ++ kfree(pdata); ++ ++ return 0; ++} ++ ++/* driver definition */ ++static struct platform_driver uio_vcodec = { ++ .probe = uio_vcodec_probe, ++ .remove = uio_vcodec_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++/** ++ * @brief kernel module init function, register the driver to kernel ++ * @author Jacky Lau ++ * @date 2011-07-05 ++ * @param ++ * @return int : return 0 when handle successful, otherwise return negative ++ * @retval 0 : handled successful. ++ * @retval <0 : handle failed. ++ */ ++static int __init uio_vcodec_init(void) ++{ ++ return platform_driver_register(&uio_vcodec); ++} ++ ++/** ++ * @brief kernel module finally function, unregister the driver from kernel ++ * @author Jacky Lau ++ * @date 2011-07-05 ++ * @param ++ * @return void ++ * @retval ++ */ ++static void __exit uio_vcodec_exit(void) ++{ ++ platform_driver_unregister(&uio_vcodec); ++} ++module_init(uio_vcodec_init); ++module_exit(uio_vcodec_exit); ++ ++MODULE_AUTHOR("Jacky Lau"); ++MODULE_DESCRIPTION("Userspace driver for anyka video hw codec"); ++MODULE_LICENSE("GPL v2"); ++ +diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile +index 53a7bc07..e10b0f3e 100644 +--- a/drivers/usb/Makefile ++++ b/drivers/usb/Makefile +@@ -27,6 +27,7 @@ obj-$(CONFIG_USB_HWA_HCD) += host/ + obj-$(CONFIG_USB_ISP1760_HCD) += host/ + obj-$(CONFIG_USB_IMX21_HCD) += host/ + obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ ++obj-$(CONFIG_USB_ANYKA_HCD) += host/ + + obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ + +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +index 67dda0db..1497207f 100644 +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -3156,7 +3156,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, + udev->tt = &hub->tt; + udev->ttport = port1; + } +- ++ + /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way? + * Because device hardware and firmware is sometimes buggy in + * this area, and this is how Linux has done it for ages. +diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig +index 2633f759..228f22d8 100644 +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -316,6 +316,8 @@ config USB_MV_UDC + USB2.0 OTG controller, which can be configured as high speed or + full speed USB peripheral. + ++source "drivers/usb/gadget/plat-anyka/Kconfig" ++ + # + # Controllers available in both integrated and discrete versions + # +@@ -846,6 +848,15 @@ config USB_G_PRINTER + For more information, see Documentation/usb/gadget_printer.txt + which includes sample code for accessing the device file. + ++config USB_G_ANDROID ++ boolean "Android Composite Gadget" ++ help ++ The Android Composite Gadget supports multiple USB ++ functions: adb, acm, mass storage, mtp, accessory ++ and rndis. ++ Each function can be configured and enabled/disabled ++ dynamically from userspace through a sysfs interface. ++ + config USB_CDC_COMPOSITE + tristate "CDC Composite Device (Ethernet and ACM)" + depends on NET +diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile +index b7f6eefc..a92bbbe8 100644 +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -31,6 +31,8 @@ obj-$(CONFIG_USB_MV_UDC) += mv_udc.o + mv_udc-y := mv_udc_core.o + obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o + obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o ++obj-$(CONFIG_USB_AKUDC) += plat-anyka/ ++obj-$(CONFIG_USB_AKUDC_PRODUCER) += plat-anyka/ + + # + # USB gadget drivers +@@ -52,6 +54,7 @@ g_nokia-y := nokia.o + g_webcam-y := webcam.o + g_ncm-y := ncm.o + g_acm_ms-y := acm_ms.o ++g_android-y := android.o + + obj-$(CONFIG_USB_ZERO) += g_zero.o + obj-$(CONFIG_USB_AUDIO) += g_audio.o +@@ -71,3 +74,4 @@ obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o + obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o + obj-$(CONFIG_USB_G_NCM) += g_ncm.o + obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o ++obj-$(CONFIG_USB_G_ANDROID) += g_android.o +diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c +new file mode 100644 +index 00000000..4e73bf1a +--- /dev/null ++++ b/drivers/usb/gadget/android.c +@@ -0,0 +1,1565 @@ ++/* ++ * Gadget Driver for Android ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood ++ * Benoit Goby ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "gadget_chips.h" ++ ++/* ++ * Kbuild is not very cooperative with respect to linking separately ++ * compiled library objects into one module. So for now we won't use ++ * separate compilation ... ensuring init/exit sections work to shrink ++ * the runtime footprint, and giving us at least some parts of what ++ * a "gcc --combine ... part1.c part2.c part3.c ... " build would. ++ */ ++#include "usbstring.c" ++#include "config.c" ++#include "epautoconf.c" ++#include "composite.c" ++ ++#include "f_fs.c" ++#include "f_audio_source.c" ++#include "f_mass_storage.c" ++#include "u_serial.c" ++#include "f_acm.c" ++#include "f_adb.c" ++#include "f_mtp.c" ++#include "f_accessory.c" ++#define USB_ETH_RNDIS y ++#include "f_rndis.c" ++#include "rndis.c" ++#include "u_ether.c" ++ ++MODULE_AUTHOR("Mike Lockwood"); ++MODULE_DESCRIPTION("Android Composite USB Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("1.0"); ++ ++static const char longname[] = "Gadget Android"; ++ ++/* Default vendor and product IDs, overridden by userspace */ ++#define VENDOR_ID 0x18D1 ++#define PRODUCT_ID 0x0001 ++ ++struct android_usb_function { ++ char *name; ++ void *config; ++ ++ struct device *dev; ++ char *dev_name; ++ struct device_attribute **attributes; ++ ++ /* for android_dev.enabled_functions */ ++ struct list_head enabled_list; ++ ++ /* Optional: initialization during gadget bind */ ++ int (*init)(struct android_usb_function *, struct usb_composite_dev *); ++ /* Optional: cleanup during gadget unbind */ ++ void (*cleanup)(struct android_usb_function *); ++ /* Optional: called when the function is added the list of ++ * enabled functions */ ++ void (*enable)(struct android_usb_function *); ++ /* Optional: called when it is removed */ ++ void (*disable)(struct android_usb_function *); ++ ++ int (*bind_config)(struct android_usb_function *, ++ struct usb_configuration *); ++ ++ /* Optional: called when the configuration is removed */ ++ void (*unbind_config)(struct android_usb_function *, ++ struct usb_configuration *); ++ /* Optional: handle ctrl requests before the device is configured */ ++ int (*ctrlrequest)(struct android_usb_function *, ++ struct usb_composite_dev *, ++ const struct usb_ctrlrequest *); ++}; ++ ++struct android_dev { ++ struct android_usb_function **functions; ++ struct list_head enabled_functions; ++ struct usb_composite_dev *cdev; ++ struct device *dev; ++ ++ bool enabled; ++ int disable_depth; ++ struct mutex mutex; ++ bool connected; ++ bool sw_connected; ++ struct work_struct work; ++ char ffs_aliases[256]; ++}; ++ ++static struct class *android_class; ++static struct android_dev *_android_dev; ++static int android_bind_config(struct usb_configuration *c); ++static void android_unbind_config(struct usb_configuration *c); ++ ++/* string IDs are assigned dynamically */ ++#define STRING_MANUFACTURER_IDX 0 ++#define STRING_PRODUCT_IDX 1 ++#define STRING_SERIAL_IDX 2 ++ ++static char manufacturer_string[256]; ++static char product_string[256]; ++static char serial_string[256]; ++ ++/* String Table */ ++static struct usb_string strings_dev[] = { ++ [STRING_MANUFACTURER_IDX].s = manufacturer_string, ++ [STRING_PRODUCT_IDX].s = product_string, ++ [STRING_SERIAL_IDX].s = serial_string, ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings stringtab_dev = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_dev, ++}; ++ ++static struct usb_gadget_strings *dev_strings[] = { ++ &stringtab_dev, ++ NULL, ++}; ++ ++static struct usb_device_descriptor device_desc = { ++ .bLength = sizeof(device_desc), ++ .bDescriptorType = USB_DT_DEVICE, ++ .bcdUSB = __constant_cpu_to_le16(0x0200), ++ .bDeviceClass = USB_CLASS_PER_INTERFACE, ++ .idVendor = __constant_cpu_to_le16(VENDOR_ID), ++ .idProduct = __constant_cpu_to_le16(PRODUCT_ID), ++ .bcdDevice = __constant_cpu_to_le16(0xffff), ++ .bNumConfigurations = 1, ++}; ++ ++static struct usb_configuration android_config_driver = { ++ .label = "android", ++ .unbind = android_unbind_config, ++ .bConfigurationValue = 1, ++ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, ++ .bMaxPower = 0xFA, /* 500ma */ ++}; ++ ++static void android_work(struct work_struct *data) ++{ ++ struct android_dev *dev = container_of(data, struct android_dev, work); ++ struct usb_composite_dev *cdev = dev->cdev; ++ char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; ++ char *connected[2] = { "USB_STATE=CONNECTED", NULL }; ++ char *configured[2] = { "USB_STATE=CONFIGURED", NULL }; ++ char **uevent_envp = NULL; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ if (cdev->config) ++ uevent_envp = configured; ++ else if (dev->connected != dev->sw_connected) ++ uevent_envp = dev->connected ? connected : disconnected; ++ dev->sw_connected = dev->connected; ++ spin_unlock_irqrestore(&cdev->lock, flags); ++ ++ if (uevent_envp) { ++ kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, uevent_envp); ++ pr_info("%s: sent uevent %s\n", __func__, uevent_envp[0]); ++ } else { ++ pr_info("%s: did not send uevent (%d %d %p)\n", __func__, ++ dev->connected, dev->sw_connected, cdev->config); ++ } ++} ++ ++static void android_enable(struct android_dev *dev) ++{ ++ struct usb_composite_dev *cdev = dev->cdev; ++ ++ if (WARN_ON(!dev->disable_depth)) ++ return; ++ ++ if (--dev->disable_depth == 0) { ++ usb_add_config(cdev, &android_config_driver, ++ android_bind_config); ++ usb_gadget_connect(cdev->gadget); ++ } ++} ++ ++static void android_disable(struct android_dev *dev) ++{ ++ struct usb_composite_dev *cdev = dev->cdev; ++ ++ if (dev->disable_depth++ == 0) { ++ usb_gadget_disconnect(cdev->gadget); ++ /* Cancel pending control requests */ ++ usb_ep_dequeue(cdev->gadget->ep0, cdev->req); ++ usb_remove_config(cdev, &android_config_driver); ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++/* Supported functions initialization */ ++ ++struct functionfs_config { ++ bool opened; ++ bool enabled; ++ struct ffs_data *data; ++}; ++ ++static int ffs_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ f->config = kzalloc(sizeof(struct functionfs_config), GFP_KERNEL); ++ if (!f->config) ++ return -ENOMEM; ++ ++ return functionfs_init(); ++} ++ ++static void ffs_function_cleanup(struct android_usb_function *f) ++{ ++ functionfs_cleanup(); ++ kfree(f->config); ++} ++ ++static void ffs_function_enable(struct android_usb_function *f) ++{ ++ struct android_dev *dev = _android_dev; ++ struct functionfs_config *config = f->config; ++ ++ config->enabled = true; ++ ++ /* Disable the gadget until the function is ready */ ++ if (!config->opened) ++ android_disable(dev); ++} ++ ++static void ffs_function_disable(struct android_usb_function *f) ++{ ++ struct android_dev *dev = _android_dev; ++ struct functionfs_config *config = f->config; ++ ++ config->enabled = false; ++ ++ /* Balance the disable that was called in closed_callback */ ++ if (!config->opened) ++ android_enable(dev); ++} ++ ++static int ffs_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ struct functionfs_config *config = f->config; ++ return functionfs_bind_config(c->cdev, c, config->data); ++} ++ ++static ssize_t ++ffs_aliases_show(struct device *pdev, struct device_attribute *attr, char *buf) ++{ ++ struct android_dev *dev = _android_dev; ++ int ret; ++ ++ mutex_lock(&dev->mutex); ++ ret = sprintf(buf, "%s\n", dev->ffs_aliases); ++ mutex_unlock(&dev->mutex); ++ ++ return ret; ++} ++ ++static ssize_t ++ffs_aliases_store(struct device *pdev, struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct android_dev *dev = _android_dev; ++ char buff[256]; ++ ++ mutex_lock(&dev->mutex); ++ ++ if (dev->enabled) { ++ mutex_unlock(&dev->mutex); ++ return -EBUSY; ++ } ++ ++ strlcpy(buff, buf, sizeof(buff)); ++ strlcpy(dev->ffs_aliases, strim(buff), sizeof(dev->ffs_aliases)); ++ ++ mutex_unlock(&dev->mutex); ++ ++ return size; ++} ++ ++static DEVICE_ATTR(aliases, S_IRUGO | S_IWUSR, ffs_aliases_show, ++ ffs_aliases_store); ++static struct device_attribute *ffs_function_attributes[] = { ++ &dev_attr_aliases, ++ NULL ++}; ++ ++static struct android_usb_function ffs_function = { ++ .name = "ffs", ++ .init = ffs_function_init, ++ .enable = ffs_function_enable, ++ .disable = ffs_function_disable, ++ .cleanup = ffs_function_cleanup, ++ .bind_config = ffs_function_bind_config, ++ .attributes = ffs_function_attributes, ++}; ++ ++static int functionfs_ready_callback(struct ffs_data *ffs) ++{ ++ struct android_dev *dev = _android_dev; ++ struct functionfs_config *config = ffs_function.config; ++ int ret = 0; ++ ++ mutex_lock(&dev->mutex); ++ ++ ret = functionfs_bind(ffs, dev->cdev); ++ if (ret) ++ goto err; ++ ++ config->data = ffs; ++ config->opened = true; ++ ++ if (config->enabled) ++ android_enable(dev); ++ ++err: ++ mutex_unlock(&dev->mutex); ++ return ret; ++} ++ ++static void functionfs_closed_callback(struct ffs_data *ffs) ++{ ++ struct android_dev *dev = _android_dev; ++ struct functionfs_config *config = ffs_function.config; ++ ++ mutex_lock(&dev->mutex); ++ ++ if (config->enabled) ++ android_disable(dev); ++ ++ config->opened = false; ++ config->data = NULL; ++ ++ functionfs_unbind(ffs); ++ ++ mutex_unlock(&dev->mutex); ++} ++ ++static int functionfs_check_dev_callback(const char *dev_name) ++{ ++ return 0; ++} ++ ++ ++struct adb_data { ++ bool opened; ++ bool enabled; ++}; ++ ++static int ++adb_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ f->config = kzalloc(sizeof(struct adb_data), GFP_KERNEL); ++ if (!f->config) ++ return -ENOMEM; ++ ++ return adb_setup(); ++} ++ ++static void adb_function_cleanup(struct android_usb_function *f) ++{ ++ adb_cleanup(); ++ kfree(f->config); ++} ++ ++static int ++adb_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ return adb_bind_config(c); ++} ++ ++static void adb_android_function_enable(struct android_usb_function *f) ++{ ++ struct android_dev *dev = _android_dev; ++ struct adb_data *data = f->config; ++ ++ data->enabled = true; ++ ++ /* Disable the gadget until adbd is ready */ ++ if (!data->opened) ++ android_disable(dev); ++} ++ ++static void adb_android_function_disable(struct android_usb_function *f) ++{ ++ struct android_dev *dev = _android_dev; ++ struct adb_data *data = f->config; ++ ++ data->enabled = false; ++ ++ /* Balance the disable that was called in closed_callback */ ++ if (!data->opened) ++ android_enable(dev); ++} ++ ++static struct android_usb_function adb_function = { ++ .name = "adb", ++ .enable = adb_android_function_enable, ++ .disable = adb_android_function_disable, ++ .init = adb_function_init, ++ .cleanup = adb_function_cleanup, ++ .bind_config = adb_function_bind_config, ++}; ++ ++static void adb_ready_callback(void) ++{ ++ struct android_dev *dev = _android_dev; ++ struct adb_data *data = adb_function.config; ++ ++ mutex_lock(&dev->mutex); ++ ++ data->opened = true; ++ ++ if (data->enabled) ++ android_enable(dev); ++ ++ mutex_unlock(&dev->mutex); ++} ++ ++static void adb_closed_callback(void) ++{ ++ struct android_dev *dev = _android_dev; ++ struct adb_data *data = adb_function.config; ++ ++ mutex_lock(&dev->mutex); ++ ++ data->opened = false; ++ ++ if (data->enabled) ++ android_disable(dev); ++ ++ mutex_unlock(&dev->mutex); ++} ++ ++ ++#define MAX_ACM_INSTANCES 4 ++struct acm_function_config { ++ int instances; ++}; ++ ++static int ++acm_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ f->config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL); ++ if (!f->config) ++ return -ENOMEM; ++ ++ return gserial_setup(cdev->gadget, MAX_ACM_INSTANCES); ++} ++ ++static void acm_function_cleanup(struct android_usb_function *f) ++{ ++ gserial_cleanup(); ++ kfree(f->config); ++ f->config = NULL; ++} ++ ++static int ++acm_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ int i; ++ int ret = 0; ++ struct acm_function_config *config = f->config; ++ ++ for (i = 0; i < config->instances; i++) { ++ ret = acm_bind_config(c, i); ++ if (ret) { ++ pr_err("Could not bind acm%u config\n", i); ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++static ssize_t acm_instances_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct acm_function_config *config = f->config; ++ return sprintf(buf, "%d\n", config->instances); ++} ++ ++static ssize_t acm_instances_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct acm_function_config *config = f->config; ++ int value; ++ ++ sscanf(buf, "%d", &value); ++ if (value > MAX_ACM_INSTANCES) ++ value = MAX_ACM_INSTANCES; ++ config->instances = value; ++ return size; ++} ++ ++static DEVICE_ATTR(instances, S_IRUGO | S_IWUSR, acm_instances_show, ++ acm_instances_store); ++static struct device_attribute *acm_function_attributes[] = { ++ &dev_attr_instances, ++ NULL ++}; ++ ++static struct android_usb_function acm_function = { ++ .name = "acm", ++ .init = acm_function_init, ++ .cleanup = acm_function_cleanup, ++ .bind_config = acm_function_bind_config, ++ .attributes = acm_function_attributes, ++}; ++ ++ ++static int ++mtp_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ return mtp_setup(); ++} ++ ++static void mtp_function_cleanup(struct android_usb_function *f) ++{ ++ mtp_cleanup(); ++} ++ ++static int ++mtp_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ return mtp_bind_config(c, false); ++} ++ ++static int ++ptp_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ /* nothing to do - initialization is handled by mtp_function_init */ ++ return 0; ++} ++ ++static void ptp_function_cleanup(struct android_usb_function *f) ++{ ++ /* nothing to do - cleanup is handled by mtp_function_cleanup */ ++} ++ ++static int ++ptp_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ return mtp_bind_config(c, true); ++} ++ ++static int mtp_function_ctrlrequest(struct android_usb_function *f, ++ struct usb_composite_dev *cdev, ++ const struct usb_ctrlrequest *c) ++{ ++ return mtp_ctrlrequest(cdev, c); ++} ++ ++static struct android_usb_function mtp_function = { ++ .name = "mtp", ++ .init = mtp_function_init, ++ .cleanup = mtp_function_cleanup, ++ .bind_config = mtp_function_bind_config, ++ .ctrlrequest = mtp_function_ctrlrequest, ++}; ++ ++/* PTP function is same as MTP with slightly different interface descriptor */ ++static struct android_usb_function ptp_function = { ++ .name = "ptp", ++ .init = ptp_function_init, ++ .cleanup = ptp_function_cleanup, ++ .bind_config = ptp_function_bind_config, ++}; ++ ++ ++struct rndis_function_config { ++ u8 ethaddr[ETH_ALEN]; ++ u32 vendorID; ++ char manufacturer[256]; ++ /* "Wireless" RNDIS; auto-detected by Windows */ ++ bool wceis; ++}; ++ ++static int ++rndis_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ f->config = kzalloc(sizeof(struct rndis_function_config), GFP_KERNEL); ++ if (!f->config) ++ return -ENOMEM; ++ return 0; ++} ++ ++static void rndis_function_cleanup(struct android_usb_function *f) ++{ ++ kfree(f->config); ++ f->config = NULL; ++} ++ ++static int ++rndis_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ int ret; ++ struct rndis_function_config *rndis = f->config; ++ ++ if (!rndis) { ++ pr_err("%s: rndis_pdata\n", __func__); ++ return -1; ++ } ++ ++ pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__, ++ rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], ++ rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); ++ ++ ret = gether_setup_name(c->cdev->gadget, rndis->ethaddr, "rndis"); ++ if (ret) { ++ pr_err("%s: gether_setup failed\n", __func__); ++ return ret; ++ } ++ ++ if (rndis->wceis) { ++ /* "Wireless" RNDIS; auto-detected by Windows */ ++ rndis_iad_descriptor.bFunctionClass = ++ USB_CLASS_WIRELESS_CONTROLLER; ++ rndis_iad_descriptor.bFunctionSubClass = 0x01; ++ rndis_iad_descriptor.bFunctionProtocol = 0x03; ++ rndis_control_intf.bInterfaceClass = ++ USB_CLASS_WIRELESS_CONTROLLER; ++ rndis_control_intf.bInterfaceSubClass = 0x01; ++ rndis_control_intf.bInterfaceProtocol = 0x03; ++ } ++ ++ return rndis_bind_config_vendor(c, rndis->ethaddr, rndis->vendorID, ++ rndis->manufacturer); ++} ++ ++static void rndis_function_unbind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ gether_cleanup(); ++} ++ ++static ssize_t rndis_manufacturer_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *config = f->config; ++ return sprintf(buf, "%s\n", config->manufacturer); ++} ++ ++static ssize_t rndis_manufacturer_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *config = f->config; ++ ++ if (size >= sizeof(config->manufacturer)) ++ return -EINVAL; ++ if (sscanf(buf, "%s", config->manufacturer) == 1) ++ return size; ++ return -1; ++} ++ ++static DEVICE_ATTR(manufacturer, S_IRUGO | S_IWUSR, rndis_manufacturer_show, ++ rndis_manufacturer_store); ++ ++static ssize_t rndis_wceis_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *config = f->config; ++ return sprintf(buf, "%d\n", config->wceis); ++} ++ ++static ssize_t rndis_wceis_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *config = f->config; ++ int value; ++ ++ if (sscanf(buf, "%d", &value) == 1) { ++ config->wceis = value; ++ return size; ++ } ++ return -EINVAL; ++} ++ ++static DEVICE_ATTR(wceis, S_IRUGO | S_IWUSR, rndis_wceis_show, ++ rndis_wceis_store); ++ ++static ssize_t rndis_ethaddr_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *rndis = f->config; ++ return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", ++ rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], ++ rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); ++} ++ ++static ssize_t rndis_ethaddr_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *rndis = f->config; ++ ++ if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", ++ (int *)&rndis->ethaddr[0], (int *)&rndis->ethaddr[1], ++ (int *)&rndis->ethaddr[2], (int *)&rndis->ethaddr[3], ++ (int *)&rndis->ethaddr[4], (int *)&rndis->ethaddr[5]) == 6) ++ return size; ++ return -EINVAL; ++} ++ ++static DEVICE_ATTR(ethaddr, S_IRUGO | S_IWUSR, rndis_ethaddr_show, ++ rndis_ethaddr_store); ++ ++static ssize_t rndis_vendorID_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *config = f->config; ++ return sprintf(buf, "%04x\n", config->vendorID); ++} ++ ++static ssize_t rndis_vendorID_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *config = f->config; ++ int value; ++ ++ if (sscanf(buf, "%04x", &value) == 1) { ++ config->vendorID = value; ++ return size; ++ } ++ return -EINVAL; ++} ++ ++static DEVICE_ATTR(vendorID, S_IRUGO | S_IWUSR, rndis_vendorID_show, ++ rndis_vendorID_store); ++ ++static struct device_attribute *rndis_function_attributes[] = { ++ &dev_attr_manufacturer, ++ &dev_attr_wceis, ++ &dev_attr_ethaddr, ++ &dev_attr_vendorID, ++ NULL ++}; ++ ++static struct android_usb_function rndis_function = { ++ .name = "rndis", ++ .init = rndis_function_init, ++ .cleanup = rndis_function_cleanup, ++ .bind_config = rndis_function_bind_config, ++ .unbind_config = rndis_function_unbind_config, ++ .attributes = rndis_function_attributes, ++}; ++ ++ ++struct mass_storage_function_config { ++ struct fsg_config fsg; ++ struct fsg_common *common; ++}; ++ ++static int mass_storage_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ struct mass_storage_function_config *config; ++ struct fsg_common *common; ++ int err; ++ ++ config = kzalloc(sizeof(struct mass_storage_function_config), ++ GFP_KERNEL); ++ if (!config) ++ return -ENOMEM; ++ ++ config->fsg.nluns = 1; ++ config->fsg.luns[0].removable = 1; ++ ++ common = fsg_common_init(NULL, cdev, &config->fsg); ++ if (IS_ERR(common)) { ++ kfree(config); ++ return PTR_ERR(common); ++ } ++ ++ err = sysfs_create_link(&f->dev->kobj, ++ &common->luns[0].dev.kobj, ++ "lun"); ++ if (err) { ++ kfree(config); ++ return err; ++ } ++ ++ config->common = common; ++ f->config = config; ++ return 0; ++} ++ ++static void mass_storage_function_cleanup(struct android_usb_function *f) ++{ ++ kfree(f->config); ++ f->config = NULL; ++} ++ ++static int mass_storage_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ struct mass_storage_function_config *config = f->config; ++ return fsg_bind_config(c->cdev, c, config->common); ++} ++ ++static ssize_t mass_storage_inquiry_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct mass_storage_function_config *config = f->config; ++ return sprintf(buf, "%s\n", config->common->inquiry_string); ++} ++ ++static ssize_t mass_storage_inquiry_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct mass_storage_function_config *config = f->config; ++ if (size >= sizeof(config->common->inquiry_string)) ++ return -EINVAL; ++ if (sscanf(buf, "%s", config->common->inquiry_string) != 1) ++ return -EINVAL; ++ return size; ++} ++ ++static DEVICE_ATTR(inquiry_string, S_IRUGO | S_IWUSR, ++ mass_storage_inquiry_show, ++ mass_storage_inquiry_store); ++ ++static struct device_attribute *mass_storage_function_attributes[] = { ++ &dev_attr_inquiry_string, ++ NULL ++}; ++ ++static struct android_usb_function mass_storage_function = { ++ .name = "mass_storage", ++ .init = mass_storage_function_init, ++ .cleanup = mass_storage_function_cleanup, ++ .bind_config = mass_storage_function_bind_config, ++ .attributes = mass_storage_function_attributes, ++}; ++ ++ ++static int accessory_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ return acc_setup(); ++} ++ ++static void accessory_function_cleanup(struct android_usb_function *f) ++{ ++ acc_cleanup(); ++} ++ ++static int accessory_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ return acc_bind_config(c); ++} ++ ++static int accessory_function_ctrlrequest(struct android_usb_function *f, ++ struct usb_composite_dev *cdev, ++ const struct usb_ctrlrequest *c) ++{ ++ return acc_ctrlrequest(cdev, c); ++} ++ ++static struct android_usb_function accessory_function = { ++ .name = "accessory", ++ .init = accessory_function_init, ++ .cleanup = accessory_function_cleanup, ++ .bind_config = accessory_function_bind_config, ++ .ctrlrequest = accessory_function_ctrlrequest, ++}; ++ ++static int audio_source_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ struct audio_source_config *config; ++ ++ config = kzalloc(sizeof(struct audio_source_config), GFP_KERNEL); ++ if (!config) ++ return -ENOMEM; ++ config->card = -1; ++ config->device = -1; ++ f->config = config; ++ return 0; ++} ++ ++static void audio_source_function_cleanup(struct android_usb_function *f) ++{ ++ kfree(f->config); ++} ++ ++static int audio_source_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ struct audio_source_config *config = f->config; ++ ++ return audio_source_bind_config(c, config); ++} ++ ++static void audio_source_function_unbind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ struct audio_source_config *config = f->config; ++ ++ config->card = -1; ++ config->device = -1; ++} ++ ++static ssize_t audio_source_pcm_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct audio_source_config *config = f->config; ++ ++ /* print PCM card and device numbers */ ++ return sprintf(buf, "%d %d\n", config->card, config->device); ++} ++ ++static DEVICE_ATTR(pcm, S_IRUGO | S_IWUSR, audio_source_pcm_show, NULL); ++ ++static struct device_attribute *audio_source_function_attributes[] = { ++ &dev_attr_pcm, ++ NULL ++}; ++ ++static struct android_usb_function audio_source_function = { ++ .name = "audio_source", ++ .init = audio_source_function_init, ++ .cleanup = audio_source_function_cleanup, ++ .bind_config = audio_source_function_bind_config, ++ .unbind_config = audio_source_function_unbind_config, ++ .attributes = audio_source_function_attributes, ++}; ++ ++static struct android_usb_function *supported_functions[] = { ++ &ffs_function, ++ &adb_function, ++ &acm_function, ++ &mtp_function, ++ &ptp_function, ++ &rndis_function, ++ &mass_storage_function, ++ &accessory_function, ++ &audio_source_function, ++ NULL ++}; ++ ++ ++static int android_init_functions(struct android_usb_function **functions, ++ struct usb_composite_dev *cdev) ++{ ++ struct android_dev *dev = _android_dev; ++ struct android_usb_function *f; ++ struct device_attribute **attrs; ++ struct device_attribute *attr; ++ int err; ++ int index = 0; ++ ++ for (; (f = *functions++); index++) { ++ f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name); ++ f->dev = device_create(android_class, dev->dev, ++ MKDEV(0, index), f, f->dev_name); ++ if (IS_ERR(f->dev)) { ++ pr_err("%s: Failed to create dev %s", __func__, ++ f->dev_name); ++ err = PTR_ERR(f->dev); ++ goto err_create; ++ } ++ ++ if (f->init) { ++ err = f->init(f, cdev); ++ if (err) { ++ pr_err("%s: Failed to init %s", __func__, ++ f->name); ++ goto err_out; ++ } ++ } ++ ++ attrs = f->attributes; ++ if (attrs) { ++ while ((attr = *attrs++) && !err) ++ err = device_create_file(f->dev, attr); ++ } ++ if (err) { ++ pr_err("%s: Failed to create function %s attributes", ++ __func__, f->name); ++ goto err_out; ++ } ++ } ++ return 0; ++ ++err_out: ++ device_destroy(android_class, f->dev->devt); ++err_create: ++ kfree(f->dev_name); ++ return err; ++} ++ ++static void android_cleanup_functions(struct android_usb_function **functions) ++{ ++ struct android_usb_function *f; ++ ++ while (*functions) { ++ f = *functions++; ++ ++ if (f->dev) { ++ device_destroy(android_class, f->dev->devt); ++ kfree(f->dev_name); ++ } ++ ++ if (f->cleanup) ++ f->cleanup(f); ++ } ++} ++ ++static int ++android_bind_enabled_functions(struct android_dev *dev, ++ struct usb_configuration *c) ++{ ++ struct android_usb_function *f; ++ int ret; ++ ++ list_for_each_entry(f, &dev->enabled_functions, enabled_list) { ++ ret = f->bind_config(f, c); ++ if (ret) { ++ pr_err("%s: %s failed", __func__, f->name); ++ return ret; ++ } ++ } ++ return 0; ++} ++ ++static void ++android_unbind_enabled_functions(struct android_dev *dev, ++ struct usb_configuration *c) ++{ ++ struct android_usb_function *f; ++ ++ list_for_each_entry(f, &dev->enabled_functions, enabled_list) { ++ if (f->unbind_config) ++ f->unbind_config(f, c); ++ } ++} ++ ++static int android_enable_function(struct android_dev *dev, char *name) ++{ ++ struct android_usb_function **functions = dev->functions; ++ struct android_usb_function *f; ++ while ((f = *functions++)) { ++ if (!strcmp(name, f->name)) { ++ list_add_tail(&f->enabled_list, ++ &dev->enabled_functions); ++ return 0; ++ } ++ } ++ return -EINVAL; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/* /sys/class/android_usb/android%d/ interface */ ++ ++static ssize_t ++functions_show(struct device *pdev, struct device_attribute *attr, char *buf) ++{ ++ struct android_dev *dev = dev_get_drvdata(pdev); ++ struct android_usb_function *f; ++ char *buff = buf; ++ ++ mutex_lock(&dev->mutex); ++ ++ list_for_each_entry(f, &dev->enabled_functions, enabled_list) ++ buff += sprintf(buff, "%s,", f->name); ++ ++ mutex_unlock(&dev->mutex); ++ ++ if (buff != buf) ++ *(buff-1) = '\n'; ++ return buff - buf; ++} ++ ++static ssize_t ++functions_store(struct device *pdev, struct device_attribute *attr, ++ const char *buff, size_t size) ++{ ++ struct android_dev *dev = dev_get_drvdata(pdev); ++ char *name; ++ char buf[256], *b; ++ char aliases[256], *a; ++ int err; ++ int is_ffs; ++ int ffs_enabled = 0; ++ ++ mutex_lock(&dev->mutex); ++ ++ if (dev->enabled) { ++ mutex_unlock(&dev->mutex); ++ return -EBUSY; ++ } ++ ++ INIT_LIST_HEAD(&dev->enabled_functions); ++ ++ strlcpy(buf, buff, sizeof(buf)); ++ b = strim(buf); ++ ++ while (b) { ++ name = strsep(&b, ","); ++ if (!name) ++ continue; ++ ++ is_ffs = 0; ++ strlcpy(aliases, dev->ffs_aliases, sizeof(aliases)); ++ a = aliases; ++ ++ while (a) { ++ char *alias = strsep(&a, ","); ++ if (alias && !strcmp(name, alias)) { ++ is_ffs = 1; ++ break; ++ } ++ } ++ ++ if (is_ffs) { ++ if (ffs_enabled) ++ continue; ++ err = android_enable_function(dev, "ffs"); ++ if (err) ++ pr_err("android_usb: Cannot enable ffs (%d)", ++ err); ++ else ++ ffs_enabled = 1; ++ continue; ++ } ++ ++ err = android_enable_function(dev, name); ++ if (err) ++ pr_err("android_usb: Cannot enable '%s' (%d)", ++ name, err); ++ } ++ ++ mutex_unlock(&dev->mutex); ++ ++ return size; ++} ++ ++static ssize_t enable_show(struct device *pdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct android_dev *dev = dev_get_drvdata(pdev); ++ return sprintf(buf, "%d\n", dev->enabled); ++} ++ ++static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, ++ const char *buff, size_t size) ++{ ++ struct android_dev *dev = dev_get_drvdata(pdev); ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct android_usb_function *f; ++ int enabled = 0; ++ ++ ++ if (!cdev) ++ return -ENODEV; ++ ++ mutex_lock(&dev->mutex); ++ ++ sscanf(buff, "%d", &enabled); ++ if (enabled && !dev->enabled) { ++ /* ++ * Update values in composite driver's copy of ++ * device descriptor. ++ */ ++ cdev->desc.idVendor = device_desc.idVendor; ++ cdev->desc.idProduct = device_desc.idProduct; ++ cdev->desc.bcdDevice = device_desc.bcdDevice; ++ cdev->desc.bDeviceClass = device_desc.bDeviceClass; ++ cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass; ++ cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol; ++ list_for_each_entry(f, &dev->enabled_functions, enabled_list) { ++ if (f->enable) ++ f->enable(f); ++ } ++ android_enable(dev); ++ dev->enabled = true; ++ } else if (!enabled && dev->enabled) { ++ android_disable(dev); ++ list_for_each_entry(f, &dev->enabled_functions, enabled_list) { ++ if (f->disable) ++ f->disable(f); ++ } ++ dev->enabled = false; ++ } else { ++ pr_err("android_usb: already %s\n", ++ dev->enabled ? "enabled" : "disabled"); ++ } ++ ++ mutex_unlock(&dev->mutex); ++ return size; ++} ++ ++static ssize_t state_show(struct device *pdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct android_dev *dev = dev_get_drvdata(pdev); ++ struct usb_composite_dev *cdev = dev->cdev; ++ char *state = "DISCONNECTED"; ++ unsigned long flags; ++ ++ if (!cdev) ++ goto out; ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ if (cdev->config) ++ state = "CONFIGURED"; ++ else if (dev->connected) ++ state = "CONNECTED"; ++ spin_unlock_irqrestore(&cdev->lock, flags); ++out: ++ return sprintf(buf, "%s\n", state); ++} ++ ++#define DESCRIPTOR_ATTR(field, format_string) \ ++static ssize_t \ ++field ## _show(struct device *dev, struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ return sprintf(buf, format_string, device_desc.field); \ ++} \ ++static ssize_t \ ++field ## _store(struct device *dev, struct device_attribute *attr, \ ++ const char *buf, size_t size) \ ++{ \ ++ int value; \ ++ if (sscanf(buf, format_string, &value) == 1) { \ ++ device_desc.field = value; \ ++ return size; \ ++ } \ ++ return -1; \ ++} \ ++static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store); ++ ++#define DESCRIPTOR_STRING_ATTR(field, buffer) \ ++static ssize_t \ ++field ## _show(struct device *dev, struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ return sprintf(buf, "%s", buffer); \ ++} \ ++static ssize_t \ ++field ## _store(struct device *dev, struct device_attribute *attr, \ ++ const char *buf, size_t size) \ ++{ \ ++ if (size >= sizeof(buffer)) \ ++ return -EINVAL; \ ++ return strlcpy(buffer, buf, sizeof(buffer)); \ ++} \ ++static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store); ++ ++ ++DESCRIPTOR_ATTR(idVendor, "%04x\n") ++DESCRIPTOR_ATTR(idProduct, "%04x\n") ++DESCRIPTOR_ATTR(bcdDevice, "%04x\n") ++DESCRIPTOR_ATTR(bDeviceClass, "%d\n") ++DESCRIPTOR_ATTR(bDeviceSubClass, "%d\n") ++DESCRIPTOR_ATTR(bDeviceProtocol, "%d\n") ++DESCRIPTOR_STRING_ATTR(iManufacturer, manufacturer_string) ++DESCRIPTOR_STRING_ATTR(iProduct, product_string) ++DESCRIPTOR_STRING_ATTR(iSerial, serial_string) ++ ++static DEVICE_ATTR(functions, S_IRUGO | S_IWUSR, functions_show, ++ functions_store); ++static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store); ++static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); ++ ++static struct device_attribute *android_usb_attributes[] = { ++ &dev_attr_idVendor, ++ &dev_attr_idProduct, ++ &dev_attr_bcdDevice, ++ &dev_attr_bDeviceClass, ++ &dev_attr_bDeviceSubClass, ++ &dev_attr_bDeviceProtocol, ++ &dev_attr_iManufacturer, ++ &dev_attr_iProduct, ++ &dev_attr_iSerial, ++ &dev_attr_functions, ++ &dev_attr_enable, ++ &dev_attr_state, ++ NULL ++}; ++ ++/*-------------------------------------------------------------------------*/ ++/* Composite driver */ ++ ++static int android_bind_config(struct usb_configuration *c) ++{ ++ struct android_dev *dev = _android_dev; ++ int ret = 0; ++ ++ ret = android_bind_enabled_functions(dev, c); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void android_unbind_config(struct usb_configuration *c) ++{ ++ struct android_dev *dev = _android_dev; ++ ++ android_unbind_enabled_functions(dev, c); ++} ++ ++static int android_bind(struct usb_composite_dev *cdev) ++{ ++ struct android_dev *dev = _android_dev; ++ struct usb_gadget *gadget = cdev->gadget; ++ int gcnum, id, ret; ++ ++ /* ++ * Start disconnected. Userspace will connect the gadget once ++ * it is done configuring the functions. ++ */ ++ usb_gadget_disconnect(gadget); ++ ++ ret = android_init_functions(dev->functions, cdev); ++ if (ret) ++ return ret; ++ ++ /* Allocate string descriptor numbers ... note that string ++ * contents can be overridden by the composite_dev glue. ++ */ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_dev[STRING_MANUFACTURER_IDX].id = id; ++ device_desc.iManufacturer = id; ++ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_dev[STRING_PRODUCT_IDX].id = id; ++ device_desc.iProduct = id; ++ ++ /* Default strings - should be updated by userspace */ ++ strncpy(manufacturer_string, "Android", sizeof(manufacturer_string)-1); ++ strncpy(product_string, "Android", sizeof(product_string) - 1); ++ strncpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1); ++ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_dev[STRING_SERIAL_IDX].id = id; ++ device_desc.iSerialNumber = id; ++ ++ gcnum = usb_gadget_controller_number(gadget); ++ if (gcnum >= 0) ++ device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); ++ else { ++ pr_warning("%s: controller '%s' not recognized\n", ++ longname, gadget->name); ++ device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); ++ } ++ ++ usb_gadget_set_selfpowered(gadget); ++ dev->cdev = cdev; ++ ++ return 0; ++} ++ ++static int android_usb_unbind(struct usb_composite_dev *cdev) ++{ ++ struct android_dev *dev = _android_dev; ++ ++ cancel_work_sync(&dev->work); ++ android_cleanup_functions(dev->functions); ++ return 0; ++} ++ ++static struct usb_composite_driver android_usb_driver = { ++ .name = "android_usb", ++ .dev = &device_desc, ++ .strings = dev_strings, ++ .unbind = android_usb_unbind, ++ .max_speed = USB_SPEED_HIGH, ++}; ++ ++static int ++android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) ++{ ++ struct android_dev *dev = _android_dev; ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ struct usb_request *req = cdev->req; ++ struct android_usb_function *f; ++ int value = -EOPNOTSUPP; ++ unsigned long flags; ++ ++ req->zero = 0; ++ req->complete = composite_setup_complete; ++ req->length = 0; ++ gadget->ep0->driver_data = cdev; ++ ++ list_for_each_entry(f, &dev->enabled_functions, enabled_list) { ++ if (f->ctrlrequest) { ++ value = f->ctrlrequest(f, cdev, c); ++ if (value >= 0) ++ break; ++ } ++ } ++ ++ /* Special case the accessory function. ++ * It needs to handle control requests before it is enabled. ++ */ ++ if (value < 0) ++ value = acc_ctrlrequest(cdev, c); ++ ++ if (value < 0) ++ value = composite_setup(gadget, c); ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ if (!dev->connected) { ++ dev->connected = 1; ++ schedule_work(&dev->work); ++ } else if (c->bRequest == USB_REQ_SET_CONFIGURATION && ++ cdev->config) { ++ schedule_work(&dev->work); ++ } ++ spin_unlock_irqrestore(&cdev->lock, flags); ++ ++ return value; ++} ++ ++static void android_disconnect(struct usb_gadget *gadget) ++{ ++ struct android_dev *dev = _android_dev; ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ unsigned long flags; ++ ++ composite_disconnect(gadget); ++ /* accessory HID support can be active while the ++ accessory function is not actually enabled, ++ so we need to inform it when we are disconnected. ++ */ ++ acc_disconnect(); ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ dev->connected = 0; ++ schedule_work(&dev->work); ++ spin_unlock_irqrestore(&cdev->lock, flags); ++} ++ ++static int android_create_device(struct android_dev *dev) ++{ ++ struct device_attribute **attrs = android_usb_attributes; ++ struct device_attribute *attr; ++ int err; ++ ++ dev->dev = device_create(android_class, NULL, ++ MKDEV(0, 0), NULL, "android0"); ++ if (IS_ERR(dev->dev)) ++ return PTR_ERR(dev->dev); ++ ++ dev_set_drvdata(dev->dev, dev); ++ ++ while ((attr = *attrs++)) { ++ err = device_create_file(dev->dev, attr); ++ if (err) { ++ device_destroy(android_class, dev->dev->devt); ++ return err; ++ } ++ } ++ return 0; ++} ++ ++ ++static int __init init(void) ++{ ++ struct android_dev *dev; ++ int err; ++ ++ android_class = class_create(THIS_MODULE, "android_usb"); ++ if (IS_ERR(android_class)) ++ return PTR_ERR(android_class); ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ dev->disable_depth = 1; ++ dev->functions = supported_functions; ++ INIT_LIST_HEAD(&dev->enabled_functions); ++ INIT_WORK(&dev->work, android_work); ++ mutex_init(&dev->mutex); ++ ++ err = android_create_device(dev); ++ if (err) { ++ class_destroy(android_class); ++ kfree(dev); ++ return err; ++ } ++ ++ _android_dev = dev; ++ ++ /* Override composite driver functions */ ++ composite_driver.setup = android_setup; ++ composite_driver.disconnect = android_disconnect; ++ ++ return usb_composite_probe(&android_usb_driver, android_bind); ++} ++module_init(init); ++ ++static void __exit cleanup(void) ++{ ++ usb_composite_unregister(&android_usb_driver); ++ class_destroy(android_class); ++ kfree(_android_dev); ++ _android_dev = NULL; ++} ++module_exit(cleanup); +diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c +index baaebf28..8f82fc0e 100644 +--- a/drivers/usb/gadget/composite.c ++++ b/drivers/usb/gadget/composite.c +@@ -734,6 +734,7 @@ int usb_add_config(struct usb_composite_dev *cdev, + + INIT_LIST_HEAD(&config->functions); + config->next_interface_id = 0; ++ memset(config->interface, 0, sizeof(config->interface)); + + status = bind(config); + if (status < 0) { +@@ -774,6 +775,55 @@ done: + return status; + } + ++static int unbind_config(struct usb_composite_dev *cdev, ++ struct usb_configuration *config) ++{ ++ while (!list_empty(&config->functions)) { ++ struct usb_function *f; ++ ++ f = list_first_entry(&config->functions, ++ struct usb_function, list); ++ list_del(&f->list); ++ if (f->unbind) { ++ DBG(cdev, "unbind function '%s'/%p\n", f->name, f); ++ f->unbind(config, f); ++ /* may free memory for "f" */ ++ } ++ } ++ if (config->unbind) { ++ DBG(cdev, "unbind config '%s'/%p\n", config->label, config); ++ config->unbind(config); ++ /* may free memory for "c" */ ++ } ++ return 0; ++} ++ ++/** ++ * usb_remove_config() - remove a configuration from a device. ++ * @cdev: wraps the USB gadget ++ * @config: the configuration ++ * ++ * Drivers must call usb_gadget_disconnect before calling this function ++ * to disconnect the device from the host and make sure the host will not ++ * try to enumerate the device while we are changing the config list. ++ */ ++int usb_remove_config(struct usb_composite_dev *cdev, ++ struct usb_configuration *config) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ ++ if (cdev->config == config) ++ reset_config(cdev); ++ ++ list_del(&config->list); ++ ++ spin_unlock_irqrestore(&cdev->lock, flags); ++ ++ return unbind_config(cdev, config); ++} ++ + /*-------------------------------------------------------------------------*/ + + /* We support strings in multiple languages ... string descriptor zero +@@ -1328,28 +1378,10 @@ composite_unbind(struct usb_gadget *gadget) + + while (!list_empty(&cdev->configs)) { + struct usb_configuration *c; +- + c = list_first_entry(&cdev->configs, + struct usb_configuration, list); +- while (!list_empty(&c->functions)) { +- struct usb_function *f; +- +- f = list_first_entry(&c->functions, +- struct usb_function, list); +- list_del(&f->list); +- if (f->unbind) { +- DBG(cdev, "unbind function '%s'/%p\n", +- f->name, f); +- f->unbind(c, f); +- /* may free memory for "f" */ +- } +- } + list_del(&c->list); +- if (c->unbind) { +- DBG(cdev, "unbind config '%s'/%p\n", c->label, c); +- c->unbind(c); +- /* may free memory for "c" */ +- } ++ unbind_config(cdev, c); + } + if (composite->unbind) + composite->unbind(cdev); +diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c +new file mode 100644 +index 00000000..a244265c +--- /dev/null ++++ b/drivers/usb/gadget/f_accessory.c +@@ -0,0 +1,1180 @@ ++/* ++ * Gadget Function Driver for Android USB accessories ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * Author: Mike Lockwood ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++/* #define DEBUG */ ++/* #define VERBOSE_DEBUG */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define BULK_BUFFER_SIZE 16384 ++#define ACC_STRING_SIZE 256 ++ ++#define PROTOCOL_VERSION 2 ++ ++/* String IDs */ ++#define INTERFACE_STRING_INDEX 0 ++ ++/* number of tx and rx requests to allocate */ ++#define TX_REQ_MAX 4 ++#define RX_REQ_MAX 2 ++ ++struct acc_hid_dev { ++ struct list_head list; ++ struct hid_device *hid; ++ struct acc_dev *dev; ++ /* accessory defined ID */ ++ int id; ++ /* HID report descriptor */ ++ u8 *report_desc; ++ /* length of HID report descriptor */ ++ int report_desc_len; ++ /* number of bytes of report_desc we have received so far */ ++ int report_desc_offset; ++}; ++ ++struct acc_dev { ++ struct usb_function function; ++ struct usb_composite_dev *cdev; ++ spinlock_t lock; ++ ++ struct usb_ep *ep_in; ++ struct usb_ep *ep_out; ++ ++ /* set to 1 when we connect */ ++ int online:1; ++ /* Set to 1 when we disconnect. ++ * Not cleared until our file is closed. ++ */ ++ int disconnected:1; ++ ++ /* strings sent by the host */ ++ char manufacturer[ACC_STRING_SIZE]; ++ char model[ACC_STRING_SIZE]; ++ char description[ACC_STRING_SIZE]; ++ char version[ACC_STRING_SIZE]; ++ char uri[ACC_STRING_SIZE]; ++ char serial[ACC_STRING_SIZE]; ++ ++ /* for acc_complete_set_string */ ++ int string_index; ++ ++ /* set to 1 if we have a pending start request */ ++ int start_requested; ++ ++ int audio_mode; ++ ++ /* synchronize access to our device file */ ++ atomic_t open_excl; ++ ++ struct list_head tx_idle; ++ ++ wait_queue_head_t read_wq; ++ wait_queue_head_t write_wq; ++ struct usb_request *rx_req[RX_REQ_MAX]; ++ int rx_done; ++ ++ /* delayed work for handling ACCESSORY_START */ ++ struct delayed_work start_work; ++ ++ /* worker for registering and unregistering hid devices */ ++ struct work_struct hid_work; ++ ++ /* list of active HID devices */ ++ struct list_head hid_list; ++ ++ /* list of new HID devices to register */ ++ struct list_head new_hid_list; ++ ++ /* list of dead HID devices to unregister */ ++ struct list_head dead_hid_list; ++}; ++ ++static struct usb_interface_descriptor acc_interface_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0, ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, ++ .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, ++ .bInterfaceProtocol = 0, ++}; ++ ++static struct usb_endpoint_descriptor acc_highspeed_in_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor acc_highspeed_out_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor acc_fullspeed_in_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor acc_fullspeed_out_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *fs_acc_descs[] = { ++ (struct usb_descriptor_header *) &acc_interface_desc, ++ (struct usb_descriptor_header *) &acc_fullspeed_in_desc, ++ (struct usb_descriptor_header *) &acc_fullspeed_out_desc, ++ NULL, ++}; ++ ++static struct usb_descriptor_header *hs_acc_descs[] = { ++ (struct usb_descriptor_header *) &acc_interface_desc, ++ (struct usb_descriptor_header *) &acc_highspeed_in_desc, ++ (struct usb_descriptor_header *) &acc_highspeed_out_desc, ++ NULL, ++}; ++ ++static struct usb_string acc_string_defs[] = { ++ [INTERFACE_STRING_INDEX].s = "Android Accessory Interface", ++ { }, /* end of list */ ++}; ++ ++static struct usb_gadget_strings acc_string_table = { ++ .language = 0x0409, /* en-US */ ++ .strings = acc_string_defs, ++}; ++ ++static struct usb_gadget_strings *acc_strings[] = { ++ &acc_string_table, ++ NULL, ++}; ++ ++/* temporary variable used between acc_open() and acc_gadget_bind() */ ++static struct acc_dev *_acc_dev; ++ ++static inline struct acc_dev *func_to_dev(struct usb_function *f) ++{ ++ return container_of(f, struct acc_dev, function); ++} ++ ++static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size) ++{ ++ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); ++ if (!req) ++ return NULL; ++ ++ /* now allocate buffers for the requests */ ++ req->buf = kmalloc(buffer_size, GFP_KERNEL); ++ if (!req->buf) { ++ usb_ep_free_request(ep, req); ++ return NULL; ++ } ++ ++ return req; ++} ++ ++static void acc_request_free(struct usb_request *req, struct usb_ep *ep) ++{ ++ if (req) { ++ kfree(req->buf); ++ usb_ep_free_request(ep, req); ++ } ++} ++ ++/* add a request to the tail of a list */ ++static void req_put(struct acc_dev *dev, struct list_head *head, ++ struct usb_request *req) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ list_add_tail(&req->list, head); ++ spin_unlock_irqrestore(&dev->lock, flags); ++} ++ ++/* remove a request from the head of a list */ ++static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) ++{ ++ unsigned long flags; ++ struct usb_request *req; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (list_empty(head)) { ++ req = 0; ++ } else { ++ req = list_first_entry(head, struct usb_request, list); ++ list_del(&req->list); ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ return req; ++} ++ ++static void acc_set_disconnected(struct acc_dev *dev) ++{ ++ dev->online = 0; ++ dev->disconnected = 1; ++} ++ ++static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct acc_dev *dev = _acc_dev; ++ ++ if (req->status != 0) ++ acc_set_disconnected(dev); ++ ++ req_put(dev, &dev->tx_idle, req); ++ ++ wake_up(&dev->write_wq); ++} ++ ++static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct acc_dev *dev = _acc_dev; ++ ++ dev->rx_done = 1; ++ if (req->status != 0) ++ acc_set_disconnected(dev); ++ ++ wake_up(&dev->read_wq); ++} ++ ++static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct acc_dev *dev = ep->driver_data; ++ char *string_dest = NULL; ++ int length = req->actual; ++ ++ if (req->status != 0) { ++ pr_err("acc_complete_set_string, err %d\n", req->status); ++ return; ++ } ++ ++ switch (dev->string_index) { ++ case ACCESSORY_STRING_MANUFACTURER: ++ string_dest = dev->manufacturer; ++ break; ++ case ACCESSORY_STRING_MODEL: ++ string_dest = dev->model; ++ break; ++ case ACCESSORY_STRING_DESCRIPTION: ++ string_dest = dev->description; ++ break; ++ case ACCESSORY_STRING_VERSION: ++ string_dest = dev->version; ++ break; ++ case ACCESSORY_STRING_URI: ++ string_dest = dev->uri; ++ break; ++ case ACCESSORY_STRING_SERIAL: ++ string_dest = dev->serial; ++ break; ++ } ++ if (string_dest) { ++ unsigned long flags; ++ ++ if (length >= ACC_STRING_SIZE) ++ length = ACC_STRING_SIZE - 1; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ memcpy(string_dest, req->buf, length); ++ /* ensure zero termination */ ++ string_dest[length] = 0; ++ spin_unlock_irqrestore(&dev->lock, flags); ++ } else { ++ pr_err("unknown accessory string index %d\n", ++ dev->string_index); ++ } ++} ++ ++static void acc_complete_set_hid_report_desc(struct usb_ep *ep, ++ struct usb_request *req) ++{ ++ struct acc_hid_dev *hid = req->context; ++ struct acc_dev *dev = hid->dev; ++ int length = req->actual; ++ ++ if (req->status != 0) { ++ pr_err("acc_complete_set_hid_report_desc, err %d\n", ++ req->status); ++ return; ++ } ++ ++ memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length); ++ hid->report_desc_offset += length; ++ if (hid->report_desc_offset == hid->report_desc_len) { ++ /* After we have received the entire report descriptor ++ * we schedule work to initialize the HID device ++ */ ++ schedule_work(&dev->hid_work); ++ } ++} ++ ++static void acc_complete_send_hid_event(struct usb_ep *ep, ++ struct usb_request *req) ++{ ++ struct acc_hid_dev *hid = req->context; ++ int length = req->actual; ++ ++ if (req->status != 0) { ++ pr_err("acc_complete_send_hid_event, err %d\n", req->status); ++ return; ++ } ++ ++ hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1); ++} ++ ++static int acc_hid_parse(struct hid_device *hid) ++{ ++ struct acc_hid_dev *hdev = hid->driver_data; ++ ++ hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len); ++ return 0; ++} ++ ++static int acc_hid_start(struct hid_device *hid) ++{ ++ return 0; ++} ++ ++static void acc_hid_stop(struct hid_device *hid) ++{ ++} ++ ++static int acc_hid_open(struct hid_device *hid) ++{ ++ return 0; ++} ++ ++static void acc_hid_close(struct hid_device *hid) ++{ ++} ++ ++static struct hid_ll_driver acc_hid_ll_driver = { ++ .parse = acc_hid_parse, ++ .start = acc_hid_start, ++ .stop = acc_hid_stop, ++ .open = acc_hid_open, ++ .close = acc_hid_close, ++}; ++ ++static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev, ++ int id, int desc_len) ++{ ++ struct acc_hid_dev *hdev; ++ ++ hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC); ++ if (!hdev) ++ return NULL; ++ hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC); ++ if (!hdev->report_desc) { ++ kfree(hdev); ++ return NULL; ++ } ++ hdev->dev = dev; ++ hdev->id = id; ++ hdev->report_desc_len = desc_len; ++ ++ return hdev; ++} ++ ++static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id) ++{ ++ struct acc_hid_dev *hid; ++ ++ list_for_each_entry(hid, list, list) { ++ if (hid->id == id) ++ return hid; ++ } ++ return NULL; ++} ++ ++static int acc_register_hid(struct acc_dev *dev, int id, int desc_length) ++{ ++ struct acc_hid_dev *hid; ++ unsigned long flags; ++ ++ /* report descriptor length must be > 0 */ ++ if (desc_length <= 0) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ /* replace HID if one already exists with this ID */ ++ hid = acc_hid_get(&dev->hid_list, id); ++ if (!hid) ++ hid = acc_hid_get(&dev->new_hid_list, id); ++ if (hid) ++ list_move(&hid->list, &dev->dead_hid_list); ++ ++ hid = acc_hid_new(dev, id, desc_length); ++ if (!hid) { ++ spin_unlock_irqrestore(&dev->lock, flags); ++ return -ENOMEM; ++ } ++ ++ list_add(&hid->list, &dev->new_hid_list); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ /* schedule work to register the HID device */ ++ schedule_work(&dev->hid_work); ++ return 0; ++} ++ ++static int acc_unregister_hid(struct acc_dev *dev, int id) ++{ ++ struct acc_hid_dev *hid; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ hid = acc_hid_get(&dev->hid_list, id); ++ if (!hid) ++ hid = acc_hid_get(&dev->new_hid_list, id); ++ if (!hid) { ++ spin_unlock_irqrestore(&dev->lock, flags); ++ return -EINVAL; ++ } ++ ++ list_move(&hid->list, &dev->dead_hid_list); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ schedule_work(&dev->hid_work); ++ return 0; ++} ++ ++static int create_bulk_endpoints(struct acc_dev *dev, ++ struct usb_endpoint_descriptor *in_desc, ++ struct usb_endpoint_descriptor *out_desc) ++{ ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *req; ++ struct usb_ep *ep; ++ int i; ++ ++ DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); ++ ++ ep = usb_ep_autoconfig(cdev->gadget, in_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_in = ep; ++ ++ ep = usb_ep_autoconfig(cdev->gadget, out_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_out = ep; ++ ++ ep = usb_ep_autoconfig(cdev->gadget, out_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_out = ep; ++ ++ /* now allocate requests for our endpoints */ ++ for (i = 0; i < TX_REQ_MAX; i++) { ++ req = acc_request_new(dev->ep_in, BULK_BUFFER_SIZE); ++ if (!req) ++ goto fail; ++ req->complete = acc_complete_in; ++ req_put(dev, &dev->tx_idle, req); ++ } ++ for (i = 0; i < RX_REQ_MAX; i++) { ++ req = acc_request_new(dev->ep_out, BULK_BUFFER_SIZE); ++ if (!req) ++ goto fail; ++ req->complete = acc_complete_out; ++ dev->rx_req[i] = req; ++ } ++ ++ return 0; ++ ++fail: ++ pr_err("acc_bind() could not allocate requests\n"); ++ while ((req = req_get(dev, &dev->tx_idle))) ++ acc_request_free(req, dev->ep_in); ++ for (i = 0; i < RX_REQ_MAX; i++) ++ acc_request_free(dev->rx_req[i], dev->ep_out); ++ return -1; ++} ++ ++static ssize_t acc_read(struct file *fp, char __user *buf, ++ size_t count, loff_t *pos) ++{ ++ struct acc_dev *dev = fp->private_data; ++ struct usb_request *req; ++ int r = count, xfer; ++ int ret = 0; ++ ++ pr_debug("acc_read(%d)\n", count); ++ ++ if (dev->disconnected) ++ return -ENODEV; ++ ++ if (count > BULK_BUFFER_SIZE) ++ count = BULK_BUFFER_SIZE; ++ ++ /* we will block until we're online */ ++ pr_debug("acc_read: waiting for online\n"); ++ ret = wait_event_interruptible(dev->read_wq, dev->online); ++ if (ret < 0) { ++ r = ret; ++ goto done; ++ } ++ ++requeue_req: ++ /* queue a request */ ++ req = dev->rx_req[0]; ++ req->length = count; ++ dev->rx_done = 0; ++ ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); ++ if (ret < 0) { ++ r = -EIO; ++ goto done; ++ } else { ++ pr_debug("rx %p queue\n", req); ++ } ++ ++ /* wait for a request to complete */ ++ ret = wait_event_interruptible(dev->read_wq, dev->rx_done); ++ if (ret < 0) { ++ r = ret; ++ usb_ep_dequeue(dev->ep_out, req); ++ goto done; ++ } ++ if (dev->online) { ++ /* If we got a 0-len packet, throw it back and try again. */ ++ if (req->actual == 0) ++ goto requeue_req; ++ ++ pr_debug("rx %p %d\n", req, req->actual); ++ xfer = (req->actual < count) ? req->actual : count; ++ r = xfer; ++ if (copy_to_user(buf, req->buf, xfer)) ++ r = -EFAULT; ++ } else ++ r = -EIO; ++ ++done: ++ pr_debug("acc_read returning %d\n", r); ++ return r; ++} ++ ++static ssize_t acc_write(struct file *fp, const char __user *buf, ++ size_t count, loff_t *pos) ++{ ++ struct acc_dev *dev = fp->private_data; ++ struct usb_request *req = 0; ++ int r = count, xfer; ++ int ret; ++ ++ pr_debug("acc_write(%d)\n", count); ++ ++ if (!dev->online || dev->disconnected) ++ return -ENODEV; ++ ++ while (count > 0) { ++ if (!dev->online) { ++ pr_debug("acc_write dev->error\n"); ++ r = -EIO; ++ break; ++ } ++ ++ /* get an idle tx request to use */ ++ req = 0; ++ ret = wait_event_interruptible(dev->write_wq, ++ ((req = req_get(dev, &dev->tx_idle)) || !dev->online)); ++ if (!req) { ++ r = ret; ++ break; ++ } ++ ++ if (count > BULK_BUFFER_SIZE) ++ xfer = BULK_BUFFER_SIZE; ++ else ++ xfer = count; ++ if (copy_from_user(req->buf, buf, xfer)) { ++ r = -EFAULT; ++ break; ++ } ++ ++ req->length = xfer; ++ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); ++ if (ret < 0) { ++ pr_debug("acc_write: xfer error %d\n", ret); ++ r = -EIO; ++ break; ++ } ++ ++ buf += xfer; ++ count -= xfer; ++ ++ /* zero this so we don't try to free it on error exit */ ++ req = 0; ++ } ++ ++ if (req) ++ req_put(dev, &dev->tx_idle, req); ++ ++ pr_debug("acc_write returning %d\n", r); ++ return r; ++} ++ ++static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) ++{ ++ struct acc_dev *dev = fp->private_data; ++ char *src = NULL; ++ int ret; ++ ++ switch (code) { ++ case ACCESSORY_GET_STRING_MANUFACTURER: ++ src = dev->manufacturer; ++ break; ++ case ACCESSORY_GET_STRING_MODEL: ++ src = dev->model; ++ break; ++ case ACCESSORY_GET_STRING_DESCRIPTION: ++ src = dev->description; ++ break; ++ case ACCESSORY_GET_STRING_VERSION: ++ src = dev->version; ++ break; ++ case ACCESSORY_GET_STRING_URI: ++ src = dev->uri; ++ break; ++ case ACCESSORY_GET_STRING_SERIAL: ++ src = dev->serial; ++ break; ++ case ACCESSORY_IS_START_REQUESTED: ++ return dev->start_requested; ++ case ACCESSORY_GET_AUDIO_MODE: ++ return dev->audio_mode; ++ } ++ if (!src) ++ return -EINVAL; ++ ++ ret = strlen(src) + 1; ++ if (copy_to_user((void __user *)value, src, ret)) ++ ret = -EFAULT; ++ return ret; ++} ++ ++static int acc_open(struct inode *ip, struct file *fp) ++{ ++ printk(KERN_INFO "acc_open\n"); ++ if (atomic_xchg(&_acc_dev->open_excl, 1)) ++ return -EBUSY; ++ ++ _acc_dev->disconnected = 0; ++ fp->private_data = _acc_dev; ++ return 0; ++} ++ ++static int acc_release(struct inode *ip, struct file *fp) ++{ ++ printk(KERN_INFO "acc_release\n"); ++ ++ WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); ++ _acc_dev->disconnected = 0; ++ return 0; ++} ++ ++/* file operations for /dev/usb_accessory */ ++static const struct file_operations acc_fops = { ++ .owner = THIS_MODULE, ++ .read = acc_read, ++ .write = acc_write, ++ .unlocked_ioctl = acc_ioctl, ++ .open = acc_open, ++ .release = acc_release, ++}; ++ ++static int acc_hid_probe(struct hid_device *hdev, ++ const struct hid_device_id *id) ++{ ++ int ret; ++ ++ ret = hid_parse(hdev); ++ if (ret) ++ return ret; ++ return hid_hw_start(hdev, HID_CONNECT_DEFAULT); ++} ++ ++static struct miscdevice acc_device = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "usb_accessory", ++ .fops = &acc_fops, ++}; ++ ++static const struct hid_device_id acc_hid_table[] = { ++ { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) }, ++ { } ++}; ++ ++static struct hid_driver acc_hid_driver = { ++ .name = "USB accessory", ++ .id_table = acc_hid_table, ++ .probe = acc_hid_probe, ++}; ++ ++static int acc_ctrlrequest(struct usb_composite_dev *cdev, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ struct acc_dev *dev = _acc_dev; ++ int value = -EOPNOTSUPP; ++ struct acc_hid_dev *hid; ++ int offset; ++ u8 b_requestType = ctrl->bRequestType; ++ u8 b_request = ctrl->bRequest; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ unsigned long flags; ++ ++/* ++ printk(KERN_INFO "acc_ctrlrequest " ++ "%02x.%02x v%04x i%04x l%u\n", ++ b_requestType, b_request, ++ w_value, w_index, w_length); ++*/ ++ ++ if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { ++ if (b_request == ACCESSORY_START) { ++ dev->start_requested = 1; ++ schedule_delayed_work( ++ &dev->start_work, msecs_to_jiffies(10)); ++ value = 0; ++ } else if (b_request == ACCESSORY_SEND_STRING) { ++ dev->string_index = w_index; ++ cdev->gadget->ep0->driver_data = dev; ++ cdev->req->complete = acc_complete_set_string; ++ value = w_length; ++ } else if (b_request == ACCESSORY_SET_AUDIO_MODE && ++ w_index == 0 && w_length == 0) { ++ dev->audio_mode = w_value; ++ value = 0; ++ } else if (b_request == ACCESSORY_REGISTER_HID) { ++ value = acc_register_hid(dev, w_value, w_index); ++ } else if (b_request == ACCESSORY_UNREGISTER_HID) { ++ value = acc_unregister_hid(dev, w_value); ++ } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) { ++ spin_lock_irqsave(&dev->lock, flags); ++ hid = acc_hid_get(&dev->new_hid_list, w_value); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ if (!hid) { ++ value = -EINVAL; ++ goto err; ++ } ++ offset = w_index; ++ if (offset != hid->report_desc_offset ++ || offset + w_length > hid->report_desc_len) { ++ value = -EINVAL; ++ goto err; ++ } ++ cdev->req->context = hid; ++ cdev->req->complete = acc_complete_set_hid_report_desc; ++ value = w_length; ++ } else if (b_request == ACCESSORY_SEND_HID_EVENT) { ++ spin_lock_irqsave(&dev->lock, flags); ++ hid = acc_hid_get(&dev->hid_list, w_value); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ if (!hid) { ++ value = -EINVAL; ++ goto err; ++ } ++ cdev->req->context = hid; ++ cdev->req->complete = acc_complete_send_hid_event; ++ value = w_length; ++ } ++ } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { ++ if (b_request == ACCESSORY_GET_PROTOCOL) { ++ *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; ++ value = sizeof(u16); ++ ++ /* clear any string left over from a previous session */ ++ memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); ++ memset(dev->model, 0, sizeof(dev->model)); ++ memset(dev->description, 0, sizeof(dev->description)); ++ memset(dev->version, 0, sizeof(dev->version)); ++ memset(dev->uri, 0, sizeof(dev->uri)); ++ memset(dev->serial, 0, sizeof(dev->serial)); ++ dev->start_requested = 0; ++ dev->audio_mode = 0; ++ } ++ } ++ ++ if (value >= 0) { ++ cdev->req->zero = 0; ++ cdev->req->length = value; ++ value = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); ++ if (value < 0) ++ ERROR(cdev, "%s setup response queue error\n", ++ __func__); ++ } ++ ++err: ++ if (value == -EOPNOTSUPP) ++ VDBG(cdev, ++ "unknown class-specific control req " ++ "%02x.%02x v%04x i%04x l%u\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ return value; ++} ++ ++static int ++acc_function_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct acc_dev *dev = func_to_dev(f); ++ int id; ++ int ret; ++ ++ DBG(cdev, "acc_function_bind dev: %p\n", dev); ++ ++ ret = hid_register_driver(&acc_hid_driver); ++ if (ret) ++ return ret; ++ ++ dev->start_requested = 0; ++ ++ /* allocate interface ID(s) */ ++ id = usb_interface_id(c, f); ++ if (id < 0) ++ return id; ++ acc_interface_desc.bInterfaceNumber = id; ++ ++ /* allocate endpoints */ ++ ret = create_bulk_endpoints(dev, &acc_fullspeed_in_desc, ++ &acc_fullspeed_out_desc); ++ if (ret) ++ return ret; ++ ++ /* support high speed hardware */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ acc_highspeed_in_desc.bEndpointAddress = ++ acc_fullspeed_in_desc.bEndpointAddress; ++ acc_highspeed_out_desc.bEndpointAddress = ++ acc_fullspeed_out_desc.bEndpointAddress; ++ } ++ ++ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ f->name, dev->ep_in->name, dev->ep_out->name); ++ return 0; ++} ++ ++static void ++kill_all_hid_devices(struct acc_dev *dev) ++{ ++ struct acc_hid_dev *hid; ++ struct list_head *entry, *temp; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ list_for_each_safe(entry, temp, &dev->hid_list) { ++ hid = list_entry(entry, struct acc_hid_dev, list); ++ list_del(&hid->list); ++ list_add(&hid->list, &dev->dead_hid_list); ++ } ++ list_for_each_safe(entry, temp, &dev->new_hid_list) { ++ hid = list_entry(entry, struct acc_hid_dev, list); ++ list_del(&hid->list); ++ list_add(&hid->list, &dev->dead_hid_list); ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ schedule_work(&dev->hid_work); ++} ++ ++static void ++acc_hid_unbind(struct acc_dev *dev) ++{ ++ hid_unregister_driver(&acc_hid_driver); ++ kill_all_hid_devices(dev); ++} ++ ++static void ++acc_function_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct acc_dev *dev = func_to_dev(f); ++ struct usb_request *req; ++ int i; ++ ++ while ((req = req_get(dev, &dev->tx_idle))) ++ acc_request_free(req, dev->ep_in); ++ for (i = 0; i < RX_REQ_MAX; i++) ++ acc_request_free(dev->rx_req[i], dev->ep_out); ++ ++ acc_hid_unbind(dev); ++} ++ ++static void acc_start_work(struct work_struct *data) ++{ ++ char *envp[2] = { "ACCESSORY=START", NULL }; ++ kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); ++} ++ ++static int acc_hid_init(struct acc_hid_dev *hdev) ++{ ++ struct hid_device *hid; ++ int ret; ++ ++ hid = hid_allocate_device(); ++ if (IS_ERR(hid)) ++ return PTR_ERR(hid); ++ ++ hid->ll_driver = &acc_hid_ll_driver; ++ hid->dev.parent = acc_device.this_device; ++ ++ hid->bus = BUS_USB; ++ hid->vendor = HID_ANY_ID; ++ hid->product = HID_ANY_ID; ++ hid->driver_data = hdev; ++ ret = hid_add_device(hid); ++ if (ret) { ++ pr_err("can't add hid device: %d\n", ret); ++ hid_destroy_device(hid); ++ return ret; ++ } ++ ++ hdev->hid = hid; ++ return 0; ++} ++ ++static void acc_hid_delete(struct acc_hid_dev *hid) ++{ ++ kfree(hid->report_desc); ++ kfree(hid); ++} ++ ++static void acc_hid_work(struct work_struct *data) ++{ ++ struct acc_dev *dev = _acc_dev; ++ struct list_head *entry, *temp; ++ struct acc_hid_dev *hid; ++ struct list_head new_list, dead_list; ++ unsigned long flags; ++ ++ INIT_LIST_HEAD(&new_list); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* copy hids that are ready for initialization to new_list */ ++ list_for_each_safe(entry, temp, &dev->new_hid_list) { ++ hid = list_entry(entry, struct acc_hid_dev, list); ++ if (hid->report_desc_offset == hid->report_desc_len) ++ list_move(&hid->list, &new_list); ++ } ++ ++ if (list_empty(&dev->dead_hid_list)) { ++ INIT_LIST_HEAD(&dead_list); ++ } else { ++ /* move all of dev->dead_hid_list to dead_list */ ++ dead_list.prev = dev->dead_hid_list.prev; ++ dead_list.next = dev->dead_hid_list.next; ++ dead_list.next->prev = &dead_list; ++ dead_list.prev->next = &dead_list; ++ INIT_LIST_HEAD(&dev->dead_hid_list); ++ } ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ /* register new HID devices */ ++ list_for_each_safe(entry, temp, &new_list) { ++ hid = list_entry(entry, struct acc_hid_dev, list); ++ if (acc_hid_init(hid)) { ++ pr_err("can't add HID device %p\n", hid); ++ acc_hid_delete(hid); ++ } else { ++ spin_lock_irqsave(&dev->lock, flags); ++ list_move(&hid->list, &dev->hid_list); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ } ++ } ++ ++ /* remove dead HID devices */ ++ list_for_each_safe(entry, temp, &dead_list) { ++ hid = list_entry(entry, struct acc_hid_dev, list); ++ list_del(&hid->list); ++ if (hid->hid) ++ hid_destroy_device(hid->hid); ++ acc_hid_delete(hid); ++ } ++} ++ ++static int acc_function_set_alt(struct usb_function *f, ++ unsigned intf, unsigned alt) ++{ ++ struct acc_dev *dev = func_to_dev(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ int ret; ++ ++ DBG(cdev, "acc_function_set_alt intf: %d alt: %d\n", intf, alt); ++ ++ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); ++ if (ret) ++ return ret; ++ ++ ret = usb_ep_enable(dev->ep_in); ++ if (ret) ++ return ret; ++ ++ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); ++ if (ret) ++ return ret; ++ ++ ret = usb_ep_enable(dev->ep_out); ++ if (ret) { ++ usb_ep_disable(dev->ep_in); ++ return ret; ++ } ++ ++ dev->online = 1; ++ ++ /* readers may be blocked waiting for us to go online */ ++ wake_up(&dev->read_wq); ++ return 0; ++} ++ ++static void acc_function_disable(struct usb_function *f) ++{ ++ struct acc_dev *dev = func_to_dev(f); ++ struct usb_composite_dev *cdev = dev->cdev; ++ ++ DBG(cdev, "acc_function_disable\n"); ++ acc_set_disconnected(dev); ++ usb_ep_disable(dev->ep_in); ++ usb_ep_disable(dev->ep_out); ++ ++ /* readers may be blocked waiting for us to go online */ ++ wake_up(&dev->read_wq); ++ ++ VDBG(cdev, "%s disabled\n", dev->function.name); ++} ++ ++static int acc_bind_config(struct usb_configuration *c) ++{ ++ struct acc_dev *dev = _acc_dev; ++ int ret; ++ ++ printk(KERN_INFO "acc_bind_config\n"); ++ ++ /* allocate a string ID for our interface */ ++ if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { ++ ret = usb_string_id(c->cdev); ++ if (ret < 0) ++ return ret; ++ acc_string_defs[INTERFACE_STRING_INDEX].id = ret; ++ acc_interface_desc.iInterface = ret; ++ } ++ ++ dev->cdev = c->cdev; ++ dev->function.name = "accessory"; ++ dev->function.strings = acc_strings, ++ dev->function.descriptors = fs_acc_descs; ++ dev->function.hs_descriptors = hs_acc_descs; ++ dev->function.bind = acc_function_bind; ++ dev->function.unbind = acc_function_unbind; ++ dev->function.set_alt = acc_function_set_alt; ++ dev->function.disable = acc_function_disable; ++ ++ return usb_add_function(c, &dev->function); ++} ++ ++static int acc_setup(void) ++{ ++ struct acc_dev *dev; ++ int ret; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ spin_lock_init(&dev->lock); ++ init_waitqueue_head(&dev->read_wq); ++ init_waitqueue_head(&dev->write_wq); ++ atomic_set(&dev->open_excl, 0); ++ INIT_LIST_HEAD(&dev->tx_idle); ++ INIT_LIST_HEAD(&dev->hid_list); ++ INIT_LIST_HEAD(&dev->new_hid_list); ++ INIT_LIST_HEAD(&dev->dead_hid_list); ++ INIT_DELAYED_WORK(&dev->start_work, acc_start_work); ++ INIT_WORK(&dev->hid_work, acc_hid_work); ++ ++ /* _acc_dev must be set before calling usb_gadget_register_driver */ ++ _acc_dev = dev; ++ ++ ret = misc_register(&acc_device); ++ if (ret) ++ goto err; ++ ++ return 0; ++ ++err: ++ kfree(dev); ++ pr_err("USB accessory gadget driver failed to initialize\n"); ++ return ret; ++} ++ ++static void acc_disconnect(void) ++{ ++ /* unregister all HID devices if USB is disconnected */ ++ kill_all_hid_devices(_acc_dev); ++} ++ ++static void acc_cleanup(void) ++{ ++ misc_deregister(&acc_device); ++ kfree(_acc_dev); ++ _acc_dev = NULL; ++} +diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c +new file mode 100644 +index 00000000..1629ffb5 +--- /dev/null ++++ b/drivers/usb/gadget/f_adb.c +@@ -0,0 +1,619 @@ ++/* ++ * Gadget Driver for Android ADB ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ADB_BULK_BUFFER_SIZE 4096 ++ ++/* number of tx requests to allocate */ ++#define TX_REQ_MAX 4 ++ ++static const char adb_shortname[] = "android_adb"; ++ ++struct adb_dev { ++ struct usb_function function; ++ struct usb_composite_dev *cdev; ++ spinlock_t lock; ++ ++ struct usb_ep *ep_in; ++ struct usb_ep *ep_out; ++ ++ int online; ++ int error; ++ ++ atomic_t read_excl; ++ atomic_t write_excl; ++ atomic_t open_excl; ++ ++ struct list_head tx_idle; ++ ++ wait_queue_head_t read_wq; ++ wait_queue_head_t write_wq; ++ struct usb_request *rx_req; ++ int rx_done; ++}; ++ ++static struct usb_interface_descriptor adb_interface_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0, ++ .bNumEndpoints = 2, ++ .bInterfaceClass = 0xFF, ++ .bInterfaceSubClass = 0x42, ++ .bInterfaceProtocol = 1, ++}; ++ ++static struct usb_endpoint_descriptor adb_highspeed_in_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor adb_highspeed_out_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor adb_fullspeed_in_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor adb_fullspeed_out_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *fs_adb_descs[] = { ++ (struct usb_descriptor_header *) &adb_interface_desc, ++ (struct usb_descriptor_header *) &adb_fullspeed_in_desc, ++ (struct usb_descriptor_header *) &adb_fullspeed_out_desc, ++ NULL, ++}; ++ ++static struct usb_descriptor_header *hs_adb_descs[] = { ++ (struct usb_descriptor_header *) &adb_interface_desc, ++ (struct usb_descriptor_header *) &adb_highspeed_in_desc, ++ (struct usb_descriptor_header *) &adb_highspeed_out_desc, ++ NULL, ++}; ++ ++static void adb_ready_callback(void); ++static void adb_closed_callback(void); ++ ++/* temporary variable used between adb_open() and adb_gadget_bind() */ ++static struct adb_dev *_adb_dev; ++ ++static inline struct adb_dev *func_to_adb(struct usb_function *f) ++{ ++ return container_of(f, struct adb_dev, function); ++} ++ ++ ++static struct usb_request *adb_request_new(struct usb_ep *ep, int buffer_size) ++{ ++ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); ++ if (!req) ++ return NULL; ++ ++ /* now allocate buffers for the requests */ ++ req->buf = kmalloc(buffer_size, GFP_KERNEL); ++ if (!req->buf) { ++ usb_ep_free_request(ep, req); ++ return NULL; ++ } ++ ++ return req; ++} ++ ++static void adb_request_free(struct usb_request *req, struct usb_ep *ep) ++{ ++ if (req) { ++ kfree(req->buf); ++ usb_ep_free_request(ep, req); ++ } ++} ++ ++static inline int adb_lock(atomic_t *excl) ++{ ++ if (atomic_inc_return(excl) == 1) { ++ return 0; ++ } else { ++ atomic_dec(excl); ++ return -1; ++ } ++} ++ ++static inline void adb_unlock(atomic_t *excl) ++{ ++ atomic_dec(excl); ++} ++ ++/* add a request to the tail of a list */ ++void adb_req_put(struct adb_dev *dev, struct list_head *head, ++ struct usb_request *req) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ list_add_tail(&req->list, head); ++ spin_unlock_irqrestore(&dev->lock, flags); ++} ++ ++/* remove a request from the head of a list */ ++struct usb_request *adb_req_get(struct adb_dev *dev, struct list_head *head) ++{ ++ unsigned long flags; ++ struct usb_request *req; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (list_empty(head)) { ++ req = 0; ++ } else { ++ req = list_first_entry(head, struct usb_request, list); ++ list_del(&req->list); ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ return req; ++} ++ ++static void adb_complete_in(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct adb_dev *dev = _adb_dev; ++ ++ if (req->status != 0) ++ dev->error = 1; ++ ++ adb_req_put(dev, &dev->tx_idle, req); ++ ++ wake_up(&dev->write_wq); ++} ++ ++static void adb_complete_out(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct adb_dev *dev = _adb_dev; ++ ++ dev->rx_done = 1; ++ if (req->status != 0 && req->status != -ECONNRESET) ++ dev->error = 1; ++ ++ wake_up(&dev->read_wq); ++} ++ ++static int adb_create_bulk_endpoints(struct adb_dev *dev, ++ struct usb_endpoint_descriptor *in_desc, ++ struct usb_endpoint_descriptor *out_desc) ++{ ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *req; ++ struct usb_ep *ep; ++ int i; ++ ++ DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); ++ ++ ep = usb_ep_autoconfig(cdev->gadget, in_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_in = ep; ++ ++ ep = usb_ep_autoconfig(cdev->gadget, out_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for adb ep_out got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_out = ep; ++ ++ /* now allocate requests for our endpoints */ ++ req = adb_request_new(dev->ep_out, ADB_BULK_BUFFER_SIZE); ++ if (!req) ++ goto fail; ++ req->complete = adb_complete_out; ++ dev->rx_req = req; ++ ++ for (i = 0; i < TX_REQ_MAX; i++) { ++ req = adb_request_new(dev->ep_in, ADB_BULK_BUFFER_SIZE); ++ if (!req) ++ goto fail; ++ req->complete = adb_complete_in; ++ adb_req_put(dev, &dev->tx_idle, req); ++ } ++ ++ return 0; ++ ++fail: ++ printk(KERN_ERR "adb_bind() could not allocate requests\n"); ++ return -1; ++} ++ ++static ssize_t adb_read(struct file *fp, char __user *buf, ++ size_t count, loff_t *pos) ++{ ++ struct adb_dev *dev = fp->private_data; ++ struct usb_request *req; ++ int r = count, xfer; ++ int ret; ++ ++ pr_debug("adb_read(%d)\n", count); ++ if (!_adb_dev) ++ return -ENODEV; ++ ++ if (count > ADB_BULK_BUFFER_SIZE) ++ return -EINVAL; ++ ++ if (adb_lock(&dev->read_excl)) ++ return -EBUSY; ++ ++ /* we will block until we're online */ ++ while (!(dev->online || dev->error)) { ++ pr_debug("adb_read: waiting for online state\n"); ++ ret = wait_event_interruptible(dev->read_wq, ++ (dev->online || dev->error)); ++ if (ret < 0) { ++ adb_unlock(&dev->read_excl); ++ return ret; ++ } ++ } ++ if (dev->error) { ++ r = -EIO; ++ goto done; ++ } ++ ++requeue_req: ++ /* queue a request */ ++ req = dev->rx_req; ++ req->length = count; ++ dev->rx_done = 0; ++ ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC); ++ if (ret < 0) { ++ pr_debug("adb_read: failed to queue req %p (%d)\n", req, ret); ++ r = -EIO; ++ dev->error = 1; ++ goto done; ++ } else { ++ pr_debug("rx %p queue\n", req); ++ } ++ ++ /* wait for a request to complete */ ++ ret = wait_event_interruptible(dev->read_wq, dev->rx_done); ++ if (ret < 0) { ++ if (ret != -ERESTARTSYS) ++ dev->error = 1; ++ r = ret; ++ usb_ep_dequeue(dev->ep_out, req); ++ goto done; ++ } ++ if (!dev->error) { ++ /* If we got a 0-len packet, throw it back and try again. */ ++ if (req->actual == 0) ++ goto requeue_req; ++ ++ pr_debug("rx %p %d\n", req, req->actual); ++ xfer = (req->actual < count) ? req->actual : count; ++ if (copy_to_user(buf, req->buf, xfer)) ++ r = -EFAULT; ++ ++ } else ++ r = -EIO; ++ ++done: ++ adb_unlock(&dev->read_excl); ++ pr_debug("adb_read returning %d\n", r); ++ return r; ++} ++ ++static ssize_t adb_write(struct file *fp, const char __user *buf, ++ size_t count, loff_t *pos) ++{ ++ struct adb_dev *dev = fp->private_data; ++ struct usb_request *req = 0; ++ int r = count, xfer; ++ int ret; ++ ++ if (!_adb_dev) ++ return -ENODEV; ++ pr_debug("adb_write(%d)\n", count); ++ ++ if (adb_lock(&dev->write_excl)) ++ return -EBUSY; ++ ++ while (count > 0) { ++ if (dev->error) { ++ pr_debug("adb_write dev->error\n"); ++ r = -EIO; ++ break; ++ } ++ ++ /* get an idle tx request to use */ ++ req = 0; ++ ret = wait_event_interruptible(dev->write_wq, ++ (req = adb_req_get(dev, &dev->tx_idle)) || dev->error); ++ ++ if (ret < 0) { ++ r = ret; ++ break; ++ } ++ ++ if (req != 0) { ++ if (count > ADB_BULK_BUFFER_SIZE) ++ xfer = ADB_BULK_BUFFER_SIZE; ++ else ++ xfer = count; ++ if (copy_from_user(req->buf, buf, xfer)) { ++ r = -EFAULT; ++ break; ++ } ++ ++ req->length = xfer; ++ ret = usb_ep_queue(dev->ep_in, req, GFP_ATOMIC); ++ if (ret < 0) { ++ pr_debug("adb_write: xfer error %d\n", ret); ++ dev->error = 1; ++ r = -EIO; ++ break; ++ } ++ ++ buf += xfer; ++ count -= xfer; ++ ++ /* zero this so we don't try to free it on error exit */ ++ req = 0; ++ } ++ } ++ ++ if (req) ++ adb_req_put(dev, &dev->tx_idle, req); ++ ++ adb_unlock(&dev->write_excl); ++ pr_debug("adb_write returning %d\n", r); ++ return r; ++} ++ ++static int adb_open(struct inode *ip, struct file *fp) ++{ ++ pr_info("adb_open\n"); ++ if (!_adb_dev) ++ return -ENODEV; ++ ++ if (adb_lock(&_adb_dev->open_excl)) ++ return -EBUSY; ++ ++ fp->private_data = _adb_dev; ++ ++ /* clear the error latch */ ++ _adb_dev->error = 0; ++ ++ adb_ready_callback(); ++ ++ return 0; ++} ++ ++static int adb_release(struct inode *ip, struct file *fp) ++{ ++ pr_info("adb_release\n"); ++ ++ adb_closed_callback(); ++ ++ adb_unlock(&_adb_dev->open_excl); ++ return 0; ++} ++ ++/* file operations for ADB device /dev/android_adb */ ++static const struct file_operations adb_fops = { ++ .owner = THIS_MODULE, ++ .read = adb_read, ++ .write = adb_write, ++ .open = adb_open, ++ .release = adb_release, ++}; ++ ++static struct miscdevice adb_device = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = adb_shortname, ++ .fops = &adb_fops, ++}; ++ ++ ++ ++ ++static int ++adb_function_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct adb_dev *dev = func_to_adb(f); ++ int id; ++ int ret; ++ ++ dev->cdev = cdev; ++ DBG(cdev, "adb_function_bind dev: %p\n", dev); ++ ++ /* allocate interface ID(s) */ ++ id = usb_interface_id(c, f); ++ if (id < 0) ++ return id; ++ adb_interface_desc.bInterfaceNumber = id; ++ ++ /* allocate endpoints */ ++ ret = adb_create_bulk_endpoints(dev, &adb_fullspeed_in_desc, ++ &adb_fullspeed_out_desc); ++ if (ret) ++ return ret; ++ ++ /* support high speed hardware */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ adb_highspeed_in_desc.bEndpointAddress = ++ adb_fullspeed_in_desc.bEndpointAddress; ++ adb_highspeed_out_desc.bEndpointAddress = ++ adb_fullspeed_out_desc.bEndpointAddress; ++ } ++ ++ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ f->name, dev->ep_in->name, dev->ep_out->name); ++ return 0; ++} ++ ++static void ++adb_function_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct adb_dev *dev = func_to_adb(f); ++ struct usb_request *req; ++ ++ ++ dev->online = 0; ++ dev->error = 1; ++ ++ wake_up(&dev->read_wq); ++ ++ adb_request_free(dev->rx_req, dev->ep_out); ++ while ((req = adb_req_get(dev, &dev->tx_idle))) ++ adb_request_free(req, dev->ep_in); ++} ++ ++static int adb_function_set_alt(struct usb_function *f, ++ unsigned intf, unsigned alt) ++{ ++ struct adb_dev *dev = func_to_adb(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ int ret; ++ ++ DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt); ++ ++ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); ++ if (ret) ++ return ret; ++ ++ ret = usb_ep_enable(dev->ep_in); ++ if (ret) ++ return ret; ++ ++ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); ++ if (ret) ++ return ret; ++ ++ ret = usb_ep_enable(dev->ep_out); ++ if (ret) { ++ usb_ep_disable(dev->ep_in); ++ return ret; ++ } ++ dev->online = 1; ++ ++ /* readers may be blocked waiting for us to go online */ ++ wake_up(&dev->read_wq); ++ return 0; ++} ++ ++static void adb_function_disable(struct usb_function *f) ++{ ++ struct adb_dev *dev = func_to_adb(f); ++ struct usb_composite_dev *cdev = dev->cdev; ++ ++ DBG(cdev, "adb_function_disable cdev %p\n", cdev); ++ dev->online = 0; ++ dev->error = 1; ++ usb_ep_disable(dev->ep_in); ++ usb_ep_disable(dev->ep_out); ++ ++ /* readers may be blocked waiting for us to go online */ ++ wake_up(&dev->read_wq); ++ ++ VDBG(cdev, "%s disabled\n", dev->function.name); ++} ++ ++static int adb_bind_config(struct usb_configuration *c) ++{ ++ struct adb_dev *dev = _adb_dev; ++ ++ printk(KERN_INFO "adb_bind_config\n"); ++ ++ dev->cdev = c->cdev; ++ dev->function.name = "adb"; ++ dev->function.descriptors = fs_adb_descs; ++ dev->function.hs_descriptors = hs_adb_descs; ++ dev->function.bind = adb_function_bind; ++ dev->function.unbind = adb_function_unbind; ++ dev->function.set_alt = adb_function_set_alt; ++ dev->function.disable = adb_function_disable; ++ ++ return usb_add_function(c, &dev->function); ++} ++ ++static int adb_setup(void) ++{ ++ struct adb_dev *dev; ++ int ret; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ spin_lock_init(&dev->lock); ++ ++ init_waitqueue_head(&dev->read_wq); ++ init_waitqueue_head(&dev->write_wq); ++ ++ atomic_set(&dev->open_excl, 0); ++ atomic_set(&dev->read_excl, 0); ++ atomic_set(&dev->write_excl, 0); ++ ++ INIT_LIST_HEAD(&dev->tx_idle); ++ ++ _adb_dev = dev; ++ ++ ret = misc_register(&adb_device); ++ if (ret) ++ goto err; ++ ++ return 0; ++ ++err: ++ kfree(dev); ++ printk(KERN_ERR "adb gadget driver failed to initialize\n"); ++ return ret; ++} ++ ++static void adb_cleanup(void) ++{ ++ misc_deregister(&adb_device); ++ ++ kfree(_adb_dev); ++ _adb_dev = NULL; ++} +diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c +new file mode 100644 +index 00000000..c757409e +--- /dev/null ++++ b/drivers/usb/gadget/f_audio_source.c +@@ -0,0 +1,828 @@ ++/* ++ * Gadget Function Driver for USB audio source device ++ * ++ * Copyright (C) 2012 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SAMPLE_RATE 44100 ++#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) ++ ++#define IN_EP_MAX_PACKET_SIZE 384 ++ ++/* Number of requests to allocate */ ++#define IN_EP_REQ_COUNT 4 ++ ++#define AUDIO_AC_INTERFACE 0 ++#define AUDIO_AS_INTERFACE 1 ++#define AUDIO_NUM_INTERFACES 2 ++ ++/* B.3.1 Standard AC Interface Descriptor */ ++static struct usb_interface_descriptor ac_interface_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bNumEndpoints = 0, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, ++}; ++ ++DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); ++ ++#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES) ++/* 1 input terminal, 1 output terminal and 1 feature unit */ ++#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ ++ + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \ ++ + UAC_DT_FEATURE_UNIT_SIZE(0)) ++/* B.3.2 Class-Specific AC Interface Descriptor */ ++static struct uac1_ac_header_descriptor_2 ac_header_desc = { ++ .bLength = UAC_DT_AC_HEADER_LENGTH, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_HEADER, ++ .bcdADC = __constant_cpu_to_le16(0x0100), ++ .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), ++ .bInCollection = AUDIO_NUM_INTERFACES, ++ .baInterfaceNr = { ++ [0] = AUDIO_AC_INTERFACE, ++ [1] = AUDIO_AS_INTERFACE, ++ } ++}; ++ ++#define INPUT_TERMINAL_ID 1 ++static struct uac_input_terminal_descriptor input_terminal_desc = { ++ .bLength = UAC_DT_INPUT_TERMINAL_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_INPUT_TERMINAL, ++ .bTerminalID = INPUT_TERMINAL_ID, ++ .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE, ++ .bAssocTerminal = 0, ++ .wChannelConfig = 0x3, ++}; ++ ++DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); ++ ++#define FEATURE_UNIT_ID 2 ++static struct uac_feature_unit_descriptor_0 feature_unit_desc = { ++ .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_FEATURE_UNIT, ++ .bUnitID = FEATURE_UNIT_ID, ++ .bSourceID = INPUT_TERMINAL_ID, ++ .bControlSize = 2, ++}; ++ ++#define OUTPUT_TERMINAL_ID 3 ++static struct uac1_output_terminal_descriptor output_terminal_desc = { ++ .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, ++ .bTerminalID = OUTPUT_TERMINAL_ID, ++ .wTerminalType = UAC_TERMINAL_STREAMING, ++ .bAssocTerminal = FEATURE_UNIT_ID, ++ .bSourceID = FEATURE_UNIT_ID, ++}; ++ ++/* B.4.1 Standard AS Interface Descriptor */ ++static struct usb_interface_descriptor as_interface_alt_0_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 0, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, ++}; ++ ++static struct usb_interface_descriptor as_interface_alt_1_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bAlternateSetting = 1, ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, ++}; ++ ++/* B.4.2 Class-Specific AS Interface Descriptor */ ++static struct uac1_as_header_descriptor as_header_desc = { ++ .bLength = UAC_DT_AS_HEADER_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_AS_GENERAL, ++ .bTerminalLink = INPUT_TERMINAL_ID, ++ .bDelay = 1, ++ .wFormatTag = UAC_FORMAT_TYPE_I_PCM, ++}; ++ ++DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); ++ ++static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { ++ .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_FORMAT_TYPE, ++ .bFormatType = UAC_FORMAT_TYPE_I, ++ .bSubframeSize = 2, ++ .bBitResolution = 16, ++ .bSamFreqType = 1, ++}; ++ ++/* Standard ISO IN Endpoint Descriptor for highspeed */ ++static struct usb_endpoint_descriptor hs_as_in_ep_desc = { ++ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_SYNC_SYNC ++ | USB_ENDPOINT_XFER_ISOC, ++ .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), ++ .bInterval = 4, /* poll 1 per millisecond */ ++}; ++ ++/* Standard ISO IN Endpoint Descriptor for highspeed */ ++static struct usb_endpoint_descriptor fs_as_in_ep_desc = { ++ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_SYNC_SYNC ++ | USB_ENDPOINT_XFER_ISOC, ++ .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), ++ .bInterval = 1, /* poll 1 per millisecond */ ++}; ++ ++/* Class-specific AS ISO OUT Endpoint Descriptor */ ++static struct uac_iso_endpoint_descriptor as_iso_in_desc = { ++ .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, ++ .bDescriptorType = USB_DT_CS_ENDPOINT, ++ .bDescriptorSubtype = UAC_EP_GENERAL, ++ .bmAttributes = 1, ++ .bLockDelayUnits = 1, ++ .wLockDelay = __constant_cpu_to_le16(1), ++}; ++ ++static struct usb_descriptor_header *hs_audio_desc[] = { ++ (struct usb_descriptor_header *)&ac_interface_desc, ++ (struct usb_descriptor_header *)&ac_header_desc, ++ ++ (struct usb_descriptor_header *)&input_terminal_desc, ++ (struct usb_descriptor_header *)&output_terminal_desc, ++ (struct usb_descriptor_header *)&feature_unit_desc, ++ ++ (struct usb_descriptor_header *)&as_interface_alt_0_desc, ++ (struct usb_descriptor_header *)&as_interface_alt_1_desc, ++ (struct usb_descriptor_header *)&as_header_desc, ++ ++ (struct usb_descriptor_header *)&as_type_i_desc, ++ ++ (struct usb_descriptor_header *)&hs_as_in_ep_desc, ++ (struct usb_descriptor_header *)&as_iso_in_desc, ++ NULL, ++}; ++ ++static struct usb_descriptor_header *fs_audio_desc[] = { ++ (struct usb_descriptor_header *)&ac_interface_desc, ++ (struct usb_descriptor_header *)&ac_header_desc, ++ ++ (struct usb_descriptor_header *)&input_terminal_desc, ++ (struct usb_descriptor_header *)&output_terminal_desc, ++ (struct usb_descriptor_header *)&feature_unit_desc, ++ ++ (struct usb_descriptor_header *)&as_interface_alt_0_desc, ++ (struct usb_descriptor_header *)&as_interface_alt_1_desc, ++ (struct usb_descriptor_header *)&as_header_desc, ++ ++ (struct usb_descriptor_header *)&as_type_i_desc, ++ ++ (struct usb_descriptor_header *)&fs_as_in_ep_desc, ++ (struct usb_descriptor_header *)&as_iso_in_desc, ++ NULL, ++}; ++ ++static struct snd_pcm_hardware audio_hw_info = { ++ .info = SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_BATCH | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER, ++ ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ .channels_min = 2, ++ .channels_max = 2, ++ .rate_min = SAMPLE_RATE, ++ .rate_max = SAMPLE_RATE, ++ ++ .buffer_bytes_max = 1024 * 1024, ++ .period_bytes_min = 64, ++ .period_bytes_max = 512 * 1024, ++ .periods_min = 2, ++ .periods_max = 1024, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++struct audio_source_config { ++ int card; ++ int device; ++}; ++ ++struct audio_dev { ++ struct usb_function func; ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ struct snd_pcm_substream *substream; ++ ++ struct list_head idle_reqs; ++ struct usb_ep *in_ep; ++ ++ spinlock_t lock; ++ ++ /* beginning, end and current position in our buffer */ ++ void *buffer_start; ++ void *buffer_end; ++ void *buffer_pos; ++ ++ /* byte size of a "period" */ ++ unsigned int period; ++ /* bytes sent since last call to snd_pcm_period_elapsed */ ++ unsigned int period_offset; ++ /* time we started playing */ ++ ktime_t start_time; ++ /* number of frames sent since start_time */ ++ s64 frames_sent; ++}; ++ ++static inline struct audio_dev *func_to_audio(struct usb_function *f) ++{ ++ return container_of(f, struct audio_dev, func); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size) ++{ ++ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); ++ if (!req) ++ return NULL; ++ ++ req->buf = kmalloc(buffer_size, GFP_KERNEL); ++ if (!req->buf) { ++ usb_ep_free_request(ep, req); ++ return NULL; ++ } ++ req->length = buffer_size; ++ return req; ++} ++ ++static void audio_request_free(struct usb_request *req, struct usb_ep *ep) ++{ ++ if (req) { ++ kfree(req->buf); ++ usb_ep_free_request(ep, req); ++ } ++} ++ ++static void audio_req_put(struct audio_dev *audio, struct usb_request *req) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&audio->lock, flags); ++ list_add_tail(&req->list, &audio->idle_reqs); ++ spin_unlock_irqrestore(&audio->lock, flags); ++} ++ ++static struct usb_request *audio_req_get(struct audio_dev *audio) ++{ ++ unsigned long flags; ++ struct usb_request *req; ++ ++ spin_lock_irqsave(&audio->lock, flags); ++ if (list_empty(&audio->idle_reqs)) { ++ req = 0; ++ } else { ++ req = list_first_entry(&audio->idle_reqs, struct usb_request, ++ list); ++ list_del(&req->list); ++ } ++ spin_unlock_irqrestore(&audio->lock, flags); ++ return req; ++} ++ ++/* send the appropriate number of packets to match our bitrate */ ++static void audio_send(struct audio_dev *audio) ++{ ++ struct snd_pcm_runtime *runtime; ++ struct usb_request *req; ++ int length, length1, length2, ret; ++ s64 msecs; ++ s64 frames; ++ ktime_t now; ++ ++ /* audio->substream will be null if we have been closed */ ++ if (!audio->substream) ++ return; ++ /* audio->buffer_pos will be null if we have been stopped */ ++ if (!audio->buffer_pos) ++ return; ++ ++ runtime = audio->substream->runtime; ++ ++ /* compute number of frames to send */ ++ now = ktime_get(); ++ msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time); ++ do_div(msecs, 1000000); ++ frames = msecs * SAMPLE_RATE; ++ do_div(frames, 1000); ++ ++ /* Readjust our frames_sent if we fall too far behind. ++ * If we get too far behind it is better to drop some frames than ++ * to keep sending data too fast in an attempt to catch up. ++ */ ++ if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC) ++ audio->frames_sent = frames - FRAMES_PER_MSEC; ++ ++ frames -= audio->frames_sent; ++ ++ /* We need to send something to keep the pipeline going */ ++ if (frames <= 0) ++ frames = FRAMES_PER_MSEC; ++ ++ while (frames > 0) { ++ req = audio_req_get(audio); ++ if (!req) ++ break; ++ ++ length = frames_to_bytes(runtime, frames); ++ if (length > IN_EP_MAX_PACKET_SIZE) ++ length = IN_EP_MAX_PACKET_SIZE; ++ ++ if (audio->buffer_pos + length > audio->buffer_end) ++ length1 = audio->buffer_end - audio->buffer_pos; ++ else ++ length1 = length; ++ memcpy(req->buf, audio->buffer_pos, length1); ++ if (length1 < length) { ++ /* Wrap around and copy remaining length ++ * at beginning of buffer. ++ */ ++ length2 = length - length1; ++ memcpy(req->buf + length1, audio->buffer_start, ++ length2); ++ audio->buffer_pos = audio->buffer_start + length2; ++ } else { ++ audio->buffer_pos += length1; ++ if (audio->buffer_pos >= audio->buffer_end) ++ audio->buffer_pos = audio->buffer_start; ++ } ++ ++ req->length = length; ++ ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC); ++ if (ret < 0) { ++ pr_err("usb_ep_queue failed ret: %d\n", ret); ++ audio_req_put(audio, req); ++ break; ++ } ++ ++ frames -= bytes_to_frames(runtime, length); ++ audio->frames_sent += bytes_to_frames(runtime, length); ++ } ++} ++ ++static void audio_control_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ /* nothing to do here */ ++} ++ ++static void audio_data_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct audio_dev *audio = req->context; ++ ++ pr_debug("audio_data_complete req->status %d req->actual %d\n", ++ req->status, req->actual); ++ ++ audio_req_put(audio, req); ++ ++ if (!audio->buffer_start || req->status) ++ return; ++ ++ audio->period_offset += req->actual; ++ if (audio->period_offset >= audio->period) { ++ snd_pcm_period_elapsed(audio->substream); ++ audio->period_offset = 0; ++ } ++ audio_send(audio); ++} ++ ++static int audio_set_endpoint_req(struct usb_function *f, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ int value = -EOPNOTSUPP; ++ u16 ep = le16_to_cpu(ctrl->wIndex); ++ u16 len = le16_to_cpu(ctrl->wLength); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ ++ pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", ++ ctrl->bRequest, w_value, len, ep); ++ ++ switch (ctrl->bRequest) { ++ case UAC_SET_CUR: ++ case UAC_SET_MIN: ++ case UAC_SET_MAX: ++ case UAC_SET_RES: ++ value = len; ++ break; ++ default: ++ break; ++ } ++ ++ return value; ++} ++ ++static int audio_get_endpoint_req(struct usb_function *f, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ struct usb_composite_dev *cdev = f->config->cdev; ++ int value = -EOPNOTSUPP; ++ u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); ++ u16 len = le16_to_cpu(ctrl->wLength); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u8 *buf = cdev->req->buf; ++ ++ pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", ++ ctrl->bRequest, w_value, len, ep); ++ ++ if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) { ++ switch (ctrl->bRequest) { ++ case UAC_GET_CUR: ++ case UAC_GET_MIN: ++ case UAC_GET_MAX: ++ case UAC_GET_RES: ++ /* return our sample rate */ ++ buf[0] = (u8)SAMPLE_RATE; ++ buf[1] = (u8)(SAMPLE_RATE >> 8); ++ buf[2] = (u8)(SAMPLE_RATE >> 16); ++ value = 3; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return value; ++} ++ ++static int ++audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) ++{ ++ struct usb_composite_dev *cdev = f->config->cdev; ++ struct usb_request *req = cdev->req; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ ++ /* composite driver infrastructure handles everything; interface ++ * activation uses set_alt(). ++ */ ++ switch (ctrl->bRequestType) { ++ case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: ++ value = audio_set_endpoint_req(f, ctrl); ++ break; ++ ++ case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: ++ value = audio_get_endpoint_req(f, ctrl); ++ break; ++ } ++ ++ /* respond with data transfer or status phase? */ ++ if (value >= 0) { ++ pr_debug("audio req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ req->zero = 0; ++ req->length = value; ++ req->complete = audio_control_complete; ++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) ++ pr_err("audio response on err %d\n", value); ++ } ++ ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct audio_dev *audio = func_to_audio(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ int ret; ++ ++ pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt); ++ ++ ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep); ++ if (ret) ++ return ret; ++ ++ usb_ep_enable(audio->in_ep); ++ return 0; ++} ++ ++static void audio_disable(struct usb_function *f) ++{ ++ struct audio_dev *audio = func_to_audio(f); ++ ++ pr_debug("audio_disable\n"); ++ usb_ep_disable(audio->in_ep); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void audio_build_desc(struct audio_dev *audio) ++{ ++ u8 *sam_freq; ++ int rate; ++ ++ /* Set channel numbers */ ++ input_terminal_desc.bNrChannels = 2; ++ as_type_i_desc.bNrChannels = 2; ++ ++ /* Set sample rates */ ++ rate = SAMPLE_RATE; ++ sam_freq = as_type_i_desc.tSamFreq[0]; ++ memcpy(sam_freq, &rate, 3); ++} ++ ++/* audio function driver setup/binding */ ++static int ++audio_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct audio_dev *audio = func_to_audio(f); ++ int status; ++ struct usb_ep *ep; ++ struct usb_request *req; ++ int i; ++ ++ audio_build_desc(audio); ++ ++ /* allocate instance-specific interface IDs, and patch descriptors */ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ ac_interface_desc.bInterfaceNumber = status; ++ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ as_interface_alt_0_desc.bInterfaceNumber = status; ++ as_interface_alt_1_desc.bInterfaceNumber = status; ++ ++ status = -ENODEV; ++ ++ /* allocate our endpoint */ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc); ++ if (!ep) ++ goto fail; ++ audio->in_ep = ep; ++ ep->driver_data = audio; /* claim */ ++ ++ if (gadget_is_dualspeed(c->cdev->gadget)) ++ hs_as_in_ep_desc.bEndpointAddress = ++ fs_as_in_ep_desc.bEndpointAddress; ++ ++ f->descriptors = fs_audio_desc; ++ f->hs_descriptors = hs_audio_desc; ++ ++ for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) { ++ req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE); ++ if (req) { ++ req->context = audio; ++ req->complete = audio_data_complete; ++ audio_req_put(audio, req); ++ } else ++ status = -ENOMEM; ++ } ++ ++fail: ++ return status; ++} ++ ++static void ++audio_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct audio_dev *audio = func_to_audio(f); ++ struct usb_request *req; ++ ++ while ((req = audio_req_get(audio))) ++ audio_request_free(req, audio->in_ep); ++ ++ snd_card_free_when_closed(audio->card); ++ audio->card = NULL; ++ audio->pcm = NULL; ++ audio->substream = NULL; ++ audio->in_ep = NULL; ++} ++ ++static void audio_pcm_playback_start(struct audio_dev *audio) ++{ ++ audio->start_time = ktime_get(); ++ audio->frames_sent = 0; ++ audio_send(audio); ++} ++ ++static void audio_pcm_playback_stop(struct audio_dev *audio) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&audio->lock, flags); ++ audio->buffer_start = 0; ++ audio->buffer_end = 0; ++ audio->buffer_pos = 0; ++ spin_unlock_irqrestore(&audio->lock, flags); ++} ++ ++static int audio_pcm_open(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct audio_dev *audio = substream->private_data; ++ ++ runtime->private_data = audio; ++ runtime->hw = audio_hw_info; ++ snd_pcm_limit_hw_rates(runtime); ++ runtime->hw.channels_max = 2; ++ ++ audio->substream = substream; ++ return 0; ++} ++ ++static int audio_pcm_close(struct snd_pcm_substream *substream) ++{ ++ struct audio_dev *audio = substream->private_data; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&audio->lock, flags); ++ audio->substream = NULL; ++ spin_unlock_irqrestore(&audio->lock, flags); ++ ++ return 0; ++} ++ ++static int audio_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ unsigned int channels = params_channels(params); ++ unsigned int rate = params_rate(params); ++ ++ if (rate != SAMPLE_RATE) ++ return -EINVAL; ++ if (channels != 2) ++ return -EINVAL; ++ ++ return snd_pcm_lib_alloc_vmalloc_buffer(substream, ++ params_buffer_bytes(params)); ++} ++ ++static int audio_pcm_hw_free(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_lib_free_vmalloc_buffer(substream); ++} ++ ++static int audio_pcm_prepare(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct audio_dev *audio = runtime->private_data; ++ ++ audio->period = snd_pcm_lib_period_bytes(substream); ++ audio->period_offset = 0; ++ audio->buffer_start = runtime->dma_area; ++ audio->buffer_end = audio->buffer_start ++ + snd_pcm_lib_buffer_bytes(substream); ++ audio->buffer_pos = audio->buffer_start; ++ ++ return 0; ++} ++ ++static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct audio_dev *audio = runtime->private_data; ++ ssize_t bytes = audio->buffer_pos - audio->buffer_start; ++ ++ /* return offset of next frame to fill in our buffer */ ++ return bytes_to_frames(runtime, bytes); ++} ++ ++static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream, ++ int cmd) ++{ ++ struct audio_dev *audio = substream->runtime->private_data; ++ int ret = 0; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ audio_pcm_playback_start(audio); ++ break; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ audio_pcm_playback_stop(audio); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static struct audio_dev _audio_dev = { ++ .func = { ++ .name = "audio_source", ++ .bind = audio_bind, ++ .unbind = audio_unbind, ++ .set_alt = audio_set_alt, ++ .setup = audio_setup, ++ .disable = audio_disable, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock), ++ .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs), ++}; ++ ++static struct snd_pcm_ops audio_playback_ops = { ++ .open = audio_pcm_open, ++ .close = audio_pcm_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = audio_pcm_hw_params, ++ .hw_free = audio_pcm_hw_free, ++ .prepare = audio_pcm_prepare, ++ .trigger = audio_pcm_playback_trigger, ++ .pointer = audio_pcm_pointer, ++}; ++ ++int audio_source_bind_config(struct usb_configuration *c, ++ struct audio_source_config *config) ++{ ++ struct audio_dev *audio; ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ int err; ++ ++ config->card = -1; ++ config->device = -1; ++ ++ audio = &_audio_dev; ++ ++ err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, ++ THIS_MODULE, 0, &card); ++ if (err) ++ return err; ++ ++ snd_card_set_dev(card, &c->cdev->gadget->dev); ++ ++ err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm); ++ if (err) ++ goto pcm_fail; ++ pcm->private_data = audio; ++ pcm->info_flags = 0; ++ audio->pcm = pcm; ++ ++ strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name)); ++ ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops); ++ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, ++ NULL, 0, 64 * 1024); ++ ++ strlcpy(card->driver, "audio_source", sizeof(card->driver)); ++ strlcpy(card->shortname, card->driver, sizeof(card->shortname)); ++ strlcpy(card->longname, "USB accessory audio source", ++ sizeof(card->longname)); ++ ++ err = snd_card_register(card); ++ if (err) ++ goto register_fail; ++ ++ err = usb_add_function(c, &audio->func); ++ if (err) ++ goto add_fail; ++ ++ config->card = pcm->card->number; ++ config->device = pcm->device; ++ audio->card = card; ++ return 0; ++ ++add_fail: ++register_fail: ++pcm_fail: ++ snd_card_free(audio->card); ++ return err; ++} +diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c +index f52cb1ae..7a86f350 100644 +--- a/drivers/usb/gadget/f_fs.c ++++ b/drivers/usb/gadget/f_fs.c +@@ -1519,7 +1519,12 @@ static int ffs_func_eps_enable(struct ffs_function *func) + spin_lock_irqsave(&func->ffs->eps_lock, flags); + do { + struct usb_endpoint_descriptor *ds; +- ds = ep->descs[ep->descs[1] ? 1 : 0]; ++ int desc_idx = ffs->gadget->speed == USB_SPEED_HIGH ? 1 : 0; ++ ds = ep->descs[desc_idx]; ++ if (!ds) { ++ ret = -EINVAL; ++ break; ++ } + + ep->ep->driver_data = ep; + ep->ep->desc = ds; +diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c +new file mode 100644 +index 00000000..1638977a +--- /dev/null ++++ b/drivers/usb/gadget/f_mtp.c +@@ -0,0 +1,1283 @@ ++/* ++ * Gadget Function Driver for MTP ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * Author: Mike Lockwood ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++/* #define DEBUG */ ++/* #define VERBOSE_DEBUG */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define MTP_BULK_BUFFER_SIZE 16384 ++#define INTR_BUFFER_SIZE 28 ++ ++/* String IDs */ ++#define INTERFACE_STRING_INDEX 0 ++ ++/* values for mtp_dev.state */ ++#define STATE_OFFLINE 0 /* initial state, disconnected */ ++#define STATE_READY 1 /* ready for userspace calls */ ++#define STATE_BUSY 2 /* processing userspace calls */ ++#define STATE_CANCELED 3 /* transaction canceled by host */ ++#define STATE_ERROR 4 /* error from completion routine */ ++ ++/* number of tx and rx requests to allocate */ ++#define TX_REQ_MAX 4 ++#define RX_REQ_MAX 2 ++#define INTR_REQ_MAX 5 ++ ++/* ID for Microsoft MTP OS String */ ++#define MTP_OS_STRING_ID 0xEE ++ ++/* MTP class reqeusts */ ++#define MTP_REQ_CANCEL 0x64 ++#define MTP_REQ_GET_EXT_EVENT_DATA 0x65 ++#define MTP_REQ_RESET 0x66 ++#define MTP_REQ_GET_DEVICE_STATUS 0x67 ++ ++/* constants for device status */ ++#define MTP_RESPONSE_OK 0x2001 ++#define MTP_RESPONSE_DEVICE_BUSY 0x2019 ++ ++static const char mtp_shortname[] = "mtp_usb"; ++ ++struct mtp_dev { ++ struct usb_function function; ++ struct usb_composite_dev *cdev; ++ spinlock_t lock; ++ ++ struct usb_ep *ep_in; ++ struct usb_ep *ep_out; ++ struct usb_ep *ep_intr; ++ ++ int state; ++ ++ /* synchronize access to our device file */ ++ atomic_t open_excl; ++ /* to enforce only one ioctl at a time */ ++ atomic_t ioctl_excl; ++ ++ struct list_head tx_idle; ++ struct list_head intr_idle; ++ ++ wait_queue_head_t read_wq; ++ wait_queue_head_t write_wq; ++ wait_queue_head_t intr_wq; ++ struct usb_request *rx_req[RX_REQ_MAX]; ++ int rx_done; ++ ++ /* for processing MTP_SEND_FILE, MTP_RECEIVE_FILE and ++ * MTP_SEND_FILE_WITH_HEADER ioctls on a work queue ++ */ ++ struct workqueue_struct *wq; ++ struct work_struct send_file_work; ++ struct work_struct receive_file_work; ++ struct file *xfer_file; ++ loff_t xfer_file_offset; ++ int64_t xfer_file_length; ++ unsigned xfer_send_header; ++ uint16_t xfer_command; ++ uint32_t xfer_transaction_id; ++ int xfer_result; ++}; ++ ++static struct usb_interface_descriptor mtp_interface_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0, ++ .bNumEndpoints = 3, ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, ++ .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, ++ .bInterfaceProtocol = 0, ++}; ++ ++static struct usb_interface_descriptor ptp_interface_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0, ++ .bNumEndpoints = 3, ++ .bInterfaceClass = USB_CLASS_STILL_IMAGE, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 1, ++}; ++ ++static struct usb_endpoint_descriptor mtp_highspeed_in_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor mtp_highspeed_out_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor mtp_fullspeed_in_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor mtp_fullspeed_out_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor mtp_intr_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(INTR_BUFFER_SIZE), ++ .bInterval = 6, ++}; ++ ++static struct usb_descriptor_header *fs_mtp_descs[] = { ++ (struct usb_descriptor_header *) &mtp_interface_desc, ++ (struct usb_descriptor_header *) &mtp_fullspeed_in_desc, ++ (struct usb_descriptor_header *) &mtp_fullspeed_out_desc, ++ (struct usb_descriptor_header *) &mtp_intr_desc, ++ NULL, ++}; ++ ++static struct usb_descriptor_header *hs_mtp_descs[] = { ++ (struct usb_descriptor_header *) &mtp_interface_desc, ++ (struct usb_descriptor_header *) &mtp_highspeed_in_desc, ++ (struct usb_descriptor_header *) &mtp_highspeed_out_desc, ++ (struct usb_descriptor_header *) &mtp_intr_desc, ++ NULL, ++}; ++ ++static struct usb_descriptor_header *fs_ptp_descs[] = { ++ (struct usb_descriptor_header *) &ptp_interface_desc, ++ (struct usb_descriptor_header *) &mtp_fullspeed_in_desc, ++ (struct usb_descriptor_header *) &mtp_fullspeed_out_desc, ++ (struct usb_descriptor_header *) &mtp_intr_desc, ++ NULL, ++}; ++ ++static struct usb_descriptor_header *hs_ptp_descs[] = { ++ (struct usb_descriptor_header *) &ptp_interface_desc, ++ (struct usb_descriptor_header *) &mtp_highspeed_in_desc, ++ (struct usb_descriptor_header *) &mtp_highspeed_out_desc, ++ (struct usb_descriptor_header *) &mtp_intr_desc, ++ NULL, ++}; ++ ++static struct usb_string mtp_string_defs[] = { ++ /* Naming interface "MTP" so libmtp will recognize us */ ++ [INTERFACE_STRING_INDEX].s = "MTP", ++ { }, /* end of list */ ++}; ++ ++static struct usb_gadget_strings mtp_string_table = { ++ .language = 0x0409, /* en-US */ ++ .strings = mtp_string_defs, ++}; ++ ++static struct usb_gadget_strings *mtp_strings[] = { ++ &mtp_string_table, ++ NULL, ++}; ++ ++/* Microsoft MTP OS String */ ++static u8 mtp_os_string[] = { ++ 18, /* sizeof(mtp_os_string) */ ++ USB_DT_STRING, ++ /* Signature field: "MSFT100" */ ++ 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, ++ /* vendor code */ ++ 1, ++ /* padding */ ++ 0 ++}; ++ ++/* Microsoft Extended Configuration Descriptor Header Section */ ++struct mtp_ext_config_desc_header { ++ __le32 dwLength; ++ __u16 bcdVersion; ++ __le16 wIndex; ++ __u8 bCount; ++ __u8 reserved[7]; ++}; ++ ++/* Microsoft Extended Configuration Descriptor Function Section */ ++struct mtp_ext_config_desc_function { ++ __u8 bFirstInterfaceNumber; ++ __u8 bInterfaceCount; ++ __u8 compatibleID[8]; ++ __u8 subCompatibleID[8]; ++ __u8 reserved[6]; ++}; ++ ++/* MTP Extended Configuration Descriptor */ ++struct { ++ struct mtp_ext_config_desc_header header; ++ struct mtp_ext_config_desc_function function; ++} mtp_ext_config_desc = { ++ .header = { ++ .dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)), ++ .bcdVersion = __constant_cpu_to_le16(0x0100), ++ .wIndex = __constant_cpu_to_le16(4), ++ .bCount = __constant_cpu_to_le16(1), ++ }, ++ .function = { ++ .bFirstInterfaceNumber = 0, ++ .bInterfaceCount = 1, ++ .compatibleID = { 'M', 'T', 'P' }, ++ }, ++}; ++ ++struct mtp_device_status { ++ __le16 wLength; ++ __le16 wCode; ++}; ++ ++/* temporary variable used between mtp_open() and mtp_gadget_bind() */ ++static struct mtp_dev *_mtp_dev; ++ ++static inline struct mtp_dev *func_to_mtp(struct usb_function *f) ++{ ++ return container_of(f, struct mtp_dev, function); ++} ++ ++static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size) ++{ ++ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); ++ if (!req) ++ return NULL; ++ ++ /* now allocate buffers for the requests */ ++ req->buf = kmalloc(buffer_size, GFP_KERNEL); ++ if (!req->buf) { ++ usb_ep_free_request(ep, req); ++ return NULL; ++ } ++ ++ return req; ++} ++ ++static void mtp_request_free(struct usb_request *req, struct usb_ep *ep) ++{ ++ if (req) { ++ kfree(req->buf); ++ usb_ep_free_request(ep, req); ++ } ++} ++ ++static inline int mtp_lock(atomic_t *excl) ++{ ++ if (atomic_inc_return(excl) == 1) { ++ return 0; ++ } else { ++ atomic_dec(excl); ++ return -1; ++ } ++} ++ ++static inline void mtp_unlock(atomic_t *excl) ++{ ++ atomic_dec(excl); ++} ++ ++/* add a request to the tail of a list */ ++static void mtp_req_put(struct mtp_dev *dev, struct list_head *head, ++ struct usb_request *req) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ list_add_tail(&req->list, head); ++ spin_unlock_irqrestore(&dev->lock, flags); ++} ++ ++/* remove a request from the head of a list */ ++static struct usb_request ++*mtp_req_get(struct mtp_dev *dev, struct list_head *head) ++{ ++ unsigned long flags; ++ struct usb_request *req; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (list_empty(head)) { ++ req = 0; ++ } else { ++ req = list_first_entry(head, struct usb_request, list); ++ list_del(&req->list); ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ return req; ++} ++ ++static void mtp_complete_in(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct mtp_dev *dev = _mtp_dev; ++ ++ if (req->status != 0) ++ dev->state = STATE_ERROR; ++ ++ mtp_req_put(dev, &dev->tx_idle, req); ++ ++ wake_up(&dev->write_wq); ++} ++ ++static void mtp_complete_out(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct mtp_dev *dev = _mtp_dev; ++ ++ dev->rx_done = 1; ++ if (req->status != 0) ++ dev->state = STATE_ERROR; ++ ++ wake_up(&dev->read_wq); ++} ++ ++static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct mtp_dev *dev = _mtp_dev; ++ ++ if (req->status != 0) ++ dev->state = STATE_ERROR; ++ ++ mtp_req_put(dev, &dev->intr_idle, req); ++ ++ wake_up(&dev->intr_wq); ++} ++ ++static int mtp_create_bulk_endpoints(struct mtp_dev *dev, ++ struct usb_endpoint_descriptor *in_desc, ++ struct usb_endpoint_descriptor *out_desc, ++ struct usb_endpoint_descriptor *intr_desc) ++{ ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *req; ++ struct usb_ep *ep; ++ int i; ++ ++ DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); ++ ++ ep = usb_ep_autoconfig(cdev->gadget, in_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_in = ep; ++ ++ ep = usb_ep_autoconfig(cdev->gadget, out_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_out = ep; ++ ++ ep = usb_ep_autoconfig(cdev->gadget, out_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_out = ep; ++ ++ ep = usb_ep_autoconfig(cdev->gadget, intr_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for mtp ep_intr got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_intr = ep; ++ ++ /* now allocate requests for our endpoints */ ++ for (i = 0; i < TX_REQ_MAX; i++) { ++ req = mtp_request_new(dev->ep_in, MTP_BULK_BUFFER_SIZE); ++ if (!req) ++ goto fail; ++ req->complete = mtp_complete_in; ++ mtp_req_put(dev, &dev->tx_idle, req); ++ } ++ for (i = 0; i < RX_REQ_MAX; i++) { ++ req = mtp_request_new(dev->ep_out, MTP_BULK_BUFFER_SIZE); ++ if (!req) ++ goto fail; ++ req->complete = mtp_complete_out; ++ dev->rx_req[i] = req; ++ } ++ for (i = 0; i < INTR_REQ_MAX; i++) { ++ req = mtp_request_new(dev->ep_intr, INTR_BUFFER_SIZE); ++ if (!req) ++ goto fail; ++ req->complete = mtp_complete_intr; ++ mtp_req_put(dev, &dev->intr_idle, req); ++ } ++ ++ return 0; ++ ++fail: ++ printk(KERN_ERR "mtp_bind() could not allocate requests\n"); ++ return -1; ++} ++ ++static ssize_t mtp_read(struct file *fp, char __user *buf, ++ size_t count, loff_t *pos) ++{ ++ struct mtp_dev *dev = fp->private_data; ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *req; ++ int r = count, xfer; ++ int ret = 0; ++ ++ DBG(cdev, "mtp_read(%d)\n", count); ++ ++ if (count > MTP_BULK_BUFFER_SIZE) ++ return -EINVAL; ++ ++ /* we will block until we're online */ ++ DBG(cdev, "mtp_read: waiting for online state\n"); ++ ret = wait_event_interruptible(dev->read_wq, ++ dev->state != STATE_OFFLINE); ++ if (ret < 0) { ++ r = ret; ++ goto done; ++ } ++ spin_lock_irq(&dev->lock); ++ if (dev->state == STATE_CANCELED) { ++ /* report cancelation to userspace */ ++ dev->state = STATE_READY; ++ spin_unlock_irq(&dev->lock); ++ return -ECANCELED; ++ } ++ dev->state = STATE_BUSY; ++ spin_unlock_irq(&dev->lock); ++ ++requeue_req: ++ /* queue a request */ ++ req = dev->rx_req[0]; ++ req->length = count; ++ dev->rx_done = 0; ++ ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); ++ if (ret < 0) { ++ r = -EIO; ++ goto done; ++ } else { ++ DBG(cdev, "rx %p queue\n", req); ++ } ++ ++ /* wait for a request to complete */ ++ ret = wait_event_interruptible(dev->read_wq, dev->rx_done); ++ if (ret < 0) { ++ r = ret; ++ usb_ep_dequeue(dev->ep_out, req); ++ goto done; ++ } ++ if (dev->state == STATE_BUSY) { ++ /* If we got a 0-len packet, throw it back and try again. */ ++ if (req->actual == 0) ++ goto requeue_req; ++ ++ DBG(cdev, "rx %p %d\n", req, req->actual); ++ xfer = (req->actual < count) ? req->actual : count; ++ r = xfer; ++ if (copy_to_user(buf, req->buf, xfer)) ++ r = -EFAULT; ++ } else ++ r = -EIO; ++ ++done: ++ spin_lock_irq(&dev->lock); ++ if (dev->state == STATE_CANCELED) ++ r = -ECANCELED; ++ else if (dev->state != STATE_OFFLINE) ++ dev->state = STATE_READY; ++ spin_unlock_irq(&dev->lock); ++ ++ DBG(cdev, "mtp_read returning %d\n", r); ++ return r; ++} ++ ++static ssize_t mtp_write(struct file *fp, const char __user *buf, ++ size_t count, loff_t *pos) ++{ ++ struct mtp_dev *dev = fp->private_data; ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *req = 0; ++ int r = count, xfer; ++ int sendZLP = 0; ++ int ret; ++ ++ DBG(cdev, "mtp_write(%d)\n", count); ++ ++ spin_lock_irq(&dev->lock); ++ if (dev->state == STATE_CANCELED) { ++ /* report cancelation to userspace */ ++ dev->state = STATE_READY; ++ spin_unlock_irq(&dev->lock); ++ return -ECANCELED; ++ } ++ if (dev->state == STATE_OFFLINE) { ++ spin_unlock_irq(&dev->lock); ++ return -ENODEV; ++ } ++ dev->state = STATE_BUSY; ++ spin_unlock_irq(&dev->lock); ++ ++ /* we need to send a zero length packet to signal the end of transfer ++ * if the transfer size is aligned to a packet boundary. ++ */ ++ if ((count & (dev->ep_in->maxpacket - 1)) == 0) ++ sendZLP = 1; ++ ++ while (count > 0 || sendZLP) { ++ /* so we exit after sending ZLP */ ++ if (count == 0) ++ sendZLP = 0; ++ ++ if (dev->state != STATE_BUSY) { ++ DBG(cdev, "mtp_write dev->error\n"); ++ r = -EIO; ++ break; ++ } ++ ++ /* get an idle tx request to use */ ++ req = 0; ++ ret = wait_event_interruptible(dev->write_wq, ++ ((req = mtp_req_get(dev, &dev->tx_idle)) ++ || dev->state != STATE_BUSY)); ++ if (!req) { ++ r = ret; ++ break; ++ } ++ ++ if (count > MTP_BULK_BUFFER_SIZE) ++ xfer = MTP_BULK_BUFFER_SIZE; ++ else ++ xfer = count; ++ if (xfer && copy_from_user(req->buf, buf, xfer)) { ++ r = -EFAULT; ++ break; ++ } ++ ++ req->length = xfer; ++ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); ++ if (ret < 0) { ++ DBG(cdev, "mtp_write: xfer error %d\n", ret); ++ r = -EIO; ++ break; ++ } ++ ++ buf += xfer; ++ count -= xfer; ++ ++ /* zero this so we don't try to free it on error exit */ ++ req = 0; ++ } ++ ++ if (req) ++ mtp_req_put(dev, &dev->tx_idle, req); ++ ++ spin_lock_irq(&dev->lock); ++ if (dev->state == STATE_CANCELED) ++ r = -ECANCELED; ++ else if (dev->state != STATE_OFFLINE) ++ dev->state = STATE_READY; ++ spin_unlock_irq(&dev->lock); ++ ++ DBG(cdev, "mtp_write returning %d\n", r); ++ return r; ++} ++ ++/* read from a local file and write to USB */ ++static void send_file_work(struct work_struct *data) ++{ ++ struct mtp_dev *dev = container_of(data, struct mtp_dev, ++ send_file_work); ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *req = 0; ++ struct mtp_data_header *header; ++ struct file *filp; ++ loff_t offset; ++ int64_t count; ++ int xfer, ret, hdr_size; ++ int r = 0; ++ int sendZLP = 0; ++ ++ /* read our parameters */ ++ smp_rmb(); ++ filp = dev->xfer_file; ++ offset = dev->xfer_file_offset; ++ count = dev->xfer_file_length; ++ ++ DBG(cdev, "send_file_work(%lld %lld)\n", offset, count); ++ ++ if (dev->xfer_send_header) { ++ hdr_size = sizeof(struct mtp_data_header); ++ count += hdr_size; ++ } else { ++ hdr_size = 0; ++ } ++ ++ /* we need to send a zero length packet to signal the end of transfer ++ * if the transfer size is aligned to a packet boundary. ++ */ ++ if ((count & (dev->ep_in->maxpacket - 1)) == 0) ++ sendZLP = 1; ++ ++ while (count > 0 || sendZLP) { ++ /* so we exit after sending ZLP */ ++ if (count == 0) ++ sendZLP = 0; ++ ++ /* get an idle tx request to use */ ++ req = 0; ++ ret = wait_event_interruptible(dev->write_wq, ++ (req = mtp_req_get(dev, &dev->tx_idle)) ++ || dev->state != STATE_BUSY); ++ if (dev->state == STATE_CANCELED) { ++ r = -ECANCELED; ++ break; ++ } ++ if (!req) { ++ r = ret; ++ break; ++ } ++ ++ if (count > MTP_BULK_BUFFER_SIZE) ++ xfer = MTP_BULK_BUFFER_SIZE; ++ else ++ xfer = count; ++ ++ if (hdr_size) { ++ /* prepend MTP data header */ ++ header = (struct mtp_data_header *)req->buf; ++ header->length = __cpu_to_le32(count); ++ header->type = __cpu_to_le16(2); /* data packet */ ++ header->command = __cpu_to_le16(dev->xfer_command); ++ header->transaction_id = ++ __cpu_to_le32(dev->xfer_transaction_id); ++ } ++ ++ ret = vfs_read(filp, req->buf + hdr_size, xfer - hdr_size, ++ &offset); ++ if (ret < 0) { ++ r = ret; ++ break; ++ } ++ xfer = ret + hdr_size; ++ hdr_size = 0; ++ ++ req->length = xfer; ++ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); ++ if (ret < 0) { ++ DBG(cdev, "send_file_work: xfer error %d\n", ret); ++ dev->state = STATE_ERROR; ++ r = -EIO; ++ break; ++ } ++ ++ count -= xfer; ++ ++ /* zero this so we don't try to free it on error exit */ ++ req = 0; ++ } ++ ++ if (req) ++ mtp_req_put(dev, &dev->tx_idle, req); ++ ++ DBG(cdev, "send_file_work returning %d\n", r); ++ /* write the result */ ++ dev->xfer_result = r; ++ smp_wmb(); ++} ++ ++/* read from USB and write to a local file */ ++static void receive_file_work(struct work_struct *data) ++{ ++ struct mtp_dev *dev = container_of(data, struct mtp_dev, ++ receive_file_work); ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *read_req = NULL, *write_req = NULL; ++ struct file *filp; ++ loff_t offset; ++ int64_t count; ++ int ret, cur_buf = 0; ++ int r = 0; ++ ++ /* read our parameters */ ++ smp_rmb(); ++ filp = dev->xfer_file; ++ offset = dev->xfer_file_offset; ++ count = dev->xfer_file_length; ++ ++ DBG(cdev, "receive_file_work(%lld)\n", count); ++ ++ while (count > 0 || write_req) { ++ if (count > 0) { ++ /* queue a request */ ++ read_req = dev->rx_req[cur_buf]; ++ cur_buf = (cur_buf + 1) % RX_REQ_MAX; ++ ++ read_req->length = (count > MTP_BULK_BUFFER_SIZE ++ ? MTP_BULK_BUFFER_SIZE : count); ++ dev->rx_done = 0; ++ ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL); ++ if (ret < 0) { ++ r = -EIO; ++ dev->state = STATE_ERROR; ++ break; ++ } ++ } ++ ++ if (write_req) { ++ DBG(cdev, "rx %p %d\n", write_req, write_req->actual); ++ ret = vfs_write(filp, write_req->buf, write_req->actual, ++ &offset); ++ DBG(cdev, "vfs_write %d\n", ret); ++ if (ret != write_req->actual) { ++ r = -EIO; ++ dev->state = STATE_ERROR; ++ break; ++ } ++ write_req = NULL; ++ } ++ ++ if (read_req) { ++ /* wait for our last read to complete */ ++ ret = wait_event_interruptible(dev->read_wq, ++ dev->rx_done || dev->state != STATE_BUSY); ++ if (dev->state == STATE_CANCELED) { ++ r = -ECANCELED; ++ if (!dev->rx_done) ++ usb_ep_dequeue(dev->ep_out, read_req); ++ break; ++ } ++ /* if xfer_file_length is 0xFFFFFFFF, then we read until ++ * we get a zero length packet ++ */ ++ if (count != 0xFFFFFFFF) ++ count -= read_req->actual; ++ if (read_req->actual < read_req->length) { ++ /* ++ * short packet is used to signal EOF for ++ * sizes > 4 gig ++ */ ++ DBG(cdev, "got short packet\n"); ++ count = 0; ++ } ++ ++ write_req = read_req; ++ read_req = NULL; ++ } ++ } ++ ++ DBG(cdev, "receive_file_work returning %d\n", r); ++ /* write the result */ ++ dev->xfer_result = r; ++ smp_wmb(); ++} ++ ++static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) ++{ ++ struct usb_request *req = NULL; ++ int ret; ++ int length = event->length; ++ ++ DBG(dev->cdev, "mtp_send_event(%d)\n", event->length); ++ ++ if (length < 0 || length > INTR_BUFFER_SIZE) ++ return -EINVAL; ++ if (dev->state == STATE_OFFLINE) ++ return -ENODEV; ++ ++ ret = wait_event_interruptible_timeout(dev->intr_wq, ++ (req = mtp_req_get(dev, &dev->intr_idle)), ++ msecs_to_jiffies(1000)); ++ if (!req) ++ return -ETIME; ++ ++ if (copy_from_user(req->buf, (void __user *)event->data, length)) { ++ mtp_req_put(dev, &dev->intr_idle, req); ++ return -EFAULT; ++ } ++ req->length = length; ++ ret = usb_ep_queue(dev->ep_intr, req, GFP_KERNEL); ++ if (ret) ++ mtp_req_put(dev, &dev->intr_idle, req); ++ ++ return ret; ++} ++ ++static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) ++{ ++ struct mtp_dev *dev = fp->private_data; ++ struct file *filp = NULL; ++ int ret = -EINVAL; ++ ++ if (mtp_lock(&dev->ioctl_excl)) ++ return -EBUSY; ++ ++ switch (code) { ++ case MTP_SEND_FILE: ++ case MTP_RECEIVE_FILE: ++ case MTP_SEND_FILE_WITH_HEADER: ++ { ++ struct mtp_file_range mfr; ++ struct work_struct *work; ++ ++ spin_lock_irq(&dev->lock); ++ if (dev->state == STATE_CANCELED) { ++ /* report cancelation to userspace */ ++ dev->state = STATE_READY; ++ spin_unlock_irq(&dev->lock); ++ ret = -ECANCELED; ++ goto out; ++ } ++ if (dev->state == STATE_OFFLINE) { ++ spin_unlock_irq(&dev->lock); ++ ret = -ENODEV; ++ goto out; ++ } ++ dev->state = STATE_BUSY; ++ spin_unlock_irq(&dev->lock); ++ ++ if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) { ++ ret = -EFAULT; ++ goto fail; ++ } ++ /* hold a reference to the file while we are working with it */ ++ filp = fget(mfr.fd); ++ if (!filp) { ++ ret = -EBADF; ++ goto fail; ++ } ++ ++ /* write the parameters */ ++ dev->xfer_file = filp; ++ dev->xfer_file_offset = mfr.offset; ++ dev->xfer_file_length = mfr.length; ++ smp_wmb(); ++ ++ if (code == MTP_SEND_FILE_WITH_HEADER) { ++ work = &dev->send_file_work; ++ dev->xfer_send_header = 1; ++ dev->xfer_command = mfr.command; ++ dev->xfer_transaction_id = mfr.transaction_id; ++ } else if (code == MTP_SEND_FILE) { ++ work = &dev->send_file_work; ++ dev->xfer_send_header = 0; ++ } else { ++ work = &dev->receive_file_work; ++ } ++ ++ /* We do the file transfer on a work queue so it will run ++ * in kernel context, which is necessary for vfs_read and ++ * vfs_write to use our buffers in the kernel address space. ++ */ ++ queue_work(dev->wq, work); ++ /* wait for operation to complete */ ++ flush_workqueue(dev->wq); ++ fput(filp); ++ ++ /* read the result */ ++ smp_rmb(); ++ ret = dev->xfer_result; ++ break; ++ } ++ case MTP_SEND_EVENT: ++ { ++ struct mtp_event event; ++ /* return here so we don't change dev->state below, ++ * which would interfere with bulk transfer state. ++ */ ++ if (copy_from_user(&event, (void __user *)value, sizeof(event))) ++ ret = -EFAULT; ++ else ++ ret = mtp_send_event(dev, &event); ++ goto out; ++ } ++ } ++ ++fail: ++ spin_lock_irq(&dev->lock); ++ if (dev->state == STATE_CANCELED) ++ ret = -ECANCELED; ++ else if (dev->state != STATE_OFFLINE) ++ dev->state = STATE_READY; ++ spin_unlock_irq(&dev->lock); ++out: ++ mtp_unlock(&dev->ioctl_excl); ++ DBG(dev->cdev, "ioctl returning %d\n", ret); ++ return ret; ++} ++ ++static int mtp_open(struct inode *ip, struct file *fp) ++{ ++ printk(KERN_INFO "mtp_open\n"); ++ if (mtp_lock(&_mtp_dev->open_excl)) ++ return -EBUSY; ++ ++ /* clear any error condition */ ++ if (_mtp_dev->state != STATE_OFFLINE) ++ _mtp_dev->state = STATE_READY; ++ ++ fp->private_data = _mtp_dev; ++ return 0; ++} ++ ++static int mtp_release(struct inode *ip, struct file *fp) ++{ ++ printk(KERN_INFO "mtp_release\n"); ++ ++ mtp_unlock(&_mtp_dev->open_excl); ++ return 0; ++} ++ ++/* file operations for /dev/mtp_usb */ ++static const struct file_operations mtp_fops = { ++ .owner = THIS_MODULE, ++ .read = mtp_read, ++ .write = mtp_write, ++ .unlocked_ioctl = mtp_ioctl, ++ .open = mtp_open, ++ .release = mtp_release, ++}; ++ ++static struct miscdevice mtp_device = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = mtp_shortname, ++ .fops = &mtp_fops, ++}; ++ ++static int mtp_ctrlrequest(struct usb_composite_dev *cdev, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ struct mtp_dev *dev = _mtp_dev; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ unsigned long flags; ++ ++ VDBG(cdev, "mtp_ctrlrequest " ++ "%02x.%02x v%04x i%04x l%u\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ ++ /* Handle MTP OS string */ ++ if (ctrl->bRequestType == ++ (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) ++ && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR ++ && (w_value >> 8) == USB_DT_STRING ++ && (w_value & 0xFF) == MTP_OS_STRING_ID) { ++ value = (w_length < sizeof(mtp_os_string) ++ ? w_length : sizeof(mtp_os_string)); ++ memcpy(cdev->req->buf, mtp_os_string, value); ++ } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { ++ /* Handle MTP OS descriptor */ ++ DBG(cdev, "vendor request: %d index: %d value: %d length: %d\n", ++ ctrl->bRequest, w_index, w_value, w_length); ++ ++ if (ctrl->bRequest == 1 ++ && (ctrl->bRequestType & USB_DIR_IN) ++ && (w_index == 4 || w_index == 5)) { ++ value = (w_length < sizeof(mtp_ext_config_desc) ? ++ w_length : sizeof(mtp_ext_config_desc)); ++ memcpy(cdev->req->buf, &mtp_ext_config_desc, value); ++ } ++ } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { ++ DBG(cdev, "class request: %d index: %d value: %d length: %d\n", ++ ctrl->bRequest, w_index, w_value, w_length); ++ ++ if (ctrl->bRequest == MTP_REQ_CANCEL && w_index == 0 ++ && w_value == 0) { ++ DBG(cdev, "MTP_REQ_CANCEL\n"); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (dev->state == STATE_BUSY) { ++ dev->state = STATE_CANCELED; ++ wake_up(&dev->read_wq); ++ wake_up(&dev->write_wq); ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ /* We need to queue a request to read the remaining ++ * bytes, but we don't actually need to look at ++ * the contents. ++ */ ++ value = w_length; ++ } else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS ++ && w_index == 0 && w_value == 0) { ++ struct mtp_device_status *status = cdev->req->buf; ++ status->wLength = ++ __constant_cpu_to_le16(sizeof(*status)); ++ ++ DBG(cdev, "MTP_REQ_GET_DEVICE_STATUS\n"); ++ spin_lock_irqsave(&dev->lock, flags); ++ /* device status is "busy" until we report ++ * the cancelation to userspace ++ */ ++ if (dev->state == STATE_CANCELED) ++ status->wCode = ++ __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY); ++ else ++ status->wCode = ++ __cpu_to_le16(MTP_RESPONSE_OK); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ value = sizeof(*status); ++ } ++ } ++ ++ /* respond with data transfer or status phase? */ ++ if (value >= 0) { ++ int rc; ++ cdev->req->zero = value < w_length; ++ cdev->req->length = value; ++ rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); ++ if (rc < 0) ++ ERROR(cdev, "%s: response queue error\n", __func__); ++ } ++ return value; ++} ++ ++static int ++mtp_function_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct mtp_dev *dev = func_to_mtp(f); ++ int id; ++ int ret; ++ ++ dev->cdev = cdev; ++ DBG(cdev, "mtp_function_bind dev: %p\n", dev); ++ ++ /* allocate interface ID(s) */ ++ id = usb_interface_id(c, f); ++ if (id < 0) ++ return id; ++ mtp_interface_desc.bInterfaceNumber = id; ++ ++ /* allocate endpoints */ ++ ret = mtp_create_bulk_endpoints(dev, &mtp_fullspeed_in_desc, ++ &mtp_fullspeed_out_desc, &mtp_intr_desc); ++ if (ret) ++ return ret; ++ ++ /* support high speed hardware */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ mtp_highspeed_in_desc.bEndpointAddress = ++ mtp_fullspeed_in_desc.bEndpointAddress; ++ mtp_highspeed_out_desc.bEndpointAddress = ++ mtp_fullspeed_out_desc.bEndpointAddress; ++ } ++ ++ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ f->name, dev->ep_in->name, dev->ep_out->name); ++ return 0; ++} ++ ++static void ++mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct mtp_dev *dev = func_to_mtp(f); ++ struct usb_request *req; ++ int i; ++ ++ while ((req = mtp_req_get(dev, &dev->tx_idle))) ++ mtp_request_free(req, dev->ep_in); ++ for (i = 0; i < RX_REQ_MAX; i++) ++ mtp_request_free(dev->rx_req[i], dev->ep_out); ++ while ((req = mtp_req_get(dev, &dev->intr_idle))) ++ mtp_request_free(req, dev->ep_intr); ++ dev->state = STATE_OFFLINE; ++} ++ ++static int mtp_function_set_alt(struct usb_function *f, ++ unsigned intf, unsigned alt) ++{ ++ struct mtp_dev *dev = func_to_mtp(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ int ret; ++ ++ DBG(cdev, "mtp_function_set_alt intf: %d alt: %d\n", intf, alt); ++ ++ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); ++ if (ret) ++ return ret; ++ ++ ret = usb_ep_enable(dev->ep_in); ++ if (ret) ++ return ret; ++ ++ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); ++ if (ret) ++ return ret; ++ ++ ret = usb_ep_enable(dev->ep_out); ++ if (ret) { ++ usb_ep_disable(dev->ep_in); ++ return ret; ++ } ++ ++ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_intr); ++ if (ret) ++ return ret; ++ ++ ret = usb_ep_enable(dev->ep_intr); ++ if (ret) { ++ usb_ep_disable(dev->ep_out); ++ usb_ep_disable(dev->ep_in); ++ return ret; ++ } ++ dev->state = STATE_READY; ++ ++ /* readers may be blocked waiting for us to go online */ ++ wake_up(&dev->read_wq); ++ return 0; ++} ++ ++static void mtp_function_disable(struct usb_function *f) ++{ ++ struct mtp_dev *dev = func_to_mtp(f); ++ struct usb_composite_dev *cdev = dev->cdev; ++ ++ DBG(cdev, "mtp_function_disable\n"); ++ dev->state = STATE_OFFLINE; ++ usb_ep_disable(dev->ep_in); ++ usb_ep_disable(dev->ep_out); ++ usb_ep_disable(dev->ep_intr); ++ ++ /* readers may be blocked waiting for us to go online */ ++ wake_up(&dev->read_wq); ++ ++ VDBG(cdev, "%s disabled\n", dev->function.name); ++} ++ ++static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) ++{ ++ struct mtp_dev *dev = _mtp_dev; ++ int ret = 0; ++ ++ printk(KERN_INFO "mtp_bind_config\n"); ++ ++ /* allocate a string ID for our interface */ ++ if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) { ++ ret = usb_string_id(c->cdev); ++ if (ret < 0) ++ return ret; ++ mtp_string_defs[INTERFACE_STRING_INDEX].id = ret; ++ mtp_interface_desc.iInterface = ret; ++ } ++ ++ dev->cdev = c->cdev; ++ dev->function.name = "mtp"; ++ dev->function.strings = mtp_strings; ++ if (ptp_config) { ++ dev->function.descriptors = fs_ptp_descs; ++ dev->function.hs_descriptors = hs_ptp_descs; ++ } else { ++ dev->function.descriptors = fs_mtp_descs; ++ dev->function.hs_descriptors = hs_mtp_descs; ++ } ++ dev->function.bind = mtp_function_bind; ++ dev->function.unbind = mtp_function_unbind; ++ dev->function.set_alt = mtp_function_set_alt; ++ dev->function.disable = mtp_function_disable; ++ ++ return usb_add_function(c, &dev->function); ++} ++ ++static int mtp_setup(void) ++{ ++ struct mtp_dev *dev; ++ int ret; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ spin_lock_init(&dev->lock); ++ init_waitqueue_head(&dev->read_wq); ++ init_waitqueue_head(&dev->write_wq); ++ init_waitqueue_head(&dev->intr_wq); ++ atomic_set(&dev->open_excl, 0); ++ atomic_set(&dev->ioctl_excl, 0); ++ INIT_LIST_HEAD(&dev->tx_idle); ++ INIT_LIST_HEAD(&dev->intr_idle); ++ ++ dev->wq = create_singlethread_workqueue("f_mtp"); ++ if (!dev->wq) { ++ ret = -ENOMEM; ++ goto err1; ++ } ++ INIT_WORK(&dev->send_file_work, send_file_work); ++ INIT_WORK(&dev->receive_file_work, receive_file_work); ++ ++ _mtp_dev = dev; ++ ++ ret = misc_register(&mtp_device); ++ if (ret) ++ goto err2; ++ ++ return 0; ++ ++err2: ++ destroy_workqueue(dev->wq); ++err1: ++ _mtp_dev = NULL; ++ kfree(dev); ++ printk(KERN_ERR "mtp gadget driver failed to initialize\n"); ++ return ret; ++} ++ ++static void mtp_cleanup(void) ++{ ++ struct mtp_dev *dev = _mtp_dev; ++ ++ if (!dev) ++ return; ++ ++ misc_deregister(&mtp_device); ++ destroy_workqueue(dev->wq); ++ _mtp_dev = NULL; ++ kfree(dev); ++} +diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c +index 345f8388..9d26ef09 100644 +--- a/drivers/usb/gadget/f_rndis.c ++++ b/drivers/usb/gadget/f_rndis.c +@@ -71,6 +71,8 @@ struct f_rndis { + struct gether port; + u8 ctrl_id, data_id; + u8 ethaddr[ETH_ALEN]; ++ u32 vendorID; ++ const char *manufacturer; + int config; + + struct usb_ep *notify; +@@ -768,12 +770,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) + rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); + rndis_set_host_mac(rndis->config, rndis->ethaddr); + +-#if 0 +-// FIXME +- if (rndis_set_param_vendor(rndis->config, vendorID, +- manufacturer)) +- goto fail0; +-#endif ++ if (rndis->manufacturer && rndis->vendorID && ++ rndis_set_param_vendor(rndis->config, rndis->vendorID, ++ rndis->manufacturer)) ++ goto fail; + + /* NOTE: all that is done without knowing or caring about + * the network link ... which is unavailable to this code +@@ -854,6 +854,13 @@ static inline bool can_support_rndis(struct usb_configuration *c) + */ + int + rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) ++{ ++ return rndis_bind_config_vendor(c, ethaddr, 0, NULL); ++} ++ ++int ++rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], ++ u32 vendorID, const char *manufacturer) + { + struct f_rndis *rndis; + int status; +@@ -861,14 +868,14 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) + if (!can_support_rndis(c) || !ethaddr) + return -EINVAL; + ++ /* setup RNDIS itself */ ++ status = rndis_init(); ++ if (status < 0) ++ return status; ++ + /* maybe allocate device-global string IDs */ + if (rndis_string_defs[0].id == 0) { + +- /* ... and setup RNDIS itself */ +- status = rndis_init(); +- if (status < 0) +- return status; +- + /* control interface label */ + status = usb_string_id(c->cdev); + if (status < 0) +@@ -898,6 +905,8 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) + goto fail; + + memcpy(rndis->ethaddr, ethaddr, ETH_ALEN); ++ rndis->vendorID = vendorID; ++ rndis->manufacturer = manufacturer; + + /* RNDIS activates when the host changes this filter */ + rndis->port.cdc_filter = 0; +diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c +index a896d73f..ab443a09 100644 +--- a/drivers/usb/gadget/file_storage.c ++++ b/drivers/usb/gadget/file_storage.c +@@ -257,7 +257,6 @@ + #include "gadget_chips.h" + + +- + /* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use +@@ -335,7 +334,11 @@ static struct { + .vendor = FSG_VENDOR_ID, + .product = FSG_PRODUCT_ID, + .release = 0xffff, // Use controller chip type ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ .buflen = 65536 ++#else + .buflen = 16384, ++#endif + }; + + +@@ -555,10 +558,15 @@ device_desc = { + .idVendor = cpu_to_le16(FSG_VENDOR_ID), + .idProduct = cpu_to_le16(FSG_PRODUCT_ID), + .bcdDevice = cpu_to_le16(0xffff), +- ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ .iManufacturer = 0,//STRING_MANUFACTURER, ++ .iProduct = 0,//STRING_PRODUCT, ++ .iSerialNumber = 0,//STRING_SERIAL, ++#else + .iManufacturer = FSG_STRING_MANUFACTURER, + .iProduct = FSG_STRING_PRODUCT, + .iSerialNumber = FSG_STRING_SERIAL, ++#endif + .bNumConfigurations = 1, + }; + +@@ -2360,6 +2368,10 @@ static int check_command_size_in_blocks(struct fsg_dev *fsg, int cmnd_size, + mask, needs_medium, name); + } + ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++#include "plat-anyka/anyka_usbburn.c" //modified by anyka Zhang Jingyuan ++#endif ++ + static int do_scsi_command(struct fsg_dev *fsg) + { + struct fsg_buffhd *bh; +@@ -2563,6 +2575,23 @@ static int do_scsi_command(struct fsg_dev *fsg) + "WRITE(12)")) == 0) + reply = do_write(fsg); + break; ++ ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ case SCSI_ANYKA_UBOOT: ++ fsg->data_size_from_cmnd = fsg->data_size; ++ if ((reply = check_anyka_command(fsg, 1)) == 0) ++ reply = usbburn_write(fsg->cmnd, 16 + 8); ++ if (fsg->data_size_from_cmnd > 0) { ++ if (fsg->data_dir == DATA_DIR_TO_HOST) ++ reply = do_anyka_read(fsg); ++ else ++ reply = do_anyka_write(fsg); ++ } ++ down(&sense_data_lock); ++ fsg->curlun->sense_data = sense_data; ++ break; ++ //end of modified by anyka Zhang Jingyuan ++#endif + + /* Some mandatory commands that we recognize but don't implement. + * They don't mean much in this setting. It's left as an exercise +diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h +index a8855d0b..941b498a 100644 +--- a/drivers/usb/gadget/gadget_chips.h ++++ b/drivers/usb/gadget/gadget_chips.h +@@ -50,6 +50,7 @@ + #define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name)) + #define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name)) + #define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name)) ++#define gadget_is_ak_hsudc(g) (!strcmp("ak-hsudc", (g)->name)) + + /** + * usb_gadget_controller_number - support bcdDevice id convention +@@ -118,6 +119,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) + return 0x31; + else if (gadget_is_dwc3(gadget)) + return 0x32; ++ else if (gadget_is_ak_hsudc(gadget)) ++ return 0x33; + + return -ENOENT; + } +diff --git a/drivers/usb/gadget/plat-anyka/Kconfig b/drivers/usb/gadget/plat-anyka/Kconfig +new file mode 100644 +index 00000000..da55d4ab +--- /dev/null ++++ b/drivers/usb/gadget/plat-anyka/Kconfig +@@ -0,0 +1,37 @@ ++config USB_GADGET_AKUDC_PRODUCER ++ bool "Anyka usb device Port for PRODUCER" ++ depends on ARCH_AK39 ++ select USB_GADGET_DUALSPEED ++ help ++ Anyka USB-OTG device support for producer ++ ++if USB_GADGET_AKUDC_PRODUCER ++config USB_AKUDC_PRODUCER ++ tristate ++ default y ++endif ++ ++config USB_GADGET_AKUDC ++ bool "Anyka usb device Port" ++ depends on ARCH_AK39 ++ select USB_GADGET_DUALSPEED ++ help ++ Anyka USB-OTG gadget device support ++ ++config USB_AKUDC ++ tristate "udc driver support(usb-otg)" ++ depends on USB_GADGET_AKUDC ++ default USB_GADGET ++ help ++ udc driver in ak39xx platfrom ++ ++config USB_AKUDC_DEBUG_FS ++ bool "Debugging information files (DEVELOPMENT)" ++ depends on USB_AKUDC ++ help ++ Some of the drivers in the "gadget" framework can expose debugging information ++ in files such as /proc/driver/udc (for a peripheral controller). The information in these ++ files may help when you're troubleshooting or bringing up a driver on a new board. ++ ++ Enable these files by choosing "Y" here. If in doubt, or to conserve kernel memory, say "N". ++ +diff --git a/drivers/usb/gadget/plat-anyka/Makefile b/drivers/usb/gadget/plat-anyka/Makefile +new file mode 100644 +index 00000000..4534ddec +--- /dev/null ++++ b/drivers/usb/gadget/plat-anyka/Makefile +@@ -0,0 +1,8 @@ ++ ++obj-$(CONFIG_USB_AKUDC_PRODUCER) += udc.o ++ifdef CONFIG_USB_AKUDC_PRODUCER ++obj-y += usbburn.o ++endif ++ ++obj-$(CONFIG_USB_AKUDC) += udc.o ++ +diff --git a/drivers/usb/gadget/plat-anyka/anyka_usbburn.c b/drivers/usb/gadget/plat-anyka/anyka_usbburn.c +new file mode 100644 +index 00000000..5685a1b8 +--- /dev/null ++++ b/drivers/usb/gadget/plat-anyka/anyka_usbburn.c +@@ -0,0 +1,188 @@ ++#include ++ ++/* ++ * to check whether the anyka command is correct ++ * modify from check_command in file_storage.c ++ */ ++static int check_anyka_command(struct fsg_dev *fsg, int needs_medium) ++{ ++ struct fsg_lun *curlun; ++ ++ fsg->residue = fsg->usb_amount_left = fsg->data_size; ++ ++ /* Check the LUN */ ++ if (fsg->lun >= 0 && fsg->lun < fsg->nluns) { ++ fsg->curlun = curlun = &fsg->luns[fsg->lun]; ++ curlun->sense_data = SS_NO_SENSE; ++ curlun->sense_data_info = 0; ++ curlun->info_valid = 0; ++ } else { ++ fsg->curlun = curlun = NULL; ++ fsg->bad_lun_okay = 0; ++ ++ DBG(fsg, "unsupported LUN %d\n", fsg->lun); ++ return -EINVAL; ++ } ++ ++ if (curlun && !fsg_lun_is_open(curlun) && needs_medium) { ++ curlun->sense_data = SS_MEDIUM_NOT_PRESENT; ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* ++ * to read data in akudc_usbburn char dev that come from producer ++ * modify from do_read in file_storage.c ++ */ ++static int do_anyka_read(struct fsg_dev *fsg) ++{ ++ struct fsg_buffhd *bh; ++ int rc; ++ u32 amount_left; ++ unsigned int amount; ++ ssize_t nread; ++ ++ /* Carry out the file reads */ ++ amount_left = fsg->data_size_from_cmnd; ++ if (unlikely(amount_left == 0)) ++ return -EIO; // No default reply ++ ++ for (;;) { ++ ++ /* Figure out how much we need to read: ++ * Try to read the remaining amount. ++ * But don't read more than the buffer size. ++ * And don't try to read past the end of the file. ++ * Finally, if we're not at a page boundary, don't read past ++ * the next page. ++ * If this means reading 0 then we were asked to read past ++ * the end of file. */ ++ amount = min((unsigned int) amount_left, mod_data.buflen); ++ ++ /* Wait for the next buffer to become available */ ++ bh = fsg->next_buffhd_to_fill; ++ while (bh->state != BUF_STATE_EMPTY) { ++ rc = sleep_thread(fsg); ++ if (rc) ++ return rc; ++ } ++ ++ nread = usbburn_read(bh->buf + nread, amount); ++ ++ amount_left -= nread; ++ fsg->residue -= nread; ++ bh->inreq->length = nread; ++ bh->state = BUF_STATE_FULL; ++ ++ if (nread < amount) ++ break; ++ if (amount_left == 0) ++ break; // No more left to read ++ ++ /* Send this buffer and go read some more */ ++ bh->inreq->zero = 0; ++ start_transfer(fsg, fsg->bulk_in, bh->inreq, ++ &bh->inreq_busy, &bh->state); ++ fsg->next_buffhd_to_fill = bh->next; ++ } ++ ++ return -EIO; // No default reply ++} ++ ++/* ++ * to write data to akudc_usbburn char dev that can be read by producer ++ * modify from do_write in file_storage.c ++ */ ++static int do_anyka_write(struct fsg_dev *fsg) ++{ ++ struct fsg_lun *curlun = fsg->curlun; ++ struct fsg_buffhd *bh; ++ int get_some_more; ++ u32 amount_left_to_req, amount_left_to_write; ++ loff_t file_offset; ++ unsigned int amount; ++ ssize_t nwritten; ++ int rc; ++ ++ /* Carry out the file writes */ ++ get_some_more = 1; ++ file_offset = 0; ++ amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; ++ ++ while (amount_left_to_write > 0) { ++ ++ /* Queue a request for more data from the host */ ++ bh = fsg->next_buffhd_to_fill; ++ if (bh->state == BUF_STATE_EMPTY && get_some_more) { ++ ++ amount = min(amount_left_to_req, mod_data.buflen); ++ ++ /* Get the next buffer */ ++ fsg->usb_amount_left -= amount; ++ amount_left_to_req -= amount; ++ if (amount_left_to_req == 0) ++ get_some_more = 0; ++ ++ /* amount is always divisible by 512, hence by ++ * the bulk-out maxpacket size */ ++ bh->outreq->length = bh->bulk_out_intended_length = ++ amount; ++ bh->outreq->short_not_ok = 1; ++ start_transfer(fsg, fsg->bulk_out, bh->outreq, ++ &bh->outreq_busy, &bh->state); ++ fsg->next_buffhd_to_fill = bh->next; ++ continue; ++ } ++ ++ /* Write the received data to the backing file */ ++ bh = fsg->next_buffhd_to_drain; ++ if (bh->state == BUF_STATE_EMPTY && !get_some_more) ++ break; // We stopped early ++ if (bh->state == BUF_STATE_FULL) { ++ smp_rmb(); ++ fsg->next_buffhd_to_drain = bh->next; ++ bh->state = BUF_STATE_EMPTY; ++ ++ /* Did something go wrong with the transfer? */ ++ if (bh->outreq->status != 0) { ++ curlun->sense_data = SS_COMMUNICATION_FAILURE; ++ // curlun->sense_data_info = file_offset >> 9; ++ curlun->info_valid = 1; ++ break; ++ } ++ ++ amount = bh->outreq->actual; ++ if (fsg->data_size_from_cmnd - file_offset < amount) { ++ LERROR(curlun, ++ "write %u @ %llu beyond end %llu\n", ++ amount, (unsigned long long) file_offset, ++ (unsigned long long) curlun->file_length); ++ amount = curlun->file_length - file_offset; ++ } ++ ++ /* Perform the write */ ++ nwritten = 0; ++ nwritten = usbburn_write(bh->buf + nwritten, amount); ++ ++ file_offset += nwritten; ++ amount_left_to_write -= nwritten; ++ fsg->residue -= nwritten; ++ ++ /* Did the host decide to stop early? */ ++ if (bh->outreq->actual != bh->outreq->length) { ++ fsg->short_packet_received = 1; ++ break; ++ } ++ continue; ++ } ++ ++ /* Wait for something to happen */ ++ rc = sleep_thread(fsg); ++ if (rc) ++ return rc; ++ } ++ ++ return -EIO; // No default reply ++} +diff --git a/drivers/usb/gadget/plat-anyka/udc.c b/drivers/usb/gadget/plat-anyka/udc.c +new file mode 100755 +index 00000000..37494006 +--- /dev/null ++++ b/drivers/usb/gadget/plat-anyka/udc.c +@@ -0,0 +1,2664 @@ ++/* ++ * akudc_udc -- driver for anyka USB peripheral controller ++ * Features ++ * The USB 2.0 HS OTG has following features: ++ * ? compliant with USB Specification Version 2.0 (HS) and On-The-Go supplement to ++ * the USB 2.0 specification ++ * ? operating as the host in point-to-point communications with another USB function ++ * or as a function controller for a USB peripheral ++ * ? supporting UTMI+ Level 2 Transceiver Interface ++ * ? 4 Transmit/Receive endpoints in addition to Endpoint 0 ++ * ? 3 DMA channels ++ * AUTHOR ANYKA Zhang Jingyuan ++ * 09-11-14 10:45:03 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_VERSION "30-May-2013" ++#define DRIVER_DESC "ANYKA AK39 USB Device Controller driver" ++ ++#if 0 ++#define dbg(fmt, arg...) printk("%s(%d): " fmt "\n", __func__, __LINE__, ##arg) ++#else ++#define dbg(fmt, arg...) ++#endif ++ ++static const char ep0name[] = "ep0"; ++static const char driver_name[] = "ak-hsudc"; ++ ++#define udc_readb(reg) __raw_readb(udc->baseaddr + (reg)) ++#define udc_readw(reg) __raw_readw(udc->baseaddr + (reg)) ++#define udc_readl(reg) __raw_readl(udc->baseaddr + (reg)) ++#define udc_writeb(reg, val) __raw_writeb(val, udc->baseaddr + (reg)) ++#define udc_writew(reg, val) __raw_writew(val, udc->baseaddr + (reg)) ++#define udc_writel(reg, val) __raw_writel(val, udc->baseaddr + (reg)) ++ ++static struct akudc_udc controller; ++struct workqueue_struct *ep_wqueue; //the workqueue for non-ep0 endpoint transmission ++ ++volatile int usb_detect; ++ ++unsigned int dma_rx; ++unsigned int dma_tx; ++dma_addr_t phys_rx; ++dma_addr_t phys_tx; ++ ++static volatile char flag = 0; ++static atomic_t udc_clk = ATOMIC_INIT(0); //the clk condition for udc ++static atomic_t usb_enable_flag = ATOMIC_INIT(0); //the current status of udc ++ ++#ifdef CONFIG_USB_AKUDC_DEBUG_FS ++#include ++#include ++ ++#define akudc_udc_read(udc, reg) \ ++ __raw_readl((udc)->baseaddr + (reg)) ++#define akudc_udc_write(udc, reg, val) \ ++ __raw_writel((val), (udc)->baseaddr + (reg)) ++ ++static const char debug_filename[] = "driver/udc"; ++ ++static char *parse_linestate(unsigned long value) ++{ ++ char *str; ++ ++ switch(value & USB_LINESTATE_WP) { ++ case 0: ++ str = "SE0"; ++ break; ++ case 1: ++ str = "'J'State"; ++ break; ++ case 2: ++ str = "'K'State"; ++ break; ++ case 3: ++ str = "SE1"; ++ break; ++ } ++ return str; ++} ++ ++static int parse_state(unsigned int val1, unsigned int val2) ++{ ++ if (val1 == val2) ++ return 1; ++ return 0; ++} ++ ++static int udc_seq_show(struct seq_file *s, void *data) ++{ ++ struct akudc_udc *udc = s->private; ++ unsigned long flags; ++ int i; ++ unsigned long tmp; ++ ++ seq_printf(s, "%s: version %s\n", "akudc", DRIVER_VERSION); ++ ++ local_irq_save(flags); ++ tmp = __raw_readl(USB_OP_MOD_REG); ++ seq_printf(s, "usb opmod control:0x%08x, linestate:%s,%s,%s,%s,%s\n\n", tmp, ++ parse_linestate(tmp), ++ (tmp & USB_DP_PU_EN) ? " DP en pullup" : " DP dis pullup", ++ parse_state(((tmp & USB_ID_CFG) >> 12), USB_ID_CLIENT) ? " slave" : " host", ++ parse_state(((tmp & USB_PHY_CFG) >> 6), USB_PHY_CLIENT) ? " slave" : " host", ++ (tmp & USB_SUSPEND_EN) ? "en suspend controller & transceiver" : " en suspend transceiver"); ++ ++ tmp = akudc_udc_read(udc, USB_POWER_CTRL); ++ seq_printf(s, "usb power control:0x%08x %s,%s,%s,%s,%s,%s\n\n", tmp, ++ (tmp & USB_ISO_UPDATE) ? " ISO" : " invalid", ++ (tmp & USB_HSPEED_EN) ? " Hspeed mode" : " Fspeed mode", ++ (tmp & USB_HSPEED_MODE) ? " HSmode success" : "", ++ (tmp & USB_RESUME_EN) ? " en resume" : " invalid", ++ (tmp & USB_SUSPEND_MODE)? " en suspend controller & transceiver" : "", ++ (tmp & USB_SUSPENDM_EN) ? " en suspendm" : " invalid"); ++ ++ tmp = akudc_udc_read(udc, USB_FRAME_NUM); ++ seq_printf(s, "frame=%d\n", tmp); ++ ++ tmp = akudc_udc_read(udc, USB_FUNCTION_ADDR); ++ seq_printf(s, "faddr=0x%p\n", tmp); ++ ++ tmp = __raw_readl(AK_VA_SYSCTRL + 0x1C); ++ seq_printf(s, "usb clock:%s\n", ++ (tmp & (1<<15)) ? " disable" : " enable"); ++ ++ local_irq_restore(flags); ++ return 0; ++} ++static int udc_debugfs_open(struct inode *inode, struct file *file) ++{ ++ single_open(file, udc_seq_show, PDE(inode)->data); ++} ++ ++static const struct file_operations proc_ops = { ++ .owner = THIS_MODULE, ++ .open = udc_debugfs_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++void create_debugfs_files(struct akudc_udc *udc) ++{ ++ udc->pde = proc_create_data(debug_filename, S_IRUGO, NULL, &proc_ops, udc); ++} ++ ++void remove_debugfs_files(struct akudc_udc *udc) ++{ ++ if(udc->pde) ++ remove_proc_entry(debug_filename, NULL); ++} ++#else ++static inline void create_debugfs_files(struct akudc_udc *udc) {} ++static inline void remove_debugfs_files(struct akudc_udc *udc) {} ++#endif ++ ++static void udc_reg_writel(unsigned long __iomem *__reg, ++ unsigned long value, int bits, int index) ++{ ++ unsigned long tmp; ++ tmp = __raw_readl(__reg); ++ tmp &= ~(((1 << bits)-1) << index); ++ tmp |= (value << index); ++ __raw_writel(tmp, __reg); ++} ++ ++#if 0 ++static unsigned long udc_reg_readl(unsigned long __iomem *__reg, ++ int bits, int index) ++{ ++ unsigned long tmp; ++ tmp = __raw_readl(__reg); ++ tmp = (tmp&(((1 << bits)-1) << index)) >> index; ++ return tmp; ++} ++#endif ++ ++static inline int whether_enable(void) ++{ ++ /* ++ * if all of the udc condition is 1 and it is disable, ++ * return 1 ++ */ ++ if ((atomic_read(&udc_clk) == 1) ++ && (atomic_read(&usb_enable_flag) == 0)) { ++ ++ atomic_set(&usb_enable_flag, 1); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static inline int whether_disable(void) ++{ ++ /* ++ * if any of the udc condition is 0 and it is enable, ++ * return 1 ++ */ ++ if ((atomic_read(&udc_clk) == 0) ++ && (atomic_read(&usb_enable_flag) == 1)) { ++ ++ atomic_set(&usb_enable_flag, 0); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* enable the ep interrupt */ ++static void ep_irq_enable(struct usb_ep *_ep) ++{ ++ static struct akudc_udc *udc = &controller; ++ ++ /* enable ep1 rx */ ++ if (!strcmp(_ep->name, udc->ep[1].ep.name)) ++ //udc_writew(USB_INTERRUPT_RX, udc_readw(USB_INTERRUPT_RX) | (0x1<<1)); ++ udc_writew(USB_INTERRUPT_TX, udc_readw(USB_INTERRUPT_TX) | (0x1<<1)); ++ ++ /* enable ep2 tx */ ++ if (!strcmp(_ep->name, udc->ep[2].ep.name)) ++ udc_writew(USB_INTERRUPT_TX, udc_readw(USB_INTERRUPT_TX) | (0x1<<2)); ++ ++ /* enable ep3 rx */ ++ if (!strcmp(_ep->name, udc->ep[3].ep.name)) ++ udc_writew(USB_INTERRUPT_RX, udc_readw(USB_INTERRUPT_RX) | (0x1<<3)); ++ ++ /* enable ep4 tx */ ++ if (!strcmp(_ep->name, udc->ep[4].ep.name)) ++ udc_writew(USB_INTERRUPT_TX, udc_readw(USB_INTERRUPT_TX) | (0x1<<4)); ++ ++ /* enable ep5 rx */ ++ if (!strcmp(_ep->name, udc->ep[5].ep.name)) ++ udc_writew(USB_INTERRUPT_RX, udc_readw(USB_INTERRUPT_RX) | (0x1<<5)); ++} ++ ++/* disable the ep interrupt */ ++static void ep_irq_disable(struct usb_ep *_ep) ++{ ++ static struct akudc_udc *udc = &controller; ++ ++ /* disable ep1 rx */ ++ if (!strcmp(_ep->name, udc->ep[1].ep.name)) ++ //udc_writew(USB_INTERRUPT_RX, udc_readw(USB_INTERRUPT_RX) & ~(0x1<<1)); ++ udc_writew(USB_INTERRUPT_TX, udc_readw(USB_INTERRUPT_TX) & ~(0x1<<1)); ++ ++ /* disable ep2 tx */ ++ if (!strcmp(_ep->name, udc->ep[2].ep.name)) ++ udc_writew(USB_INTERRUPT_TX, udc_readw(USB_INTERRUPT_TX) & ~(0x1<<2)); ++ ++ /* disable ep3 rx */ ++ if (!strcmp(_ep->name, udc->ep[3].ep.name)) ++ udc_writew(USB_INTERRUPT_RX, udc_readw(USB_INTERRUPT_RX) & ~(0x1<<3)); ++ ++ /* disable ep4 tx */ ++ if (!strcmp(_ep->name, udc->ep[4].ep.name)) ++ udc_writew(USB_INTERRUPT_TX, udc_readw(USB_INTERRUPT_TX) & ~(0x1<<4)); ++ ++ /* disable ep5 rx */ ++ if (!strcmp(_ep->name, udc->ep[5].ep.name)) ++ udc_writew(USB_INTERRUPT_RX, udc_readw(USB_INTERRUPT_RX) & ~(0x1<<5)); ++} ++ ++ ++/* ++ * enable or disable the udc controller ++ * @enable: if 1, enable. if 0, disable ++ */ ++void usbcontroller_enable(int enable) ++{ ++ struct akudc_udc *udc = &controller; ++ if (enable) { ++ clk_enable(udc->clk); //enable clk for udc ++ /* reset usb phy and suspend transceiver, and power up(ak98) */ ++ udc_reg_writel(USB_OP_MOD_REG, 0x6, 3, 0); ++ set_usb_as_slave(); ++ ++ udc_writeb(USB_POWER_CTRL, 1<<5); // enable high speed negotiation ++ udc_writeb(USB_INTERRUPT_TX, 0x1<<0); // enable ep0 interrupt and disable other tx endpoint ++ udc_writeb(USB_INTERRUPT_RX, 0); // disable rx endpoint inttrupt ++ } else { ++ clk_disable(udc->clk); //disable clk for udc ++ /* reset usb phy */ ++ udc_reg_writel(USB_OP_MOD_REG, 0x1, 3, 0); ++ udc_writeb(USB_POWER_CTRL, 0); ++ ak_soft_reset(AK_SRESET_USBHS); //reset the udc controller ++ } ++} ++ ++/* ++ * set the udc condition, and judge whether the udc ++ * can be enabled, if is , enable it ++ */ ++void set_condition(atomic_t *cond) { ++ atomic_set(cond, 1); ++ if (whether_enable()) ++ usbcontroller_enable(1); ++} ++ ++/* ++ * clear the udc condition, and judge whether the udc ++ * can be disabled, if is , disable it ++ */ ++void clear_condition(atomic_t *cond) { ++ atomic_set(cond, 0); ++ if (whether_disable()) ++ usbcontroller_enable(0); ++} ++ ++/* ++ * enable the udc ++ */ ++ ++/* ++ * when the req is complete, call this function ++ * @ep: which ep's request is complete ++ * @req: the request which is complete ++ * @status: the request's status ++ */ ++static void done(struct akudc_ep *ep, struct akudc_request *req, int status) ++{ ++ unsigned stopped = ep->stopped; ++ ++ /* delete req from ep queue */ ++ list_del_init(&req->queue); ++ ++ if (likely (req->req.status == -EINPROGRESS)) ++ req->req.status = status; ++ else ++ status = req->req.status; ++ ++ ep->stopped = 1; ++ /* call complete callback before return */ ++ req->req.complete(&ep->ep, &req->req); ++ ep->stopped = stopped; ++ ++ dbg("%s done, req.status(%d)", ep->ep.name, req->req.status); ++} ++ ++/* ++ * send data to pc in ep0 ++ * @ep: point to ep0 ++ * @req: the request which is doing ++ * return: 0 = still running, 1 = completed, negative = errno ++ */ ++static int write_ep0_fifo(struct akudc_ep *ep, struct akudc_request *req) ++{ ++ int i; ++ unsigned total, count, is_last; ++ struct akudc_udc *udc = ep->udc; ++ unsigned char *buf; ++ ++ total = req->req.length - req->req.actual; ++ ++ if (ep->ep.maxpacket < total) { /* if this transition is not last */ ++ count = ep->ep.maxpacket; ++ is_last = 0; ++ } else { /* if this transition is last */ ++ count = total; ++ /* ++ * if the req zero flag not set, the "count == ep->ep.maxpacket" ++ * is the last ++ */ ++ is_last = (count < ep->ep.maxpacket) || !req->req.zero; ++ } ++ ++ dbg("is_last(%d), count(%d), total(%d), actual(%d), length(%d)", ++ is_last, count, total, req->req.actual, req->req.length); ++ ++ udc_writeb(USB_EP_INDEX, 0); ++ /* send zero packet */ ++ if (count == 0) { ++ dbg(" count == 0 "); ++ udc_writel(USB_EP0_NUM, count&0x7f); ++ udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<1); ++ udc->ep0state = EP0_IDLE; ++ done(ep, req, 0); ++ return 1; ++ } ++ ++ buf = req->req.buf + req->req.actual; ++ for (i = 0; i < count; i++) { ++ /* write data to the ep0 fifo */ ++ udc_writeb(USB_EP0_FIFO, buf[i]); ++ } ++ ++ udc_writel(USB_EP0_NUM, count&0x7f); /* 7bits */ ++ if (is_last) /* if last, set data end */ ++ udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<1); ++ else ++ udc_writeb(USB_CTRL_1, 0x1<<1); ++ ++ req->req.actual += count; ++ if (is_last) { /* if last ,done the req with status 0 */ ++ udc->ep0state = EP0_IDLE; ++ done(ep, req, 0); ++ } ++ ++ ep->done = is_last; ++ return is_last; ++ /* return 1; */ ++} ++ ++/* ++ * read data from pc in ep0 ++ * @ep: point to ep0 ++ * @req: the request which is doing ++ * return: 0 = still running, 1 = completed, negative = errno ++ */ ++static int read_ep0_fifo(struct akudc_ep *ep, struct akudc_request *req) ++{ ++ int i; ++ unsigned total, count, is_last; ++ struct akudc_udc *udc = ep->udc; ++ unsigned char *buf; ++ ++ total = req->req.length - req->req.actual; ++ ++ if (ep->ep.maxpacket < total) { ++ count = ep->ep.maxpacket; ++ is_last = 0; ++ } else { ++ count = total; ++ is_last = (count < ep->ep.maxpacket) || !req->req.zero; ++ } ++ ++ dbg("is_last(%d), count(%d), total(%d), actual(%d), length(%d)", ++ is_last, count, total, req->req.actual, req->req.length); ++ ++ udc_writeb(USB_EP_INDEX, 0); ++ /* read zero packet */ ++ if (count == 0) { ++ dbg(" count == 0 "); ++ udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<6); ++ udc->ep0state = EP0_IDLE; ++ done(ep, req, 0); ++ return 1; ++ } ++ ++ buf = req->req.buf + req->req.actual; ++ for (i = 0; i < count; i++) { ++ /* read data from ep0 fifo */ ++ buf[i] = udc_readb(USB_EP0_FIFO); ++ } ++ ++ if (is_last) /* if last, set data end */ ++ udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<6); ++ else ++ udc_writeb(USB_CTRL_1, 0x1<<6); ++ ++ req->req.actual += count; ++ if (is_last) { /* if last ,done the req with status 0 */ ++ udc->ep0state = EP0_IDLE; ++ done(ep, req, 0); ++ } ++ ++ ep->done = is_last; ++ return is_last; ++ /* return 1; */ ++} ++ ++/* the ep1 is not used */ ++static int write_ep1_fifo(struct akudc_ep *ep, struct akudc_request *req) ++{ ++ struct akudc_udc *udc = ep->udc; ++ unsigned total, count, is_last; ++ unsigned char *buf; ++ int dma = 0, i; ++ ++ total = req->req.length - req->req.actual; ++ if (ep->ep.maxpacket <= total) { ++ count = ep->ep.maxpacket; ++ is_last = (total == ep->ep.maxpacket) && !req->req.zero; ++#if 0 ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ if (udc->high_speed) ++ dma = 1; ++#endif ++#endif ++ } else { ++ count = total; ++ is_last = count < ep->ep.maxpacket; ++ dma = 0; ++ } ++ dbg("total(%d), count(%d), is_last(%d)", total, count, is_last); ++ ++ if (count == 0) { /* not support command */ ++ dbg("count == 0\n"); ++ ep->done = 1; ++ udc_writeb(USB_EP_INDEX, 1); ++ udc_writeb(USB_CTRL_1, 0x1); ++ return 0; ++ } ++ udc_writeb(USB_EP_INDEX, 1); ++ ++ buf = req->req.buf + req->req.actual; ++#if 0 ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ dbg("producer"); ++ if (udc->high_speed && (dma == 1)) { ++ dma_tx = total - (total % ep->ep.maxpacket); ++ /* map the dma buffer */ ++ phys_tx = dma_map_single(NULL, buf, dma_tx, DMA_TO_DEVICE); ++ if (phys_tx == 0) { ++ printk("tx dma_map_single error!\n"); ++ goto cpu; ++ } ++ ++ udc_writeb(USB_CTRL_1_2, (1<<2) | (1<<4) | (1<<5) | (1<<7)); ++ ++ //send data to l2 ++ l2_clr_status(ep->l2_buf_id); ++ l2_combuf_dma(phys_tx, ep->l2_buf_id, dma_tx, MEM2BUF, false); ++ udc_writel(USB_DMA_ADDR3, 0x72000000); ++ udc_writel(USB_DMA_COUNT3, dma_tx); ++ udc_writel(USB_DMA_CTRL3, (USB_ENABLE_DMA | USB_DIRECTION_TX | USB_DMA_MODE1 | USB_DMA_INT_ENABLE| (USB_EP4_INDEX<<4) | USB_DMA_BUS_MODE3)); ++ ++ ep->done = 0; ++ return 0; ++ } ++#endif ++cpu: ++#endif ++ ++ for (i = 0; i < count; i++) { ++ /* send data to ep4 fifo without dma */ ++ udc_writeb(USB_EP1_FIFO, buf[i]); ++ } ++ ++ udc_writeb(USB_CTRL_1, 0x1); ++ ++ req->req.actual += count; ++ /* wait a tx complete int */ ++ if (is_last) ++ done(ep, req, 0); ++ ++ return is_last; ++} ++ ++/* ++ * send data to pc in ep2 ++ * @ep: point to ep2 ++ * @req: the request which is doing ++ */ ++static int write_ep2_fifo(struct akudc_ep *ep, struct akudc_request *req) /* ep2 */ ++{ ++ struct akudc_udc *udc = ep->udc; ++ unsigned total, count, is_last; ++ unsigned char *buf; ++ int dma = 0, i; ++ ++ total = req->req.length - req->req.actual; ++ if (ep->ep.maxpacket <= total) { ++ count = ep->ep.maxpacket; ++ is_last = (total == ep->ep.maxpacket) && !req->req.zero; ++#if 0 ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ if (udc->high_speed) ++ dma = 1; ++#endif ++#endif ++ } else { ++ count = total; ++ is_last = count < ep->ep.maxpacket; ++ dma = 0; ++ } ++ dbg("total(%d), count(%d), is_last(%d)", total, count, is_last); ++ ++ if (count == 0) { /* not support command */ ++ dbg("count == 0\n"); ++ ep->done = 1; ++ udc_writeb(USB_EP_INDEX, 2); ++ udc_writeb(USB_CTRL_1, 0x1); ++ return 0; ++ } ++ udc_writeb(USB_EP_INDEX, 2); ++ ++ buf = req->req.buf + req->req.actual; ++#if 0 ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ if (udc->high_speed && (dma == 1)) { ++ dma_tx = total - (total % ep->ep.maxpacket); ++ /* map the dma buffer */ ++ phys_tx = dma_map_single(NULL, buf, dma_tx, DMA_TO_DEVICE); ++ if (phys_tx == 0) { ++ printk("tx dma_map_single error!\n"); ++ goto cpu; ++ } ++ ++ udc_writeb(USB_CTRL_1_2, (1<<2) | (1<<4) | (1<<5) | (1<<7)); ++ ++ //send data to l2 ++ l2_clr_status(ep->l2_buf_id); ++ l2_combuf_dma(phys_tx, ep->l2_buf_id, dma_tx, MEM2BUF, false); ++ udc_writel(USB_DMA_ADDR1, 0x70000000); ++ udc_writel(USB_DMA_COUNT1, dma_tx); ++ udc_writel(USB_DMA_CTRL1, (USB_ENABLE_DMA | USB_DIRECTION_TX | USB_DMA_MODE1 | USB_DMA_INT_ENABLE| (USB_EP2_INDEX<<4) | USB_DMA_BUS_MODE3)); ++ ++ ep->done = 0; ++ return 0; ++ } ++#endif ++cpu: ++#endif ++ ++ for (i = 0; i < count; i++) { ++ /* send data to ep2 fifo without dma */ ++ udc_writeb(USB_EP2_FIFO, buf[i]); ++ } ++ udc_writeb(USB_CTRL_1, 0x1); ++ ++ req->req.actual += count; ++ /* wait a tx complete int */ ++ /* ++ * if (is_last) ++ * done(ep, req, 0); ++ */ ++ ++ /* return is_last; */ ++ ep->done = is_last; ++ ++ return 0; ++} ++ ++/* ++ * read data from pc in ep3 ++ * @ep: point to ep3 ++ * @req: the request which is doing ++ */ ++static int read_ep3_fifo(struct akudc_ep *ep, struct akudc_request *req) /* ep3 */ ++{ ++ struct akudc_udc *udc = ep->udc; ++ unsigned char *buf; ++ unsigned int csr, i; ++ unsigned int count, bufferspace, is_done; ++ ++ if (flag == 1) ++ return 0; ++ ++ bufferspace = req->req.length - req->req.actual; ++ ++ udc_writeb(USB_EP_INDEX, 3); ++ csr = udc_readb(USB_CTRL_2); ++ if ((csr & 0x1) == 0) { ++ dbg("waiting bulkout data"); ++ return 0; ++ } ++ ++ count = udc_readw(USB_EP_COUNT); ++ if (count == 0) { ++ dbg("USB_CTRL_2(0x%x), USB_EP_COUNT(%d), bufferspace(%d)", ++ csr, count, bufferspace); ++ goto stall; ++ } else if (count > ep->ep.maxpacket) ++ count = ep->ep.maxpacket; ++ ++ if (count > bufferspace) { ++ dbg("%s buffer overflow\n", ep->ep.name); ++ req->req.status = -EOVERFLOW; ++ count = bufferspace; ++ } ++ dbg("USB_CTRL_2(0x%x), USB_EP_COUNT(%d), bufferspace(%d)", csr, count, bufferspace); ++ ++ buf = req->req.buf + req->req.actual; ++ ++ for (i = 0; i < count; i++) { ++ /* read data from ep3 fifo without dma */ ++ buf[i] = udc_readb(USB_EP3_FIFO); ++ } ++#if 0 ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ if (udc->high_speed) { ++ dma_rx = bufferspace - count; ++ if (dma_rx >= ep->ep.maxpacket) { ++ dma_rx -= (dma_rx % ep->ep.maxpacket); ++ ++ buf = req->req.buf + req->req.actual + count; ++ /* send data to ep2 fifo without dma */ ++ phys_rx = dma_map_single(NULL, buf, dma_rx, DMA_FROM_DEVICE); ++ if (phys_rx == 0) { ++ printk("rx dma_map_single error!\n"); ++ goto stall; ++ } ++ ++ req->req.actual += count; ++ flag = 1; ++ udc_writeb(USB_CTRL_2_2, udc_readb(USB_CTRL_2_2) | (1<<3) | (1<<5) | (1<<7)); ++ l2_combuf_dma(phys_rx, ep->l2_buf_id, dma_rx, BUF2MEM, false); ++ udc_writel(USB_DMA_ADDR2, 0x71000000); ++ udc_writel(USB_DMA_COUNT2, dma_rx); ++ udc_writel(USB_DMA_CTRL2, (USB_ENABLE_DMA|USB_DIRECTION_RX|USB_DMA_MODE1|USB_DMA_INT_ENABLE|(USB_EP3_INDEX<<4)|USB_DMA_BUS_MODE3)); ++ udc_writeb(USB_EP_INDEX, 3); ++ udc_writeb(USB_CTRL_2, csr & ~0x1); ++ ep->done = 0; ++ ++ return 0; ++ } ++ } ++#endif ++#endif ++stall: ++ udc_writeb(USB_CTRL_2, csr & ~0x1); ++ ++ req->req.actual += count; ++ is_done = (count < ep->ep.maxpacket); ++ if (count == bufferspace) ++ is_done = 1; ++ ++ ep->done = is_done; ++ if (is_done) { ++ done(ep, req, 0); ++ } ++ ++ return is_done; ++} ++ ++/* ++ * send data to pc in ep4 ++ * @ep: point to ep4 ++ * @req: the request which is doing ++ */ ++static int write_ep4_fifo(struct akudc_ep *ep, struct akudc_request *req) /* ep4 */ ++{ ++ struct akudc_udc *udc = ep->udc; ++ unsigned total, count, is_last; ++ unsigned char *buf; ++ int dma = 0, i; ++ ++ total = req->req.length - req->req.actual; ++ if (ep->ep.maxpacket <= total) { ++ count = ep->ep.maxpacket; ++ is_last = (total == ep->ep.maxpacket) && !req->req.zero; ++#if 0 ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ if (udc->high_speed) ++ dma = 1; ++#endif ++#endif ++ } else { ++ count = total; ++ is_last = count < ep->ep.maxpacket; ++ dma = 0; ++ } ++ dbg("total(%d), count(%d), is_last(%d)", total, count, is_last); ++ ++ if (count == 0) { /* not support command */ ++ dbg("count == 0\n"); ++ ep->done = 1; ++ udc_writeb(USB_EP_INDEX, 4); ++ udc_writeb(USB_CTRL_1, 0x1); ++ return 0; ++ } ++ udc_writeb(USB_EP_INDEX, 4); ++ ++ buf = req->req.buf + req->req.actual; ++#if 0 ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ if (udc->high_speed && (dma == 1)) { ++ dma_tx = total - (total % ep->ep.maxpacket); ++ /* map the dma buffer */ ++ phys_tx = dma_map_single(NULL, buf, dma_tx, DMA_TO_DEVICE); ++ if (phys_tx == 0) { ++ printk("tx dma_map_single error!\n"); ++ goto cpu; ++ } ++ ++ udc_writeb(USB_CTRL_1_2, (1<<2) | (1<<4) | (1<<5) | (1<<7)); ++ ++ //send data to l2 ++ l2_clr_status(ep->l2_buf_id); ++ l2_combuf_dma(phys_tx, ep->l2_buf_id, dma_tx, MEM2BUF, false); ++ udc_writel(USB_DMA_ADDR3, 0x72000000); ++ udc_writel(USB_DMA_COUNT3, dma_tx); ++ udc_writel(USB_DMA_CTRL3, (USB_ENABLE_DMA | USB_DIRECTION_TX | USB_DMA_MODE1 | USB_DMA_INT_ENABLE| (USB_EP4_INDEX<<4) | USB_DMA_BUS_MODE3)); ++ ++ ep->done = 0; ++ return 0; ++ } ++#endif ++cpu: ++#endif ++ ++ for (i = 0; i < count; i++) { ++ /* send data to ep4 fifo without dma */ ++ udc_writeb(USB_EP4_FIFO, buf[i]); ++ } ++ ++ udc_writeb(USB_CTRL_1, 0x1); ++ ++ req->req.actual += count; ++ /* wait a tx complete int */ ++ /* ++ * if (is_last) ++ * done(ep, req, 0); ++ */ ++ ++ /* return is_last; */ ++ ep->done = is_last; ++ ++ return 0; ++} ++ ++/* ++ * read data from pc in ep5 ++ * @ep: point to ep5 ++ * @req: the request which is doing ++ */ ++static int read_ep5_fifo(struct akudc_ep *ep, struct akudc_request *req) /* ep5 */ ++{ ++ struct akudc_udc *udc = ep->udc; ++ unsigned char *buf; ++ unsigned int csr, i; ++ unsigned int count, bufferspace, is_done; ++ ++ if (flag == 1) ++ return 0; ++ ++ bufferspace = req->req.length - req->req.actual; ++ ++ udc_writeb(USB_EP_INDEX, 5); ++ csr = udc_readb(USB_CTRL_2); ++ if ((csr & 0x1) == 0) { ++ dbg("waiting bulkout data"); ++ return 0; ++ } ++ ++ count = udc_readw(USB_EP_COUNT); ++ if (count == 0) { ++ dbg("USB_CTRL_2(0x%x), USB_EP_COUNT(%d), bufferspace(%d)", ++ csr, count, bufferspace); ++ goto stall; ++ } else if (count > ep->ep.maxpacket) ++ count = ep->ep.maxpacket; ++ ++ if (count > bufferspace) { ++ dbg("%s buffer overflow\n", ep->ep.name); ++ req->req.status = -EOVERFLOW; ++ count = bufferspace; ++ } ++ dbg("USB_CTRL_2(0x%x), USB_EP_COUNT(%d), bufferspace(%d)", csr, count, bufferspace); ++ ++ buf = req->req.buf + req->req.actual; ++ ++ for (i = 0; i < count; i++) { ++ /* read data from ep5 fifo without dma */ ++ buf[i] = udc_readb(USB_EP5_FIFO); ++ } ++#if 0 ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ if (udc->high_speed) { ++ dma_rx = bufferspace - count; ++ if (dma_rx >= ep->ep.maxpacket) { ++ dma_rx -= (dma_rx % ep->ep.maxpacket); ++ ++ buf = req->req.buf + req->req.actual + count; ++ /* map the dma buffer */ ++ phys_rx = dma_map_single(NULL, buf, dma_rx, DMA_FROM_DEVICE); ++ if (phys_rx == 0) { ++ printk("rx dma_map_single error!\n"); ++ goto stall; ++ } ++ ++ req->req.actual += count; ++ flag = 1; ++ udc_writeb(USB_CTRL_2_2, udc_readb(USB_CTRL_2_2) | (1<<3) | (1<<5) | (1<<7)); ++ l2_combuf_dma(phys_rx, ep->l2_buf_id, dma_rx, BUF2MEM, false); ++ udc_writel(USB_DMA_ADDR4, 0x73000000); ++ udc_writel(USB_DMA_COUNT4, dma_rx); ++ udc_writel(USB_DMA_CTRL4, (USB_ENABLE_DMA|USB_DIRECTION_RX|USB_DMA_MODE1|USB_DMA_INT_ENABLE|(USB_EP5_INDEX<<4)|USB_DMA_BUS_MODE3)); ++ udc_writeb(USB_EP_INDEX, 5); ++ udc_writeb(USB_CTRL_2, csr & ~0x1); ++ ep->done = 0; ++ ++ return 0; ++ } ++ } ++#endif ++#endif ++ ++stall: ++ udc_writeb(USB_CTRL_2, csr & ~0x1); ++ ++ req->req.actual += count; ++ is_done = (count < ep->ep.maxpacket); ++ if (count == bufferspace) ++ is_done = 1; ++ ++ ep->done = is_done; ++ if (is_done) { ++ done(ep, req, 0); ++ } ++ ++ return is_done; ++} ++ ++ ++/* the funciton is not used */ ++static int akudc_get_frame(struct usb_gadget *gadget) ++{ ++ dbg(""); ++ return 0; ++} ++ ++/* the funciton is not used */ ++static int akudc_wakeup(struct usb_gadget *gadget) ++{ ++ dbg(""); ++ return 0; ++} ++ ++/* ++ * the function to enable or disable the udc ++ * @is_on: 1,enable 0,disable ++ */ ++static int akudc_pullup(struct usb_gadget *gadget, int is_on) ++{ ++ if (is_on) ++ set_condition(&udc_clk); ++ else ++ clear_condition(&udc_clk); ++ return 0; ++} ++ ++/* the function is not used */ ++static int akudc_vbus_session(struct usb_gadget *gadget, int is_active) ++{ ++ dbg(""); ++ return 0; ++} ++ ++/* ++ * the function set the device power status ++ * @value: 1,selfpowered 0,powered by usb cable ++ */ ++static int akudc_set_selfpowered(struct usb_gadget *gadget, int value) ++{ ++ struct akudc_udc *udc = &controller; ++ ++ dbg("%d", value); ++ if (value) ++ udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED); ++ else ++ udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); ++ ++ return 0; ++} ++ ++static int akudc_start(struct usb_gadget_driver *driver, ++ int (*bind)(struct usb_gadget *)); ++static int akudc_stop(struct usb_gadget_driver *driver); ++ ++/* the operation struct for gadget */ ++static const struct usb_gadget_ops akudc_udc_ops = { ++ .get_frame = akudc_get_frame, ++ .wakeup = akudc_wakeup, ++ .set_selfpowered = akudc_set_selfpowered, ++ .vbus_session = akudc_vbus_session, ++ .pullup = akudc_pullup, ++ .start = akudc_start, ++ .stop = akudc_stop, ++}; ++ ++static void ep1_work(struct work_struct *work) ++{ ++ struct akudc_request *req = NULL; ++ struct akudc_udc *udc = &controller; ++ struct akudc_ep *ep = &udc->ep[1]; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ /* if the queue is not empty, get the head of the queue */ ++ if (!list_empty(&ep->queue)){ ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ } ++ ++ if (req) ++ req->status = write_ep1_fifo(ep, req); ++ else ++ dbg("something happend"); ++ local_irq_restore(flags); ++} ++ ++static void ep2_work(struct work_struct *work) ++{ ++ struct akudc_request *req = NULL; ++ struct akudc_udc *udc = &controller; ++ struct akudc_ep *ep = &udc->ep[2]; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ /* if the queue is not empty, get the head of the queue */ ++ if (!list_empty(&ep->queue)) ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ ++ if (req) ++ req->status = write_ep2_fifo(ep, req); ++ else ++ dbg("something happend"); ++ local_irq_restore(flags); ++} ++ ++static void ep3_work(struct work_struct *work) ++{ ++ struct akudc_request *req = NULL; ++ struct akudc_udc *udc = &controller; ++ struct akudc_ep *ep = &udc->ep[3]; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ /* if the queue is not empty, get the head of the queue */ ++ if (!list_empty(&ep->queue)) ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ ++ if (req) ++ req->status = read_ep3_fifo(ep, req); ++ else ++ dbg("something happend"); ++ local_irq_restore(flags); ++} ++ ++static void ep4_work(struct work_struct *work) ++{ ++ struct akudc_request *req = NULL; ++ struct akudc_udc *udc = &controller; ++ struct akudc_ep *ep = &udc->ep[4]; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ /* if the queue is not empty, get the head of the queue */ ++ if (!list_empty(&ep->queue)) ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ ++ if (req) ++ req->status = write_ep4_fifo(ep, req); ++ else ++ dbg("something happend"); ++ local_irq_restore(flags); ++} ++ ++static void ep5_work(struct work_struct *work) ++{ ++ struct akudc_request *req = NULL; ++ struct akudc_udc *udc = &controller; ++ struct akudc_ep *ep = &udc->ep[5]; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ /* if the queue is not empty, get the head of the queue */ ++ if (!list_empty(&ep->queue)) ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ ++ if (req) ++ req->status = read_ep5_fifo(ep, req); ++ else ++ dbg("something happend"); ++ local_irq_restore(flags); ++} ++ ++/* ++ * enable the ep, include enable the irq for the ep ++ * init the fifo and toggle for ep ++ * @ep: the ep which is enable ++ * @desc: the ep descriptor ++ */ ++static int akudc_ep_enable(struct usb_ep *_ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ struct akudc_ep *ep = container_of(_ep, struct akudc_ep, ep); ++ struct akudc_udc *udc = ep->udc; ++ int tmp, maxpacket; ++ unsigned long flags; ++ ++ if (!_ep || !ep ++ || !desc || ep->desc ++ || _ep->name == ep0name ++ || desc->bDescriptorType != USB_DT_ENDPOINT ++ || (maxpacket = usb_endpoint_maxp(desc)) == 0 ++ || maxpacket > ep->maxpacket) { ++ dbg("bad ep or descriptor"); ++ dbg("%p, %p, %p, %p", _ep, ep, desc, ep->desc); ++ return -EINVAL; ++ } ++ ++ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { ++ dbg("bogus udcice state\n"); ++ return -ESHUTDOWN; ++ } ++ ++ local_irq_save (flags); ++ ++ tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; ++ switch (tmp) { ++ case USB_ENDPOINT_XFER_CONTROL: ++ dbg("only one control endpoint\n"); ++ return -EINVAL; ++ case USB_ENDPOINT_XFER_INT: ++ if (maxpacket > EP1_FIFO_SIZE) ++ dbg("maxpacket too large"); ++ break; ++ case USB_ENDPOINT_XFER_BULK: ++ switch (maxpacket) { ++ case 8: ++ case 16: ++ case 32: ++ case 64: ++ case 512: /* for usb20 */ ++ _ep->maxpacket = maxpacket & 0x7ff; ++ break; ++ default: ++ dbg("bogus maxpacket %d\n", maxpacket); ++ return -EINVAL; ++ } ++ break; ++ case USB_ENDPOINT_XFER_ISOC: ++ dbg("USB_ENDPOINT_XFER_ISOC"); ++ break; ++ } ++ ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0; ++ ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); ++ ++ ep->stopped = 0; ++ ep->desc = (struct usb_endpoint_descriptor *)desc; ++ ++ dbg("%s, maxpacket(%d), desc->bEndpointAddress (0x%x) is_in(%d)", ++ _ep->name, maxpacket, desc->bEndpointAddress, ep->is_in); ++ ++ if (!strcmp(_ep->name, udc->ep[1].ep.name)) { /* ep1 -- int */ ++ dbg(""); ++ udc_writeb(USB_EP_INDEX, 1); ++ udc_writeb(USB_CTRL_1, (1<<3) | (1<<6)); // flush fifo and clear toggle ++ udc_writeb(USB_CTRL_1_2, 1<<5); //set tx ++ udc_writew(USB_TX_MAX, 64); //set maxpacket size ++ } else if (!strcmp(_ep->name, udc->ep[2].ep.name)) { /* ep2 -- tx */ ++ dbg(""); ++ udc_writeb(USB_EP_INDEX, 2); ++ udc_writeb(USB_CTRL_1, (1<<3) | (1<<6)); // flush fifo and clear toggle ++ udc_writeb(USB_CTRL_1_2, 1<<5); //set tx ++ udc_writew(USB_TX_MAX, 512); //set maxpacket size ++ } else if (!strcmp(_ep->name, udc->ep[3].ep.name)){ /* ep3 -- rx */ ++ udc_writeb(USB_EP_INDEX, 3); ++ udc_writeb(USB_CTRL_1, 1<<6); // clear tx toggle ++ udc_writeb(USB_CTRL_1_2, 0); // set rx ++ udc_writew(USB_RX_MAX, 512); // set maxpacket size ++ udc_writeb(USB_CTRL_2, udc_readb(USB_CTRL_2) & ~(0x1)); ++ udc_writeb(USB_CTRL_2, (1<<4) | (1<<7)); // flush fifo and clear rx toggle ++ udc_writeb(USB_CTRL_2_2, 0); //enable rx endpint ++ } else if (!strcmp(_ep->name, udc->ep[4].ep.name)) { /* ep4 -- tx */ ++ udc_writeb(USB_EP_INDEX, 4); ++ udc_writeb(USB_CTRL_1, (1<<3) | (1<<6)); // flush fifo and clear toggle ++ udc_writeb(USB_CTRL_1_2, 1<<5); //set tx ++ udc_writew(USB_TX_MAX, 512); //set maxpacket size ++ } else if (!strcmp(_ep->name, udc->ep[5].ep.name)){ /* ep5 -- rx */ ++ udc_writeb(USB_EP_INDEX, 5); ++ udc_writeb(USB_CTRL_1, 1<<6); // clear tx toggle ++ udc_writeb(USB_CTRL_1_2, 0); // set rx ++ udc_writew(USB_RX_MAX, 512); //set maxpacket size ++ udc_writeb(USB_CTRL_2, udc_readb(USB_CTRL_2) & ~(0x1)); ++ udc_writeb(USB_CTRL_2, (1<<4) | (1<<7)); // flush fifo and clear rx toggle ++ udc_writeb(USB_CTRL_2_2, 0); //enable rx endpint ++ } else { ++ printk("Invalid ep"); ++ return -EINVAL; ++ } ++ ++ ep_irq_enable(_ep); ++ local_irq_restore (flags); ++ ++ return 0; ++} ++ ++/* ++ * disable the ep, include disable the irq for the ep ++ * @ep: the ep which is disable ++ */ ++static int akudc_ep_disable (struct usb_ep * _ep) ++{ ++ struct akudc_ep *ep = container_of(_ep, struct akudc_ep, ep); ++ struct akudc_request *req; ++ unsigned long flags; ++ ++ if (!_ep || !ep->desc) { ++ dbg("%s not enabled\n", ++ _ep ? ep->ep.name : NULL); ++ return -EINVAL; ++ } ++ ++ local_irq_save(flags); ++ dbg("%s", _ep->name); ++ ep->desc = NULL; ++ ep->stopped = 1; ++ ++ /* ++ * delete every req in the endpoint queue ++ * dont it by status -ESHUTDOWN ++ */ ++ while (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ done(ep, req, -ESHUTDOWN); ++ } ++ ep_irq_disable(_ep); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++/* ++ * alloc the request for function driver ++ * @ep: the ep which the request deal with ++ */ ++static struct usb_request * ++ akudc_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) ++{ ++ struct akudc_request *req; ++ ++ dbg("%s", _ep->name); ++ req = kzalloc(sizeof (struct akudc_request), gfp_flags); ++ if (!req) ++ return NULL; ++ ++ INIT_LIST_HEAD(&req->queue); ++ return &req->req; ++} ++ ++/* ++ * free the request for function driver ++ * @ep: the ep which the request deal with ++ */ ++static void akudc_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct akudc_request *req; ++ ++ dbg("%s", _ep->name); ++ req = container_of(_req, struct akudc_request, req); ++ WARN_ON(!list_empty(&req->queue)); ++ kfree(req); ++} ++ ++/* ++ * add the request to the ep request queue ++ * @ep: the ep which the request deal with ++ */ ++static int akudc_ep_queue(struct usb_ep *_ep, ++ struct usb_request *_req, gfp_t gfp_flags) ++{ ++ struct akudc_request *req; ++ struct akudc_ep *ep; ++ struct akudc_udc *udc; ++ int status = 0; ++ unsigned long flags; ++ ++ req = container_of(_req, struct akudc_request, req); ++ ep = container_of(_ep, struct akudc_ep, ep); ++ ++ if (!_ep || (!ep->desc && ep->ep.name != ep0name)) { ++ dbg("invalid ep\n"); ++ return -EINVAL; ++ } ++ ++ udc = ep->udc; ++ if (!udc || !udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { ++ printk("invalid device\n"); ++ return -EINVAL; ++ } ++ ++ local_irq_save(flags); ++ ++ if (!_req || !_req->complete ++ || !_req->buf || !list_empty(&req->queue)) { ++ printk("%s invalid request\n", _ep->name); ++ local_irq_restore(flags); ++ return -EINVAL; ++ } ++ ++ _req->status = -EINPROGRESS; ++ _req->actual = 0; ++ ++ dbg("%s queue is_in(%d)", ep->ep.name, ep->is_in); ++ ++ /* ++ * if the queue is empty and the ep is not stopped, ++ * do the request ++ */ ++ if (list_empty(&ep->queue) && !ep->stopped) { ++ if (ep->ep.name == ep0name) { ++ udc_writeb(USB_EP_INDEX, 0); ++ ++ switch (udc->ep0state) { ++ case EP0_IN_DATA_PHASE: ++ if ((udc_readb(USB_CTRL_1) & (1 << 1)) == 0 ++ && write_ep0_fifo(ep, req)) { ++ udc->ep0state = EP0_IDLE; ++ req = NULL; ++ } ++ break; ++ ++ case EP0_OUT_DATA_PHASE: ++ if ((!_req->length) || ++ ((udc_readb(USB_CTRL_1) & (1 << 0)) ++ && read_ep0_fifo(ep, req))) { ++ udc->ep0state = EP0_IDLE; ++ req = NULL; ++ } ++ break; ++ ++ default: ++ local_irq_restore(flags); ++ return -EL2HLT; ++ } ++ if (req) ++ list_add_tail(&req->queue, &ep->queue); ++ } else { ++ list_add_tail(&req->queue, &ep->queue); ++ if (!strcmp(_ep->name, udc->ep[1].ep.name)) { /* ep1 */ ++ dbg("ep1"); ++ //status = write_ep1_fifo(ep, req); ++ udc_writeb(USB_EP_INDEX, 1); ++ if ((udc_readb(USB_CTRL_1) & 1) == 0) ++ queue_work(ep_wqueue, &ep->work); ++ } else if (!strcmp(_ep->name, udc->ep[2].ep.name)) { /* ep2 */ ++ udc_writeb(USB_EP_INDEX, 2); ++ if ((udc_readb(USB_CTRL_1) & 1) == 0) ++ queue_work(ep_wqueue, &ep->work); ++ } else if (!strcmp(_ep->name, udc->ep[3].ep.name)){ /* ep3 */ ++ udc_writeb(USB_EP_INDEX, 3); ++ if (udc_readb(USB_CTRL_2) & 1) ++ queue_work(ep_wqueue, &ep->work); ++ } else if (!strcmp(_ep->name, udc->ep[4].ep.name)) { /* ep4 */ ++ udc_writeb(USB_EP_INDEX, 4); ++ if ((udc_readb(USB_CTRL_1) & 1) == 0) ++ queue_work(ep_wqueue, &ep->work); ++ } else if (!strcmp(_ep->name, udc->ep[5].ep.name)){ /* ep5 */ ++ udc_writeb(USB_EP_INDEX, 5); ++ if (udc_readb(USB_CTRL_2) & 1) ++ queue_work(ep_wqueue, &ep->work); ++ } else { ++ printk("Invalid ep"); ++ status = -EINVAL; ++ goto stall; ++ } ++ } ++ } else { ++ /* ++ * if the queue is not empty, add the req to the queue only ++ */ ++ status = 0; ++ list_add_tail(&req->queue, &ep->queue); ++ dbg("waiting for %s int", ep->ep.name); ++ } ++ ++stall: ++ local_irq_restore(flags); ++ /* return (status < 0) ? status : 0; */ ++ return status; ++} ++ ++/* ++ * delete the request from the ep request queue ++ * @ep: the ep which the request deal with ++ */ ++static int akudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct akudc_ep *ep; ++ struct akudc_request *req; ++ unsigned long flags; ++ struct akudc_udc *udc = &controller; ++ ++ dbg("dequeue"); ++ ++ if (!udc->driver) ++ return -ESHUTDOWN; ++ ++ ep = container_of(_ep, struct akudc_ep, ep); ++ if (!_ep || !_req) ++ return -EINVAL; ++ ++ local_irq_save(flags); ++ /* find the req from head */ ++ list_for_each_entry (req, &ep->queue, queue) { ++ if (&req->req == _req) ++ break; ++ } ++ /* if cannot find, return error */ ++ if (&req->req != _req) { ++ local_irq_restore(flags); ++ return -EINVAL; ++ } ++ /* delete the req and set the req status -ECONNRESET */ ++ done(ep, req, -ECONNRESET); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++/* ++ * set or clear the stall function for ep ++ * @ep: which ep is being setting ++ * @value: 1 stall, 0 clear stall ++ */ ++static int akudc_ep_set_halt(struct usb_ep *_ep, int value) ++{ ++ struct akudc_ep *ep = container_of(_ep, struct akudc_ep, ep); ++ unsigned int csr = 0; ++ unsigned long flags; ++ struct akudc_udc *udc = &controller; ++ ++ if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { ++ dbg("inval 2"); ++ return -EINVAL; ++ } ++ ++ local_irq_save (flags); ++ ++ if ((ep->ep.name == ep0name) && value) { ++ udc_writeb(USB_EP_INDEX, 0); ++ udc_writeb(USB_CTRL_1, 1<<5); ++ udc_writeb(USB_CTRL_1, 1<<6 | 1<<3); ++ }else if(!strcmp(ep->ep.name, udc->ep[1].ep.name)){/* ep1 */ ++ udc_writeb(USB_EP_INDEX, 1); ++ csr = udc_readw(USB_CTRL_1); ++ if (value) ++ udc_writew(USB_CTRL_1, csr | 1<<4); ++ else { ++ csr &= ~(1<<4 | 1<<5); ++ udc_writew(USB_CTRL_1, csr); ++ csr |= 1<<6; ++ udc_writew(USB_CTRL_1, csr); ++ } ++ } else if (ep->is_in) { /* ep2 or ep4*/ ++ if (!strcmp(ep->ep.name, udc->ep[2].ep.name)) ++ udc_writeb(USB_EP_INDEX, 2); ++ else ++ udc_writeb(USB_EP_INDEX, 4); ++ ++ csr = udc_readw(USB_CTRL_1); ++ if (value) ++ udc_writew(USB_CTRL_1, csr | 1<<4); ++ else { ++ csr &= ~(1<<4 | 1<<5); ++ udc_writew(USB_CTRL_1, csr); ++ csr |= 1<<6; ++ udc_writew(USB_CTRL_1, csr); ++ } ++ } else { /* ep3 or ep5*/ ++ if (!strcmp(ep->ep.name, udc->ep[3].ep.name)) ++ udc_writeb(USB_EP_INDEX, 3); ++ else ++ udc_writeb(USB_EP_INDEX, 5); ++ ++ csr = udc_readw(USB_CTRL_2); ++ if (value) ++ udc_writew(USB_CTRL_2, csr | 1<<5); ++ else { ++ csr &= ~(1<<5 | 1<<6); ++ udc_writew(USB_CTRL_2, csr); ++ csr |= 1<<7; ++ udc_writew(USB_CTRL_2, csr); ++ } ++ } ++ ++ ep->stopped= value ? 1 : 0; ++ local_irq_restore (flags); ++ ++ return 0; ++} ++ ++/* the operation struct for ep */ ++static const struct usb_ep_ops akudc_ep_ops = { ++ .enable = akudc_ep_enable, ++ .disable = akudc_ep_disable, ++ .alloc_request = akudc_ep_alloc_request, ++ .free_request = akudc_ep_free_request, ++ .queue = akudc_ep_queue, ++ .dequeue = akudc_ep_dequeue, ++ .set_halt = akudc_ep_set_halt, ++ // there's only imprecise fifo status reporting ++}; ++ ++static void nop_release(struct device *dev) ++{ ++ /* nothing to free */ ++} ++ ++/* the core struct for udc that include many important struct */ ++static struct akudc_udc controller = { ++ /* the gadget , include ep0 info*/ ++ .gadget = { ++ .ops = &akudc_udc_ops, ++ .ep0 = &controller.ep[0].ep, ++ .name = driver_name, ++ .dev = { ++ .init_name = "gadget", ++ .release = nop_release, ++ } ++ }, ++ /* the array of endpoint */ ++ .ep[0] = { ++ .ep = { ++ .name = ep0name,//ep_name[0], ++ .ops = &akudc_ep_ops, ++ }, ++ .udc = &controller, ++ .maxpacket = EP0_FIFO_SIZE, ++ }, ++ .ep[1] = { ++ .ep = { ++ .name = "ep1-int",//ep_name[1], ++ .ops = &akudc_ep_ops, ++ }, ++ .udc = &controller, ++ .maxpacket = EP1_FIFO_SIZE, ++ }, ++ .ep[2] = { ++ .ep = { ++ .name = "ep2in-bulk",//ep_name[2], ++ .ops = &akudc_ep_ops, ++ }, ++ .udc = &controller, ++ .maxpacket = EP2_FIFO_SIZE, ++ }, ++ .ep[3] = { ++ .ep = { ++ /* could actually do bulk too */ ++ .name = "ep3out-bulk",//ep_name[3], ++ .ops = &akudc_ep_ops, ++ }, ++ .udc = &controller, ++ .maxpacket = EP3_FIFO_SIZE, ++ }, ++ .ep[4] = { ++ .ep = { ++ .name = "ep4in-bulk",//ep_name[4], ++ .ops = &akudc_ep_ops, ++ }, ++ .udc = &controller, ++ .maxpacket = EP4_FIFO_SIZE, ++ }, ++ .ep[5] = { ++ .ep = { ++ .name = "ep5out-bulk",//ep_name[5], ++ .ops = &akudc_ep_ops, ++ }, ++ .udc = &controller, ++ .maxpacket = EP5_FIFO_SIZE, ++ }, ++}; ++ ++/** ++ * akudc_udc_get_status - process request GET_STATUS ++ * @udc: The device state ++ * @ctrl: USB control request ++ */ ++static int akudc_udc_get_status(struct akudc_udc *udc, ++ struct usb_ctrlrequest *ctrl) ++{ ++ u16 status = 0; ++ u8 ep_num = ctrl->wIndex & 0x7F; ++ u8 is_in = ctrl->wIndex & USB_DIR_IN; ++ ++ switch (ctrl->bRequestType & USB_RECIP_MASK) { ++ case USB_RECIP_DEVICE: ++ status = udc->devstatus; ++ break; ++ ++ case USB_RECIP_INTERFACE: ++ /* currently, the data result should be zero */ ++ break; ++ ++ case USB_RECIP_ENDPOINT: ++ if (ep_num > 5 || ctrl->wLength > 2) ++ return 1; ++ ++ if (ep_num == 0) { ++ udc_writeb(USB_EP_INDEX, 0); ++ status = udc_readb(USB_CTRL_1); ++ status = status & (1<<5); ++ } else { ++ udc_writeb(USB_EP_INDEX, ep_num); ++ if (is_in) { ++ status = udc_readb(USB_CTRL_1); ++ status = status & (1<<4); ++ } else { ++ status = udc_readb(USB_CTRL_2); ++ status = status & (1<<5); ++ } ++ } ++ ++ status = status ? 1 : 0; ++ break; ++ default: ++ return 1; ++ } ++ ++ udc_writeb(USB_EP0_FIFO, status & 0xff); ++ udc_writeb(USB_EP0_FIFO, status >> 8); ++ ++ udc_writel(USB_EP0_NUM, 2); /* 7bits */ ++ udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<1); ++ ++ udc->ep0state = EP0_END_XFER; ++ ++ return 0; ++} ++ ++/* function for ep0 setup phase */ ++static void akudc_udc_handle_ep0_idle(struct akudc_udc *udc, ++ struct akudc_ep *ep, u32 ep0csr) ++{ ++ struct usb_ctrlrequest crq; ++ int i, len, ret, tmp, timeout; ++ unsigned char *buf; ++ ++ /* start control request? */ ++ if (!(ep0csr & 1)) ++ return; ++ ++ len = udc_readw(USB_EP_COUNT); ++ buf = (unsigned char *)&crq; ++ if (len > sizeof(struct usb_ctrlrequest)) ++ len = sizeof(struct usb_ctrlrequest); ++ for (i = 0; i < len; i++) { ++ /* read the setup data from ep0 fifo */ ++ buf[i] = udc_readb(USB_EP0_FIFO); ++ } ++ if (len != sizeof(crq)) { ++ dbg("setup begin: fifo READ ERROR" ++ " wanted %d bytes got %d. Stalling out...", ++ sizeof(crq), len); ++ udc_writeb(USB_CTRL_1, 1<<5); ++ return; ++ } ++ ++ dbg("bRequest = %d bRequestType %d wLength = %d", ++ crq.bRequest, crq.bRequestType, crq.wLength); ++ ++ /* cope with automagic for some standard requests. */ ++ udc->req_std = (crq.bRequestType & USB_TYPE_MASK) ++ == USB_TYPE_STANDARD; ++ udc->req_config = 0; ++ udc->req_pending = 1; ++ ++ switch (crq.bRequest) { ++ case USB_REQ_SET_CONFIGURATION: ++ if (crq.bRequestType == USB_RECIP_DEVICE) { ++ udc->req_config = 1; ++ udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<6); ++ } ++ break; ++ ++ case USB_REQ_SET_INTERFACE: ++ if (crq.bRequestType == USB_RECIP_INTERFACE) { ++ udc->req_config = 1; ++ udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<6); ++ } ++ break; ++ ++ case USB_REQ_SET_ADDRESS: ++ if (crq.bRequestType == USB_RECIP_DEVICE) { ++ tmp = crq.wValue & 0x7F; ++ udc_writeb(USB_CTRL_1, 0x1<<3 | 0x1<<6); ++ timeout = 20000; ++ /* waiting for next interrupt */ ++ while (!(udc_readb(USB_INTERRUPT_1) & 0x1) && timeout) {timeout--;} ++ udc_writeb(USB_FUNCTION_ADDR, tmp); ++ udc->addr = tmp; ++ return; ++ } ++ break; ++ ++ case USB_REQ_GET_STATUS: ++ udc_writeb(USB_CTRL_1, 0x1<<6); ++ ++ if (udc->req_std) { ++ if (!akudc_udc_get_status(udc, &crq)) { ++ return; ++ } ++ } ++ break; ++ ++ case USB_REQ_CLEAR_FEATURE: ++ udc_writeb(USB_CTRL_1, 0x1<<6); ++ ++ if (crq.bRequestType != USB_RECIP_ENDPOINT) ++ break; ++ ++ if (crq.wValue != USB_ENDPOINT_HALT || crq.wLength != 0) ++ break; ++ ++ akudc_ep_set_halt(&udc->ep[crq.wIndex & 0x7f].ep, 0); ++ udc_writeb(USB_CTRL_1, 0x1<<6 | 0x1<<3); ++ ++ return; ++ ++ case USB_REQ_SET_FEATURE: ++ udc_writeb(USB_CTRL_1, 0x1<<6); ++ ++ if (crq.bRequestType != USB_RECIP_ENDPOINT) ++ break; ++ ++ if (crq.wValue != USB_ENDPOINT_HALT || crq.wLength != 0) ++ break; ++ ++ akudc_ep_set_halt(&udc->ep[crq.wIndex & 0x7f].ep, 1); ++ udc_writeb(USB_CTRL_1, 0x1<<6 | 0x1<<3); ++ return; ++ ++ default: ++ udc_writeb(USB_CTRL_1, 0x1<<6); ++ break; ++ } ++ ++ /* set ep0state according to the command */ ++ if (crq.bRequestType & USB_DIR_IN) ++ udc->ep0state = EP0_IN_DATA_PHASE; ++ else ++ udc->ep0state = EP0_OUT_DATA_PHASE; ++ ++ /* call the function drvier setup */ ++ ret = udc->driver->setup(&udc->gadget, &crq); ++ /* ++ * if the setup failed, sent stall ep0 ++ */ ++ if (ret < 0) { ++ if (udc->req_config) { ++ dbg("config change %02x fail %d?", ++ crq.bRequest, ret); ++ return; ++ } ++ ++ if (ret == -EOPNOTSUPP) ++ dbg("Operation not supported"); ++ else ++ dbg("udc->driver->setup failed. (%d)", ret); ++ ++ udc_writeb(USB_CTRL_1, 1<<5); ++ udc_writeb(USB_CTRL_1, 1<<3 | 1<<6); ++ udc->ep0state = EP0_IDLE; ++ /* deferred i/o == no response yet */ ++ } else if (udc->req_pending) { ++ dbg("dev->req_pending... what now?"); ++ udc->req_pending=0; ++ } ++} ++ ++/* deal with interrupt for ep0 */ ++static void handle_ep0(struct akudc_udc *udc) ++{ ++ int csr, error = 0; ++ struct akudc_ep *ep0 = &udc->ep[0]; ++ struct akudc_request *req = NULL; ++ ++ if (!list_empty(&ep0->queue)) ++ req = list_entry(ep0->queue.next, struct akudc_request, queue); ++ ++ udc_writeb(USB_EP_INDEX, 0); ++ csr = udc_readb(USB_CTRL_1); ++ dbg("csr(0x%x)", csr); ++ ++ if (csr & 0x1<<3) { ++ dbg("data end"); ++ } ++ if (csr & 0x1<<4) { ++ dbg("A control transaction ends before the DataEnd bit has been set"); ++ udc_writeb(USB_CTRL_1, 0x1<<7); ++ /* do something else? */ ++ udc->ep0state = EP0_IDLE; ++ error = 1; ++ } ++ if (csr & 0x1<<2) { ++ udc_writeb(USB_CTRL_1, udc_readb(USB_CTRL_1) & ~(1 << 2)); ++ udc->ep0state = EP0_IDLE; ++ error = 1; ++ } ++ switch (udc->ep0state) { ++ case EP0_IDLE: ++ akudc_udc_handle_ep0_idle(udc, ep0, csr); ++ break; ++ ++ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ ++ dbg("EP0_IN_DATA_PHASE ... what now?"); ++ if (!(csr & (1<<1)) && req && error == 0) ++ write_ep0_fifo(ep0, req); ++ break; ++ ++ case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ ++ dbg("EP0_OUT_DATA_PHASE ... what now?"); ++ if ((csr & (1<<0)) && req) ++ read_ep0_fifo(ep0, req); ++ break; ++ ++ case EP0_END_XFER: ++ dbg("EP0_END_XFER ... what now?"); ++ udc->ep0state = EP0_IDLE; ++ break; ++ ++ case EP0_STALL: ++ dbg("EP0_STALL ... what now?"); ++ udc->ep0state = EP0_IDLE; ++ break; ++ } ++} ++ ++/* not ep0 */ ++static void handle_ep(struct akudc_ep *ep) ++{ ++ struct akudc_request *req; ++ struct akudc_udc *udc = ep->udc; ++ unsigned int csr; ++ ++ if (!list_empty(&ep->queue)) ++ req = list_entry(ep->queue.next, ++ struct akudc_request, queue); ++ else { ++ dbg("%s: no req waiting", ep->ep.name); ++ req = NULL; ++ } ++ ++ if (!strcmp(ep->ep.name, udc->ep[1].ep.name)) { /* ep1 */ ++ dbg("ep1"); ++ udc_writeb(USB_EP_INDEX, 1); ++ ++ /* read the epx status */ ++ csr = udc_readb(USB_CTRL_1); ++ dbg("ep1 csr(0x%x), req(0x%p)", csr, req); ++ ++ if (csr & 0x1<<2) { /* clear underrun */ ++ udc_writeb(USB_CTRL_1, csr & ~(0x1<<2)); ++ } ++ if (csr & 0x1<<5) { /* clear sentstall */ ++ udc_writeb(USB_CTRL_1, csr & ~(0x1<<5)); ++ return; ++ } ++ ++ if (req) { ++ /* ++ * if the current req is complete, done it with status 0 ++ * and if the next req is being, and the epx can be written ++ * data, do it with work queue ++ * else do the current req with work queue if the epx can be written ++ */ ++ if (ep->done) { ++ done(ep, req, 0); ++ if (!list_empty(&ep->queue)) { ++ dbg("do next queue"); ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ if ((csr & 1) == 0) ++ queue_work(ep_wqueue, &ep->work); ++ } ++ } else { ++ if ((csr & 1) == 0) ++ queue_work(ep_wqueue, &ep->work); ++ } ++ ++ } ++ } else if (ep->is_in) { /* ep2 or ep4*/ ++ ++ if (!strcmp(ep->ep.name, udc->ep[2].ep.name)) ++ udc_writeb(USB_EP_INDEX, 2); ++ else ++ udc_writeb(USB_EP_INDEX, 4); ++ ++ /* read the epx status */ ++ csr = udc_readb(USB_CTRL_1); ++ dbg("ep2 or ep4 csr(0x%x), req(0x%p)", csr, req); ++ ++ /* udc_writeb(USB_CTRL_1, 0x1); */ ++ if (csr & 0x1<<2) { /* clear underrun */ ++ udc_writeb(USB_CTRL_1, csr & ~(0x1<<2)); ++ } ++ if (csr & 0x1<<5) { /* clear sentstall */ ++ udc_writeb(USB_CTRL_1, csr & ~(0x1<<5)); ++ return; ++ } ++ ++ if (req) { ++ /* ++ * if the current req is complete, done it with status 0 ++ * and if the next req is being, and the epx can be written ++ * data, do it with work queue ++ * else do the current req with work queue if the epx can be written ++ */ ++ if (ep->done) { ++ done(ep, req, 0); ++ if (!list_empty(&ep->queue)) { ++ dbg("do next queue"); ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ if ((csr & 1) == 0) ++ queue_work(ep_wqueue, &ep->work); ++ } ++ } else { ++ if ((csr & 1) == 0) ++ queue_work(ep_wqueue, &ep->work); ++ } ++ ++ } ++ } else { /* ep3 or ep 5*/ ++ if (!strcmp(ep->ep.name, udc->ep[3].ep.name)) ++ udc_writeb(USB_EP_INDEX, 3); ++ else ++ udc_writeb(USB_EP_INDEX, 5); ++ ++ /* read the epx status */ ++ csr = udc_readb(USB_CTRL_2); ++ dbg("ep3 or ep5 csr(0x%x)", csr); ++ ++ if (csr & 0x1<<6) { /* clear sentstall */ ++ udc_writeb(USB_CTRL_2, csr & ~(0x1<<6)); ++ } ++ /* ++ * if the req is being and the data come to the endpoint fifo ++ * do the current req with work queue ++ */ ++ if (req && (csr & 0x1<<0)) ++ queue_work(ep_wqueue, &ep->work); ++ } ++} ++ ++/* ++ * the function execute when the suspend signal come ++ * or the system is being suspend ++ */ ++static void udc_disconnect(struct akudc_udc *udc) ++{ ++ struct usb_gadget_driver *driver = udc->driver; ++ int i; ++ ++ if (udc->gadget.speed == USB_SPEED_UNKNOWN) ++ driver = NULL; ++ ++ for (i = 0; i < ENDPOINTS_NUM; i++) { ++ struct akudc_ep *ep = &udc->ep[i]; ++ struct akudc_request *req; ++ ++ /* sign every endpoint stop */ ++ ep->stopped = 1; ++ ++ // terminer chaque requete dans la queue ++ if (list_empty(&ep->queue)) ++ continue; ++ ++ /* ++ * delete every req in every endpoint queue ++ * dont it by status -ESHUTDOWN ++ */ ++ while (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ done(ep, req, -ESHUTDOWN); ++ } ++ } ++ ++ /* if the driver exist, call the function drvier disconnect */ ++ if (driver) ++ driver->disconnect(&udc->gadget); ++ ++ /* init the udc */ ++ udc_reinit(udc); ++} ++ ++#if 0 ++static void akudc_udc_fun(void *data) ++{ ++ struct akudc_udc *udc = data; ++ ++ msleep(500); ++ clk_enable(udc->clk); ++ udc_reg_writel(USB_OP_MOD_REG, 0x6, 3, 0); ++ udc_writeb(USB_POWER_CTRL, 0x1<<5); ++} ++#endif ++ ++static void akudc_conctrol_reset(struct akudc_udc *udc) ++{ ++ // reset the udc controller module ++ ak_soft_reset(AK_SRESET_USBHS); ++ REG32(AK_VA_SYSCTRL + 0x58) &= ~(0xff << 0); ++} ++ ++/* the function execute when the reset signal come */ ++static void udc_reset(struct akudc_udc *udc) ++{ ++ int i ,temp; ++ ++ udc_writeb(USB_FUNCTION_ADDR, 0); // set the usb device addr 0 ++ udc_writeb(USB_INTERRUPT_USB, ~(0x1<<3)); // clear SOF interrupt ++ udc_writeb(USB_INTERRUPT_TX, 0x1<<0); // enable ep0 interrupt and disable other tx endpoint ++ udc_writeb(USB_INTERRUPT_RX, 0); // disable rx endpoint inttrupt ++ ++ /* negotiated high speed */ ++ if ((udc_readb(USB_POWER_CTRL) & (0x1 << 4)) == (0x1 << 4)) { ++ udc_writel(USB_MODE_STATUS, udc_readl(USB_MODE_STATUS) & (~0x1)); ++ /* enable high speed for udc */ ++ udc_writeb(USB_POWER_CTRL, 0x1<<5); ++ udc->gadget.speed = USB_SPEED_HIGH; ++ udc->high_speed = 1; ++ } else { /* negotiated full speed */ ++ udc_writeb(USB_POWER_CTRL, 0); ++ udc_writel(USB_MODE_STATUS, udc_readl(USB_MODE_STATUS) | 0x1); ++ udc->gadget.speed = USB_SPEED_FULL; ++ udc->high_speed = 0; ++ } ++ ++ /* read and clear the common interrupt status */ ++ temp = udc_readw(USB_INTERRUPT_COMM); ++ /* read and clear the ep0 and all tx ep interrupt status */ ++ temp = udc_readw(USB_INTERRUPT_1); ++ /* read and clear all rx ep interrupt status */ ++ temp = udc_readw(USB_INTERRUPT_2); ++ ++ /* init the ep0 status */ ++ udc->ep0state = EP0_IDLE; ++ ++ for (i = 0; i < ENDPOINTS_NUM; i++) { ++ struct akudc_ep *ep = &udc->ep[i]; ++ struct akudc_request *req; ++ ++ if (list_empty(&ep->queue)) ++ continue; ++ ++ /* ++ * delete every req in every endpoint queue ++ * dont it by status -ECONNRESET ++ */ ++ while (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ done(ep, req, -ECONNRESET); ++ } ++ } ++} ++ ++ ++/* the udc irq irqhandler */ ++static irqreturn_t udc_irqhandler(int irq, void *_udc) ++{ ++ struct akudc_udc *udc = _udc; ++ short status_1, status_2; ++ char status_int; ++ ++ status_int = udc_readb(USB_INTERRUPT_COMM); ++ if (status_int & 0x1<<2) { ++ /* dbg("status_int(0x%x), reset", status_int); */ ++ printk("\n\nstatus_int(0x%x), reset\n\n", status_int); ++ if (usb_detect) { ++ usb_detect = 0; ++ if (!udc->driver) { ++ panic("If you see this, come to find Zhang Jingyuan\n"); ++ akudc_disable(udc); ++ return IRQ_HANDLED; ++ } ++ } ++ udc_reset(udc); ++ goto done; ++ } else if(status_int & 0x1<<1) { /* resume */ ++ dbg("status_int(0x%x)", status_int); ++ goto done; ++ } else if(status_int & 0x1<<0) { /* suspend */ ++ dbg("status_int(0x%x)", status_int); ++#if 0 ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ pm_power_off(); ++#endif ++#endif ++ udc_disconnect(udc); ++ goto done; ++ } ++ ++ status_1 = udc_readb(USB_INTERRUPT_1); ++ status_2 = udc_readb(USB_INTERRUPT_2); ++ /* ep0 */ ++ if (status_1 & 0x1<<0) { ++ handle_ep0(udc); ++ } ++ /* ep2 */ ++ if (status_1 & 0x1<<2) { ++ dbg("endpoint2"); ++ handle_ep(&udc->ep[2]); ++ } ++ /* ep4 */ ++ if (status_1 & 0x1<<4) { ++ dbg("endpoint4"); ++ handle_ep(&udc->ep[4]); ++ } ++ ++ /* ep1 */ ++ if (status_2 & 0x1<<1) { ++ dbg("endpoint 1"); ++ handle_ep(&udc->ep[1]); ++ } ++ /* ep3 */ ++ if (status_2 & 0x1<<3) { ++ dbg("endpoint3"); ++ handle_ep(&udc->ep[3]); ++ } ++ /* ep5 */ ++ if (status_2 & 0x1<<5) { ++ dbg("endpoint5"); ++ handle_ep(&udc->ep[5]); ++ } ++ ++done: ++ return IRQ_HANDLED; ++} ++ ++#if 0 ++/* the l2 buffer dma irqhandler */ ++static irqreturn_t udc_dmahandler(int irq, void *_udc) ++{ ++ struct akudc_request *req = NULL; ++ struct akudc_udc *udc = _udc; ++ struct akudc_ep *ep; ++ unsigned int is_done = 0; ++ ++ u32 usb_dma_int = udc_readl(USB_DMA_INTR); ++ ++ /* ++ * affirm which channel's interrupt ++ * channel1 -- ep2 ++ * channel2 -- ep3 ++ * channel3 -- ep4 ++ * channel4 -- ep5 ++ */ ++ if ((usb_dma_int & DMA_CHANNEL1_INT) == DMA_CHANNEL1_INT) { ++ ep = &udc->ep[2]; ++ udc_writeb(USB_EP_INDEX, USB_EP2_INDEX); ++ udc_writeb(USB_CTRL_1_2, USB_TXCSR_MODE1); ++ udc_writel(USB_DMA_CTRL1, 0); ++ ++ l2_combuf_wait_dma_finish(ep->l2_buf_id); ++ ++ /* if the queue is not empty, get the head req */ ++ if (!list_empty(&ep->queue)) ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ ++ if (req) { ++ /* ++ * judge whether the req is complete ++ * if it is, done the req and do next req transmission ++ * else do the current req transmission ++ */ ++ if (dma_tx == req->req.length - req->req.actual && !req->req.zero) ++ is_done = 1; ++ req->req.actual += dma_tx; ++ ep->done = is_done; ++ if (is_done) { ++ done(ep, req, 0); ++ if (!list_empty(&ep->queue)) { ++ dbg("do next queue"); ++ queue_work(ep_wqueue, &ep->work); ++ } ++ } else { ++ queue_work(ep_wqueue, &ep->work); ++ } ++ /* unmap the buffer */ ++ dma_unmap_single(NULL, phys_tx, dma_tx, DMA_TO_DEVICE); ++ dma_tx = 0; ++ ++ req->status = is_done; ++ } ++ else ++ dbg("something happend"); ++ } ++ ++ if ((usb_dma_int & DMA_CHANNEL2_INT) == DMA_CHANNEL2_INT) { ++ ep = &udc->ep[3]; ++ ++ udc_writeb(USB_EP_INDEX, USB_EP3_INDEX); ++ udc_writeb(USB_CTRL_2_2, 0); ++ udc_writel(USB_DMA_CTRL2, 0); ++ ++ l2_combuf_wait_dma_finish(ep->l2_buf_id); ++ ++ req = NULL; ++ is_done = 0; ++ /* if the queue is not empty, get the head req */ ++ if (!list_empty(&ep->queue)) ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ ++ if (req) { ++ /* ++ * judge whether the req is complete ++ * if it is, done the req ++ */ ++ if (dma_rx == req->req.length - req->req.actual) ++ is_done = 1; ++ req->req.actual += dma_rx; ++ ep->done = is_done; ++ if (is_done) ++ done(ep, req, 0); ++ /* unmap the buffer */ ++ dma_unmap_single(NULL, phys_rx, dma_rx, DMA_FROM_DEVICE); ++ req->status = is_done; ++ dma_rx = 0; ++ ++ flag = 0; ++ } ++ else ++ dbg("something happend"); ++ } ++ ++ if ((usb_dma_int & DMA_CHANNEL3_INT) == DMA_CHANNEL3_INT) { ++ ep = &udc->ep[4]; ++ udc_writeb(USB_EP_INDEX, USB_EP4_INDEX); ++ udc_writeb(USB_CTRL_1_2, USB_TXCSR_MODE1); ++ udc_writel(USB_DMA_CTRL1, 0); ++ ++ l2_combuf_wait_dma_finish(ep->l2_buf_id); ++ ++ /* if the queue is not empty, get the head req */ ++ if (!list_empty(&ep->queue)) ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ ++ if (req) { ++ /* ++ * judge whether the req is complete ++ * if it is, done the req and do next req transmission ++ * else do the current req transmission ++ */ ++ if (dma_tx == req->req.length - req->req.actual && !req->req.zero) ++ is_done = 1; ++ req->req.actual += dma_tx; ++ ep->done = is_done; ++ if (is_done) { ++ done(ep, req, 0); ++ if (!list_empty(&ep->queue)) { ++ dbg("do next queue"); ++ queue_work(ep_wqueue, &ep->work); ++ } ++ } else { ++ queue_work(ep_wqueue, &ep->work); ++ } ++ /* unmap the buffer */ ++ dma_unmap_single(NULL, phys_tx, dma_tx, DMA_TO_DEVICE); ++ dma_tx = 0; ++ ++ req->status = is_done; ++ } ++ else ++ dbg("something happend"); ++ } ++ ++ if ((usb_dma_int & DMA_CHANNEL4_INT) == DMA_CHANNEL4_INT) { ++ ep = &udc->ep[5]; ++ ++ udc_writeb(USB_EP_INDEX, USB_EP5_INDEX); ++ udc_writeb(USB_CTRL_2_2, 0); ++ udc_writel(USB_DMA_CTRL2, 0); ++ ++ l2_combuf_wait_dma_finish(ep->l2_buf_id); ++ ++ req = NULL; ++ is_done = 0; ++ /* if the queue is not empty, get the head req */ ++ if (!list_empty(&ep->queue)) ++ req = list_entry(ep->queue.next, struct akudc_request, queue); ++ ++ if (req) { ++ /* ++ * judge whether the req is complete ++ * if it is, done the req ++ */ ++ if (dma_rx == req->req.length - req->req.actual) ++ is_done = 1; ++ req->req.actual += dma_rx; ++ ep->done = is_done; ++ if (is_done) ++ done(ep, req, 0); ++ /* unmap the buffer */ ++ dma_unmap_single(NULL, phys_rx, dma_rx, DMA_FROM_DEVICE); ++ req->status = is_done; ++ dma_rx = 0; ++ ++ flag = 0; ++ } ++ else ++ dbg("something happend"); ++ } ++ ++ return IRQ_HANDLED; ++} ++#endif ++ ++void akudc_enable(struct akudc_udc *udc) ++{ ++ set_condition(&udc_clk); ++} ++ ++void akudc_disable(struct akudc_udc *udc) ++{ ++ clear_condition(&udc_clk); ++} ++ ++/* ++ * usb_gadget_register_driver for upper function driver ++ */ ++static int akudc_start(struct usb_gadget_driver *driver, ++ int (*bind)(struct usb_gadget *)) ++{ ++ struct akudc_udc *udc = &controller; ++ int retval; ++ ++ printk("akudc start() '%s'\n", driver->driver.name); ++ ++ if (!bind || !driver->setup ++ || driver->max_speed < USB_SPEED_FULL) { ++ printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n", ++ bind, driver->setup, driver->max_speed); ++ return -EINVAL; ++ } ++ ++#if defined(MODULE) ++ if (!driver->unbind) { ++ printk(KERN_ERR "Invalid driver: no unbind method\n"); ++ return -EINVAL; ++ } ++#endif ++ ++ udc->driver = driver; ++ udc->gadget.dev.driver = &driver->driver; ++ ++ /* ++ * bind the gadget to function driver ++ * give the gadget point to function driver ++ */ ++ retval = bind(&udc->gadget); ++ if (retval) { ++ dbg("driver->bind() returned %d\n", retval); ++ udc->driver = NULL; ++ udc->gadget.dev.driver = NULL; ++ return retval; ++ } ++ ++ /* init work for every endpoint */ ++ INIT_WORK(&udc->ep[1].work, ep1_work); ++ INIT_WORK(&udc->ep[2].work, ep2_work); ++ INIT_WORK(&udc->ep[3].work, ep3_work); ++ INIT_WORK(&udc->ep[4].work, ep4_work); ++ INIT_WORK(&udc->ep[5].work, ep5_work); ++ ++ disable_irq(udc->mcu_irq); ++ akudc_enable(udc); ++ enable_irq(udc->mcu_irq); ++ ++ printk("binding gadget driver '%s'\n", driver->driver.name); ++ return 0; ++} ++ ++ ++/* ++ * usb_gadget_unregister_driver for upper function driver ++ */ ++static int akudc_stop(struct usb_gadget_driver *driver) ++{ ++ struct akudc_udc *udc = &controller; ++ ++ printk("akudc_stop() '%s'\n", driver->driver.name); ++ ++ if (!driver || driver != udc->driver || !driver->unbind) ++ return -EINVAL; ++ ++ /* report disconnect */ ++ if(driver->disconnect) ++ driver->disconnect(&udc->gadget); ++ ++ /* unbind the gadget for function driver */ ++ driver->unbind(&udc->gadget); ++ udc->gadget.dev.driver = NULL; ++ //udc->gadget.dev.driver_data = NULL; ++ udc->driver = NULL; ++ ++ disable_irq(udc->mcu_irq); ++ ++ akudc_disable(udc); ++ ++ /*cacel work for every endpoint */ ++ cancel_work_sync(&udc->ep[1].work); ++ cancel_work_sync(&udc->ep[2].work); ++ cancel_work_sync(&udc->ep[3].work); ++ cancel_work_sync(&udc->ep[4].work); ++ cancel_work_sync(&udc->ep[5].work); ++ ++ flush_workqueue(ep_wqueue); ++ ++ enable_irq(udc->mcu_irq); ++ ++ dbg("unbound from '%s'\n", driver->driver.name); ++ udc_reinit(udc); ++ return 0; ++} ++ ++/* reinit == restore initial software state */ ++static void udc_reinit(struct akudc_udc *udc) ++{ ++ u32 i; ++ ++ /* init non-ep0 endpint list and ep0 list */ ++ INIT_LIST_HEAD(&udc->gadget.ep_list); ++ INIT_LIST_HEAD(&udc->gadget.ep0->ep_list); ++ udc->ep0state = EP0_IDLE; ++ ++ for (i = 0; i < ENDPOINTS_NUM; i++) { ++ struct akudc_ep *ep = &udc->ep[i]; ++ /* add every non-ep0 endpoint to gadget endpoint list */ ++ if (i != 0) ++ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); ++ ep->desc = NULL; ++ ep->stopped = 0; ++ ep->ep.maxpacket = ep->maxpacket; ++ /* initialize one queue per endpoint */ ++ INIT_LIST_HEAD(&ep->queue); ++ } ++} ++ ++static int __init akudc_udc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct akudc_udc *udc = &controller; ++ struct resource *res; ++ int retval; ++ ++ if (pdev->num_resources < 2) { ++ dbg("invalid num_resources\n"); ++ return -ENODEV; ++ } ++ if ((pdev->resource[0].flags != IORESOURCE_MEM) ++ || (pdev->resource[1].flags != IORESOURCE_IRQ)) { ++ dbg("invalid resource type\n"); ++ return -ENODEV; ++ } ++ ++ /* get the mem resoure */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -ENXIO; ++ ++ if (!request_mem_region(res->start, resource_size(res), driver_name)) { ++ dbg("someone's using UDC memory\n"); ++ return -EBUSY; ++ } ++ ++ /* map the physical mem to virtual for register */ ++ udc->baseaddr = ioremap_nocache(res->start, resource_size(res)); ++ if (!udc->baseaddr) { ++ retval = -ENOMEM; ++ goto fail0a; ++ } ++ /* init software state */ ++ udc->gadget.dev.parent = dev; //is null, ++ ++ /* get interface and function clocks */ ++ udc->clk = clk_get(dev, "usb-slave"); ++ if (IS_ERR(udc->clk)) { ++ dbg("clocks missing\n"); ++ retval = -ENODEV; ++ /* NOTE: we "know" here that refcounts on these are NOPs */ ++ goto fail0b; ++ } ++ ++ akudc_conctrol_reset(udc); ++ udc_reinit(udc); ++ ++ retval = device_register(&udc->gadget.dev); ++ if (retval < 0) ++ goto fail0b; ++ ++ /* creat the work queue for every ep */ ++ ep_wqueue = create_workqueue("akudc"); ++ ++ /* request UDC and maybe VBUS irqs */ ++ udc->mcu_irq = platform_get_irq(pdev, 0); ++ /* register the udc irq handler */ ++ retval = request_irq(udc->mcu_irq, udc_irqhandler, ++ 0, driver_name, udc); ++ if (retval < 0) { ++ dbg("request irq %d failed\n", udc->mcu_irq); ++ goto fail1; ++ } ++ ++#if 0 ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ /* request DMA irqs */ ++ udc->dma_irq = platform_get_irq(pdev, 1); ++ retval = request_irq(udc->dma_irq, udc_dmahandler, ++ 0, driver_name, udc); ++ if (retval < 0) { ++ dbg("request irq %d failed\n", udc->dma_irq); ++ goto fail1; ++ } ++ ++ /* USB slave L2 buffer initialization */ ++ udc->ep[2].l2_buf_id = l2_alloc(ADDR_USB_EP2); ++ udc->ep[3].l2_buf_id = l2_alloc(ADDR_USB_EP3); ++ udc->ep[4].l2_buf_id = l2_alloc(ADDR_USB_EP4); ++ udc->ep[5].l2_buf_id = l2_alloc(ADDR_USB_EP5); ++#endif ++#endif ++ ++ /* set the udc pointer to pdev private pointer */ ++ platform_set_drvdata(pdev, udc); ++ ++ create_debugfs_files(udc); ++ ++ retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); ++ if (retval) ++ goto fail1; ++ ++ return 0; ++ ++ free_irq(udc->mcu_irq, udc); ++fail1: ++ device_unregister(&udc->gadget.dev); ++fail0b: ++ iounmap(udc->baseaddr); ++fail0a: ++ release_mem_region(res->start, resource_size(res)); ++ ++ return retval; ++} ++ ++static int __exit akudc_udc_remove(struct platform_device *pdev) ++{ ++ struct akudc_udc *udc = platform_get_drvdata(pdev); ++ struct resource *res; ++ ++ if (udc->driver) ++ return -EBUSY; ++ ++ usb_del_gadget_udc(&udc->gadget); ++ ++ /* clear the udc_clk condition for udc */ ++ clear_condition(&udc_clk); ++ ++ create_debugfs_files(udc); ++ ++ free_irq(udc->mcu_irq, udc); ++ device_unregister(&udc->gadget.dev); ++ ++ destroy_workqueue(ep_wqueue); ++ ++ iounmap(udc->baseaddr); ++ ++#if 0 ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++ free_irq(udc->dma_irq, udc); ++ ++ /* USB slave L2 buffer initialization */ ++ l2_free(ADDR_USB_EP2); ++ l2_free(ADDR_USB_EP3); ++ l2_free(ADDR_USB_EP4); ++ l2_free(ADDR_USB_EP5); ++#endif ++#endif ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ release_mem_region(res->start, resource_size(res)); ++ ++ return 0; ++} ++ ++static void akudc_udc_shutdown(struct platform_device *dev) ++{ ++} ++ ++#ifdef CONFIG_PM ++static int akudc_udc_suspend(struct platform_device *pdev, pm_message_t mesg) ++{ ++ struct akudc_udc *udc = platform_get_drvdata(pdev); ++ ++ /*cacel work for every endpoint */ ++ cancel_work_sync(&udc->ep[1].work); ++ cancel_work_sync(&udc->ep[2].work); ++ cancel_work_sync(&udc->ep[3].work); ++ cancel_work_sync(&udc->ep[4].work); ++ cancel_work_sync(&udc->ep[5].work); ++ ++ flush_workqueue(ep_wqueue); ++ ++ /* enable usb vbus wakeup function before enter standby */ ++ usb_vbus_wakeup(true); ++ ++ /* clear the udc_clk condition for udc */ ++ clear_condition(&udc_clk); ++ ++ udc_disconnect(udc); ++ ++ return 0; ++} ++ ++static int akudc_udc_resume(struct platform_device *pdev) ++{ ++ struct akudc_udc *udc = platform_get_drvdata(pdev); ++ ++ /* disable usb vbus wakeup function after wakeup */ ++ usb_vbus_wakeup(false); ++ ++ /* if we have the function driver, we set the udc_clk condition */ ++ if (udc->driver) ++ set_condition(&udc_clk); ++ ++ return 0; ++} ++#else ++#define akudc_udc_suspend NULL ++#define akudc_udc_resume NULL ++#endif ++ ++static struct platform_driver akudc_udc_driver = { ++ .remove = __exit_p(akudc_udc_remove), ++ .shutdown = akudc_udc_shutdown, ++ .suspend = akudc_udc_suspend, ++ .resume = akudc_udc_resume, ++ .driver = { ++ .name = (char *) driver_name, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init udc_init_module(void) ++{ ++ printk("udc driver initialize, (c) 2013 Anyka\n"); ++ ++ return platform_driver_probe(&akudc_udc_driver, akudc_udc_probe); ++} ++module_init(udc_init_module); ++ ++static void __exit udc_exit_module(void) ++{ ++ platform_driver_unregister(&akudc_udc_driver); ++} ++module_exit(udc_exit_module); ++ ++MODULE_DESCRIPTION("Anyka udc driver"); ++MODULE_AUTHOR("Anyka"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform: usb-slave"); +diff --git a/drivers/usb/gadget/plat-anyka/usbburn.c b/drivers/usb/gadget/plat-anyka/usbburn.c +new file mode 100644 +index 00000000..a5551da0 +--- /dev/null ++++ b/drivers/usb/gadget/plat-anyka/usbburn.c +@@ -0,0 +1,356 @@ ++/* ++ * akudc_usbburn -- driver for Anyka USB burntool; ++ * Features ++ * AUTHOR Zhang Jingyuan ++ * 10-09-27 16:28:08 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define DEVICE_NAME "akudc_usbburn" ++ ++#define AK_USBBURN_STALL _IO('U', 0xf0) // set stall feature command for producer ++#define AK_USBBURN_STATUS _IO('U', 0xf1) // set CSW status command ++#define AK_GET_CHANNEL_ID _IOR('U', 0xf2, __u32) //get channel id command ++ ++#define CHANNEL_ID_ADDR (CONFIG_RAM_BASE + SZ_1M) /* the address for abtain channel id for burntool */ ++ ++unsigned int sense_data; // the CSW status ++struct semaphore sense_data_lock; // synchronization sense_data ++ ++EXPORT_SYMBOL(sense_data); ++EXPORT_SYMBOL(sense_data_lock); ++ ++static int major = 0; ++struct usbburn_dev { ++ struct cdev cdev; ++ wait_queue_head_t rq_rbuf, wq_rbuf; /* read and write queues for rbuf */ ++ wait_queue_head_t rq_wbuf, wq_wbuf; /* read and write queues for wbuf */ ++ void *rbuf; /* This buffer is used for user reading */ ++ size_t rlen; /* The length of rbuf */ ++ void *wbuf; /* This buffer is used for user writing */ ++ size_t wlen; /* The length of wbuf */ ++ struct semaphore r_sem, w_sem; /* mutual exclusion semaphore for rbuf and wbuf; */ ++ int r_stall; /* The flag of stop of rbuf */ ++ int w_stall; /* The flag of sotp of wbuf */ ++} *b_dev; ++struct class *usbburn_class; ++ ++static int akudc_usbburn_open(struct inode *inode, struct file *file) ++{ ++ printk("akudc_usbburn device is open\n"); ++ ++ return 0; ++} ++ ++static int akudc_usbburn_close(struct inode *inode, struct file *file) ++{ ++ printk("akudc_usbburn device is closed\n"); ++ ++ return 0; ++} ++ ++/* the read function for the producer */ ++ssize_t akudc_usbburn_read(struct file *filp, char __user *buf, ++ size_t count, loff_t *f_pos) ++{ ++ /* ++ * count is used to record the number of data to read; ++ * count1 is used to record the total number of data been read, ++ * readn is used to record the number of data been read each time. ++ */ ++ size_t count1 = 0, readn; ++ ++ while (count != 0) /* when the all data has been read, return. */ ++ { ++ down(&b_dev->r_sem); /* before access the rbuf member of b_dev, down the r_sem */ ++ while (b_dev->rbuf == NULL) { /* when there is no data to read, sleep to wait. */ ++ up(&b_dev->r_sem); /* up the r_sem */ ++ wait_event(b_dev->rq_rbuf, b_dev->rbuf != NULL); /* to sleep until some data is coming. */ ++ down(&b_dev->r_sem); ++ } ++ readn = min(count, b_dev->rlen); ++ ++ /* copy the data to user space */ ++ if (copy_to_user(buf + count1, b_dev->rbuf, readn)) { ++ up(&b_dev->r_sem); ++ return -EFAULT; ++ } ++ count1 += readn; ++ count -= readn; ++ ++ /* the number of data which has been read is less than b_dev->rlen */ ++ if (readn < b_dev->rlen) { ++ b_dev->rbuf += readn; ++ b_dev->rlen -= readn; ++ up(&b_dev->r_sem); ++ } else { /* wake up the wq_rbuf if the all data in rbuf has been read. */ ++ b_dev->rbuf = NULL; ++ up(&b_dev->r_sem); ++ wake_up(&b_dev->wq_rbuf); ++ ++ /* if the r_stall is set, return */ ++ if (b_dev->r_stall == 1) { ++ b_dev->r_stall = 0; ++ break; ++ } ++ } ++ } ++ ++ return count1; ++} ++ ++/* the write function for the usb file_storage */ ++int usbburn_write(void *buf, size_t count) ++{ ++ down(&b_dev->r_sem); /* down the r_sem before access the rbuf member of b_dev. */ ++ b_dev->rbuf = buf; ++ b_dev->rlen = count; ++ ++ /* sleep until the all data in rbuf has been read. */ ++ while (b_dev->rbuf != NULL) { ++ up(&b_dev->r_sem); ++ wake_up(&b_dev->rq_rbuf); ++ wait_event(b_dev->wq_rbuf, b_dev->rbuf == NULL); ++ down(&b_dev->r_sem); ++ } ++ up(&b_dev->r_sem); ++ ++ return count; ++} ++EXPORT_SYMBOL(usbburn_write); ++ ++/* the write function for the producer */ ++ssize_t akudc_usbburn_write(struct file *filp, const char __user *buf, ++ size_t count, loff_t *f_pos) ++{ ++ /* ++ * count is used to record the number of data to write; ++ * count1 is used to record the total number of data been written, ++ * written is used to record the number of data been written each time. ++ */ ++ size_t count1 = 0, written; ++ ++ while (count != 0) { ++ down(&b_dev->w_sem); /* before access the wbuf member of b_dev, down the w_sem. */ ++ while (b_dev->wbuf == NULL) { ++ up(&b_dev->w_sem); /* up the w_sem. */ ++ wait_event(b_dev->wq_wbuf, b_dev->wbuf != NULL); /* sleep until wbuf can be written. */ ++ down(&b_dev->w_sem); ++ } ++ written = min(count, b_dev->wlen); ++ ++ /* copy the user space buffer to the kernel. */ ++ if (copy_from_user(b_dev->wbuf, buf + count1, written)) { ++ up(&b_dev->w_sem); ++ return -EFAULT; ++ } ++ count1 += written; ++ count -= written; ++ ++ /* if written if less than b_dev->wlen. */ ++ if (written < b_dev->wlen) { ++ b_dev->wbuf += written; ++ b_dev->wlen -= written; ++ ++ /* if w_stall is set, wake up the rq_buf and return. */ ++ if (b_dev->w_stall == 1) { ++ b_dev->wbuf = NULL; ++ b_dev->wlen = written; ++ b_dev->w_stall = 0; ++ up(&b_dev->w_sem); ++ wake_up(&b_dev->rq_wbuf); ++ break; ++ } ++ up(&b_dev->w_sem); ++ } else { /* wake up rq_wbuf if the wbuf is full written. */ ++ b_dev->wbuf = NULL; ++ up(&b_dev->w_sem); ++ wake_up(&b_dev->rq_wbuf); ++ } ++ } ++ ++ return count1; ++} ++ ++/* the read function for the usb file_storage */ ++int usbburn_read(void *buf, size_t count) ++{ ++ down(&b_dev->w_sem); ++ b_dev->wbuf = buf; ++ b_dev->wlen = count; ++ ++ /* sleep until wbuf has been written. */ ++ while (b_dev->wbuf != NULL) { ++ up(&b_dev->w_sem); ++ wake_up(&b_dev->wq_wbuf); ++ wait_event(b_dev->rq_wbuf, b_dev->wbuf == NULL); ++ down(&b_dev->w_sem); ++ } ++ count = b_dev->wlen; ++ up(&b_dev->w_sem); ++ ++ return count; ++} ++EXPORT_SYMBOL(usbburn_read); ++ ++int akudc_usbburn_ioctl(struct file *filp, unsigned int cmd, ++ unsigned long arg) ++{ ++ unsigned long *channel_id; ++ ++ switch (cmd) { ++ case AK_USBBURN_STALL: ++ down(&b_dev->w_sem); ++ b_dev->w_stall = 1; /*set the w_stall flag for wbuf. */ ++ up(&b_dev->w_sem); ++ break; ++ case AK_USBBURN_STATUS: ++ sense_data = arg; ++ up(&sense_data_lock); ++ break; ++ case AK_GET_CHANNEL_ID: ++ channel_id = ioremap(CHANNEL_ID_ADDR, sizeof (*channel_id)); ++ if (channel_id == NULL) { ++ printk("ioremap error\n"); ++ return -1; ++ } ++ if (copy_to_user((void __user *)arg, channel_id, sizeof (*channel_id))) { ++ printk("copy id to user space failed!\n"); ++ iounmap(channel_id); ++ return -1; ++ } ++ iounmap(channel_id); ++ break; ++ default: ++ return -1; ++ } ++ ++ return 0; ++} ++ ++void usbburn_ioctl(void) ++{ ++ down(&b_dev->r_sem); ++ b_dev->r_stall = 1; /*set the r_stall flag for rbuf. */ ++ up(&b_dev->r_sem); ++} ++EXPORT_SYMBOL(usbburn_ioctl); ++ ++/* The file operation for the usbburn device.*/ ++static struct file_operations akudc_usbburn_fops = { ++ .owner = THIS_MODULE, ++ .read = akudc_usbburn_read, ++ .write = akudc_usbburn_write, ++ .unlocked_ioctl = akudc_usbburn_ioctl, ++ .open = akudc_usbburn_open, ++ .release = akudc_usbburn_close, ++}; ++ ++int __init akudc_usbburn_init(void) ++{ ++ int ret = 0; ++ struct device *device; ++ dev_t dev = MKDEV(major, 0); ++ ++ /* allocate the burn device. */ ++ b_dev = kmalloc(sizeof(*b_dev), GFP_KERNEL); ++ if (unlikely (!b_dev)) { ++ ret = -ENOMEM; ++ printk("error, out of memory\n"); ++ goto out1; ++ } ++ memset(b_dev, 0, sizeof(*b_dev)); ++ ++ /* Register device major, and accept a dynamic number. */ ++ if (major) ++ ret = register_chrdev_region(dev, 1, DEVICE_NAME); ++ else { ++ ret = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME); ++ major = MAJOR(dev); ++ } ++ if (ret < 0) { ++ printk("register chrdev major and minor number failed\n"); ++ goto out1; ++ } ++ ++ cdev_init(&b_dev->cdev, &akudc_usbburn_fops); ++ b_dev->cdev.owner = THIS_MODULE; ++ ret = cdev_add(&b_dev->cdev, dev, 1); ++ /* Fail gracefully if need be */ ++ if (ret) { ++ printk("Error %d adding akudc_usbburn_dev", ret); ++ goto out1; ++ } ++ ++ /* create the usbburn char device's class */ ++ usbburn_class = class_create(THIS_MODULE, "usbburn"); ++ if (IS_ERR(usbburn_class)) { ++ ret = PTR_ERR(usbburn_class); ++ printk("create usbburn class failed\n"); ++ goto out2; ++ } ++ ++ /* create the usbburn char device node in /dev/akudc_usbburn */ ++ device = device_create(usbburn_class, NULL, dev, NULL,"akudc_usbburn"); ++ if (IS_ERR(device)) { ++ printk("akudc_usbburn chrdev create failed! %x\n", ret); ++ ret = PTR_ERR(device); ++ goto out2; ++ } ++ else ++ printk("akudc_usbburn chrdev create success!\n"); ++ ++ /* init the waitqueue head for the rbuf and wbuf */ ++ init_waitqueue_head(&b_dev->rq_rbuf); ++ init_waitqueue_head(&b_dev->wq_rbuf); ++ init_waitqueue_head(&b_dev->rq_wbuf); ++ init_waitqueue_head(&b_dev->wq_wbuf); ++ ++ /* init the r_sem and w_sem with 1 */ ++ sema_init(&b_dev->r_sem, 1); ++ sema_init(&b_dev->w_sem, 1); ++ ++ /* init the sense_data_lock with 0 */ ++ memset(&sense_data_lock, 0, sizeof(sense_data_lock)); ++ sema_init(&sense_data_lock, 0); ++ ++ return ret; ++out2: ++ cdev_del(&b_dev->cdev); ++out1: ++ kfree(b_dev); ++ ++ return ret; ++} ++ ++void __exit akudc_usbburn_exit(void) ++{ ++ /* delete the device node */ ++ device_destroy(usbburn_class, MKDEV(major, 0)); ++ /* delete the class for usbburn */ ++ class_destroy(usbburn_class); ++ cdev_del(&b_dev->cdev); ++ kfree(b_dev); ++ unregister_chrdev_region(MKDEV(major, 0), 1); ++} ++ ++module_init(akudc_usbburn_init); ++module_exit(akudc_usbburn_exit); ++ ++MODULE_DESCRIPTION("AKudc usbburn driver"); ++MODULE_AUTHOR("Anyka"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c +index 73a934a1..0cb21218 100644 +--- a/drivers/usb/gadget/rndis.c ++++ b/drivers/usb/gadget/rndis.c +@@ -1146,11 +1146,15 @@ static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; + + #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + ++static bool rndis_initialized; + + int rndis_init(void) + { + u8 i; + ++ if (rndis_initialized) ++ return 0; ++ + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + #ifdef CONFIG_USB_GADGET_DEBUG_FILES + char name [20]; +@@ -1177,6 +1181,7 @@ int rndis_init(void) + INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue)); + } + ++ rndis_initialized = true; + return 0; + } + +@@ -1185,7 +1190,13 @@ void rndis_exit(void) + #ifdef CONFIG_USB_GADGET_DEBUG_FILES + u8 i; + char name[20]; ++#endif + ++ if (!rndis_initialized) ++ return; ++ rndis_initialized = false; ++ ++#ifdef CONFIG_USB_GADGET_DEBUG_FILES + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + sprintf(name, NAME_TEMPLATE, i); + remove_proc_entry(name, NULL); +diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c +index 9a2a1ae9..dd2615a3 100644 +--- a/drivers/usb/gadget/u_ether.c ++++ b/drivers/usb/gadget/u_ether.c +@@ -763,6 +763,26 @@ static struct device_type gadget_type = { + * Returns negative errno, or zero on success + */ + int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) ++{ ++ return gether_setup_name(g, ethaddr, "usb"); ++} ++ ++/** ++ * gether_setup_name - initialize one ethernet-over-usb link ++ * @g: gadget to associated with these links ++ * @ethaddr: NULL, or a buffer in which the ethernet address of the ++ * host side of the link is recorded ++ * @netname: name for network device (for example, "usb") ++ * Context: may sleep ++ * ++ * This sets up the single network link that may be exported by a ++ * gadget driver using this framework. The link layer addresses are ++ * set up using module parameters. ++ * ++ * Returns negative errno, or zero on success ++ */ ++int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], ++ const char *netname) + { + struct eth_dev *dev; + struct net_device *net; +@@ -786,7 +806,7 @@ int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) + + /* network device setup */ + dev->net = net; +- strcpy(net->name, "usb%d"); ++ snprintf(net->name, sizeof(net->name), "%s%%d", netname); + + if (get_ether_addr(dev_addr, net->dev_addr)) + dev_warn(&g->dev, +diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h +index 8012357e..37431f52 100644 +--- a/drivers/usb/gadget/u_ether.h ++++ b/drivers/usb/gadget/u_ether.h +@@ -73,6 +73,9 @@ struct gether { + /* netdev setup/teardown as directed by the gadget driver */ + int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]); + void gether_cleanup(void); ++/* variant of gether_setup that allows customizing network device name */ ++int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], ++ const char *netname); + + /* connect/disconnect is handled by individual functions */ + struct net_device *gether_connect(struct gether *); +@@ -100,6 +103,8 @@ int eem_bind_config(struct usb_configuration *c); + #ifdef USB_ETH_RNDIS + + int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); ++int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], ++ u32 vendorID, const char *manufacturer); + + #else + +@@ -109,6 +114,13 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) + return 0; + } + ++static inline int ++rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], ++ u32 vendorID, const char *manufacturer) ++{ ++ return 0; ++} ++ + #endif + + #endif /* __U_ETHER_H */ +diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c +index 6c23938d..380a87f6 100644 +--- a/drivers/usb/gadget/u_serial.c ++++ b/drivers/usb/gadget/u_serial.c +@@ -1025,7 +1025,7 @@ static const struct tty_operations gs_tty_ops = { + + static struct tty_driver *gs_tty_driver; + +-static int __init ++static int + gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) + { + struct gs_port *port; +@@ -1071,7 +1071,7 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) + * + * Returns negative errno or zero. + */ +-int __init gserial_setup(struct usb_gadget *g, unsigned count) ++int gserial_setup(struct usb_gadget *g, unsigned count) + { + unsigned i; + struct usb_cdc_line_coding coding; +diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig +index f788eb86..056a1ccf 100644 +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -638,3 +638,6 @@ config USB_OCTEON_OHCI + config USB_OCTEON2_COMMON + bool + default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI ++ ++source "drivers/usb/host/plat-anyka/Kconfig" ++ +diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile +index 0982bcc1..33a6dc12 100644 +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -41,3 +41,4 @@ obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o + obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o + obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o + obj-$(CONFIG_MIPS_ALCHEMY) += alchemy-common.o ++obj-$(CONFIG_USB_ANYKA_HCD) += plat-anyka/ +diff --git a/drivers/usb/host/plat-anyka/Kconfig b/drivers/usb/host/plat-anyka/Kconfig +new file mode 100644 +index 00000000..f58dc4ee +--- /dev/null ++++ b/drivers/usb/host/plat-anyka/Kconfig +@@ -0,0 +1,26 @@ ++config USB_ANYKA_HCD ++ bool "Anyka on-chip HCD support" ++ depends on USB && ARCH_AK39 ++ help ++ Enable support for the ANYKA on-chip HCD, It is support full speed and high speed ++ ++config USB_AKOTG_HS_HCD ++ tristate "USB(otg) High-Speed HCD support" ++ depends on USB_ANYKA_HCD ++ help ++ The ANYKA chips inner USB otg high speed host controllers. Enable this ++ option if your board has this chip. If unsure, say N. ++ ++ This driver does not support isochronous transfers. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called otg-hs. ++ ++config USB_AKOTG_DMA ++ bool "USB(otg) support for DMA transfer" ++ depends on USB_AKOTG_HS_HCD ++ help ++ Say 'Y' to turn on dma support for anyka otg host. ++ ++ If no need, say N. ++ +diff --git a/drivers/usb/host/plat-anyka/Makefile b/drivers/usb/host/plat-anyka/Makefile +new file mode 100644 +index 00000000..f749f649 +--- /dev/null ++++ b/drivers/usb/host/plat-anyka/Makefile +@@ -0,0 +1,7 @@ ++# ++# Makefile for USB Host Controller Drivers ++# ++ ++obj-$(CONFIG_USB_AKOTG_HS_HCD) += otg-hs.o ++ otg-hs-objs := otg-hshcd.o usb-hc.o ++ +diff --git a/drivers/usb/host/plat-anyka/otg-hshcd.c b/drivers/usb/host/plat-anyka/otg-hshcd.c +new file mode 100755 +index 00000000..f7d5ed14 +--- /dev/null ++++ b/drivers/usb/host/plat-anyka/otg-hshcd.c +@@ -0,0 +1,337 @@ ++/* ++ * Anyka OTG HS HCD (Full-Speed Host Controller Driver) for USB. ++ * ++ * Derived from the SL811 HCD, rewritten for AKOTG HS HCD. ++ * Copyright (C) 2010 ANYKA LTD. ++ * ++ * Periodic scheduling is based on Roman's OHCI code ++ * Copyright (C) 1999 Roman Weissgaerber ++ * ++ * The AK OTG HS Host controller handles host side USB. For Documentation, ++ * refer to chapter 22 USB Controllers of Anyka chip Mobile Multimedia Application ++ * Processor Programmer's Guide. ++ * ++ */ ++ ++/* ++ * Status: Enumeration of USB Optical Mouse, USB Keyboard, USB Flash Disk, Ralink 2070/3070 USB WiFi OK. ++ * Pass basic test with USB Optical Mouse/USB Keyboard/USB Flash Disk. ++ * Ralink 2070/3070 USB WiFI Scanning/WEP basic test OK. Full Functions TBD. ++ * ++ * TODO: ++ * - Use up to 6 active queues of FS HC(for now only 2 queues: EP0 & EPX(1-6)) ++ * - USB Suspend/Resume support ++ * - Use urb->iso_frame_desc[] with ISO transfers ++ * - Optimize USB FIFO data transfer/receive(4B->2B->1B) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++static char *host_sw = "onboard"; ++module_param(host_sw, charp, S_IRUGO); ++ ++static const char hcd_name[] = "usb-host"; ++extern struct akotghc_epfifo_mapping akotg_epfifo_mapping;; ++ ++ ++static struct hc_driver akhs_otg_driver = { ++ .description = hcd_name, ++ .product_desc = "Anyka usb host controller", ++ .hcd_priv_size = sizeof(struct akotg_usbhc), ++ ++ /* ++ * generic hardware linkage ++ */ ++ .irq = akotg_usbhc_irq, ++ .flags = HCD_USB2 | HCD_MEMORY, ++ ++ /* Basic lifecycle operations */ ++ .start = akotg_usbhc_start, ++ .stop = akotg_usbhc_stop, ++ ++ /* ++ * managing i/o requests and associated device resources ++ */ ++ ++ .urb_dequeue = akotg_usbhc_urb_dequeue, ++ .urb_enqueue = akotg_usbhc_urb_enqueue, ++ .endpoint_reset = akotg_usbhc_endpoint_reset, ++ .endpoint_disable = akotg_usbhc_endpoint_disable, ++ ++ /* ++ * periodic schedule support ++ */ ++ .get_frame_number = akotg_usbhc_get_frame, ++ ++ /* ++ * root hub support ++ */ ++ .hub_status_data = akotg_usbhc_hub_status_data, ++ .hub_control = akotg_usbhc_hub_control, ++ #ifdef CONFIG_PM ++ .bus_suspend = akotg_usbhc_bus_suspend, ++ .bus_resume = akotg_usbhc_bus_resume, ++ #else ++ .bus_suspend = NULL, ++ .bus_resume = NULL, ++ #endif ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void usb_poweron_for_device(struct akotghc_usb_platform_data *pdata) ++{ ++ /* power on for usb device */ ++ if(pdata->gpio_pwr_on.pin >= 0) ++ pdata->gpio_init(&pdata->gpio_pwr_on); ++ ++ /* ust otg host port switch */ ++ if((pdata->switch_onboard.pin >= 0)&&(pdata->switch_extport.pin >= 0) ++ && (pdata->switch_onboard.pin == pdata->switch_extport.pin)) { ++ if (!strcmp(host_sw, "extport")) ++ pdata->gpio_init(&pdata->switch_extport); ++ else ++ pdata->gpio_init(&pdata->switch_onboard); ++ ++ } ++} ++ ++static void usb_poweroff_for_device(struct akotghc_usb_platform_data *pdata) ++{ ++ /* ust otg host port switch */ ++ if((pdata->switch_onboard.pin >= 0)&&(pdata->switch_extport.pin >= 0) ++ && (pdata->switch_onboard.pin == pdata->switch_extport.pin)) { ++ if (!strcmp(host_sw, "onboard")) ++ pdata->gpio_init(&pdata->switch_extport); ++ else ++ pdata->gpio_init(&pdata->switch_onboard); ++ } ++ ++ /* power off for usb device */ ++ if(pdata->gpio_pwr_off.pin >= 0) ++ pdata->gpio_init(&pdata->gpio_pwr_off); ++} ++ ++static int __devexit ++akotg_hc_remove(struct platform_device *pdev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(pdev); ++ struct akotghc_usb_platform_data *pdata = NULL; ++ ++#ifdef CONFIG_USB_AKOTG_DMA ++ struct akotg_usbhc *akotghc; ++#endif ++ ++ pdata = pdev->dev.platform_data; ++ if (pdata != NULL) ++ /* hwinit power off */ ++ usb_poweroff_for_device(pdata); ++ ++ usb_remove_hcd(hcd); ++ usb_put_hcd(hcd); ++ ++#ifdef CONFIG_USB_AKOTG_DMA ++ ++ //free dma irq ++ akotghc = hcd_to_akotg_usbhc(hcd); ++ if(akotghc->dma_irq > 0) ++ { ++ free_irq(akotghc->dma_irq, hcd); ++ akotghc->dma_irq = -1; ++ } ++ ++#endif ++ ++ return 0; ++} ++ ++static int __devinit ++akotg_hc_probe(struct platform_device *pdev) ++{ ++ struct usb_hcd *hcd; ++ struct akotg_usbhc *akotghc; ++ struct resource *res; ++ struct akotghc_usb_platform_data *pdata = NULL; ++ int irq; ++ int retval = 0; ++ unsigned long irqflags; ++ int i, j; ++ ++ pdata = pdev->dev.platform_data; ++ if (pdata == NULL) ++ return -ENODEV; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if(!res) ++ return -ENODEV; ++ ++ /* allocate and initialize hcd */ ++ hcd = usb_create_hcd(&akhs_otg_driver, &pdev->dev, akhs_otg_driver.product_desc); ++ if (!hcd) { ++ retval = -ENOMEM; ++ goto err_nomem; ++ } ++ ++ hcd->rsrc_start = res->start; ++ hcd->rsrc_len = resource_size(res); ++ akotghc = hcd_to_akotg_usbhc(hcd); ++ ++ usb_poweron_for_device(pdata); ++ ++ akotghc->clk = clk_get(&pdev->dev, "usb-host"); ++ if (IS_ERR(akotghc->clk)) { ++ dbg("usb otg hs clocks missing\n"); ++ akotghc->clk = NULL; ++ goto err_nomem; ++ } ++ ++ /* basic sanity checks first. board-specific init logic should ++ * have initialized these three resources and probably board ++ * specific platform_data. we don't probe for IRQs, and do only ++ * minimal sanity checking. ++ */ ++ irq = platform_get_irq(pdev, 0); ++ if (irq <= 0) { ++ dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n", ++ dev_name(&pdev->dev)); ++ retval = -ENODEV; ++ goto err_nodev; ++ } ++ akotghc->mcu_irq = irq; ++ ++ spin_lock_init(&akotghc->lock); ++ INIT_LIST_HEAD(&akotghc->async_ep0); ++ for(i=0; iasync_epx[i]); ++ ++ akotghc->active_ep0 = NULL; ++ for(j=0; jactive_epx[j] = NULL; ++ ++ init_timer(&akotghc->timer); ++ akotghc->timer.function = akotg_usbhc_timer; ++ akotghc->timer.data = (unsigned long) akotghc; ++ ++ hcd->speed = HCD_USB2; ++ //hcd->power_budget = 0; //or get from platfrom data ++ ++ /* The chip's IRQ is level triggered, active high. A requirement ++ * for platform device setup is to cope with things like signal ++ * inverters (e.g. CF is active low) or working only with edge ++ * triggers (e.g. most ARM CPUs). Initial driver stress testing ++ * was on a system with single edge triggering, so most sorts of ++ * triggering arrangement should work. ++ * ++ * Use resource IRQ flags if set by platform device setup. ++ */ ++ irqflags = IRQF_SHARED; ++ retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | irqflags); ++ if (retval != 0) ++ goto err_addhcd; ++ ++ init_epfifo_mapping(&akotg_epfifo_mapping); ++ ++#ifdef CONFIG_USB_AKOTG_DMA ++ ++ akotg_dma_init(akotghc); ++ ++ //request dma irq ++ irq = platform_get_irq(pdev, 1); ++ retval = request_irq(irq, akotg_dma_irq, IRQF_DISABLED, "otgdma", hcd); ++ if (retval < 0){ ++ akotghc->dma_irq = -1; ++ printk("request irq %d failed\n", irq); ++ } ++ else { ++ akotghc->dma_irq = irq; ++ } ++ ++#endif ++ ++ printk("Usb otg-hs controller driver initialized\n"); ++ return retval; ++ ++ err_addhcd: ++ usb_put_hcd(hcd); ++ err_nomem: ++ err_nodev: ++ ++ return retval; ++} ++ ++#ifdef CONFIG_PM ++ ++/* for this device there's no useful distinction between the controller ++ * and its root hub, except that the root hub only gets direct PM calls ++ * when CONFIG_USB_SUSPEND is enabled. ++ */ ++static int ++akotg_hc_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ int retval = 0; ++ ++ switch (state.event) { ++ case PM_EVENT_FREEZE: ++ break; ++ case PM_EVENT_SUSPEND: ++ case PM_EVENT_HIBERNATE: ++ case PM_EVENT_PRETHAW: /* explicitly discard hw state */ ++ break; ++ } ++ return retval; ++} ++ ++static int ++akotg_hc_resume(struct platform_device *dev) ++{ ++ return 0; ++} ++ ++#else ++#define akotg_hc_suspend NULL ++#define akotg_hc_resume NULL ++#endif ++ ++ ++struct platform_driver akotg_hc_driver = { ++ .probe = akotg_hc_probe, ++ .remove = __devexit_p(akotg_hc_remove), ++ ++ .suspend = akotg_hc_suspend, ++ .resume = akotg_hc_resume, ++ .driver = { ++ .name = (char *) hcd_name, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init akotg_hc_init(void) ++{ ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ return platform_driver_register(&akotg_hc_driver); ++} ++module_init(akotg_hc_init); ++ ++static void __exit akotg_hc_cleanup(void) ++{ ++ platform_driver_unregister(&akotg_hc_driver); ++} ++module_exit(akotg_hc_cleanup); ++MODULE_DESCRIPTION("Anyka Host Controller Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:ak_hcd"); +diff --git a/drivers/usb/host/plat-anyka/usb-hc.c b/drivers/usb/host/plat-anyka/usb-hc.c +new file mode 100755 +index 00000000..370f87a8 +--- /dev/null ++++ b/drivers/usb/host/plat-anyka/usb-hc.c +@@ -0,0 +1,1802 @@ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#ifdef AKOTG_HS_DEBUG ++#define HDBG(stuff...) printk("USBHS: " stuff) ++#else ++#define HDBG(fmt, args...) do{}while(0) ++#endif ++ ++#ifdef AKOTG_HS_VERBOSE_DEBUG ++#define VDBG HDBG ++#else ++#define VDBG(fmt, args...) do{}while(0) ++#endif ++ ++ ++static int period_epfifo; ++static struct workqueue_struct *g_otghc_wq; ++static struct delayed_work g_otg_rest; ++//static char *trans_desc[4] = {"iso", "intterrup", "conroller", "bulk"}; ++static u8 ep_fifos[] = { USB_FIFO_EP0, USB_FIFO_EP1, USB_FIFO_EP2, USB_FIFO_EP3, USB_FIFO_EP4, USB_FIFO_EP5}; ++struct akotghc_epfifo_mapping akotg_epfifo_mapping; ++ ++static inline int get_ep_type(int pipe) ++{ ++ int eptype = 0; ++ ++ switch(usb_pipetype(pipe)) { ++ case PIPE_ISOCHRONOUS: ++ eptype = 1; ++ break; ++ case PIPE_BULK: ++ eptype = 2; ++ break; ++ case PIPE_INTERRUPT: ++ eptype = 3; ++ break; ++ } ++ return eptype; ++} ++ ++void port_power(struct akotg_usbhc *akotghc, int is_on) ++{ ++ /*Anyka usb host is self power currently.*/ ++ struct usb_hcd *hcd = akotg_usbhc_to_hcd(akotghc); ++ ++ /* hub is inactive unless the port is powered */ ++ if (is_on) { ++ if (akotghc->port_status & (1 << USB_PORT_FEAT_POWER)) ++ return; ++ ++ akotghc->port_status = (1 << USB_PORT_FEAT_POWER); ++ } else { ++ akotghc->port_status = 0; ++ hcd->state = HC_STATE_HALT; ++ } ++ ++ /* reset as thoroughly as we can */ ++ ak_soft_reset(AK_SRESET_USBHS); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* This is a PIO-only HCD. Queueing appends URBs to the endpoint's queue, ++ * and may start I/O. Endpoint queues are scanned during completion irq ++ * handlers (one per packet: ACK, NAK, faults, etc) and urb cancellation. ++ * ++ * Using an external DMA engine to copy a packet at a time could work, ++ * though setup/teardown costs may be too big to make it worthwhile. ++ */ ++ ++/* SETUP starts a new control request. Devices are not allowed to ++ * STALL or NAK these; they must cancel any pending control requests. ++ */ ++static void setup_packet( ++ struct akotg_usbhc *akotghc, ++ struct akotghc_ep *ep, ++ struct urb *urb ++) ++{ ++ int i; ++ unsigned int fifo_val; ++ u8 len; ++ unsigned char *buf = urb->setup_packet; ++ ++ HDBG("Packet: Setup Packet (EP %d)\n", ep->epnum); ++ ++ len = sizeof(struct usb_ctrlrequest); ++ ++ hc_index_writeb(0, 0, USB_REG_TXCSR1); ++ hc_writeb(usb_pipedevice(urb->pipe), USB_REG_FADDR); ++ for (i = 0; i < len; i += 4) { ++ fifo_val = (*(buf+i) | ( *(buf+i+1)<<8 ) ++ | ( *(buf+i+2)<<16 ) | ( *(buf+i+3)<<24 )); ++ hc_writel(fifo_val, USB_FIFO_EP0); ++ } ++ hc_index_writeb(0, USB_TXCSR1_FLUSHFIFO |USB_TXCSR1_FIFONOTEMPTY, USB_REG_TXCSR1); ++ ++ ep->length = 0; ++} ++ ++/* STATUS finishes control requests, often after IN or OUT data packets */ ++static void status_packet( ++ struct akotg_usbhc *akotghc, ++ struct akotghc_ep *ep, ++ struct urb *urb ++) ++{ ++ int is_in; ++ ++ HDBG("Packet: Status Packet (EP %d)\n", ep->epnum); ++ ++ is_in = urb->transfer_buffer_length && usb_pipein(urb->pipe); ++ //if (!epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, !usb_pipein(urb->pipe), &epfifo)) ++ // BUG(); /* Impossible, USB Device Endpoint *MUST* have been mapped to EPFIFO */ ++ ++ hc_writeb(usb_pipedevice(urb->pipe), USB_REG_FADDR); ++ ++ if (is_in) { ++ /* send a state packet when IN transaction is finished */ ++ hc_index_writeb(ep->epfifo, 0x42, USB_REG_TXCSR1); ++ } else { ++ /* request a state packet when OUT transaction is finished */ ++ hc_index_writeb(ep->epfifo, 0x60, USB_REG_TXCSR1); ++ } ++ ++ ep->length = 0; ++} ++ ++/* IN packets can be used with any type of endpoint. here we just ++ * start the transfer, data from the peripheral may arrive later. ++ * urb->iso_frame_desc is currently ignored here... ++ */ ++static void in_packet( ++ struct akotg_usbhc *akotghc, ++ struct akotghc_ep *ep, ++ struct urb *urb ++) ++{ ++ u16 len; ++ u32 remain; ++ ++ bool bDMA = false; ++ ++ HDBG("Packet: IN Packet (EP %d), Length=%d,ep->maxpacket=%d\n", ++ ep->epnum, urb->transfer_buffer_length - urb->actual_length, ep->maxpacket); ++ ++ /* avoid losing data on overflow */ ++ len = ep->maxpacket; ++ remain = urb->transfer_buffer_length - urb->actual_length; ++ ++ ep->length = min_t(u32, len, remain); ++ ++ hc_writeb(usb_pipedevice(urb->pipe), USB_REG_FADDR); ++ if (ep->epnum == 0) { ++ hc_index_writeb(0, USB_TXCSR1_H_RXSTALL, USB_REG_TXCSR1); ++ } else { ++ int eptype = 0; ++ //if (!epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, 0, &epfifo)) ++ // BUG(); /* Impossible, USB Device Endpoint *MUST* have been mapped to EPFIFO */ ++ ++ eptype = get_ep_type(urb->pipe); ++ ++ /** ++ dma condition: ++ 1. high speed and ep size >= 512 ++ 2. data leng >= 512 ++ 3. l2 buffer is alloced for epfifo ++ */ ++#ifdef CONFIG_USB_AKOTG_DMA ++ if((len == 512) && (remain >= len)) ++ { ++ struct akotg_dma *usbdma; ++ u8 regvalue; ++ u32 pktnum = remain / 512; ++ u32 count = remain - remain%512; ++ unsigned char *buf; ++ dma_addr_t phyaddr; ++ u8 l2buffer; ++ ++ if(count < remain){ ++ count += 512; ++ } ++ ++ do ++ { ++ //map dma buffer ++ buf = urb->transfer_buffer + urb->actual_length; ++ phyaddr = dma_map_single(NULL, buf, count, DMA_FROM_DEVICE); ++ if (phyaddr == 0) { ++ printk("tx dma_map_single error!\n"); ++ break; ++ } ++ ++ //alloc dma channel ++ usbdma = akotg_dma_alloc(akotghc, ep->epfifo); ++ if(!usbdma){ ++ //printk("dma alloc fail for in_packet\n"); ++ dma_unmap_single(NULL, phyaddr, count, DMA_FROM_DEVICE); ++ break; ++ } ++ ++ l2buffer = usbdma->l2buffer; ++ ++ //config rx reg ++ regvalue = hc_index_readb(ep->epfifo, USB_REG_RXCSR2); ++ regvalue |= (USB_RXCSR2_AUTOCLEAR | USB_RXCSR2_AUTOREQ | USB_RXCSR2_DMAENAB | USB_RXCSR2_DMAMODE); ++ hc_index_writeb(ep->epfifo, regvalue, USB_REG_RXCSR2); ++ ++ hc_writew(pktnum, USB_REG_REQPKTCNT(ep->epfifo)); ++ ++ //config l2 ++ l2_clr_status(l2buffer); ++ l2_combuf_dma(phyaddr, l2buffer, count, BUF2MEM, AK_FALSE); ++ ++ //config dma ++ akotg_dma_set_struct(usbdma, USB_DIRECTION_RX, ep->epfifo, l2buffer, count, phyaddr); ++ akotg_dma_config(usbdma); ++ ++ ep->length = count; ++ ++ bDMA = true; ++ } ++ while(0); ++ ++ } ++#endif ++ ep->bdma = bDMA; ++ ++ hc_index_writeb(ep->epfifo, USB_RXCSR1_H_REQPKT, USB_REG_RXCSR1); ++ } ++} ++ ++/* OUT packets can be used with any type of endpoint. ++ * urb->iso_frame_desc is currently ignored here... ++ */ ++static void out_packet( ++ struct akotg_usbhc *akotghc, ++ struct akotghc_ep *ep, ++ struct urb *urb ++) ++{ ++ int i; ++ unsigned char *buf; ++ u16 len; ++ u32 remain; ++ ++ bool bDMA = false; ++ ++ HDBG("Packet: OUT Packet (EP %d), Length=%d\n", ep->epnum, urb->transfer_buffer_length - urb->actual_length); ++ ++ buf = (unsigned char *)(urb->transfer_buffer + urb->actual_length); ++ prefetch(buf); ++ ++ remain = urb->transfer_buffer_length - urb->actual_length; ++ len = min_t(u32, ep->maxpacket, remain); ++ ++ //if (!epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, 1, &epfifo)) ++ // BUG(); /* Impossible, USB Device Endpoint *MUST* have been mapped to EPFIFO */ ++ ++ /** ++ dma condition: ++ 1. high speed and ep size >= 512 ++ 2. data leng >= 512 ++ 3. l2 buffer is alloced for epfifo ++ */ ++#ifdef CONFIG_USB_AKOTG_DMA ++ if((len == 512) && (remain >= len)) ++ { ++ struct akotg_dma *usbdma; ++ u8 regvalue; ++ u32 count = remain - remain%512; ++ unsigned char *buf; ++ dma_addr_t phyaddr; ++ u8 l2buffer; ++ ++ do ++ { ++ //map dma buffer ++ buf = urb->transfer_buffer + urb->actual_length; ++ phyaddr = dma_map_single(NULL, buf, count, DMA_TO_DEVICE); ++ if (phyaddr == 0) { ++ printk("rx dma_map_single error!\n"); ++ break; ++ } ++ ++ //alloc dma channel ++ usbdma = akotg_dma_alloc(akotghc, ep->epfifo); ++ if(!usbdma){ ++ //printk("dma alloc fail for out_packet\n"); ++ dma_unmap_single(NULL, phyaddr, count, DMA_TO_DEVICE); ++ break; ++ } ++ ++ l2buffer = usbdma->l2buffer; ++ ++ ++ //config tx reg ++ regvalue = hc_index_readb(ep->epfifo, USB_REG_TXCSR2); ++ regvalue |= (USB_TXCSR2_DMAMODE1|USB_TXCSR2_DMAENAB|USB_TXCSR2_MODE|USB_TXCSR2_AUTOSET); ++ hc_index_writeb(ep->epfifo, regvalue, USB_REG_TXCSR2); ++ ++ //config l2 ++ l2_clr_status(l2buffer); ++ l2_combuf_dma(phyaddr, l2buffer, count, MEM2BUF, AK_FALSE); ++ ++ //config dma ++ akotg_dma_set_struct(usbdma, USB_DIRECTION_TX, ep->epfifo, l2buffer, count, phyaddr); ++ akotg_dma_config(usbdma); ++ ++ ep->length = count; ++ ++ bDMA = true; ++ } ++ while(0); ++ ++ } ++#endif ++ ep->bdma = bDMA; ++ ++ if(!bDMA) ++ { ++ hc_index_writeb(ep->epfifo, 0, USB_REG_TXCSR1); ++ hc_writeb(usb_pipedevice(urb->pipe), USB_REG_FADDR); ++ for (i = 0; i < len; i ++) { ++ hc_writeb(buf[i], ep_fifos[ep->epfifo]); ++ } ++ if (ep->epnum == 0) { ++ hc_index_writeb(0, 0x02, USB_REG_TXCSR1); ++ } else { ++ ++ hc_index_writeb(ep->epfifo, USB_TXCSR1_TXPKTRDY, USB_REG_TXCSR1); ++ } ++ ++ ep->length = len; ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* caller updates on-chip enables later */ ++static inline void sofirq_on(struct akotg_usbhc *akotghc) ++{ ++ unsigned int regval; ++ ++ regval = hc_readb(USB_REG_INTRUSBE); ++ regval |= USB_INTR_SOF; ++ hc_writeb(regval, USB_REG_INTRUSBE); ++} ++ ++static inline void sofirq_off(struct akotg_usbhc *akotghc) ++{ ++ unsigned int regval; ++ ++ regval = hc_readb(USB_REG_INTRUSBE); ++ regval &= ~USB_INTR_SOF; ++ hc_writeb(regval, USB_REG_INTRUSBE); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct akotghc_ep *start_ep0(struct akotg_usbhc *akotghc) ++{ ++ struct akotghc_ep *ep; ++ struct urb *urb; ++ ++ /* use endpoint at schedule head */ ++ if (akotghc->next_async_ep0) ++ ep = akotghc->next_async_ep0; ++ else if (!list_empty(&akotghc->async_ep0)) { ++ ep = container_of(akotghc->async_ep0.next, ++ struct akotghc_ep, schedule); ++ } else { ++ /* could set up the first fullspeed periodic ++ * transfer for the next frame ... ++ */ ++ return NULL; ++ } ++ ++ if (ep->schedule.next == &akotghc->async_ep0) ++ akotghc->next_async_ep0 = NULL; ++ else ++ akotghc->next_async_ep0 = container_of(ep->schedule.next, ++ struct akotghc_ep, schedule); ++ ++ if (unlikely(list_empty(&ep->hep->urb_list))) { ++ HDBG("empty %p queue?\n", ep); ++ return NULL; ++ } ++ ++ urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); ++ ++ switch (ep->nextpid) { ++ case USB_PID_IN: ++ in_packet(akotghc, ep, urb); ++ break; ++ case USB_PID_OUT: ++ out_packet(akotghc, ep, urb); ++ break; ++ case USB_PID_SETUP: ++ setup_packet(akotghc, ep, urb); ++ break; ++ case USB_PID_ACK: /* for control status */ ++ status_packet(akotghc, ep, urb); ++ break; ++ default: ++ HDBG("bad ep%p pid %02x\n", ep, ep->nextpid); ++ ep = NULL; ++ } ++ return ep; ++} ++ ++ ++/* pick the next endpoint for a transaction, and issue it. ++ * frames start with periodic transfers (after whatever is pending ++ * from the previous frame), and the rest of the time is async ++ * transfers, scheduled round-robin. ++ */ ++static struct akotghc_ep *start_epx(struct akotg_usbhc *akotghc, int epfifo) ++{ ++ struct akotghc_ep *ep; ++ struct urb *urb; ++ ++ ++ /* use endpoint at schedule head */ ++ if (akotghc->next_periodic) { ++ ep = akotghc->next_periodic; ++ akotghc->next_periodic = ep->next; ++ } else { ++ if (akotghc->next_async_epx[epfifo-1]) ++ ep = akotghc->next_async_epx[epfifo-1]; ++ else if (!list_empty(&akotghc->async_epx[epfifo-1])) { ++ ep = container_of(akotghc->async_epx[epfifo-1].next, ++ struct akotghc_ep, schedule); ++ ++ } else { ++ /* could set up the first fullspeed periodic ++ * transfer for the next frame ... ++ */ ++ return NULL; ++ } ++ ++ if (ep->schedule.next == &akotghc->async_epx[epfifo-1]) ++ akotghc->next_async_epx[epfifo-1] = NULL; ++ else { ++ akotghc->next_async_epx[epfifo-1] = container_of(ep->schedule.next, ++ struct akotghc_ep, schedule); ++ } ++ } ++ ++ if (unlikely(list_empty(&ep->hep->urb_list))) { ++ HDBG("empty %p queue?\n", ep); ++ return NULL; ++ } ++ urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); ++ switch (ep->nextpid) { ++ case USB_PID_IN: ++ in_packet(akotghc, ep, urb); ++ break; ++ case USB_PID_OUT: ++ out_packet(akotghc, ep, urb); ++ break; ++ case USB_PID_SETUP: ++ setup_packet(akotghc, ep, urb); ++ break; ++ case USB_PID_ACK: ++ status_packet(akotghc, ep, urb); ++ break; ++ default: ++ HDBG("bad ep%p pid %02x\n", ep, ep->nextpid); ++ ep = NULL; ++ } ++ return ep; ++} ++ ++#define MIN_JIFFIES ((msecs_to_jiffies(2) > 1) ? msecs_to_jiffies(2) : 2) ++ ++static inline void start_transfer_ep0(struct akotg_usbhc *akotghc) ++{ ++ if (akotghc->port_status & (1 << USB_PORT_FEAT_SUSPEND)) ++ return; ++ if (akotghc->active_ep0 == NULL) { ++ akotghc->active_ep0 = start_ep0(akotghc); ++ if (akotghc->active_ep0 != NULL) ++ akotghc->jiffies_ep0 = jiffies + MIN_JIFFIES; ++ } ++} ++ ++static inline void start_transfer_epx(struct akotg_usbhc *akotghc, int epfifo) ++{ ++ if (akotghc->port_status & (1 << USB_PORT_FEAT_SUSPEND)) ++ return; ++ if (akotghc->active_epx[epfifo-1] == NULL) { ++ akotghc->active_epx[epfifo-1] = start_epx(akotghc, epfifo); ++ if (akotghc->active_epx[epfifo-1] != NULL) ++ akotghc->jiffies_epx[epfifo-1] = jiffies + MIN_JIFFIES; ++ } ++} ++ ++static inline void start_transfer(struct akotg_usbhc *akotghc, int epfifo) ++{ ++ ++ if(epfifo == 0) ++ start_transfer_ep0(akotghc); ++ else ++ start_transfer_epx(akotghc, epfifo); ++ ++} ++ ++static void finish_request_ep0( ++ struct akotg_usbhc *akotghc, ++ struct akotghc_ep *ep, ++ struct urb *urb, ++ int status ++) __releases(akotghc->lock) __acquires(akotghc->lock) ++{ ++ VDBG("Finishing EP0 URB Request...\n"); ++ ++ if (usb_pipecontrol(urb->pipe)) ++ ep->nextpid = USB_PID_SETUP; ++ ++ usb_hcd_unlink_urb_from_ep(akotg_usbhc_to_hcd(akotghc), urb); ++ spin_unlock(&akotghc->lock); ++ usb_hcd_giveback_urb(akotg_usbhc_to_hcd(akotghc), urb, status); ++ spin_lock(&akotghc->lock); ++ ++ /* leave active endpoints in the schedule */ ++ if (!list_empty(&ep->hep->urb_list)) ++ return; ++ ++ /* async deschedule? */ ++ if (!list_empty(&ep->schedule)) { ++ list_del_init(&ep->schedule); ++ if (ep == akotghc->next_async_ep0) ++ akotghc->next_async_ep0 = NULL; ++ return; ++ } ++} ++ ++ ++static void finish_request_epx( ++ struct akotg_usbhc *akotghc, ++ struct akotghc_ep *ep, ++ struct urb *urb, ++ int status ++) __releases(akotghc->lock) __acquires(akotghc->lock) ++{ ++ unsigned i; ++ int is_out; ++ is_out = usb_pipeout(urb->pipe); ++ ++ VDBG("Finishing EPx URB Request...\n"); ++ ++ if (usb_pipecontrol(urb->pipe)) ++ ep->nextpid = USB_PID_SETUP; ++ ++ usb_hcd_unlink_urb_from_ep(akotg_usbhc_to_hcd(akotghc), urb); ++ spin_unlock(&akotghc->lock); ++ usb_hcd_giveback_urb(akotg_usbhc_to_hcd(akotghc), urb, status); ++ spin_lock(&akotghc->lock); ++ ++ /* leave active endpoints in the schedule */ ++ if (!list_empty(&ep->hep->urb_list)) ++ return; ++ ++ ++ /* async deschedule? */ ++ //if(epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, is_out, &epfifo) && !list_empty(&ep->schedule)) { ++ if (!list_empty(&ep->schedule)) { ++ list_del_init(&ep->schedule); ++ if (ep == akotghc->next_async_epx[ep->epfifo-1]) { ++ akotghc->next_async_epx[ep->epfifo-1] = NULL; ++ } ++ return; ++ } ++ ++ switch(usb_pipetype(urb->pipe)) { ++ case PIPE_ISOCHRONOUS: ++ case PIPE_INTERRUPT: ++ /* periodic deschedule */ ++ HDBG("%s(): deschedule qh%d/%p branch %d\n", ++ __func__, ep->period, ep, ep->branch); ++ for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { ++ struct akotghc_ep *temp; ++ struct akotghc_ep **prev = &akotghc->periodic[i]; ++ ++ while (*prev && ((temp = *prev) != ep)) ++ prev = &temp->next; ++ if (*prev) ++ *prev = ep->next; ++ akotghc->load[i] -= ep->load; ++ } ++ ep->branch = PERIODIC_SIZE; ++ akotghc->periodic_count--; ++ ++ akotg_usbhc_to_hcd(akotghc)->self.bandwidth_allocated ++ -= ep->load / ep->period; ++ if (ep == akotghc->next_periodic) ++ akotghc->next_periodic = ep->next; ++ ++ /* we might turn SOFs back on again for the async schedule */ ++ if (akotghc->periodic_count == 0) ++ sofirq_off(akotghc); ++ } ++} ++ ++static void ++done(struct akotg_usbhc *akotghc, struct akotghc_ep *ep) ++{ ++ int i; ++ int err_occurred = 0; ++ int epfifo; ++ u8 status; ++ u8 rxstatus; ++ struct urb *urb; ++ unsigned int pipe; ++ int is_out; ++ int urbstat = -EINPROGRESS; ++ ++ if (unlikely(!ep)) { ++ return; ++ } ++ ++ urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); ++ pipe = urb->pipe; ++ is_out = usb_pipeout(pipe); ++ if (!epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, is_out, &epfifo)) ++ BUG(); /* Impossible, USB Device Endpoint *MUST* have been mapped to EPFIFO */ ++ ++ status = hc_index_readw(epfifo, USB_REG_TXCSR1); ++ rxstatus = hc_index_readw(epfifo, USB_REG_RXCSR1); ++ ++ //STALL ++ if (((epfifo != 0) && ((status & (1 << 5)) || rxstatus & (1 << 6))) ++ || ((epfifo == 0) && (status & (1 << 2)))) { ++ ep->error_count = 0; ++ urbstat = -EPIPE; ++ err_occurred = 1; ++ } ++ ++ //NAK timeout ++ if ((usb_pipebulk(pipe)) ++ &&((status & (1 << 7))||(rxstatus & (1 << 3)))) { ++ if (!ep->period) ++ ep->nak_count++; ++ ep->error_count = 0; ++ err_occurred = 1; ++ } ++ ++ //Error, times expired ++ if (((epfifo == 0) && (status & (1 << 4))) ++ ||((epfifo != 0)&&((status || rxstatus) & (1 << 2)) ++ &&(usb_pipeint(pipe)||usb_pipebulk(pipe)))) { ++ urbstat = -EPROTO; ++ ep->error_count = 0; ++ err_occurred = 1; ++ } ++ ++ if (err_occurred) { ++ if (epfifo == 0) ++ hc_index_writew(0, 0, USB_REG_TXCSR1); ++ else if (is_out) ++ if (urbstat == -EPIPE) ++ // clear stall bit and clear toggle ++ hc_index_writew(epfifo, (1 << 6), USB_REG_TXCSR1); ++ else ++ hc_index_writew(epfifo, 0, USB_REG_TXCSR1); ++ else ++ if (urbstat == -EPIPE) ++ // clear stall bit and clear toggle ++ hc_index_writew(epfifo, (1 << 7), USB_REG_RXCSR1); ++ else ++ hc_index_writew(epfifo, 0, USB_REG_RXCSR1); ++ } ++ else { ++ ++ struct usb_device *udev = urb->dev; ++ int len; ++ unsigned char *buf; ++ ++ /* urb->iso_frame_desc is currently ignored here... */ ++ ++ ep->nak_count = ep->error_count = 0; ++ switch (ep->nextpid) { ++ case USB_PID_OUT: ++ urb->actual_length += ep->length; ++ usb_dotoggle(udev, ep->epnum, 1); ++ if (urb->actual_length == urb->transfer_buffer_length) { ++ if (usb_pipecontrol(urb->pipe)) { ++ VDBG("NEXT Packet: Status Packet.\n"); ++ ep->nextpid = USB_PID_ACK; ++ } ++ ++ /* some bulk protocols terminate OUT transfers ++ * by a short packet, using ZLPs not padding. ++ */ ++ else if (ep->length < ep->maxpacket ++ || !(urb->transfer_flags & URB_ZERO_PACKET)) ++ urbstat = 0; ++ } ++ break; ++ case USB_PID_IN: ++ ++#ifdef CONFIG_USB_AKOTG_DMA ++ if(ep->bdma){ ++ urb->actual_length += akotg_dma_get_trans_length(akotghc, epfifo); ++ ++ akotg_dma_clear(akotghc, epfifo); ++ ep->bdma = false; ++ } ++#endif ++ ++ buf = urb->transfer_buffer + urb->actual_length; ++ ++ prefetchw(buf); ++ len = hc_index_readw(epfifo, USB_REG_COUNT0); ++ if (len > ep->length) { ++ printk(" USB_PID_IN(OverFlow): len=%d, ep->length=%d\n", ++ len, ep->length); ++ len = ep->length; ++ urbstat = -EOVERFLOW; ++ } ++ // read data from fifo ++ urb->actual_length += len; ++ for (i = 0; i < len; i++) ++ *buf++ = hc_readb(ep_fifos[epfifo]); ++ ++ if (ep->epnum == 0) { ++ u8 regval = hc_index_readb(epfifo, USB_REG_TXCSR1); ++ regval &= ~USB_TXCSR1_TXPKTRDY; ++ hc_index_writeb(epfifo, regval, USB_REG_TXCSR1); ++ } else { ++ u8 regval = hc_index_readb(epfifo, USB_REG_RXCSR1); ++ regval &= ~USB_RXCSR1_RXPKTRDY; ++ hc_index_writeb(epfifo, regval, USB_REG_RXCSR1); ++ } ++ ++ usb_dotoggle(udev, ep->epnum, 0); ++ if (urbstat == -EINPROGRESS && ++ (len < ep->maxpacket || ++ urb->actual_length == urb->transfer_buffer_length)) { ++ if (usb_pipecontrol(urb->pipe)) { ++ VDBG("NEXT Packet: Status Packet.\n"); ++ ep->nextpid = USB_PID_ACK; ++ } ++ else ++ urbstat = 0; ++ } ++ break; ++ case USB_PID_SETUP: ++ if (urb->transfer_buffer_length == urb->actual_length) { ++ VDBG("NEXT Packet: Status Packet.\n"); ++ ep->nextpid = USB_PID_ACK; ++ } ++ else if (usb_pipeout(urb->pipe)) { ++ VDBG("NEXT Packet: OUT Packet.\n"); ++ usb_settoggle(udev, 0, 1, 1); ++ ep->nextpid = USB_PID_OUT; ++ } else { ++ VDBG("NEXT Packet: IN Packet.\n"); ++ usb_settoggle(udev, 0, 0, 1); ++ ep->nextpid = USB_PID_IN; ++ } ++ break; ++ case USB_PID_ACK: ++ if (!is_out) { ++ u8 regval = hc_index_readb(epfifo, USB_REG_RXCSR1); ++ regval &= ~USB_RXCSR1_RXPKTRDY; ++ hc_index_writeb(epfifo, regval, USB_REG_RXCSR1); ++ } ++ urbstat = 0; ++ break; ++ } ++ } ++ ++ if (urbstat != -EINPROGRESS || urb->unlinked) { ++ if (ep->epnum == 0) ++ finish_request_ep0(akotghc, ep, urb, urbstat); ++ else ++ finish_request_epx(akotghc, ep, urb, urbstat); ++ } ++} ++ ++static int balance(struct akotg_usbhc *akotghc, u16 period, u16 load) ++{ ++ int i, branch = -ENOSPC; ++ ++ /* search for the least loaded schedule branch of that period ++ * which has enough bandwidth left unreserved. ++ */ ++ for (i = 0; i < period ; i++) { ++ if (branch < 0 || akotghc->load[branch] > akotghc->load[i]) { ++ int j; ++ ++ for (j = i; j < PERIODIC_SIZE; j += period) { ++ if ((akotghc->load[j] + load) ++ > MAX_PERIODIC_LOAD) ++ break; ++ } ++ if (j < PERIODIC_SIZE) ++ continue; ++ branch = i; ++ } ++ } ++ return branch; ++} ++ ++static void reset_otg(struct work_struct *work) ++{ ++ unsigned long con; ++ ++ /* power up and set usb suspend bit */ ++ con = __raw_readl(USB_OP_MOD_REG); ++ con &= ~(0x7 << 0); ++ con |= (0x3 << 1); ++ __raw_writel(con, USB_OP_MOD_REG); ++ ++ set_usb_as_host(); ++ ++ /* start fs host session*/ ++ hc_writeb(USB_DEVCTL_SESSION, USB_REG_DEVCTL); ++} ++ ++#ifdef CONFIG_USB_AKOTG_DMA ++ ++static bool handle_dma_irq(struct akotg_usbhc *akotghc, struct akotg_dma *dma) ++{ ++ u8 epfifo = dma->epfifo; ++ u8 channel = dma->channel; ++ u32 regtmp; ++ u32 trans_len; ++ int urbstat = -EINPROGRESS; ++ ++ struct akotghc_ep *ep; ++ struct urb *urb; ++ ++ if(!akotghc || !dma) ++ return false; ++ ++ if(USB_DIRECTION_RX == dma->dir){ ++ hc_index_writeb(epfifo, 0, USB_REG_RXCSR2); ++ ++ regtmp = hc_readl(USB_DMA_ADDR(channel)); ++ trans_len = regtmp - dma->addr; ++ ++ dma_unmap_single(NULL, dma->phyaddr, dma->count, DMA_FROM_DEVICE); ++ } ++ else{ ++ hc_index_writeb(epfifo, USB_TXCSR2_MODE, USB_REG_TXCSR2); ++ ++ trans_len = dma->count; ++ ++ dma_unmap_single(NULL, dma->phyaddr, dma->count, DMA_TO_DEVICE); ++ } ++ ++ ++ hc_writel(0, USB_DMA_COUNT(channel)); ++ ++ //wait dma finish ++ l2_combuf_wait_dma_finish(dma->l2buffer); ++ ++ //free l2 buffer ++ akotg_free_l2_buffer(epfifo); ++ dma->l2buffer = BUF_NULL; ++ ++ //change status ++ dma->status = USB_DMA_CHANNEL_IDLE; ++ ++ //get urb ++ ep = akotghc->active_epx[epfifo-1]; ++ ++ ep->bdma = false; ++ urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); ++ ++ urb->actual_length += trans_len; ++ ++ //done ++ if((urb->actual_length == urb->transfer_buffer_length) || urb->unlinked) ++ { ++ urbstat = 0; ++ finish_request_epx(akotghc, ep, urb, urbstat); ++ } ++ ++ akotghc->active_epx[epfifo-1] = NULL; ++ ++ return true; ++ ++} ++ ++irqreturn_t akotg_dma_irq(int irqnum, void *__hcd) ++{ ++ struct usb_hcd *hcd = __hcd; ++ unsigned long flags; ++ irqreturn_t rc; ++ ++ local_irq_save(flags); ++ ++ if (unlikely(hcd->state == HC_STATE_HALT || ++ !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { ++ rc = IRQ_NONE; ++ } else if (akotg_usbhc_irq(hcd) == IRQ_NONE) { ++ rc = IRQ_NONE; ++ } else { ++ set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); // ??? ++ ++ if (unlikely(hcd->state == HC_STATE_HALT)) ++ usb_hc_died(hcd); ++ rc = IRQ_HANDLED; ++ } ++ ++ //rc = akotg_usbhc_irq(hcd); ++ ++ local_irq_restore(flags); ++ ++ return rc; ++} ++ ++#endif ++ ++irqreturn_t akotg_usbhc_irq(struct usb_hcd *hcd) ++{ ++ struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); ++ irqreturn_t ret = IRQ_NONE; ++ struct urb *urb; ++ struct akotghc_ep *ep; ++ int epnum[MAX_EP_NUM + 1] = { 0 }; ++ int i; ++ unsigned index = 0; ++ ++ char rINTCOM; ++ unsigned short rINTTX, rINTRX; ++ ++ u32 rINTDMA; ++ ++#ifdef CONFIG_USB_AKOTG_DMA ++ struct akotg_dma *dma; ++#endif ++ ++ spin_lock(&akotghc->lock); ++ ++ /*Read & Clear all interrupt status.*/ ++ rINTCOM = hc_readb(USB_REG_INTRUSB); ++ rINTTX =hc_readw(USB_REG_INTRTX); ++ rINTRX = hc_readw(USB_REG_INTRRX); ++ rINTDMA =hc_readl(USB_DMA_INTR); ++ ++ if (rINTTX &USB_INTR_EP0) { ++ epnum[0] = 1; ++ done(akotghc, akotghc->active_ep0); ++ akotghc->active_ep0 = NULL; ++ } ++ ++#ifdef CONFIG_USB_AKOTG_DMA ++ if(rINTDMA) ++ { ++ //printk("|%x|", rINTDMA); ++ //printk("C[%x,%x]", hcd, m_hcd); ++ for(i = 0; i < USBDMA_CHANNEL_NUM; i++) ++ { ++ if(rINTDMA & (1<active_epx[i]); ++ akotghc->active_epx[i] = NULL; ++ } ++ ++ if (rINTCOM & USB_INTR_SOF) { ++ index = akotghc->frame++ & (PERIODIC_SIZE - 1); ++ ++ ++ /* be graceful about almost-inevitable periodic schedule ++ * overruns: continue the previous frame's transfers iff ++ * this one has nothing scheduled. ++ */ ++ ++ if (akotghc->periodic[index]) { ++ akotghc->next_periodic = akotghc->periodic[index]; ++ } ++ } ++ ++ /* manages debouncing and wakeup */ ++ if(rINTCOM & (USB_INTR_CONNECT | USB_INTR_DISCONNECT)) { ++ ++ /* most stats are reset for each VBUS session */ ++ ++ /* usbcore nukes other pending transactions on disconnect */ ++ if (akotghc->active_ep0) { ++ VDBG("Finishing EP0 Active URBs...\n"); ++ ep = akotghc->active_ep0; ++ hc_index_writeb(ep->epfifo, 0, USB_REG_TXCSR1); ++ hc_index_writeb(ep->epfifo, USB_CSR02_FLUSHFIFO, USB_REG_TXCSR2); ++ ++ finish_request_ep0(akotghc, akotghc->active_ep0, ++ container_of(akotghc->active_ep0->hep->urb_list.next, struct urb, urb_list), ++ -ESHUTDOWN); ++ akotghc->active_ep0 = NULL; ++ } ++ ++ for(i=0; iactive_epx[i]) { ++ VDBG("Finishing EPx Active URBs...\n"); ++ ep = akotghc->active_epx[i]; ++ urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); ++ //if (!epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, is_out, &epfifo)) ++ // BUG(); ++ if (usb_pipeout(urb->pipe)) { ++ hc_index_writeb(ep->epfifo, USB_TXCSR1_FLUSHFIFO, USB_REG_TXCSR1); ++ } else { ++ hc_index_writeb(ep->epfifo, USB_RXCSR1_FLUSHFIFO, USB_REG_RXCSR1); ++ } ++ ++ finish_request_epx(akotghc, akotghc->active_epx[i], ++ container_of(akotghc->active_epx[i]->hep->urb_list.next, struct urb, urb_list), ++ -ESHUTDOWN); ++ akotghc->active_epx[i] = NULL; ++ } ++ ++ /* port status seems weird until after reset, so ++ * force the reset and make khubd clean up later. ++ */ ++ if (rINTCOM & USB_INTR_CONNECT) { ++ akotghc->port_status |= 1 << USB_PORT_FEAT_CONNECTION; ++ akotghc->port_status |= 1 << USB_PORT_FEAT_C_CONNECTION; ++ } else { ++ ++#ifdef CONFIG_USB_AKOTG_DMA ++ //clear all dma ++ akotg_dma_clear(akotghc, 0); ++#endif ++ period_epfifo = 0; ++ akotghc->port_status &= ~(1 << USB_PORT_FEAT_CONNECTION); ++ akotghc->port_status |= 1 << USB_PORT_FEAT_C_CONNECTION; ++ init_epfifo_mapping(&akotg_epfifo_mapping); ++ reset_endpoints(); ++ REG32(USB_OP_MOD_REG) &= ~(0xff << 6); ++ REG32(USB_OP_MOD_REG) |= (0x1f << 6); ++ hc_writeb(0x0, USB_REG_DEVCTL); ++ hc_writeb(0x0, USB_REG_FADDR); ++ ++ queue_delayed_work(g_otghc_wq, &g_otg_rest, 0); ++ } ++ } ++ ++ if (rINTCOM & USB_INTR_RESUME) { ++ if (akotghc->port_status & (1 << USB_PORT_FEAT_SUSPEND)) { ++ HDBG("wakeup\n"); ++ akotghc->port_status |= 1 << USB_PORT_FEAT_C_SUSPEND; ++ ++ } ++ rINTCOM &= ~(USB_INTR_RESUME); ++ } ++ ++ if (akotghc->port_status & (1 << USB_PORT_FEAT_ENABLE)) { ++ if (epnum[0]) { ++ start_transfer(akotghc, 0); ++ ret = IRQ_HANDLED; ++ } ++ if((rINTCOM & USB_INTR_SOF) && akotghc->periodic[index] && period_epfifo){ ++ start_transfer(akotghc, period_epfifo); ++ ret = IRQ_HANDLED; ++ } ++ for(i = 0; i < MAX_EP_NUM; i++) { ++ if(epnum[i + 1]) { ++ start_transfer(akotghc, i + 1); ++ ret = IRQ_HANDLED; ++ } ++ } ++ ++#ifdef CONFIG_USB_AKOTG_DMA ++ for(i = 0; i < USBDMA_CHANNEL_NUM; i++) ++ { ++ if(rINTDMA & (1<epfifo); ++ ret = IRQ_HANDLED; ++ } ++ } ++#endif ++ } ++ ++ if(akotghc->periodic_count == 0 && list_empty(&akotghc->async_ep0)) { ++ for(i = 0; i < MAX_EP_NUM; i++) { ++ if(!list_empty(&akotghc->async_epx[i])) ++ break; ++ } ++ if(i == MAX_EP_NUM) ++ sofirq_off(akotghc); ++ } ++ ++ spin_unlock(&akotghc->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL(akotg_usbhc_irq); ++ ++int akotg_usbhc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ++{ ++ struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); ++ struct usb_host_endpoint *hep; ++ unsigned long flags; ++ struct akotghc_ep *ep; ++ int retval, i; ++ int is_out = usb_pipeout(urb->pipe); ++ ++ HDBG("Dequeue: Direction=%s, Type=%s, EP Num=%d, urb=%p\n", ++ is_out ? "OUT" : "IN", trans_desc[usb_pipetype(urb->pipe)], ++ usb_pipeendpoint(urb->pipe), urb); ++ ++ spin_lock_irqsave(&akotghc->lock, flags); ++ ++ retval = usb_hcd_check_unlink_urb(hcd, urb, status); ++ if (retval) { ++ printk("Dequeue: check and unlink urb failed!\n"); ++ goto fail; ++ } ++ ++ hep = urb->hcpriv; ++ ep = hep->hcpriv; ++ if (ep) { ++ /* finish right away if this urb can't be active ... ++ * note that some drivers wrongly expect delays ++ */ ++ if (ep->hep->urb_list.next != &urb->urb_list) { ++ /* not front of queue? never active */ ++ ++ /* for active transfers, we expect an IRQ */ ++ } else if (akotghc->active_ep0 == ep) { ++ if (time_before_eq(akotghc->jiffies_ep0, jiffies)) { ++ hc_index_writeb(0, 0, USB_REG_TXCSR1); ++ hc_index_writeb(0, 1 << 0, USB_REG_TXCSR2); ++ akotghc->active_ep0 = NULL; ++ } else ++ urb = NULL; ++ ++ } else { ++ for(i=0; iactive_epx[i] == ep) { ++ if(time_before_eq(akotghc->jiffies_epx[i], jiffies)) { ++ //if(!epnum_to_epfifo(&akotg_epfifo_mapping, ep->epnum, is_out, &epfifo)) ++ // BUG(); ++ if (is_out) { ++ hc_index_writeb(ep->epfifo, USB_TXCSR1_FLUSHFIFO, USB_REG_TXCSR1); ++ } else { ++ hc_index_writeb(ep->epfifo, USB_RXCSR1_FLUSHFIFO, USB_REG_RXCSR1); ++ } ++ akotghc->active_epx[i] = NULL; ++ } else ++ urb = NULL; ++ ++ } ++ } ++ /* front of queue for inactive endpoint */ ++ } ++ ++ if (urb) { ++ if (akotghc->active_ep0 == ep) ++ finish_request_ep0(akotghc, ep, urb, 0); ++ else ++ finish_request_epx(akotghc, ep, urb, 0); ++ } else { ++ HDBG("dequeue, urb %p active, wait for irq.\n", urb); ++ } ++ } else ++ retval = -EINVAL; ++ fail: ++ spin_unlock_irqrestore(&akotghc->lock, flags); ++ ++ return retval; ++} ++ ++ ++ ++int ++akotg_usbhc_urb_enqueue( ++ struct usb_hcd *hcd, ++ struct urb *urb, ++ gfp_t mem_flags ++) ++{ ++ struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); ++ struct usb_device *udev = urb->dev; ++ int is_out = usb_pipeout(urb->pipe); ++ int type = usb_pipetype(urb->pipe); ++ int epnum = usb_pipeendpoint(urb->pipe); ++ int epfifo = 0; ++ ++ struct akotghc_ep *ep = NULL; ++ unsigned long flags; ++ int i; ++ int retval; ++ struct usb_host_endpoint *hep = urb->ep; ++ ++ HDBG("Enqueue: Direction=%s, Type=%s, EP Num=%d, urb=%p" ++ " urb->transfer_buffer_length=%d\n", ++ is_out ? "OUT" : "IN", trans_desc[type], epnum, urb, ++ urb->transfer_buffer_length); ++ ++ if (usb_pipeisoc(urb->pipe)) ++ return -ENOSPC; ++ ++ spin_lock_irqsave(&akotg_epfifo_mapping.lock,flags); ++ if (!__is_epnum_mapped(&akotg_epfifo_mapping, epnum, is_out)) { ++ if (!__map_epnum_to_epfifo(&akotg_epfifo_mapping, epnum, is_out, &epfifo)) { ++ spin_unlock_irqrestore(&akotg_epfifo_mapping.lock, flags); ++ return -ENOSPC; ++ } ++ ++ if (epnum != 0) { ++ ++ int eptype = get_ep_type(urb->pipe); ++ ++ if (is_out) { ++ disable_epx_tx_interrupt(epfifo); ++ set_epx_tx_type(epfifo, epnum, eptype); ++ hc_index_writew(epfifo, hep->desc.wMaxPacketSize, USB_REG_TXMAXP); ++ hc_index_writeb(epfifo, 16, USB_REG_TXINTERVAL); /* Tx Interval */ ++ clear_epx_tx_data_toggle(epfifo); ++ set_epx_tx_mode(epfifo); ++ flush_epx_tx_fifo(epfifo); ++ enable_epx_tx_interrupt(epfifo); ++ } else { ++ disable_epx_rx_interrupt(epfifo); ++ set_epx_rx_type(epfifo, epnum, eptype); ++ hc_index_writew(epfifo, hep->desc.wMaxPacketSize, USB_REG_RXMAXP); ++ if (usb_endpoint_xfer_isoc(&hep->desc) || usb_endpoint_xfer_int(&hep->desc)) ++ hc_index_writeb(epfifo, hep->desc.bInterval, USB_REG_RXINTERVAL); ++ else ++ hc_index_writeb(epfifo, 0, USB_REG_RXINTERVAL); ++ set_epx_rx_mode(epfifo); ++ clear_epx_rx_data_toggle(epfifo); ++ flush_epx_rx_fifo(epfifo); ++ enable_epx_rx_interrupt(epfifo); ++ } ++ } ++ } else { ++ if(!epnum_to_epfifo(&akotg_epfifo_mapping, epnum, is_out, &epfifo)) ++ BUG(); ++ } ++ ++ spin_unlock_irqrestore(&akotg_epfifo_mapping.lock, flags); ++ ++ spin_lock_irqsave(&akotghc->lock, flags); ++ ++ /* don't submit to a dead or disabled port */ ++ if (!(akotghc->port_status & (1 << USB_PORT_FEAT_ENABLE)) ++ || !HC_IS_RUNNING(hcd->state)) { ++ retval = -ENODEV; ++ goto fail_not_linked; ++ } ++ ++ retval = usb_hcd_link_urb_to_ep(hcd, urb); ++ if (retval) { ++ goto fail_not_linked; ++ } ++ ++ /* avoid all allocations within spinlocks */ ++ if (hep->hcpriv) { ++ ep = hep->hcpriv; ++ } else { ++ ep = kzalloc(sizeof *ep, mem_flags); ++ if (ep == NULL) { ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ INIT_LIST_HEAD(&ep->schedule); ++ ep->udev = udev; ++ ep->epnum = epnum; ++ ep->epfifo = epfifo; ++ ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); ++ ++ usb_settoggle(udev, epnum, is_out, 0); ++ ++ if (type == PIPE_CONTROL) ++ ep->nextpid = USB_PID_SETUP; ++ else if (is_out) ++ ep->nextpid = USB_PID_OUT; ++ else ++ ep->nextpid = USB_PID_IN; ++ ++ if (ep->maxpacket > H_MAXPACKET) { ++ /* iso packets up to 240 bytes could work... */ ++ HDBG("dev %d ep%d maxpacket %d\n", ++ udev->devnum, epnum, ep->maxpacket); ++ retval = -EINVAL; ++ goto fail; ++ } ++ ++ switch (type) { ++ case PIPE_ISOCHRONOUS: ++ case PIPE_INTERRUPT: ++ if (urb->interval > PERIODIC_SIZE) ++ urb->interval = PERIODIC_SIZE; ++ ep->period = urb->interval; ++ ep->branch = PERIODIC_SIZE; ++ ep->load = usb_calc_bus_time(udev->speed, !is_out, ++ (type == PIPE_ISOCHRONOUS), ++ usb_maxpacket(udev, urb->pipe, is_out)) / 1000; ++ period_epfifo = epfifo; ++ break; ++ } ++ ++ ep->hep = hep; ++ hep->hcpriv = ep; ++ } ++ ++ /* maybe put endpoint into schedule */ ++ switch (type) { ++ case PIPE_CONTROL: ++ case PIPE_BULK: ++ if (list_empty(&ep->schedule)) { ++ if (epnum == 0) ++ list_add_tail(&ep->schedule, &akotghc->async_ep0); ++ else ++ list_add_tail(&ep->schedule, &akotghc->async_epx[epfifo-1]); ++ } ++ break; ++ case PIPE_ISOCHRONOUS: ++ case PIPE_INTERRUPT: ++ urb->interval = ep->period; ++ if (ep->branch < PERIODIC_SIZE) { ++ /* NOTE: the phase is correct here, but the value ++ * needs offsetting by the transfer queue depth. ++ * All current drivers ignore start_frame, so this ++ * is unlikely to ever matter... ++ */ ++ urb->start_frame = (akotghc->frame & (PERIODIC_SIZE - 1)) ++ + ep->branch; ++ break; ++ } ++ ++ retval = balance(akotghc, ep->period, ep->load); ++ if (retval < 0) ++ goto fail; ++ ep->branch = retval; ++ retval = 0; ++ urb->start_frame = (akotghc->frame & (PERIODIC_SIZE - 1)) ++ + ep->branch; ++ ++ /* sort each schedule branch by period (slow before fast) ++ * to share the faster parts of the tree without needing ++ * dummy/placeholder nodes ++ */ ++ VDBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); ++ for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { ++ struct akotghc_ep **prev = &akotghc->periodic[i]; ++ struct akotghc_ep *here = *prev; ++ ++ while (here && ep != here) { ++ if (ep->period > here->period) ++ break; ++ prev = &here->next; ++ here = *prev; ++ } ++ if (ep != here) { ++ ep->next = here; ++ *prev = ep; ++ } ++ akotghc->load[i] += ep->load; ++ } ++ akotghc->periodic_count++; ++ hcd->self.bandwidth_allocated += ep->load / ep->period; ++ sofirq_on(akotghc); ++ } ++ ++ urb->hcpriv = hep; ++ start_transfer(akotghc, epfifo); ++ ++fail: ++ if (retval) ++ usb_hcd_unlink_urb_from_ep(hcd, urb); ++fail_not_linked: ++ spin_unlock_irqrestore(&akotghc->lock, flags); ++ return retval; ++} ++ ++ ++void ++akotg_usbhc_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *hep) ++{ ++ struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); ++ int epnum = usb_endpoint_num(&hep->desc); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&akotghc->lock, flags); ++ ++ HDBG("Resetting EP %d, Type=%s, Dir=%s\n", ++ epnum, xfer_name[usb_endpoint_type(&hep->desc)], usb_endpoint_dir_out(&hep->desc)? "OUT" : "IN"); ++ ++ if (epnum == 0) { ++ hc_index_writeb(0, 0, USB_REG_TXINTERVAL); ++ hc_index_writew(0, 0, USB_REG_TXCSR1); ++ flush_ep0_fifo(); ++ enable_ep0_interrupt(); ++ } else { ++ ++ } ++ ++ spin_unlock_irqrestore(&akotghc->lock, flags); ++} ++ ++void ++akotg_usbhc_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) ++{ ++ struct akotghc_ep *ep = hep->hcpriv; ++ ++ int epnum = usb_endpoint_num(&hep->desc); ++ int is_out = usb_endpoint_dir_out(&hep->desc); ++ ++ int epfifo = 0; ++ ++ ++ if (!ep) { ++ return; ++ } ++ ++ if (is_epnum_mapped(&akotg_epfifo_mapping, epnum, is_out)) { ++ ++ disable_ep_interrupt(epfifo); ++ flush_ep_fifo(epfifo); ++ } ++ /* assume we'd just wait for the irq */ ++ if (!list_empty(&hep->urb_list)) ++ msleep(3); ++ if (!list_empty(&hep->urb_list)) ++ HDBG("ep %p not empty?\n", ep); ++ ++ kfree(ep); ++ hep->hcpriv = NULL; ++ ++} ++ ++int ++akotg_usbhc_get_frame(struct usb_hcd *hcd) ++{ ++ struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); ++ ++ /* wrong except while periodic transfers are scheduled; ++ * never matches the on-the-wire frame; ++ * subject to overruns. ++ */ ++ return akotghc->frame; ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* the virtual root hub timer IRQ checks for hub status */ ++int ++akotg_usbhc_hub_status_data(struct usb_hcd *hcd, char *buf) ++{ ++ struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); ++ unsigned long flags; ++ ++ /* non-SMP HACK: use root hub timer as i/o watchdog ++ * this seems essential when SOF IRQs aren't in use... ++ */ ++ local_irq_save(flags); ++ if (!timer_pending(&akotghc->timer)) { ++ if (akotg_usbhc_irq( /* ~0, */ hcd) != IRQ_NONE) ++ ;//akotghc->stat_lost++; ++ } ++ local_irq_restore(flags); ++ ++ if (!(akotghc->port_status & (0xffff << 16))) { ++ return 0; ++ } ++ ++ /* tell khubd port 1 changed */ ++ *buf = (1 << 1); ++ ++ return 1; ++} ++ ++void ++akotg_usbhc_hub_descriptor ( ++ struct akotg_usbhc *akotghc, ++ struct usb_hub_descriptor *desc ++) { ++ u16 temp = 0; ++ ++ desc->bDescriptorType = 0x29; ++ desc->bHubContrCurrent = 0; ++ ++ desc->bNbrPorts = 1; ++ desc->bDescLength = 9; ++ ++ /* per-port power switching (gang of one!), or none */ ++ desc->bPwrOn2PwrGood = 0; ++ ++ /* no over current errors detection/handling */ ++ temp |= HUB_CHAR_COMMON_LPSM|HUB_CHAR_NO_OCPM; ++ ++ desc->wHubCharacteristics = cpu_to_le16(temp); ++ ++ /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ ++ desc->u.hs.DeviceRemovable[0] = 0 << 1; ++ desc->u.hs.DeviceRemovable[1] = ~0; ++} ++ ++void ++akotg_usbhc_timer(unsigned long _akotghs) ++{ ++ struct akotg_usbhc *akotghc = (void *) _akotghs; ++ unsigned long flags; ++ const u32 mask = (1 << USB_PORT_FEAT_CONNECTION) ++ | (1 << USB_PORT_FEAT_ENABLE); ++ ++ ++ spin_lock_irqsave(&akotghc->lock, flags); ++ ++ if (akotghc->port_status & USB_PORT_STAT_RESET) { ++ akotghc->port_status = (1 << USB_PORT_FEAT_C_RESET) ++ | (1 << USB_PORT_FEAT_POWER); ++ akotghc->port_status |= mask; ++ ++ if (akotghc->port_status & (1 << USB_PORT_FEAT_CONNECTION)) { ++ if ((hc_readb(USB_REG_DEVCTL) & USB_DEVCTL_FSDEV)) { ++ akotghc->port_status |= USB_PORT_STAT_HIGH_SPEED; ++ } else { ++ /* Plug-in & plug-out quickly could lead to this... */ ++ akotghc->port_status &= ~mask; ++ } ++ } ++ } else { ++ /* NOT IMPLEMENTED YET */ ++ BUG(); ++ } ++ ++ ++ spin_unlock_irqrestore(&akotghc->lock, flags); ++ ++} ++ ++int ++akotg_usbhc_hub_control( ++ struct usb_hcd *hcd, ++ u16 typeReq, ++ u16 wValue, ++ u16 wIndex, ++ char *buf, ++ u16 wLength ++) { ++ struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); ++ int retval = 0; ++ unsigned long flags; ++ char reg8val; ++ ++ HDBG("%s(): typeReq=0x%x, wValue=%d, wIndex=%d, wLength=%d\t", ++ __func__, typeReq, wValue, wIndex, wLength); ++ ++ spin_lock_irqsave(&akotghc->lock, flags); ++ ++ switch (typeReq) { ++ case ClearHubFeature: ++ case SetHubFeature: ++ switch (wValue) { ++ case C_HUB_OVER_CURRENT: ++ case C_HUB_LOCAL_POWER: ++ break; ++ default: ++ goto error; ++ } ++ break; ++ case ClearPortFeature: ++ if (wIndex != 1 || wLength != 0) ++ goto error; ++ switch (wValue) { ++ case USB_PORT_FEAT_ENABLE: ++ HDBG("ClearPortFeature: USB_PORT_FEAT_ENABLE\n"); ++ akotghc->port_status &= (1 << USB_PORT_FEAT_POWER); ++ break; ++ case USB_PORT_FEAT_SUSPEND: ++ HDBG("ClearPortFeature: USB_PORT_FEAT_SUSPEND\n"); ++ if (!(akotghc->port_status & (1 << USB_PORT_FEAT_SUSPEND))) ++ break; ++ ++ /* 20 msec of resume/K signaling, other irqs blocked */ ++ HDBG(" start resume...\n"); ++ hc_writeb(0x0, USB_REG_INTRUSBE); ++ reg8val = hc_readb(USB_REG_POWER); ++ reg8val |= USB_POWER_RESUME; ++ hc_writeb(reg8val, USB_REG_POWER); ++ ++ mod_timer(&akotghc->timer, jiffies + msecs_to_jiffies(20)); ++ break; ++ case USB_PORT_FEAT_POWER: ++ port_power(akotghc, 0); ++ break; ++ case USB_PORT_FEAT_C_ENABLE: ++ case USB_PORT_FEAT_C_SUSPEND: ++ case USB_PORT_FEAT_C_CONNECTION: ++ break; ++ case USB_PORT_FEAT_C_OVER_CURRENT: ++ case USB_PORT_FEAT_C_RESET: ++ break; ++ default: ++ goto error; ++ } ++ akotghc->port_status &= ~(1 << wValue); ++ break; ++ case GetHubDescriptor: ++ akotg_usbhc_hub_descriptor(akotghc, (struct usb_hub_descriptor *) buf); ++ break; ++ case GetHubStatus: ++ put_unaligned_le32(0, buf); ++ break; ++ case GetPortStatus: ++ if (wIndex != 1) ++ goto error; ++ put_unaligned_le32(akotghc->port_status, buf); ++ if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ ++ HDBG(" GetPortStatus 0x%04x\n", akotghc->port_status); ++ break; ++ case SetPortFeature: ++ if (wIndex != 1 || wLength != 0) ++ goto error; ++ switch (wValue) { ++ case USB_PORT_FEAT_SUSPEND: ++ HDBG("SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); ++ if (akotghc->port_status & (1 << USB_PORT_FEAT_RESET)) ++ goto error; ++ if (!(akotghc->port_status & (1 << USB_PORT_FEAT_ENABLE))) ++ goto error; ++ /*to suspend the usb host controller.*/ ++ reg8val = hc_readb(USB_REG_POWER); ++ reg8val |= USB_POWER_SUSPENDM; ++ hc_writeb(reg8val, USB_REG_POWER); ++ break; ++ case USB_PORT_FEAT_POWER: ++ HDBG("SetPortFeature: USB_PORT_FEAT_POWER\n"); ++ port_power(akotghc, 1); ++ break; ++ case USB_PORT_FEAT_RESET: ++ HDBG("SetPortFeature: USB_PORT_FEAT_RESET, Port Status=0x%04x\n", ++ akotghc->port_status); ++ if (akotghc->port_status & (1 << USB_PORT_FEAT_SUSPEND)) { ++ HDBG(" USB_PORT_FEAT_SUSPEND ....\n"); ++ goto error; ++ } ++ if (!(akotghc->port_status & (1 << USB_PORT_FEAT_POWER))) { ++ HDBG(" USB_PORT_FEAT_POWER NOT SET.\n"); ++ break; ++ } ++ clear_all_interrupts(); ++ ++ /* 50 msec of reset/SE0 signaling, irqs blocked */ ++ /*reset device.*/ ++ reg8val = hc_readb(USB_REG_POWER); ++ reg8val |= USB_POWER_RESET; ++ hc_writeb(reg8val, USB_REG_POWER); ++ mdelay(30); ++ reg8val &= ~USB_POWER_RESET; ++ hc_writeb(reg8val, USB_REG_POWER); ++ ++ hc_writeb(0xF7, USB_REG_INTRUSBE); ++ ++ akotghc->port_status |= (1 << USB_PORT_FEAT_RESET); ++ mod_timer(&akotghc->timer, jiffies + msecs_to_jiffies(50)); ++ break; ++ default: ++ goto error; ++ } ++ ++ akotghc->port_status |= 1 << wValue; ++ break; ++error: ++ /* "protocol stall" on error */ ++ retval = -EPIPE; ++ } ++ ++ spin_unlock_irqrestore(&akotghc->lock, flags); ++ return retval; ++} ++ ++ ++ int ++akotg_usbhc_bus_suspend(struct usb_hcd *hcd) ++{ ++ u8 reg; ++ unsigned long flags; ++ msleep(10); ++ local_irq_save(flags); ++ reg = hc_readb(USB_REG_POWER); ++ reg |= USB_POWER_SUSPENDM; ++ hc_writeb(reg, USB_REG_POWER); ++ local_irq_restore(flags); ++ msleep(20); ++ return 0; ++} ++ ++ int ++akotg_usbhc_bus_resume(struct usb_hcd *hcd) ++{ ++ u8 reg; ++ unsigned long flags; ++ local_irq_save(flags); ++ reg = hc_readb(USB_REG_POWER); ++ reg |= USB_POWER_RESUME; ++ hc_writeb(reg, USB_REG_POWER); ++ local_irq_restore(flags); ++ msleep(20); ++ local_irq_save(flags); ++ reg = hc_readb(USB_REG_POWER); ++ reg &= ~USB_POWER_RESUME; ++ hc_writeb(reg, USB_REG_POWER); ++ local_irq_restore(flags); ++ msleep(100); ++ return 0; ++} ++ ++void akotg_usbhc_stop(struct usb_hcd *hcd) ++{ ++ struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); ++ unsigned long flags; ++ ++ del_timer_sync(&hcd->rh_timer); ++ clk_disable(akotghc->clk); ++ ++ /* reset usb phy */ ++ usb_reset_phy(akotghc); ++ ++ //rMULFUN_CON2 &= ~(0x1 << 18); //iddig is invalid ++ //REG32(USB_OP_MOD_REG) |= (0x3 << 12); ++ ++ spin_lock_irqsave(&akotghc->lock, flags); ++ port_power(akotghc, 0); ++ spin_unlock_irqrestore(&akotghc->lock, flags); ++} ++ ++static void usb_hwinit_control(struct akotg_usbhc *otghc) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&otghc->lock, flags); ++ ++ port_power(otghc, 1); ++ set_usb_as_host(); ++ /* reset usb phy */ ++ usb_reset_phy(otghc); ++ ++ clear_all_interrupts(); ++ reset_endpoints(); ++ hc_writeb(0x0, USB_REG_FADDR); ++ ++ usb_power_up(otghc); ++ ++ hc_writeb(USB_POWER_ENSUSPEND|USB_HOSG_HIGH_SPEED, USB_REG_POWER); ++ hc_writeb(0xF7, USB_REG_INTRUSBE); ++ ++ spin_unlock_irqrestore(&otghc->lock, flags); ++} ++ ++int akotg_usbhc_start(struct usb_hcd *hcd) ++{ ++ struct akotg_usbhc *akotghc = hcd_to_akotg_usbhc(hcd); ++ ++ g_otghc_wq = create_singlethread_workqueue("usb_otg_wq"); ++ INIT_DELAYED_WORK(&g_otg_rest, reset_otg); ++ /*after reset usbhc, set stat to running.*/ ++ hcd->state = HC_STATE_RUNNING; ++ ++ /* chip has been reset, VBUS power is off */ ++ disable_irq(akotghc->mcu_irq); ++ //disable_irq(akotghc->dma_irq); ++ ++ clk_enable(akotghc->clk); ++ ++ usb_hwinit_control(akotghc); ++ ++ /* start host session*/ // is end session ? ++ hc_writeb(USB_DEVCTL_SESSION, USB_REG_DEVCTL); ++ ++ enable_irq(akotghc->mcu_irq); ++ //enable_irq(akotghc->dma_irq); ++ ++ return 0; ++} ++ +diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig +index 5c87db06..c2902a86 100644 +--- a/drivers/usb/otg/Kconfig ++++ b/drivers/usb/otg/Kconfig +@@ -12,6 +12,14 @@ config USB_OTG_UTILS + Select this to make sure the build includes objects from + the OTG infrastructure directory. + ++config USB_OTG_WAKELOCK ++ bool "Hold a wakelock when USB connected" ++ depends on WAKELOCK ++ select USB_OTG_UTILS ++ help ++ Select this to automatically hold a wakelock when USB is ++ connected, preventing suspend. ++ + if USB || USB_GADGET + + # +diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile +index 41aa5098..638d040c 100644 +--- a/drivers/usb/otg/Makefile ++++ b/drivers/usb/otg/Makefile +@@ -7,6 +7,7 @@ ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG + + # infrastructure + obj-$(CONFIG_USB_OTG_UTILS) += otg.o ++obj-$(CONFIG_USB_OTG_WAKELOCK) += otg-wakelock.o + + # transceiver drivers + obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o +diff --git a/drivers/usb/otg/otg-wakelock.c b/drivers/usb/otg/otg-wakelock.c +new file mode 100644 +index 00000000..e17e2729 +--- /dev/null ++++ b/drivers/usb/otg/otg-wakelock.c +@@ -0,0 +1,170 @@ ++/* ++ * otg-wakelock.c ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TEMPORARY_HOLD_TIME 2000 ++ ++static bool enabled = true; ++static struct usb_phy *otgwl_xceiv; ++static struct notifier_block otgwl_nb; ++ ++/* ++ * otgwl_spinlock is held while the VBUS lock is grabbed or dropped and the ++ * held field is updated to match. ++ */ ++ ++static DEFINE_SPINLOCK(otgwl_spinlock); ++ ++/* ++ * Only one lock, but since these 3 fields are associated with each other... ++ */ ++ ++struct otgwl_lock { ++ char name[40]; ++ struct wake_lock wakelock; ++ bool held; ++}; ++ ++/* ++ * VBUS present lock. Also used as a timed lock on charger ++ * connect/disconnect and USB host disconnect, to allow the system ++ * to react to the change in power. ++ */ ++ ++static struct otgwl_lock vbus_lock; ++ ++static void otgwl_hold(struct otgwl_lock *lock) ++{ ++ if (!lock->held) { ++ wake_lock(&lock->wakelock); ++ lock->held = true; ++ } ++} ++ ++static void otgwl_temporary_hold(struct otgwl_lock *lock) ++{ ++ wake_lock_timeout(&lock->wakelock, ++ msecs_to_jiffies(TEMPORARY_HOLD_TIME)); ++ lock->held = false; ++} ++ ++static void otgwl_drop(struct otgwl_lock *lock) ++{ ++ if (lock->held) { ++ wake_unlock(&lock->wakelock); ++ lock->held = false; ++ } ++} ++ ++static void otgwl_handle_event(unsigned long event) ++{ ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&otgwl_spinlock, irqflags); ++ ++ if (!enabled) { ++ otgwl_drop(&vbus_lock); ++ spin_unlock_irqrestore(&otgwl_spinlock, irqflags); ++ return; ++ } ++ ++ switch (event) { ++ case USB_EVENT_VBUS: ++ case USB_EVENT_ENUMERATED: ++ otgwl_hold(&vbus_lock); ++ break; ++ ++ case USB_EVENT_NONE: ++ case USB_EVENT_ID: ++ case USB_EVENT_CHARGER: ++ otgwl_temporary_hold(&vbus_lock); ++ break; ++ ++ default: ++ break; ++ } ++ ++ spin_unlock_irqrestore(&otgwl_spinlock, irqflags); ++} ++ ++static int otgwl_otg_notifications(struct notifier_block *nb, ++ unsigned long event, void *unused) ++{ ++ otgwl_handle_event(event); ++ return NOTIFY_OK; ++} ++ ++static int set_enabled(const char *val, const struct kernel_param *kp) ++{ ++ int rv = param_set_bool(val, kp); ++ ++ if (rv) ++ return rv; ++ ++ if (otgwl_xceiv) ++ otgwl_handle_event(otgwl_xceiv->last_event); ++ ++ return 0; ++} ++ ++static struct kernel_param_ops enabled_param_ops = { ++ .set = set_enabled, ++ .get = param_get_bool, ++}; ++ ++module_param_cb(enabled, &enabled_param_ops, &enabled, 0644); ++MODULE_PARM_DESC(enabled, "enable wakelock when VBUS present"); ++ ++static int __init otg_wakelock_init(void) ++{ ++ int ret; ++ ++ otgwl_xceiv = usb_get_transceiver(); ++ ++ if (!otgwl_xceiv) { ++ pr_err("%s: No USB transceiver found\n", __func__); ++ return -ENODEV; ++ } ++ ++ snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s", ++ dev_name(otgwl_xceiv->dev)); ++ wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND, ++ vbus_lock.name); ++ ++ otgwl_nb.notifier_call = otgwl_otg_notifications; ++ ret = usb_register_notifier(otgwl_xceiv, &otgwl_nb); ++ ++ if (ret) { ++ pr_err("%s: usb_register_notifier on transceiver %s" ++ " failed\n", __func__, ++ dev_name(otgwl_xceiv->dev)); ++ otgwl_xceiv = NULL; ++ wake_lock_destroy(&vbus_lock.wakelock); ++ return ret; ++ } ++ ++ otgwl_handle_event(otgwl_xceiv->last_event); ++ return ret; ++} ++ ++late_initcall(otg_wakelock_init); +diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig +index a290be51..65b07f4d 100644 +--- a/drivers/video/Kconfig ++++ b/drivers/video/Kconfig +@@ -23,6 +23,8 @@ source "drivers/gpu/drm/Kconfig" + + source "drivers/gpu/stub/Kconfig" + ++source "drivers/gpu/ion/Kconfig" ++ + config VGASTATE + tristate + default n +diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c +index e5f74416..d409352f 100644 +--- a/drivers/w1/masters/ds2482.c ++++ b/drivers/w1/masters/ds2482.c +@@ -18,6 +18,8 @@ + #include + #include + #include ++#include ++#include + #include + + #include "../w1.h" +@@ -84,7 +86,8 @@ static const u8 ds2482_chan_rd[8] = + static int ds2482_probe(struct i2c_client *client, + const struct i2c_device_id *id); + static int ds2482_remove(struct i2c_client *client); +- ++static int ds2482_suspend(struct device *dev); ++static int ds2482_resume(struct device *dev); + + /** + * Driver data (common to all clients) +@@ -94,10 +97,16 @@ static const struct i2c_device_id ds2482_id[] = { + { } + }; + ++static const struct dev_pm_ops ds2482_pm_ops = { ++ .suspend = ds2482_suspend, ++ .resume = ds2482_resume, ++}; ++ + static struct i2c_driver ds2482_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ds2482", ++ .pm = &ds2482_pm_ops, + }, + .probe = ds2482_probe, + .remove = ds2482_remove, +@@ -119,6 +128,7 @@ struct ds2482_w1_chan { + struct ds2482_data { + struct i2c_client *client; + struct mutex access_lock; ++ int slpz_gpio; + + /* 1-wire interface(s) */ + int w1_count; /* 1 or 8 */ +@@ -407,11 +417,31 @@ static u8 ds2482_w1_reset_bus(void *data) + return retval; + } + ++static int ds2482_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct ds2482_data *data = i2c_get_clientdata(client); ++ ++ if (data->slpz_gpio >= 0) ++ gpio_set_value(data->slpz_gpio, 0); ++ return 0; ++} ++ ++static int ds2482_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct ds2482_data *data = i2c_get_clientdata(client); ++ ++ if (data->slpz_gpio >= 0) ++ gpio_set_value(data->slpz_gpio, 1); ++ return 0; ++} + + static int ds2482_probe(struct i2c_client *client, + const struct i2c_device_id *id) + { + struct ds2482_data *data; ++ struct ds2482_platform_data *pdata; + int err = -ENODEV; + int temp1; + int idx; +@@ -476,6 +506,16 @@ static int ds2482_probe(struct i2c_client *client, + } + } + ++ pdata = client->dev.platform_data; ++ data->slpz_gpio = pdata ? pdata->slpz_gpio : -1; ++ ++ if (data->slpz_gpio >= 0) { ++ err = gpio_request_one(data->slpz_gpio, GPIOF_OUT_INIT_HIGH, ++ "ds2482.slpz"); ++ if (err < 0) ++ goto exit_w1_remove; ++ } ++ + return 0; + + exit_w1_remove: +@@ -500,6 +540,11 @@ static int ds2482_remove(struct i2c_client *client) + w1_remove_master_device(&data->w1_ch[idx].w1_bm); + } + ++ if (data->slpz_gpio >= 0) { ++ gpio_set_value(data->slpz_gpio, 0); ++ gpio_free(data->slpz_gpio); ++ } ++ + /* Free the memory */ + kfree(data); + return 0; +diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig +index 37096246..fe518917 100644 +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -349,6 +349,12 @@ config IMX2_WDT + To compile this driver as a module, choose M here: the + module will be called imx2_wdt. + ++config AK39_WATCHDOG ++ tristate "ANYKA ak39 watchdog" ++ depends on ARCH_AK39 ++ help ++ Say Y here if you want support for the watchdog timer on ANYKA ak39 boards. ++ + # AVR32 Architecture + + config AT32AP700X_WDT +diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile +index e8f479a1..5a4917d6 100644 +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -167,3 +167,4 @@ obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o + obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o + obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o + obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o ++obj-$(CONFIG_AK39_WATCHDOG) += ak39_wdt.o +diff --git a/drivers/watchdog/ak39_wdt.c b/drivers/watchdog/ak39_wdt.c +new file mode 100755 +index 00000000..9622ab99 +--- /dev/null ++++ b/drivers/watchdog/ak39_wdt.c +@@ -0,0 +1,347 @@ ++/* ++ * drivers/char/watchdog/ak98_wdt.c ++ * ++ * Watchdog driver for ANYKA ak98 processors ++ * ++ * Author: Wenyong Zhou ++ * ++ * Adapted from the IXP2000 watchdog driver by Lennert Buytenhek. ++ * The original version carries these notices: ++ * ++ * Author: Deepak Saxena ++ * ++ * Copyright 2004 (c) MontaVista, Software, Inc. ++ * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define SELECT_WTC 1 ++#define SELECT_RTC 0 ++ ++#define WDT_DEBUG ++#undef PDEBUG ++#ifdef WDT_DEBUG ++#define PDEBUG(fmt, args...) printk(KERN_INFO fmt,## args) ++#else ++#define PDEBUG(fmt, args...) ++#endif ++ ++static int nowayout = WATCHDOG_NOWAYOUT; ++static unsigned int def_heartbeat = (0x1FFF); /* Default is 8 seconds */ ++static unsigned int now_heartbeat = (0x1FFF); ++ ++static unsigned long in_use; ++static atomic_t in_write = ATOMIC_INIT(0); ++static DEFINE_SPINLOCK(wdt_lock); ++ ++static int ak98_wdt_disable_nb(struct notifier_block *n, unsigned long state,void *cmd); ++static struct notifier_block ak98_wdt_nb = { ++ .notifier_call = ak98_wdt_disable_nb, ++}; ++ ++module_param(def_heartbeat, int, 0); ++MODULE_PARM_DESC(def_heartbeat, "Watchdog heartbeat in seconds (default 8s)"); ++ ++module_param(nowayout, int, 0); ++MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); ++ ++static void select_wdt_rtc(int which) ++{ ++ unsigned long val; ++ ++ val = ak_rtc_read(AK_RTC_SETTING); ++ if (which == 0) ++ val &= ~(1 << 10); ++ else ++ val |= (1 << 10); ++ ak_rtc_write(AK_RTC_SETTING, val); ++} ++ ++void wdt_enable(void) ++{ ++ unsigned long val; ++ ++ spin_lock(&wdt_lock); ++ select_wdt_rtc(SELECT_WTC); ++ ++ //enable watchdog timer ++ val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); ++ val |= (1<<13); ++ ak_rtc_write(AK_WDT_RTC_TIMER_CONF, val); ++ ++ //set timer ++ val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); ++ val &= (1<<13); ++ val |= (def_heartbeat & 0x1FFF); ++ ak_rtc_write(AK_WDT_RTC_TIMER_CONF, val); ++ ++ //open watchdog and watchdog output ++ val = ak_rtc_read(AK_RTC_SETTING); ++ val |= ((1<<5) | (1<<2)); ++ val &= ~(1<<11); ++ ak_rtc_write(AK_RTC_SETTING, val); ++ ++ select_wdt_rtc(SELECT_RTC); ++ spin_unlock(&wdt_lock); ++} ++ ++static void wdt_disable(void) ++{ ++ unsigned long val; ++ ++ spin_lock(&wdt_lock); ++ select_wdt_rtc(SELECT_WTC); ++ ++ //clear watchdog timer ++ val = ak_rtc_read(AK_RTC_SETTING); ++ val |= (1<<6); ++ ak_rtc_write(AK_RTC_SETTING, val); ++ ++ //disable watchdog timer ++ val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); ++ val &= ~(1<<13); ++ ak_rtc_write(AK_WDT_RTC_TIMER_CONF, val); ++ ++ //close watchdog and watchdog output ++ val = ak_rtc_read(AK_RTC_SETTING); ++ val &= ~((1<<2) | (1<<5)); ++ ak_rtc_write(AK_RTC_SETTING, val); ++ ++ select_wdt_rtc(SELECT_RTC); ++ spin_unlock(&wdt_lock); ++} ++ ++void wdt_keepalive(unsigned int heartbeat) ++{ ++ unsigned long val; ++ ++ PDEBUG("heartbeat = %x\n", heartbeat); ++ spin_lock(&wdt_lock); ++ select_wdt_rtc(SELECT_WTC); ++ ++ //clear watchdog timer ++ val = ak_rtc_read(AK_RTC_SETTING); ++ val |= (1<<6); ++ ak_rtc_write(AK_RTC_SETTING, val); ++ ++ val = ak_rtc_read(AK_WDT_RTC_TIMER_CONF); ++ val &= (1<<13); ++ val |= (heartbeat & 0x1FFF); ++ ak_rtc_write(AK_WDT_RTC_TIMER_CONF, val); ++ ++ select_wdt_rtc(SELECT_RTC); ++ spin_unlock(&wdt_lock); ++} ++ ++/** ++ * @brief: ak98_wdt_disable_nb ++ * @author: zhongjunchao ++ * @date: 2011-10-11 ++ * ++ * @note: Sometimes, system reboot and doesn`t disable watchdog. we disable it ++ * for safe. ++ */ ++static int ak98_wdt_disable_nb(struct notifier_block *n, unsigned long state,void *cmd) ++{ ++ wdt_disable(); ++ return NOTIFY_DONE; ++} ++ ++static struct watchdog_info ident = { ++ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, ++ .identity = "ANYKA ak98 Watchdog", ++}; ++ ++/** ++ * @brief: ak98_wdt_ioctl ++ * @author: zhouwenyong ++ * @modify: zhongjunchao ++ * @date: 2011-9-26 ++ * ++ * @note: WDIOC_GETSUPPORT,return struct watchdog_info. ++ * WDIOC_KEEPALIVE, set a default timeout (8s). ++ * WDIOC_SETTIMEOUT, set a timeout value what you want (max 8s). ++ * WDIOC_GETTIMEOUT, query the current timeout. ++ * More detail, refer to kernel/Documentation/watchdog/watchdog-api.txt ++ */ ++static long ak98_wdt_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ void __user *argp = (void __user *)arg; ++ int __user *p = argp; ++ int ret = -ENOTTY; ++ int time; ++ ++ switch (cmd) { ++ ++ case WDIOC_GETSUPPORT: ++ return copy_to_user((struct watchdog_info *)argp, &ident, ++ sizeof(ident)) ? -EFAULT : 0; ++ case WDIOC_GETSTATUS: ++ case WDIOC_GETBOOTSTATUS: ++ return put_user(0, p); ++ ++ case WDIOC_KEEPALIVE: ++ wdt_keepalive(def_heartbeat); ++ now_heartbeat = def_heartbeat; ++ return 0; ++ ++ case WDIOC_SETTIMEOUT: ++ if (get_user(time, p)) ++ return -EFAULT; ++ ++ PDEBUG("timeout = %d\n", time); ++ if (time <= 0 || time > 8) ++ return -EINVAL; ++ ++ now_heartbeat = time * 1024 - 1; ++ wdt_keepalive(now_heartbeat); ++ return 0; ++ ++ case WDIOC_GETTIMEOUT: ++ return put_user((now_heartbeat + 1)/1024, p); ++ ++ default: ++ return -ENOTTY; ++ } ++ ++ return ret; ++} ++ ++/** ++ * @brief: ak98_wdt_write ++ * @author: zhouwenyong ++ * @modify: zhongjunchao ++ * @date: 2011-9-26 ++ * ++ * @note: We support "Magic Close" that driver will not disable the watchdog unless ++ * a specific magic character 'V' has been sent to /dev/watchdog just before ++ * closing the file. ++ */ ++static ssize_t ak98_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) ++{ ++ if (len) { ++ size_t i; ++ ++ atomic_set(&in_write, 1); ++ for (i = 0; i != len; i++) { ++ char c; ++ ++ if (get_user(c, data + i)) ++ return -EFAULT; ++ if (c == 'V') { ++ PDEBUG("Detect \"V\" Magic Character\n"); ++ atomic_set(&in_write, 0); ++ } ++ } ++ wdt_keepalive(def_heartbeat); ++ } ++ ++ return len; ++} ++ ++/** ++ * @brief: ak98_wdt_open ++ * @author: zhouwenyong ++ * @modify: zhongjunchao ++ * @date: 2011-9-26 ++ * ++ * @note: We only support one process open /dev/watchdog, to avoid overwrite watchdog ++ * timeout value. ++ */ ++static int ak98_wdt_open(struct inode *inode, struct file *file) ++{ ++ if (test_and_set_bit(0, &in_use)) ++ return -EBUSY; ++ ++ if (nowayout) ++ __module_get(THIS_MODULE); ++ ++ wdt_enable(); ++ ++ return nonseekable_open(inode, file); ++} ++ ++/** ++ * @brief: ak98_wdt_release ++ * @author: zhouwenyong ++ * @modify: zhongjunchao ++ * @date: 2011-9-26 ++ * ++ * @note: When open CONFIG_WATCHDOG_NOWAYOUT option, nowayout = 1. ++ * When you open and write to /dev/watchdog, please write magic character 'V' ++ * before close the file. If not, watchdog will not disable after close ++ * /dev/watchdog. ++ */ ++static int ak98_wdt_release(struct inode *inode, struct file *file) ++{ ++ if (nowayout) ++ printk(KERN_INFO "WATCHDOG: Driver support nowayout option -" ++ "no way to disable watchdog\n"); ++ else if (atomic_read(&in_write)) ++ printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " ++ "timer will not stop\n"); ++ else ++ wdt_disable(); ++ clear_bit(0, &in_use); ++ ++ return 0; ++} ++ ++static const struct file_operations ak98_wdt_fops = { ++ .owner = THIS_MODULE, ++ .llseek = no_llseek, ++ .write = ak98_wdt_write, ++ .unlocked_ioctl = ak98_wdt_ioctl, ++ .open = ak98_wdt_open, ++ .release = ak98_wdt_release, ++}; ++ ++static struct miscdevice ak98_wdt_miscdev = { ++ .minor = WATCHDOG_MINOR, ++ .name = "watchdog", ++ .fops = &ak98_wdt_fops, ++}; ++ ++static int __init ak98_wdt_init(void) ++{ ++ spin_lock(&wdt_lock); ++ ak_rtc_power(RTC_ON); ++ spin_unlock(&wdt_lock); ++ register_reboot_notifier(&ak98_wdt_nb); ++ ++ return misc_register(&ak98_wdt_miscdev); ++} ++ ++static void __exit ak98_wdt_exit(void) ++{ ++ unregister_reboot_notifier(&ak98_wdt_nb); ++ ak_rtc_power(RTC_OFF); ++ ++ misc_deregister(&ak98_wdt_miscdev); ++} ++module_init(ak98_wdt_init); ++module_exit(ak98_wdt_exit); ++ ++MODULE_DESCRIPTION("ANYKA ak98 Watchdog"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +diff --git a/fs/Kconfig b/fs/Kconfig +index f95ae3a0..1dd49481 100644 +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -203,6 +203,10 @@ source "fs/hfsplus/Kconfig" + source "fs/befs/Kconfig" + source "fs/bfs/Kconfig" + source "fs/efs/Kconfig" ++ ++# Patched by YAFFS ++source "fs/yaffs2/Kconfig" ++ + source "fs/jffs2/Kconfig" + # UBIFS File system configuration + source "fs/ubifs/Kconfig" +diff --git a/fs/Makefile b/fs/Makefile +index 2fb97793..95cf9de6 100644 +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -125,3 +125,6 @@ obj-$(CONFIG_GFS2_FS) += gfs2/ + obj-y += exofs/ # Multiple modules + obj-$(CONFIG_CEPH_FS) += ceph/ + obj-$(CONFIG_PSTORE) += pstore/ ++ ++# Patched by YAFFS ++obj-$(CONFIG_YAFFS_FS) += yaffs2/ +diff --git a/fs/eventpoll.c b/fs/eventpoll.c +index 33c9599c..0863bab4 100644 +--- a/fs/eventpoll.c ++++ b/fs/eventpoll.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -87,7 +88,7 @@ + */ + + /* Epoll private bits inside the event mask */ +-#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET) ++#define EP_PRIVATE_BITS (EPOLLWAKEUP | EPOLLONESHOT | EPOLLET) + + /* Maximum number of nesting allowed inside epoll sets */ + #define EP_MAX_NESTS 4 +@@ -154,6 +155,9 @@ struct epitem { + /* List header used to link this item to the "struct file" items list */ + struct list_head fllink; + ++ /* wakeup_source used when EPOLLWAKEUP is set */ ++ struct wakeup_source *ws; ++ + /* The structure that describe the interested events and the source fd */ + struct epoll_event event; + }; +@@ -194,6 +198,9 @@ struct eventpoll { + */ + struct epitem *ovflist; + ++ /* wakeup_source used when ep_scan_ready_list is running */ ++ struct wakeup_source *ws; ++ + /* The user that created the eventpoll descriptor */ + struct user_struct *user; + +@@ -588,8 +595,10 @@ static int ep_scan_ready_list(struct eventpoll *ep, + * queued into ->ovflist but the "txlist" might already + * contain them, and the list_splice() below takes care of them. + */ +- if (!ep_is_linked(&epi->rdllink)) ++ if (!ep_is_linked(&epi->rdllink)) { + list_add_tail(&epi->rdllink, &ep->rdllist); ++ __pm_stay_awake(epi->ws); ++ } + } + /* + * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after +@@ -602,6 +611,7 @@ static int ep_scan_ready_list(struct eventpoll *ep, + * Quickly re-inject items left on "txlist". + */ + list_splice(&txlist, &ep->rdllist); ++ __pm_relax(ep->ws); + + if (!list_empty(&ep->rdllist)) { + /* +@@ -656,6 +666,8 @@ static int ep_remove(struct eventpoll *ep, struct epitem *epi) + list_del_init(&epi->rdllink); + spin_unlock_irqrestore(&ep->lock, flags); + ++ wakeup_source_unregister(epi->ws); ++ + /* At this point it is safe to free the eventpoll item */ + kmem_cache_free(epi_cache, epi); + +@@ -706,6 +718,7 @@ static void ep_free(struct eventpoll *ep) + mutex_unlock(&epmutex); + mutex_destroy(&ep->mtx); + free_uid(ep->user); ++ wakeup_source_unregister(ep->ws); + kfree(ep); + } + +@@ -737,6 +750,7 @@ static int ep_read_events_proc(struct eventpoll *ep, struct list_head *head, + * callback, but it's not actually ready, as far as + * caller requested events goes. We can remove it here. + */ ++ __pm_relax(epi->ws); + list_del_init(&epi->rdllink); + } + } +@@ -927,13 +941,23 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *k + if (epi->next == EP_UNACTIVE_PTR) { + epi->next = ep->ovflist; + ep->ovflist = epi; ++ if (epi->ws) { ++ /* ++ * Activate ep->ws since epi->ws may get ++ * deactivated at any time. ++ */ ++ __pm_stay_awake(ep->ws); ++ } ++ + } + goto out_unlock; + } + + /* If this file is already in the ready list we exit soon */ +- if (!ep_is_linked(&epi->rdllink)) ++ if (!ep_is_linked(&epi->rdllink)) { + list_add_tail(&epi->rdllink, &ep->rdllist); ++ __pm_stay_awake(epi->ws); ++ } + + /* + * Wake up ( if active ) both the eventpoll wait list and the ->poll() +@@ -1091,6 +1115,30 @@ static int reverse_path_check(void) + return error; + } + ++static int ep_create_wakeup_source(struct epitem *epi) ++{ ++ const char *name; ++ ++ if (!epi->ep->ws) { ++ epi->ep->ws = wakeup_source_register("eventpoll"); ++ if (!epi->ep->ws) ++ return -ENOMEM; ++ } ++ ++ name = epi->ffd.file->f_path.dentry->d_name.name; ++ epi->ws = wakeup_source_register(name); ++ if (!epi->ws) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static void ep_destroy_wakeup_source(struct epitem *epi) ++{ ++ wakeup_source_unregister(epi->ws); ++ epi->ws = NULL; ++} ++ + /* + * Must be called with "mtx" held. + */ +@@ -1118,6 +1166,13 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, + epi->event = *event; + epi->nwait = 0; + epi->next = EP_UNACTIVE_PTR; ++ if (epi->event.events & EPOLLWAKEUP) { ++ error = ep_create_wakeup_source(epi); ++ if (error) ++ goto error_create_wakeup_source; ++ } else { ++ epi->ws = NULL; ++ } + + /* Initialize the poll table using the queue callback */ + epq.epi = epi; +@@ -1164,6 +1219,7 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, + /* If the file is already "ready" we drop it inside the ready list */ + if ((revents & event->events) && !ep_is_linked(&epi->rdllink)) { + list_add_tail(&epi->rdllink, &ep->rdllist); ++ __pm_stay_awake(epi->ws); + + /* Notify waiting tasks that events are available */ + if (waitqueue_active(&ep->wq)) +@@ -1204,6 +1260,9 @@ error_unregister: + list_del_init(&epi->rdllink); + spin_unlock_irqrestore(&ep->lock, flags); + ++ wakeup_source_unregister(epi->ws); ++ ++error_create_wakeup_source: + kmem_cache_free(epi_cache, epi); + + return error; +@@ -1229,6 +1288,12 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even + epi->event.events = event->events; /* need barrier below */ + pt._key = event->events; + epi->event.data = event->data; /* protected by mtx */ ++ if (epi->event.events & EPOLLWAKEUP) { ++ if (!epi->ws) ++ ep_create_wakeup_source(epi); ++ } else if (epi->ws) { ++ ep_destroy_wakeup_source(epi); ++ } + + /* + * The following barrier has two effects: +@@ -1264,6 +1329,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even + spin_lock_irq(&ep->lock); + if (!ep_is_linked(&epi->rdllink)) { + list_add_tail(&epi->rdllink, &ep->rdllist); ++ __pm_stay_awake(epi->ws); + + /* Notify waiting tasks that events are available */ + if (waitqueue_active(&ep->wq)) +@@ -1302,6 +1368,18 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, + !list_empty(head) && eventcnt < esed->maxevents;) { + epi = list_first_entry(head, struct epitem, rdllink); + ++ /* ++ * Activate ep->ws before deactivating epi->ws to prevent ++ * triggering auto-suspend here (in case we reactive epi->ws ++ * below). ++ * ++ * This could be rearranged to delay the deactivation of epi->ws ++ * instead, but then epi->ws would temporarily be out of sync ++ * with ep_is_linked(). ++ */ ++ if (epi->ws && epi->ws->active) ++ __pm_stay_awake(ep->ws); ++ __pm_relax(epi->ws); + list_del_init(&epi->rdllink); + + pt._key = epi->event.events; +@@ -1318,6 +1396,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, + if (__put_user(revents, &uevent->events) || + __put_user(epi->event.data, &uevent->data)) { + list_add(&epi->rdllink, head); ++ __pm_stay_awake(epi->ws); + return eventcnt ? eventcnt : -EFAULT; + } + eventcnt++; +@@ -1337,6 +1416,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, + * poll callback will queue them in ep->ovflist. + */ + list_add_tail(&epi->rdllink, &ep->rdllist); ++ __pm_stay_awake(epi->ws); + } + } + } +@@ -1649,6 +1729,10 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, + if (!tfile->f_op || !tfile->f_op->poll) + goto error_tgt_fput; + ++ /* Check if EPOLLWAKEUP is allowed */ ++ if ((epds.events & EPOLLWAKEUP) && !capable(CAP_EPOLLWAKEUP)) ++ epds.events &= ~EPOLLWAKEUP; ++ + /* + * We have to check that the file structure underneath the file descriptor + * the user passed to us _is_ an eventpoll file. And also we do not permit +diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c +index 902544e7..8b15179b 100644 +--- a/fs/ext4/ialloc.c ++++ b/fs/ext4/ialloc.c +@@ -778,7 +778,10 @@ got: + ext4_itable_unused_set(sb, gdp, + (EXT4_INODES_PER_GROUP(sb) - ino)); + up_read(&grp->alloc_sem); ++ } else { ++ ext4_lock_group(sb, group); + } ++ + ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1); + if (S_ISDIR(mode)) { + ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1); +@@ -790,8 +793,8 @@ got: + } + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp); +- ext4_unlock_group(sb, group); + } ++ ext4_unlock_group(sb, group); + + BUFFER_TRACE(group_desc_bh, "call ext4_handle_dirty_metadata"); + err = ext4_handle_dirty_metadata(handle, NULL, group_desc_bh); +diff --git a/fs/fat/dir.c b/fs/fat/dir.c +index aca191bd..65e174bd 100644 +--- a/fs/fat/dir.c ++++ b/fs/fat/dir.c +@@ -754,6 +754,13 @@ static int fat_ioctl_readdir(struct inode *inode, struct file *filp, + return ret; + } + ++static int fat_ioctl_volume_id(struct inode *dir) ++{ ++ struct super_block *sb = dir->i_sb; ++ struct msdos_sb_info *sbi = MSDOS_SB(sb); ++ return sbi->vol_id; ++} ++ + static long fat_dir_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) + { +@@ -770,6 +777,8 @@ static long fat_dir_ioctl(struct file *filp, unsigned int cmd, + short_only = 0; + both = 1; + break; ++ case VFAT_IOCTL_GET_VOLUME_ID: ++ return fat_ioctl_volume_id(inode); + default: + return fat_generic_ioctl(filp, cmd, arg); + } +diff --git a/fs/fat/fat.h b/fs/fat/fat.h +index 66994f31..341f7537 100644 +--- a/fs/fat/fat.h ++++ b/fs/fat/fat.h +@@ -78,6 +78,7 @@ struct msdos_sb_info { + const void *dir_ops; /* Opaque; default directory operations */ + int dir_per_block; /* dir entries per block */ + int dir_per_block_bits; /* log2(dir_per_block) */ ++ unsigned long vol_id; /* volume ID */ + + int fatent_shift; + struct fatent_operations *fatent_ops; +diff --git a/fs/fat/inode.c b/fs/fat/inode.c +index 21687e31..d403f76c 100644 +--- a/fs/fat/inode.c ++++ b/fs/fat/inode.c +@@ -1246,6 +1246,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, + struct inode *root_inode = NULL, *fat_inode = NULL; + struct buffer_head *bh; + struct fat_boot_sector *b; ++ struct fat_boot_bsx *bsx; + struct msdos_sb_info *sbi; + u16 logical_sector_size; + u32 total_sectors, total_clusters, fat_clusters, rootdir_sectors; +@@ -1390,6 +1391,8 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, + goto out_fail; + } + ++ bsx = (struct fat_boot_bsx *)(bh->b_data + FAT32_BSX_OFFSET); ++ + fsinfo = (struct fat_boot_fsinfo *)fsinfo_bh->b_data; + if (!IS_FSINFO(fsinfo)) { + fat_msg(sb, KERN_WARNING, "Invalid FSINFO signature: " +@@ -1405,8 +1408,14 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, + } + + brelse(fsinfo_bh); ++ } else { ++ bsx = (struct fat_boot_bsx *)(bh->b_data + FAT16_BSX_OFFSET); + } + ++ /* interpret volume ID as a little endian 32 bit integer */ ++ sbi->vol_id = (((u32)bsx->vol_id[0]) | ((u32)bsx->vol_id[1] << 8) | ++ ((u32)bsx->vol_id[2] << 16) | ((u32)bsx->vol_id[3] << 24)); ++ + sbi->dir_per_block = sb->s_blocksize / sizeof(struct msdos_dir_entry); + sbi->dir_per_block_bits = ffs(sbi->dir_per_block) - 1; + +diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c +index b35bd64f..1b21e0a0 100644 +--- a/fs/fs-writeback.c ++++ b/fs/fs-writeback.c +@@ -1083,7 +1083,7 @@ void __mark_inode_dirty(struct inode *inode, int flags) + if ((inode->i_state & flags) == flags) + return; + +- if (unlikely(block_dump)) ++ if (unlikely(block_dump > 1)) + block_dump___mark_inode_dirty(inode); + + spin_lock(&inode->i_lock); +diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c +index f4246cfc..1f81f70d 100644 +--- a/fs/fuse/dev.c ++++ b/fs/fuse/dev.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + MODULE_ALIAS_MISCDEV(FUSE_MINOR); + MODULE_ALIAS("devname:fuse"); +@@ -387,7 +388,10 @@ __acquires(fc->lock) + * Wait it out. + */ + spin_unlock(&fc->lock); +- wait_event(req->waitq, req->state == FUSE_REQ_FINISHED); ++ ++ while (req->state != FUSE_REQ_FINISHED) ++ wait_event_freezable(req->waitq, ++ req->state == FUSE_REQ_FINISHED); + spin_lock(&fc->lock); + + if (!req->aborted) +diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c +index 8608f877..d77910e3 100644 +--- a/fs/jffs2/file.c ++++ b/fs/jffs2/file.c +@@ -148,6 +148,10 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, + jffs2_dbg(1, "%s()\n", __func__); + + if (pageofs > inode->i_size) { ++ /* FIXME ++ * How about a huage hole ? ++ * If i make it happy, system will crash! ++ */ + ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len, + ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); + if (ret) +diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c +index dc0437e8..17f709f0 100644 +--- a/fs/jffs2/readinode.c ++++ b/fs/jffs2/readinode.c +@@ -228,6 +228,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, + node with highest version -- i.e. the one which will end up as f->metadata. + Note that such nodes won't be REF_UNCHECKED since there are no data to + check anyway. */ ++ /* FIXME: But sometimes it is a regular file node, create by O_TRUNC flag */ + if (!tn->fn->size) { + if (rii->mdata_tn) { + if (rii->mdata_tn->version < tn->version) { +@@ -455,9 +456,12 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c, + + if (rii->mdata_tn) { + dbg_readinode("potential mdata is ver %d at %p\n", rii->mdata_tn->version, rii->mdata_tn); +- high_ver = rii->mdata_tn->version; ++ /* FIXME: When it is not a regular file node, just do it. Otherwise, skip it */ ++ if (rii->fds) ++ high_ver = rii->mdata_tn->version; + rii->latest_ref = rii->mdata_tn->fn->raw; + } ++ + #ifdef JFFS2_DBG_READINODE_MESSAGES + this = tn_last(&rii->tn_root); + while (this) { +@@ -505,6 +509,8 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c, + highest_version, because this one is only + counting _valid_ nodes which could give the + latest inode metadata */ ++ /* FIXME: Make sure latest_ref point to valid node, ++ * but not empty node */ + high_ver = this->version; + rii->latest_ref = this->fn->raw; + } +diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c +index f9916f31..a05e4b78 100644 +--- a/fs/jffs2/super.c ++++ b/fs/jffs2/super.c +@@ -355,6 +355,7 @@ static struct file_system_type jffs2_fs_type = { + .name = "jffs2", + .mount = jffs2_mount, + .kill_sb = jffs2_kill_sb, ++ .fs_flags = FS_REQUIRES_DEV, + }; + + static int __init init_jffs2_fs(void) +diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c +index 6bec5c0b..ac15a4f5 100644 +--- a/fs/jffs2/wbuf.c ++++ b/fs/jffs2/wbuf.c +@@ -1263,7 +1263,7 @@ void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) { + int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) { + /* Cleanmarker currently occupies whole programming regions, + * either one or 2 for 8Byte STMicro flashes. */ +- c->cleanmarker_size = max(16u, c->mtd->writesize); ++ //c->cleanmarker_size = max(16u, c->mtd->writesize); + + /* Initialize write buffer */ + init_rwsem(&c->wbuf_sem); +diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c +index b634de4c..7d01a5bd 100644 +--- a/fs/jffs2/write.c ++++ b/fs/jffs2/write.c +@@ -359,7 +359,16 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + jffs2_dbg(2, "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", + writelen, offset); + +- ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, ++ /* FIXME ++ * Make one write op as atomic, less data frag lose when power off. ++ * But, still lose some data when the file huge enough. ++ * And, wasted more space case of biger minisize. ++ * And, GC will work harder. ++ * And, performance is bad ++ */ ++ //ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, ++ //&alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); ++ ret = jffs2_reserve_space(c, writelen, + &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); + if (ret) { + jffs2_dbg(1, "jffs2_reserve_space returned %d\n", ret); +diff --git a/fs/proc/base.c b/fs/proc/base.c +index 9fc77b41..c8cb15dc 100644 +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -137,6 +137,12 @@ struct pid_entry { + + static int proc_fd_permission(struct inode *inode, int mask); + ++/* ANDROID is for special files in /proc. */ ++#define ANDROID(NAME, MODE, OTYPE) \ ++ NOD(NAME, (S_IFREG|(MODE)), \ ++ &proc_##OTYPE##_inode_operations, \ ++ &proc_##OTYPE##_operations, {}) ++ + /* + * Count the number of hardlinks for the pid_entry table, excluding the . + * and .. links. +@@ -969,6 +975,35 @@ out: + return err < 0 ? err : count; + } + ++static int oom_adjust_permission(struct inode *inode, int mask) ++{ ++ uid_t uid; ++ struct task_struct *p; ++ ++ p = get_proc_task(inode); ++ if(p) { ++ uid = task_uid(p); ++ put_task_struct(p); ++ } ++ ++ /* ++ * System Server (uid == 1000) is granted access to oom_adj of all ++ * android applications (uid > 10000) as and services (uid >= 1000) ++ */ ++ if (p && (current_fsuid() == 1000) && (uid >= 1000)) { ++ if (inode->i_mode >> 6 & mask) { ++ return 0; ++ } ++ } ++ ++ /* Fall back to default. */ ++ return generic_permission(inode, mask); ++} ++ ++static const struct inode_operations proc_oom_adjust_inode_operations = { ++ .permission = oom_adjust_permission, ++}; ++ + static const struct file_operations proc_oom_adjust_operations = { + .read = oom_adjust_read, + .write = oom_adjust_write, +@@ -3011,7 +3046,7 @@ static const struct pid_entry tgid_base_stuff[] = { + REG("cgroup", S_IRUGO, proc_cgroup_operations), + #endif + INF("oom_score", S_IRUGO, proc_oom_score), +- REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adjust_operations), ++ ANDROID("oom_adj",S_IRUGO|S_IWUSR, oom_adjust), + REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations), + #ifdef CONFIG_AUDITSYSCALL + REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), +diff --git a/fs/yaffs2/Kconfig b/fs/yaffs2/Kconfig +new file mode 100644 +index 00000000..63541405 +--- /dev/null ++++ b/fs/yaffs2/Kconfig +@@ -0,0 +1,161 @@ ++# ++# YAFFS file system configurations ++# ++ ++config YAFFS_FS ++ tristate "YAFFS2 file system support" ++ default n ++ depends on MTD_BLOCK ++ select YAFFS_YAFFS1 ++ select YAFFS_YAFFS2 ++ help ++ YAFFS2, or Yet Another Flash Filing System, is a filing system ++ optimised for NAND Flash chips. ++ ++ To compile the YAFFS2 file system support as a module, choose M ++ here: the module will be called yaffs2. ++ ++ If unsure, say N. ++ ++ Further information on YAFFS2 is available at ++ . ++ ++config YAFFS_YAFFS1 ++ bool "512 byte / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable YAFFS1 support -- yaffs for 512 byte / page devices ++ ++ Not needed for 2K-page devices. ++ ++ If unsure, say Y. ++ ++config YAFFS_9BYTE_TAGS ++ bool "Use older-style on-NAND data format with pageStatus byte" ++ depends on YAFFS_YAFFS1 ++ default n ++ help ++ ++ Older-style on-NAND data format has a "pageStatus" byte to record ++ chunk/page state. This byte is zero when the page is discarded. ++ Choose this option if you have existing on-NAND data using this ++ format that you need to continue to support. New data written ++ also uses the older-style format. Note: Use of this option ++ generally requires that MTD's oob layout be adjusted to use the ++ older-style format. See notes on tags formats and MTD versions ++ in yaffs_mtdif1.c. ++ ++ If unsure, say N. ++ ++config YAFFS_DOES_ECC ++ bool "Lets Yaffs do its own ECC" ++ depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This enables Yaffs to use its own ECC functions instead of using ++ the ones from the generic MTD-NAND driver. ++ ++ If unsure, say N. ++ ++config YAFFS_ECC_WRONG_ORDER ++ bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" ++ depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This makes yaffs_ecc.c use the same ecc byte order as Steven ++ Hill's nand_ecc.c. If not set, then you get the same ecc byte ++ order as SmartMedia. ++ ++ If unsure, say N. ++ ++config YAFFS_YAFFS2 ++ bool "2048 byte (or larger) / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable YAFFS2 support -- yaffs for >= 2K bytes per page devices ++ ++ If unsure, say Y. ++ ++config YAFFS_AUTO_YAFFS2 ++ bool "Autoselect yaffs2 format" ++ depends on YAFFS_YAFFS2 ++ default y ++ help ++ Without this, you need to explicitely use yaffs2 as the file ++ system type. With this, you can say "yaffs" and yaffs or yaffs2 ++ will be used depending on the device page size (yaffs on ++ 512-byte page devices, yaffs2 on 2K page devices). ++ ++ If unsure, say Y. ++ ++config YAFFS_DISABLE_TAGS_ECC ++ bool "Disable YAFFS from doing ECC on tags by default" ++ depends on YAFFS_FS && YAFFS_YAFFS2 ++ default n ++ help ++ This defaults Yaffs to using its own ECC calculations on tags instead of ++ just relying on the MTD. ++ This behavior can also be overridden with tags_ecc_on and ++ tags_ecc_off mount options. ++ ++ If unsure, say N. ++ ++config YAFFS_ALWAYS_CHECK_CHUNK_ERASED ++ bool "Force chunk erase check" ++ depends on YAFFS_FS ++ default n ++ help ++ Normally YAFFS only checks chunks before writing until an erased ++ chunk is found. This helps to detect any partially written ++ chunks that might have happened due to power loss. ++ ++ Enabling this forces on the test that chunks are erased in flash ++ before writing to them. This takes more time but is potentially ++ a bit more secure. ++ ++ Suggest setting Y during development and ironing out driver ++ issues etc. Suggest setting to N if you want faster writing. ++ ++ If unsure, say Y. ++ ++config YAFFS_EMPTY_LOST_AND_FOUND ++ bool "Empty lost and found on boot" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is enabled then the contents of lost and found is ++ automatically dumped at mount. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BLOCK_REFRESHING ++ bool "Disable yaffs2 block refreshing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then block refreshing is disabled. ++ Block refreshing infrequently refreshes the oldest block in ++ a yaffs2 file system. This mechanism helps to refresh flash to ++ mitigate against data loss. This is particularly useful for MLC. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BACKGROUND ++ bool "Disable yaffs2 background processing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then background processing is disabled. ++ Background processing makes many foreground activities faster. ++ ++ If unsure, say N. ++ ++config YAFFS_XATTR ++ bool "Enable yaffs2 xattr support" ++ depends on YAFFS_FS ++ default y ++ help ++ If this is set then yaffs2 will provide xattr support. ++ If unsure, say Y. +diff --git a/fs/yaffs2/Makefile b/fs/yaffs2/Makefile +new file mode 100644 +index 00000000..e63a28aa +--- /dev/null ++++ b/fs/yaffs2/Makefile +@@ -0,0 +1,17 @@ ++# ++# Makefile for the linux YAFFS filesystem routines. ++# ++ ++obj-$(CONFIG_YAFFS_FS) += yaffs.o ++ ++yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o ++yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o ++yaffs-y += yaffs_tagscompat.o yaffs_tagsvalidity.o ++yaffs-y += yaffs_mtdif.o yaffs_mtdif1.o yaffs_mtdif2.o ++yaffs-y += yaffs_nameval.o yaffs_attribs.o ++yaffs-y += yaffs_allocator.o ++yaffs-y += yaffs_yaffs1.o ++yaffs-y += yaffs_yaffs2.o ++yaffs-y += yaffs_bitmap.o ++yaffs-y += yaffs_verify.o ++ +diff --git a/fs/yaffs2/yaffs_allocator.c b/fs/yaffs2/yaffs_allocator.c +new file mode 100644 +index 00000000..f9cd5bec +--- /dev/null ++++ b/fs/yaffs2/yaffs_allocator.c +@@ -0,0 +1,396 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_allocator.h" ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yportenv.h" ++ ++#ifdef CONFIG_YAFFS_KMALLOC_ALLOCATOR ++ ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ dev = dev; ++} ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ dev = dev; ++} ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) ++{ ++ return (struct yaffs_tnode *)kmalloc(dev->tnode_size, GFP_NOFS); ++} ++ ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ dev = dev; ++ kfree(tn); ++} ++ ++void yaffs_init_raw_objs(struct yaffs_dev *dev) ++{ ++ dev = dev; ++} ++ ++void yaffs_deinit_raw_objs(struct yaffs_dev *dev) ++{ ++ dev = dev; ++} ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) ++{ ++ dev = dev; ++ return (struct yaffs_obj *)kmalloc(sizeof(struct yaffs_obj)); ++} ++ ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ ++ dev = dev; ++ kfree(obj); ++} ++ ++#else ++ ++struct yaffs_tnode_list { ++ struct yaffs_tnode_list *next; ++ struct yaffs_tnode *tnodes; ++}; ++ ++struct yaffs_obj_list { ++ struct yaffs_obj_list *next; ++ struct yaffs_obj *objects; ++}; ++ ++struct yaffs_allocator { ++ int n_tnodes_created; ++ struct yaffs_tnode *free_tnodes; ++ int n_free_tnodes; ++ struct yaffs_tnode_list *alloc_tnode_list; ++ ++ int n_obj_created; ++ struct yaffs_obj *free_objs; ++ int n_free_objects; ++ ++ struct yaffs_obj_list *allocated_obj_list; ++}; ++ ++static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) ++{ ++ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ ++ struct yaffs_tnode_list *tmp; ++ ++ if (!allocator) { ++ YBUG(); ++ return; ++ } ++ ++ while (allocator->alloc_tnode_list) { ++ tmp = allocator->alloc_tnode_list->next; ++ ++ kfree(allocator->alloc_tnode_list->tnodes); ++ kfree(allocator->alloc_tnode_list); ++ allocator->alloc_tnode_list = tmp; ++ ++ } ++ ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (allocator) { ++ allocator->alloc_tnode_list = NULL; ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++ } else { ++ YBUG(); ++ } ++} ++ ++static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ int i; ++ struct yaffs_tnode *new_tnodes; ++ u8 *mem; ++ struct yaffs_tnode *curr; ++ struct yaffs_tnode *next; ++ struct yaffs_tnode_list *tnl; ++ ++ if (!allocator) { ++ YBUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_tnodes < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ ++ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); ++ mem = (u8 *) new_tnodes; ++ ++ if (!new_tnodes) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: Could not allocate Tnodes"); ++ return YAFFS_FAIL; ++ } ++ ++ /* New hookup for wide tnodes */ ++ for (i = 0; i < n_tnodes - 1; i++) { ++ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; ++ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; ++ curr->internal[0] = next; ++ } ++ ++ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; ++ curr->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = (struct yaffs_tnode *)mem; ++ ++ allocator->n_free_tnodes += n_tnodes; ++ allocator->n_tnodes_created += n_tnodes; ++ ++ /* Now add this bunch of tnodes to a list for freeing up. ++ * NB If we can't add this to the management list it isn't fatal ++ * but it just means we can't free this bunch of tnodes later. ++ */ ++ ++ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); ++ if (!tnl) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Could not add tnodes to management list"); ++ return YAFFS_FAIL; ++ } else { ++ tnl->tnodes = new_tnodes; ++ tnl->next = allocator->alloc_tnode_list; ++ allocator->alloc_tnode_list = tnl; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALLOCATE,"Tnodes added"); ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (!allocator) { ++ YBUG(); ++ return NULL; ++ } ++ ++ /* If there are none left make more */ ++ if (!allocator->free_tnodes) ++ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); ++ ++ if (allocator->free_tnodes) { ++ tn = allocator->free_tnodes; ++ allocator->free_tnodes = allocator->free_tnodes->internal[0]; ++ allocator->n_free_tnodes--; ++ } ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ YBUG(); ++ return; ++ } ++ ++ if (tn) { ++ tn->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = tn; ++ allocator->n_free_tnodes++; ++ } ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++static void yaffs_init_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (allocator) { ++ allocator->allocated_obj_list = NULL; ++ allocator->free_objs = NULL; ++ allocator->n_free_objects = 0; ++ } else { ++ YBUG(); ++ } ++} ++ ++static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ struct yaffs_obj_list *tmp; ++ ++ if (!allocator) { ++ YBUG(); ++ return; ++ } ++ ++ while (allocator->allocated_obj_list) { ++ tmp = allocator->allocated_obj_list->next; ++ kfree(allocator->allocated_obj_list->objects); ++ kfree(allocator->allocated_obj_list); ++ ++ allocator->allocated_obj_list = tmp; ++ } ++ ++ allocator->free_objs = NULL; ++ allocator->n_free_objects = 0; ++ allocator->n_obj_created = 0; ++} ++ ++static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ int i; ++ struct yaffs_obj *new_objs; ++ struct yaffs_obj_list *list; ++ ++ if (!allocator) { ++ YBUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_obj < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); ++ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); ++ ++ if (!new_objs || !list) { ++ if (new_objs) { ++ kfree(new_objs); ++ new_objs = NULL; ++ } ++ if (list) { ++ kfree(list); ++ list = NULL; ++ } ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Could not allocate more objects"); ++ return YAFFS_FAIL; ++ } ++ ++ /* Hook them into the free list */ ++ for (i = 0; i < n_obj - 1; i++) { ++ new_objs[i].siblings.next = ++ (struct list_head *)(&new_objs[i + 1]); ++ } ++ ++ new_objs[n_obj - 1].siblings.next = (void *)allocator->free_objs; ++ allocator->free_objs = new_objs; ++ allocator->n_free_objects += n_obj; ++ allocator->n_obj_created += n_obj; ++ ++ /* Now add this bunch of Objects to a list for freeing up. */ ++ ++ list->objects = new_objs; ++ list->next = allocator->allocated_obj_list; ++ allocator->allocated_obj_list = list; ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ YBUG(); ++ return obj; ++ } ++ ++ /* If there are none left make more */ ++ if (!allocator->free_objs) ++ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); ++ ++ if (allocator->free_objs) { ++ obj = allocator->free_objs; ++ allocator->free_objs = ++ (struct yaffs_obj *)(allocator->free_objs->siblings.next); ++ allocator->n_free_objects--; ++ } ++ ++ return obj; ++} ++ ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) ++ YBUG(); ++ else { ++ /* Link into the free list. */ ++ obj->siblings.next = (struct list_head *)(allocator->free_objs); ++ allocator->free_objs = obj; ++ allocator->n_free_objects++; ++ } ++} ++ ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ if (dev->allocator) { ++ yaffs_deinit_raw_tnodes(dev); ++ yaffs_deinit_raw_objs(dev); ++ ++ kfree(dev->allocator); ++ dev->allocator = NULL; ++ } else { ++ YBUG(); ++ } ++} ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator; ++ ++ if (!dev->allocator) { ++ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); ++ if (allocator) { ++ dev->allocator = allocator; ++ yaffs_init_raw_tnodes(dev); ++ yaffs_init_raw_objs(dev); ++ } ++ } else { ++ YBUG(); ++ } ++} ++ ++#endif +diff --git a/fs/yaffs2/yaffs_allocator.h b/fs/yaffs2/yaffs_allocator.h +new file mode 100644 +index 00000000..4d5f2aec +--- /dev/null ++++ b/fs/yaffs2/yaffs_allocator.h +@@ -0,0 +1,30 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ALLOCATOR_H__ ++#define __YAFFS_ALLOCATOR_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_attribs.c b/fs/yaffs2/yaffs_attribs.c +new file mode 100644 +index 00000000..9b47d376 +--- /dev/null ++++ b/fs/yaffs2/yaffs_attribs.c +@@ -0,0 +1,124 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) ++{ ++ obj->yst_uid = oh->yst_uid; ++ obj->yst_gid = oh->yst_gid; ++ obj->yst_atime = oh->yst_atime; ++ obj->yst_mtime = oh->yst_mtime; ++ obj->yst_ctime = oh->yst_ctime; ++ obj->yst_rdev = oh->yst_rdev; ++} ++ ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) ++{ ++ oh->yst_uid = obj->yst_uid; ++ oh->yst_gid = obj->yst_gid; ++ oh->yst_atime = obj->yst_atime; ++ oh->yst_mtime = obj->yst_mtime; ++ oh->yst_ctime = obj->yst_ctime; ++ oh->yst_rdev = obj->yst_rdev; ++ ++} ++ ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) ++{ ++ obj->yst_mtime = Y_CURRENT_TIME; ++ if (do_a) ++ obj->yst_atime = obj->yst_mtime; ++ if (do_c) ++ obj->yst_ctime = obj->yst_mtime; ++} ++ ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) ++{ ++ yaffs_load_current_time(obj, 1, 1); ++ obj->yst_rdev = rdev; ++ obj->yst_uid = uid; ++ obj->yst_gid = gid; ++} ++ ++loff_t yaffs_get_file_size(struct yaffs_obj *obj) ++{ ++ YCHAR *alias = NULL; ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return obj->variant.file_variant.file_size; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = obj->variant.symlink_variant.alias; ++ if (!alias) ++ return 0; ++ return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); ++ default: ++ return 0; ++ } ++} ++ ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = attr->ia_valid; ++ ++ if (valid & ATTR_MODE) ++ obj->yst_mode = attr->ia_mode; ++ if (valid & ATTR_UID) ++ obj->yst_uid = attr->ia_uid; ++ if (valid & ATTR_GID) ++ obj->yst_gid = attr->ia_gid; ++ ++ if (valid & ATTR_ATIME) ++ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); ++ if (valid & ATTR_CTIME) ++ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); ++ if (valid & ATTR_MTIME) ++ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); ++ ++ if (valid & ATTR_SIZE) ++ yaffs_resize_file(obj, attr->ia_size); ++ ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ return YAFFS_OK; ++ ++} ++ ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = 0; ++ ++ attr->ia_mode = obj->yst_mode; ++ valid |= ATTR_MODE; ++ attr->ia_uid = obj->yst_uid; ++ valid |= ATTR_UID; ++ attr->ia_gid = obj->yst_gid; ++ valid |= ATTR_GID; ++ ++ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; ++ valid |= ATTR_ATIME; ++ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; ++ valid |= ATTR_CTIME; ++ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; ++ valid |= ATTR_MTIME; ++ ++ attr->ia_size = yaffs_get_file_size(obj); ++ valid |= ATTR_SIZE; ++ ++ attr->ia_valid = valid; ++ ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_attribs.h b/fs/yaffs2/yaffs_attribs.h +new file mode 100644 +index 00000000..33d541d6 +--- /dev/null ++++ b/fs/yaffs2/yaffs_attribs.h +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ATTRIBS_H__ ++#define __YAFFS_ATTRIBS_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_bitmap.c b/fs/yaffs2/yaffs_bitmap.c +new file mode 100644 +index 00000000..7df42cd0 +--- /dev/null ++++ b/fs/yaffs2/yaffs_bitmap.c +@@ -0,0 +1,98 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_bitmap.h" ++#include "yaffs_trace.h" ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "BlockBits block %d is not valid", ++ blk); ++ YBUG(); ++ } ++ return dev->chunk_bits + ++ (dev->chunk_bit_stride * (blk - dev->internal_start_block)); ++} ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block || ++ chunk < 0 || chunk >= dev->param.chunks_per_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Chunk Id (%d:%d) invalid", ++ blk, chunk); ++ YBUG(); ++ } ++} ++ ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ memset(blk_bits, 0, dev->chunk_bit_stride); ++} ++ ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ ++ blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); ++} ++ ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ ++ blk_bits[chunk / 8] |= (1 << (chunk & 7)); ++} ++ ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ ++ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; ++} ++ ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ for (i = 0; i < dev->chunk_bit_stride; i++) { ++ if (*blk_bits) ++ return 1; ++ blk_bits++; ++ } ++ return 0; ++} ++ ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ int n = 0; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) ++ n += hweight8(*blk_bits); ++ ++ return n; ++} +diff --git a/fs/yaffs2/yaffs_bitmap.h b/fs/yaffs2/yaffs_bitmap.h +new file mode 100644 +index 00000000..cf9ea58d +--- /dev/null ++++ b/fs/yaffs2/yaffs_bitmap.h +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++#ifndef __YAFFS_BITMAP_H__ ++#define __YAFFS_BITMAP_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_checkptrw.c b/fs/yaffs2/yaffs_checkptrw.c +new file mode 100644 +index 00000000..4e40f437 +--- /dev/null ++++ b/fs/yaffs2/yaffs_checkptrw.c +@@ -0,0 +1,415 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_checkptrw.h" ++#include "yaffs_getblockinfo.h" ++ ++static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) ++{ ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpt blocks_avail = %d", blocks_avail); ++ ++ return (blocks_avail <= 0) ? 0 : 1; ++} ++ ++static int yaffs_checkpt_erase(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (!dev->param.erase_fn) ++ return 0; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checking blocks %d to %d", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "erasing checkpt block %d", i); ++ ++ dev->n_erasures++; ++ ++ if (dev->param. ++ erase_fn(dev, ++ i - dev->block_offset /* realign */ )) { ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += ++ dev->param.chunks_per_block; ++ } else { ++ dev->param.bad_block_fn(dev, i); ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ } ++ } ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ return 1; ++} ++ ++static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) ++{ ++ int i; ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block: erased %d reserved %d avail %d next %d ", ++ dev->n_erased_blocks, dev->param.n_reserved_blocks, ++ blocks_avail, dev->checkpt_next_block); ++ ++ if (dev->checkpt_next_block >= 0 && ++ dev->checkpt_next_block <= dev->internal_end_block && ++ blocks_avail > 0) { ++ ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ struct yaffs_block_info *bi = ++ yaffs_get_block_info(dev, i); ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ dev->checkpt_next_block = i + 1; ++ dev->checkpt_cur_block = i; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block %d", i); ++ return; ++ } ++ } ++ } ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_ext_tags tags; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: start: blocks %d next %d", ++ dev->blocks_in_checkpt, dev->checkpt_next_block); ++ ++ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ int chunk = i * dev->param.chunks_per_block; ++ int realigned_chunk = chunk - dev->chunk_offset; ++ ++ dev->param.read_chunk_tags_fn(dev, realigned_chunk, ++ NULL, &tags); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: search: block %d oid %d seq %d eccr %d", ++ i, tags.obj_id, tags.seq_number, ++ tags.ecc_result); ++ ++ if (tags.seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) { ++ /* Right kind of block */ ++ dev->checkpt_next_block = tags.obj_id; ++ dev->checkpt_cur_block = i; ++ dev->checkpt_block_list[dev-> ++ blocks_in_checkpt] = i; ++ dev->blocks_in_checkpt++; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "found checkpt block %d", i); ++ return; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) ++{ ++ ++ dev->checkpt_open_write = writing; ++ ++ /* Got the functions we need? */ ++ if (!dev->param.write_chunk_tags_fn || ++ !dev->param.read_chunk_tags_fn || ++ !dev->param.erase_fn || !dev->param.bad_block_fn) ++ return 0; ++ ++ if (writing && !yaffs2_checkpt_space_ok(dev)) ++ return 0; ++ ++ if (!dev->checkpt_buffer) ++ dev->checkpt_buffer = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ dev->checkpt_page_seq = 0; ++ dev->checkpt_byte_count = 0; ++ dev->checkpt_sum = 0; ++ dev->checkpt_xor = 0; ++ dev->checkpt_cur_block = -1; ++ dev->checkpt_cur_chunk = -1; ++ dev->checkpt_next_block = dev->internal_start_block; ++ ++ /* Erase all the blocks in the checkpoint area */ ++ if (writing) { ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ dev->checkpt_byte_offs = 0; ++ return yaffs_checkpt_erase(dev); ++ } else { ++ int i; ++ /* Set to a value that will kick off a read */ ++ dev->checkpt_byte_offs = dev->data_bytes_per_chunk; ++ /* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully) ++ * going to be way more than we need */ ++ dev->blocks_in_checkpt = 0; ++ dev->checkpt_max_blocks = ++ (dev->internal_end_block - dev->internal_start_block) / 16 + ++ 2; ++ dev->checkpt_block_list = ++ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); ++ if (!dev->checkpt_block_list) ++ return 0; ++ ++ for (i = 0; i < dev->checkpt_max_blocks; i++) ++ dev->checkpt_block_list[i] = -1; ++ } ++ ++ return 1; ++} ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) ++{ ++ u32 composite_sum; ++ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xFF); ++ *sum = composite_sum; ++ return 1; ++} ++ ++static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) ++{ ++ int chunk; ++ int realigned_chunk; ++ ++ struct yaffs_ext_tags tags; ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_erased_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ if (dev->checkpt_cur_block < 0) ++ return 0; ++ ++ tags.is_deleted = 0; ++ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ ++ tags.chunk_id = dev->checkpt_page_seq + 1; ++ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; ++ tags.n_bytes = dev->data_bytes_per_chunk; ++ if (dev->checkpt_cur_chunk == 0) { ++ /* First chunk we write for the block? Set block state to ++ checkpoint */ ++ struct yaffs_block_info *bi = ++ yaffs_get_block_info(dev, dev->checkpt_cur_block); ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ dev->blocks_in_checkpt++; ++ } ++ ++ chunk = ++ dev->checkpt_cur_block * dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", ++ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, ++ tags.obj_id, tags.chunk_id); ++ ++ realigned_chunk = chunk - dev->chunk_offset; ++ ++ dev->n_page_writes++; ++ ++ dev->param.write_chunk_tags_fn(dev, realigned_chunk, ++ dev->checkpt_buffer, &tags); ++ dev->checkpt_byte_offs = 0; ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { ++ dev->checkpt_cur_chunk = 0; ++ dev->checkpt_cur_block = -1; ++ } ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ ++ return 1; ++} ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) ++{ ++ int i = 0; ++ int ok = 1; ++ ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (!dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes && ok) { ++ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) ++ ok = yaffs2_checkpt_flush_buffer(dev); ++ } ++ ++ return i; ++} ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) ++{ ++ int i = 0; ++ int ok = 1; ++ struct yaffs_ext_tags tags; ++ ++ int chunk; ++ int realigned_chunk; ++ ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes && ok) { ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ if (dev->checkpt_cur_block < 0) ++ ok = 0; ++ else { ++ chunk = dev->checkpt_cur_block * ++ dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ realigned_chunk = chunk - dev->chunk_offset; ++ ++ dev->n_page_reads++; ++ ++ /* read in the next chunk */ ++ dev->param.read_chunk_tags_fn(dev, ++ realigned_chunk, ++ dev-> ++ checkpt_buffer, ++ &tags); ++ ++ if (tags.chunk_id != (dev->checkpt_page_seq + 1) ++ || tags.ecc_result > YAFFS_ECC_RESULT_FIXED ++ || tags.seq_number != ++ YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ ok = 0; ++ ++ dev->checkpt_byte_offs = 0; ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ ++ if (dev->checkpt_cur_chunk >= ++ dev->param.chunks_per_block) ++ dev->checkpt_cur_block = -1; ++ } ++ } ++ ++ if (ok) { ++ *data_bytes = ++ dev->checkpt_buffer[dev->checkpt_byte_offs]; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ } ++ } ++ ++ return i; ++} ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev) ++{ ++ ++ if (dev->checkpt_open_write) { ++ if (dev->checkpt_byte_offs != 0) ++ yaffs2_checkpt_flush_buffer(dev); ++ } else if (dev->checkpt_block_list) { ++ int i; ++ for (i = 0; ++ i < dev->blocks_in_checkpt ++ && dev->checkpt_block_list[i] >= 0; i++) { ++ int blk = dev->checkpt_block_list[i]; ++ struct yaffs_block_info *bi = NULL; ++ if (dev->internal_start_block <= blk ++ && blk <= dev->internal_end_block) ++ bi = yaffs_get_block_info(dev, blk); ++ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ else { ++ /* Todo this looks odd... */ ++ } ++ } ++ kfree(dev->checkpt_block_list); ++ dev->checkpt_block_list = NULL; ++ } ++ ++ dev->n_free_chunks -= ++ dev->blocks_in_checkpt * dev->param.chunks_per_block; ++ dev->n_erased_blocks -= dev->blocks_in_checkpt; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT,"checkpoint byte count %d", ++ dev->checkpt_byte_count); ++ ++ if (dev->checkpt_buffer) { ++ /* free the buffer */ ++ kfree(dev->checkpt_buffer); ++ dev->checkpt_buffer = NULL; ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) ++{ ++ /* Erase the checkpoint data */ ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint invalidate of %d blocks", ++ dev->blocks_in_checkpt); ++ ++ return yaffs_checkpt_erase(dev); ++} +diff --git a/fs/yaffs2/yaffs_checkptrw.h b/fs/yaffs2/yaffs_checkptrw.h +new file mode 100644 +index 00000000..361c6067 +--- /dev/null ++++ b/fs/yaffs2/yaffs_checkptrw.h +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_CHECKPTRW_H__ ++#define __YAFFS_CHECKPTRW_H__ ++ ++#include "yaffs_guts.h" ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev); ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_ecc.c b/fs/yaffs2/yaffs_ecc.c +new file mode 100644 +index 00000000..e95a8069 +--- /dev/null ++++ b/fs/yaffs2/yaffs_ecc.c +@@ -0,0 +1,298 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC ++ * blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++/* Table generated by gen-ecc.c ++ * Using a table means we do not have to calculate p1..p4 and p1'..p4' ++ * for each byte of data. These are instead provided in a table in bits7..2. ++ * Bit 0 of each entry indicates whether the entry has an odd or even parity, and therefore ++ * this bytes influence on the line parity. ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_ecc.h" ++ ++static const unsigned char column_parity_table[] = { ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++}; ++ ++ ++/* Calculate the ECC for a 256-byte block of data */ ++void yaffs_ecc_cacl(const unsigned char *data, unsigned char *ecc) ++{ ++ unsigned int i; ++ ++ unsigned char col_parity = 0; ++ unsigned char line_parity = 0; ++ unsigned char line_parity_prime = 0; ++ unsigned char t; ++ unsigned char b; ++ ++ for (i = 0; i < 256; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ } ++ ++ ecc[2] = (~col_parity) | 0x03; ++ ++ t = 0; ++ if (line_parity & 0x80) ++ t |= 0x80; ++ if (line_parity_prime & 0x80) ++ t |= 0x40; ++ if (line_parity & 0x40) ++ t |= 0x20; ++ if (line_parity_prime & 0x40) ++ t |= 0x10; ++ if (line_parity & 0x20) ++ t |= 0x08; ++ if (line_parity_prime & 0x20) ++ t |= 0x04; ++ if (line_parity & 0x10) ++ t |= 0x02; ++ if (line_parity_prime & 0x10) ++ t |= 0x01; ++ ecc[1] = ~t; ++ ++ t = 0; ++ if (line_parity & 0x08) ++ t |= 0x80; ++ if (line_parity_prime & 0x08) ++ t |= 0x40; ++ if (line_parity & 0x04) ++ t |= 0x20; ++ if (line_parity_prime & 0x04) ++ t |= 0x10; ++ if (line_parity & 0x02) ++ t |= 0x08; ++ if (line_parity_prime & 0x02) ++ t |= 0x04; ++ if (line_parity & 0x01) ++ t |= 0x02; ++ if (line_parity_prime & 0x01) ++ t |= 0x01; ++ ecc[0] = ~t; ++ ++#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER ++ /* Swap the bytes into the wrong order */ ++ t = ecc[0]; ++ ecc[0] = ecc[1]; ++ ecc[1] = t; ++#endif ++} ++ ++/* Correct the ECC on a 256 byte block of data */ ++ ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc) ++{ ++ unsigned char d0, d1, d2; /* deltas */ ++ ++ d0 = read_ecc[0] ^ test_ecc[0]; ++ d1 = read_ecc[1] ^ test_ecc[1]; ++ d2 = read_ecc[2] ^ test_ecc[2]; ++ ++ if ((d0 | d1 | d2) == 0) ++ return 0; /* no error */ ++ ++ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && ++ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && ++ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { ++ /* Single bit (recoverable) error in data */ ++ ++ unsigned byte; ++ unsigned bit; ++ ++#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER ++ /* swap the bytes to correct for the wrong order */ ++ unsigned char t; ++ ++ t = d0; ++ d0 = d1; ++ d1 = t; ++#endif ++ ++ bit = byte = 0; ++ ++ if (d1 & 0x80) ++ byte |= 0x80; ++ if (d1 & 0x20) ++ byte |= 0x40; ++ if (d1 & 0x08) ++ byte |= 0x20; ++ if (d1 & 0x02) ++ byte |= 0x10; ++ if (d0 & 0x80) ++ byte |= 0x08; ++ if (d0 & 0x20) ++ byte |= 0x04; ++ if (d0 & 0x08) ++ byte |= 0x02; ++ if (d0 & 0x02) ++ byte |= 0x01; ++ ++ if (d2 & 0x80) ++ bit |= 0x04; ++ if (d2 & 0x20) ++ bit |= 0x02; ++ if (d2 & 0x08) ++ bit |= 0x01; ++ ++ data[byte] ^= (1 << bit); ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ read_ecc[0] = test_ecc[0]; ++ read_ecc[1] = test_ecc[1]; ++ read_ecc[2] = test_ecc[2]; ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++ ++} ++ ++/* ++ * ECCxxxOther does ECC calcs on arbitrary n bytes of data ++ */ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc_other) ++{ ++ unsigned int i; ++ ++ unsigned char col_parity = 0; ++ unsigned line_parity = 0; ++ unsigned line_parity_prime = 0; ++ unsigned char b; ++ ++ for (i = 0; i < n_bytes; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { ++ /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ ++ } ++ ++ ecc_other->col_parity = (col_parity >> 2) & 0x3f; ++ ecc_other->line_parity = line_parity; ++ ecc_other->line_parity_prime = line_parity_prime; ++} ++ ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc) ++{ ++ unsigned char delta_col; /* column parity delta */ ++ unsigned delta_line; /* line parity delta */ ++ unsigned delta_line_prime; /* line parity delta */ ++ unsigned bit; ++ ++ delta_col = read_ecc->col_parity ^ test_ecc->col_parity; ++ delta_line = read_ecc->line_parity ^ test_ecc->line_parity; ++ delta_line_prime = ++ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; ++ ++ if ((delta_col | delta_line | delta_line_prime) == 0) ++ return 0; /* no error */ ++ ++ if (delta_line == ~delta_line_prime && ++ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { ++ /* Single bit (recoverable) error in data */ ++ ++ bit = 0; ++ ++ if (delta_col & 0x20) ++ bit |= 0x04; ++ if (delta_col & 0x08) ++ bit |= 0x02; ++ if (delta_col & 0x02) ++ bit |= 0x01; ++ ++ if (delta_line >= n_bytes) ++ return -1; ++ ++ data[delta_line] ^= (1 << bit); ++ ++ return 1; /* corrected */ ++ } ++ ++ if ((hweight32(delta_line) + ++ hweight32(delta_line_prime) + ++ hweight8(delta_col)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ *read_ecc = *test_ecc; ++ return 1; /* corrected */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++} +diff --git a/fs/yaffs2/yaffs_ecc.h b/fs/yaffs2/yaffs_ecc.h +new file mode 100644 +index 00000000..b0c461d6 +--- /dev/null ++++ b/fs/yaffs2/yaffs_ecc.h +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC ++ * blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#ifndef __YAFFS_ECC_H__ ++#define __YAFFS_ECC_H__ ++ ++struct yaffs_ecc_other { ++ unsigned char col_parity; ++ unsigned line_parity; ++ unsigned line_parity_prime; ++}; ++ ++void yaffs_ecc_cacl(const unsigned char *data, unsigned char *ecc); ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc); ++ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc); ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc); ++#endif +diff --git a/fs/yaffs2/yaffs_getblockinfo.h b/fs/yaffs2/yaffs_getblockinfo.h +new file mode 100644 +index 00000000..d87acbde +--- /dev/null ++++ b/fs/yaffs2/yaffs_getblockinfo.h +@@ -0,0 +1,35 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GETBLOCKINFO_H__ ++#define __YAFFS_GETBLOCKINFO_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++ ++/* Function to manipulate block info */ ++static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev ++ *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs: get_block_info block %d is not valid", ++ blk); ++ YBUG(); ++ } ++ return &dev->block_info[blk - dev->internal_start_block]; ++} ++ ++#endif +diff --git a/fs/yaffs2/yaffs_guts.c b/fs/yaffs2/yaffs_guts.c +new file mode 100644 +index 00000000..f4ae9dee +--- /dev/null ++++ b/fs/yaffs2/yaffs_guts.c +@@ -0,0 +1,5164 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++#include "yaffs_guts.h" ++#include "yaffs_tagsvalidity.h" ++#include "yaffs_getblockinfo.h" ++ ++#include "yaffs_tagscompat.h" ++ ++#include "yaffs_nand.h" ++ ++#include "yaffs_yaffs1.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_verify.h" ++ ++#include "yaffs_nand.h" ++#include "yaffs_packedtags2.h" ++ ++#include "yaffs_nameval.h" ++#include "yaffs_allocator.h" ++ ++#include "yaffs_attribs.h" ++ ++/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ ++#define YAFFS_GC_GOOD_ENOUGH 2 ++#define YAFFS_GC_PASSIVE_THRESHOLD 4 ++ ++#include "yaffs_ecc.h" ++ ++/* Forward declarations */ ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 * buffer, int n_bytes, int use_reserve); ++ ++ ++ ++/* Function to calculate chunk and offset */ ++ ++static void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 * offset_out) ++{ ++ int chunk; ++ u32 offset; ++ ++ chunk = (u32) (addr >> dev->chunk_shift); ++ ++ if (dev->chunk_div == 1) { ++ /* easy power of 2 case */ ++ offset = (u32) (addr & dev->chunk_mask); ++ } else { ++ /* Non power-of-2 case */ ++ ++ loff_t chunk_base; ++ ++ chunk /= dev->chunk_div; ++ ++ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; ++ offset = (u32) (addr - chunk_base); ++ } ++ ++ *chunk_out = chunk; ++ *offset_out = offset; ++} ++ ++/* Function to return the number of shifts for a power of 2 greater than or ++ * equal to the given number ++ * Note we don't try to cater for all possible numbers and this does not have to ++ * be hellishly efficient. ++ */ ++ ++static u32 calc_shifts_ceiling(u32 x) ++{ ++ int extra_bits; ++ int shifts; ++ ++ shifts = extra_bits = 0; ++ ++ while (x > 1) { ++ if (x & 1) ++ extra_bits++; ++ x >>= 1; ++ shifts++; ++ } ++ ++ if (extra_bits) ++ shifts++; ++ ++ return shifts; ++} ++ ++/* Function to return the number of shifts to get a 1 in bit 0 ++ */ ++ ++static u32 calc_shifts(u32 x) ++{ ++ u32 shifts; ++ ++ shifts = 0; ++ ++ if (!x) ++ return 0; ++ ++ while (!(x & 1)) { ++ x >>= 1; ++ shifts++; ++ } ++ ++ return shifts; ++} ++ ++/* ++ * Temporary buffer manipulations. ++ */ ++ ++static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) ++{ ++ int i; ++ u8 *buf = (u8 *) 1; ++ ++ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); ++ ++ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { ++ dev->temp_buffer[i].line = 0; /* not in use */ ++ dev->temp_buffer[i].buffer = buf = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ } ++ ++ return buf ? YAFFS_OK : YAFFS_FAIL; ++} ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev, int line_no) ++{ ++ int i, j; ++ ++ dev->temp_in_use++; ++ if (dev->temp_in_use > dev->max_temp) ++ dev->max_temp = dev->temp_in_use; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].line == 0) { ++ dev->temp_buffer[i].line = line_no; ++ if ((i + 1) > dev->max_temp) { ++ dev->max_temp = i + 1; ++ for (j = 0; j <= i; j++) ++ dev->temp_buffer[j].max_line = ++ dev->temp_buffer[j].line; ++ } ++ ++ return dev->temp_buffer[i].buffer; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, ++ "Out of temp buffers at line %d, other held by lines:", ++ line_no); ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) ++ yaffs_trace(YAFFS_TRACE_BUFFERS," %d", dev->temp_buffer[i].line); ++ ++ /* ++ * If we got here then we have to allocate an unmanaged one ++ * This is not good. ++ */ ++ ++ dev->unmanaged_buffer_allocs++; ++ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); ++ ++} ++ ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 * buffer, int line_no) ++{ ++ int i; ++ ++ dev->temp_in_use--; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].buffer == buffer) { ++ dev->temp_buffer[i].line = 0; ++ return; ++ } ++ } ++ ++ if (buffer) { ++ /* assume it is an unmanaged one. */ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, ++ "Releasing unmanaged temp buffer in line %d", ++ line_no); ++ kfree(buffer); ++ dev->unmanaged_buffer_deallocs++; ++ } ++ ++} ++ ++/* ++ * Determine if we have a managed buffer. ++ */ ++int yaffs_is_managed_tmp_buffer(struct yaffs_dev *dev, const u8 * buffer) ++{ ++ int i; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].buffer == buffer) ++ return 1; ++ } ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].data == buffer) ++ return 1; ++ } ++ ++ if (buffer == dev->checkpt_buffer) ++ return 1; ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: unmaged buffer detected."); ++ return 0; ++} ++ ++/* ++ * Functions for robustisizing TODO ++ * ++ */ ++ ++static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, ++ const u8 * data, ++ const struct yaffs_ext_tags *tags) ++{ ++ dev = dev; ++ nand_chunk = nand_chunk; ++ data = data; ++ tags = tags; ++} ++ ++static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, ++ const struct yaffs_ext_tags *tags) ++{ ++ dev = dev; ++ nand_chunk = nand_chunk; ++ tags = tags; ++} ++ ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ if (!bi->gc_prioritise) { ++ bi->gc_prioritise = 1; ++ dev->has_pending_prioritised_gc = 1; ++ bi->chunk_error_strikes++; ++ ++ if (bi->chunk_error_strikes > 3) { ++ bi->needs_retiring = 1; /* Too many stikes, so retire this */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "yaffs: Block struck out"); ++ ++ } ++ } ++} ++ ++static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, ++ int erased_ok) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs_handle_chunk_error(dev, bi); ++ ++ if (erased_ok) { ++ /* Was an actual write failure, so mark the block for retirement */ ++ bi->needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d needs retiring", flash_block); ++ } ++ ++ /* Delete the chunk */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++} ++ ++/* ++ * Verification code ++ */ ++ ++/* ++ * Simple hash function. Needs to have a reasonable spread ++ */ ++ ++static inline int yaffs_hash_fn(int n) ++{ ++ n = abs(n); ++ return n % YAFFS_NOBJECT_BUCKETS; ++} ++ ++/* ++ * Access functions to useful fake objects. ++ * Note that root might have a presence in NAND if permissions are set. ++ */ ++ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) ++{ ++ return dev->root_dir; ++} ++ ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) ++{ ++ return dev->lost_n_found; ++} ++ ++/* ++ * Erased NAND checking functions ++ */ ++ ++int yaffs_check_ff(u8 * buffer, int n_bytes) ++{ ++ /* Horrible, slow implementation */ ++ while (n_bytes--) { ++ if (*buffer != 0xFF) ++ return 0; ++ buffer++; ++ } ++ return 1; ++} ++ ++static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int retval = YAFFS_OK; ++ u8 *data = yaffs_get_temp_buffer(dev, __LINE__); ++ struct yaffs_ext_tags tags; ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); ++ ++ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) ++ retval = YAFFS_FAIL; ++ ++ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || ++ tags.chunk_used) { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, "Chunk %d not erased", nand_chunk); ++ retval = YAFFS_FAIL; ++ } ++ ++ yaffs_release_temp_buffer(dev, data, __LINE__); ++ ++ return retval; ++ ++} ++ ++static int yaffs_verify_chunk_written(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 * data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = YAFFS_OK; ++ struct yaffs_ext_tags temp_tags; ++ u8 *buffer = yaffs_get_temp_buffer(dev, __LINE__); ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); ++ if (memcmp(buffer, data, dev->data_bytes_per_chunk) || ++ temp_tags.obj_id != tags->obj_id || ++ temp_tags.chunk_id != tags->chunk_id || ++ temp_tags.n_bytes != tags->n_bytes) ++ retval = YAFFS_FAIL; ++ ++ yaffs_release_temp_buffer(dev, buffer, __LINE__); ++ ++ return retval; ++} ++ ++ ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) ++{ ++ int reserved_chunks; ++ int reserved_blocks = dev->param.n_reserved_blocks; ++ int checkpt_blocks; ++ ++ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); ++ ++ reserved_chunks = ++ ((reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block); ++ ++ return (dev->n_free_chunks > (reserved_chunks + n_chunks)); ++} ++ ++static int yaffs_find_alloc_block(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ struct yaffs_block_info *bi; ++ ++ if (dev->n_erased_blocks < 1) { ++ /* Hoosterman we've got a problem. ++ * Can't get space to gc ++ */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: no more erased blocks" ); ++ ++ return -1; ++ } ++ ++ /* Find an empty block. */ ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ dev->alloc_block_finder++; ++ if (dev->alloc_block_finder < dev->internal_start_block ++ || dev->alloc_block_finder > dev->internal_end_block) { ++ dev->alloc_block_finder = dev->internal_start_block; ++ } ++ ++ bi = yaffs_get_block_info(dev, dev->alloc_block_finder); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->seq_number++; ++ bi->seq_number = dev->seq_number; ++ dev->n_erased_blocks--; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Allocated block %d, seq %d, %d left" , ++ dev->alloc_block_finder, dev->seq_number, ++ dev->n_erased_blocks); ++ return dev->alloc_block_finder; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs tragedy: no more erased blocks, but there should have been %d", ++ dev->n_erased_blocks); ++ ++ return -1; ++} ++ ++static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, ++ struct yaffs_block_info **block_ptr) ++{ ++ int ret_val; ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block < 0) { ++ /* Get next block to allocate off */ ++ dev->alloc_block = yaffs_find_alloc_block(dev); ++ dev->alloc_page = 0; ++ } ++ ++ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { ++ /* Not enough space to allocate unless we're allowed to use the reserve. */ ++ return -1; ++ } ++ ++ if (dev->n_erased_blocks < dev->param.n_reserved_blocks ++ && dev->alloc_page == 0) ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); ++ ++ /* Next page please.... */ ++ if (dev->alloc_block >= 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ ++ ret_val = (dev->alloc_block * dev->param.chunks_per_block) + ++ dev->alloc_page; ++ bi->pages_in_use++; ++ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); ++ ++ dev->alloc_page++; ++ ++ dev->n_free_chunks--; ++ ++ /* If the block is full set the state to full */ ++ if (dev->alloc_page >= dev->param.chunks_per_block) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ if (block_ptr) ++ *block_ptr = bi; ++ ++ return ret_val; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" ); ++ ++ return -1; ++} ++ ++static int yaffs_get_erased_chunks(struct yaffs_dev *dev) ++{ ++ int n; ++ ++ n = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ if (dev->alloc_block > 0) ++ n += (dev->param.chunks_per_block - dev->alloc_page); ++ ++ return n; ++ ++} ++ ++/* ++ * yaffs_skip_rest_of_block() skips over the rest of the allocation block ++ * if we don't want to write to it. ++ */ ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev) ++{ ++ if (dev->alloc_block > 0) { ++ struct yaffs_block_info *bi = ++ yaffs_get_block_info(dev, dev->alloc_block); ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ } ++} ++ ++static int yaffs_write_new_chunk(struct yaffs_dev *dev, ++ const u8 * data, ++ struct yaffs_ext_tags *tags, int use_reserver) ++{ ++ int attempts = 0; ++ int write_ok = 0; ++ int chunk; ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ do { ++ struct yaffs_block_info *bi = 0; ++ int erased_ok = 0; ++ ++ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); ++ if (chunk < 0) { ++ /* no space */ ++ break; ++ } ++ ++ /* First check this chunk is erased, if it needs ++ * checking. The checking policy (unless forced ++ * always on) is as follows: ++ * ++ * Check the first page we try to write in a block. ++ * If the check passes then we don't need to check any ++ * more. If the check fails, we check again... ++ * If the block has been erased, we don't need to check. ++ * ++ * However, if the block has been prioritised for gc, ++ * then we think there might be something odd about ++ * this block and stop using it. ++ * ++ * Rationale: We should only ever see chunks that have ++ * not been erased if there was a partially written ++ * chunk due to power loss. This checking policy should ++ * catch that case with very few checks and thus save a ++ * lot of checks that are most likely not needed. ++ * ++ * Mods to the above ++ * If an erase check fails or the write fails we skip the ++ * rest of the block. ++ */ ++ ++ /* let's give it a try */ ++ attempts++; ++ ++ if (dev->param.always_check_erased) ++ bi->skip_erased_check = 0; ++ ++ if (!bi->skip_erased_check) { ++ erased_ok = yaffs_check_chunk_erased(dev, chunk); ++ if (erased_ok != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs chunk %d was not erased", ++ chunk); ++ ++ /* If not erased, delete this one, ++ * skip rest of block and ++ * try another chunk */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++ continue; ++ } ++ } ++ ++ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); ++ ++ if (!bi->skip_erased_check) ++ write_ok = ++ yaffs_verify_chunk_written(dev, chunk, data, tags); ++ ++ if (write_ok != YAFFS_OK) { ++ /* Clean up aborted write, skip to next block and ++ * try another chunk */ ++ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); ++ continue; ++ } ++ ++ bi->skip_erased_check = 1; ++ ++ /* Copy the data into the robustification buffer */ ++ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); ++ ++ } while (write_ok != YAFFS_OK && ++ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); ++ ++ if (!write_ok) ++ chunk = -1; ++ ++ if (attempts > 1) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs write required %d attempts", ++ attempts); ++ dev->n_retired_writes += (attempts - 1); ++ } ++ ++ return chunk; ++} ++ ++/* ++ * Block retiring for handling a broken block. ++ */ ++ ++static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { ++ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to mark bad and erase block %d", ++ flash_block); ++ } else { ++ struct yaffs_ext_tags tags; ++ int chunk_id = ++ flash_block * dev->param.chunks_per_block; ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev, __LINE__); ++ ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ yaffs_init_tags(&tags); ++ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; ++ if (dev->param.write_chunk_tags_fn(dev, chunk_id - ++ dev->chunk_offset, ++ buffer, ++ &tags) != YAFFS_OK) ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to write bad block marker to block %d", ++ flash_block); ++ ++ yaffs_release_temp_buffer(dev, buffer, __LINE__); ++ } ++ } ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ bi->gc_prioritise = 0; ++ bi->needs_retiring = 0; ++ ++ dev->n_retired_blocks++; ++} ++ ++/*---------------- Name handling functions ------------*/ ++ ++static u16 yaffs_calc_name_sum(const YCHAR * name) ++{ ++ u16 sum = 0; ++ u16 i = 1; ++ ++ const YUCHAR *bname = (const YUCHAR *)name; ++ if (bname) { ++ while ((*bname) && (i < (YAFFS_MAX_NAME_LENGTH / 2))) { ++ ++ /* 0x1f mask is case insensitive */ ++ sum += ((*bname) & 0x1f) * i; ++ i++; ++ bname++; ++ } ++ } ++ return sum; ++} ++ ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) ++{ ++#ifndef CONFIG_YAFFS_NO_SHORT_NAMES ++ memset(obj->short_name, 0, sizeof(obj->short_name)); ++ if (name && ++ strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= ++ YAFFS_SHORT_NAME_LENGTH) ++ strcpy(obj->short_name, name); ++ else ++ obj->short_name[0] = _Y('\0'); ++#endif ++ obj->sum = yaffs_calc_name_sum(name); ++} ++ ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ memset(tmp_name, 0, sizeof(tmp_name)); ++ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_set_obj_name(obj, tmp_name); ++#else ++ yaffs_set_obj_name(obj, oh->name); ++#endif ++} ++ ++/*-------------------- TNODES ------------------- ++ ++ * List of spare tnodes ++ * The list is hooked together using the first pointer ++ * in the tnode. ++ */ ++ ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); ++ if (tn) { ++ memset(tn, 0, dev->tnode_size); ++ dev->n_tnodes++; ++ } ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ yaffs_free_raw_tnode(dev, tn); ++ dev->n_tnodes--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ yaffs_deinit_raw_tnodes_and_objs(dev); ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++} ++ ++void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos, unsigned val) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 mask; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ val >>= dev->chunk_grp_bits; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ mask = dev->tnode_mask << bit_in_word; ++ ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val << bit_in_word)); ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++;; ++ mask = ++ dev->tnode_mask >> ( /*dev->tnode_width - */ bit_in_word); ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val >> bit_in_word)); ++ } ++} ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 val; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ val = map[word_in_map] >> bit_in_word; ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++;; ++ val |= (map[word_in_map] << bit_in_word); ++ } ++ ++ val &= dev->tnode_mask; ++ val <<= dev->chunk_grp_bits; ++ ++ return val; ++} ++ ++/* ------------------- End of individual tnode manipulation -----------------*/ ++ ++/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ ++ * The look up tree is represented by the top tnode and the number of top_level ++ * in the tree. 0 means only the level 0 tnode is in the tree. ++ */ ++ ++/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id) ++{ ++ struct yaffs_tnode *tn = file_struct->top; ++ u32 i; ++ int required_depth; ++ int level = file_struct->top_level; ++ ++ dev = dev; ++ ++ /* Check sane level and chunk Id */ ++ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (i) { ++ i >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) ++ return NULL; /* Not tall enough, so we can't find it */ ++ ++ /* Traverse down to level 0 */ ++ while (level > 0 && tn) { ++ tn = tn->internal[(chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (level - 1) * ++ YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK]; ++ level--; ++ } ++ ++ return tn; ++} ++ ++/* AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree. ++ * This happens in two steps: ++ * 1. If the tree isn't tall enough, then make it taller. ++ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. ++ * ++ * Used when modifying the tree. ++ * ++ * If the tn argument is NULL, then a fresh tnode will be added otherwise the specified tn will ++ * be plugged into the ttree. ++ */ ++ ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn) ++{ ++ int required_depth; ++ int i; ++ int l; ++ struct yaffs_tnode *tn; ++ ++ u32 x; ++ ++ /* Check sane level and page Id */ ++ if (file_struct->top_level < 0 ++ || file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) { ++ /* Not tall enough, gotta make the tree taller */ ++ for (i = file_struct->top_level; i < required_depth; i++) { ++ ++ tn = yaffs_get_tnode(dev); ++ ++ if (tn) { ++ tn->internal[0] = file_struct->top; ++ file_struct->top = tn; ++ file_struct->top_level++; ++ } else { ++ yaffs_trace(YAFFS_TRACE_ERROR, "yaffs: no more tnodes"); ++ return NULL; ++ } ++ } ++ } ++ ++ /* Traverse down to level 0, adding anything we need */ ++ ++ l = file_struct->top_level; ++ tn = file_struct->top; ++ ++ if (l > 0) { ++ while (l > 0 && tn) { ++ x = (chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK; ++ ++ if ((l > 1) && !tn->internal[x]) { ++ /* Add missing non-level-zero tnode */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } else if (l == 1) { ++ /* Looking from level 1 at level 0 */ ++ if (passed_tn) { ++ /* If we already have one, then release it. */ ++ if (tn->internal[x]) ++ yaffs_free_tnode(dev, ++ tn-> ++ internal[x]); ++ tn->internal[x] = passed_tn; ++ ++ } else if (!tn->internal[x]) { ++ /* Don't have one, none passed in */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } ++ } ++ ++ tn = tn->internal[x]; ++ l--; ++ } ++ } else { ++ /* top is level 0 */ ++ if (passed_tn) { ++ memcpy(tn, passed_tn, ++ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); ++ yaffs_free_tnode(dev, passed_tn); ++ } ++ } ++ ++ return tn; ++} ++ ++static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, ++ int chunk_obj) ++{ ++ return (tags->chunk_id == chunk_obj && ++ tags->obj_id == obj_id && !tags->is_deleted) ? 1 : 0; ++ ++} ++ ++static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, ++ struct yaffs_ext_tags *tags, int obj_id, ++ int inode_chunk) ++{ ++ int j; ++ ++ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { ++ if (yaffs_check_chunk_bit ++ (dev, the_chunk / dev->param.chunks_per_block, ++ the_chunk % dev->param.chunks_per_block)) { ++ ++ if (dev->chunk_grp_size == 1) ++ return the_chunk; ++ else { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ tags); ++ if (yaffs_tags_match(tags, obj_id, inode_chunk)) { ++ /* found it; */ ++ return the_chunk; ++ } ++ } ++ } ++ the_chunk++; ++ } ++ return -1; ++} ++ ++static int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /*Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ int ret_val = -1; ++ ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (tn) { ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = ++ yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ } ++ return ret_val; ++} ++ ++static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /* Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ ++ struct yaffs_dev *dev = in->my_dev; ++ int ret_val = -1; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (tn) { ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = ++ yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ ++ /* Delete the entry in the filestructure (if found) */ ++ if (ret_val != -1) ++ yaffs_load_tnode_0(dev, tn, inode_chunk, 0); ++ } ++ ++ return ret_val; ++} ++ ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan) ++{ ++ /* NB in_scan is zero unless scanning. ++ * For forward scanning, in_scan is > 0; ++ * for backward scanning in_scan is < 0 ++ * ++ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. ++ */ ++ ++ struct yaffs_tnode *tn; ++ struct yaffs_dev *dev = in->my_dev; ++ int existing_cunk; ++ struct yaffs_ext_tags existing_tags; ++ struct yaffs_ext_tags new_tags; ++ unsigned existing_serial, new_serial; ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { ++ /* Just ignore an attempt at putting a chunk into a non-file during scanning ++ * If it is not during Scanning then something went wrong! ++ */ ++ if (!in_scan) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy:attempt to put data chunk into a non-file" ++ ); ++ YBUG(); ++ } ++ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ ++ tn = yaffs_add_find_tnode_0(dev, ++ &in->variant.file_variant, ++ inode_chunk, NULL); ++ if (!tn) ++ return YAFFS_FAIL; ++ ++ if (!nand_chunk) ++ /* Dummy insert, bail now */ ++ return YAFFS_OK; ++ ++ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ if (in_scan != 0) { ++ /* If we're scanning then we need to test for duplicates ++ * NB This does not need to be efficient since it should only ever ++ * happen when the power fails during a write, then only one ++ * chunk should ever be affected. ++ * ++ * Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO ++ * Update: For backward scanning we don't need to re-read tags so this is quite cheap. ++ */ ++ ++ if (existing_cunk > 0) { ++ /* NB Right now existing chunk will not be real chunk_id if the chunk group size > 1 ++ * thus we have to do a FindChunkInFile to get the real chunk id. ++ * ++ * We have a duplicate now we need to decide which one to use: ++ * ++ * Backwards scanning YAFFS2: The old one is what we use, dump the new one. ++ * Forward scanning YAFFS2: The new one is what we use, dump the old one. ++ * YAFFS1: Get both sets of tags and compare serial numbers. ++ */ ++ ++ if (in_scan > 0) { ++ /* Only do this for forward scanning */ ++ yaffs_rd_chunk_tags_nand(dev, ++ nand_chunk, ++ NULL, &new_tags); ++ ++ /* Do a proper find */ ++ existing_cunk = ++ yaffs_find_chunk_in_file(in, inode_chunk, ++ &existing_tags); ++ } ++ ++ if (existing_cunk <= 0) { ++ /*Hoosterman - how did this happen? */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: existing chunk < 0 in scan" ++ ); ++ ++ } ++ ++ /* NB The deleted flags should be false, otherwise the chunks will ++ * not be loaded during a scan ++ */ ++ ++ if (in_scan > 0) { ++ new_serial = new_tags.serial_number; ++ existing_serial = existing_tags.serial_number; ++ } ++ ++ if ((in_scan > 0) && ++ (existing_cunk <= 0 || ++ ((existing_serial + 1) & 3) == new_serial)) { ++ /* Forward scanning. ++ * Use new ++ * Delete the old one and drop through to update the tnode ++ */ ++ yaffs_chunk_del(dev, existing_cunk, 1, ++ __LINE__); ++ } else { ++ /* Backward scanning or we want to use the existing one ++ * Use existing. ++ * Delete the new one and return early so that the tnode isn't changed ++ */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ } ++ ++ } ++ ++ if (existing_cunk == 0) ++ in->n_data_chunks++; ++ ++ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); ++ ++ return YAFFS_OK; ++} ++ ++static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) ++{ ++ struct yaffs_block_info *the_block; ++ unsigned block_no; ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); ++ ++ block_no = chunk / dev->param.chunks_per_block; ++ the_block = yaffs_get_block_info(dev, block_no); ++ if (the_block) { ++ the_block->soft_del_pages++; ++ dev->n_free_chunks++; ++ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); ++ } ++} ++ ++/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file. ++ * All soft deleting does is increment the block's softdelete count and pulls the chunk out ++ * of the tnode. ++ * Thus, essentially this is the same as DeleteWorker except that the chunks are soft deleted. ++ */ ++ ++static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, ++ u32 level, int chunk_offset) ++{ ++ int i; ++ int the_chunk; ++ int all_done = 1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (tn) { ++ if (level > 0) { ++ ++ for (i = YAFFS_NTNODES_INTERNAL - 1; all_done && i >= 0; ++ i--) { ++ if (tn->internal[i]) { ++ all_done = ++ yaffs_soft_del_worker(in, ++ tn->internal ++ [i], ++ level - 1, ++ (chunk_offset ++ << ++ YAFFS_TNODES_INTERNAL_BITS) ++ + i); ++ if (all_done) { ++ yaffs_free_tnode(dev, ++ tn->internal ++ [i]); ++ tn->internal[i] = NULL; ++ } else { ++ /* Hoosterman... how could this happen? */ ++ } ++ } ++ } ++ return (all_done) ? 1 : 0; ++ } else if (level == 0) { ++ ++ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk) { ++ /* Note this does not find the real chunk, only the chunk group. ++ * We make an assumption that a chunk group is not larger than ++ * a block. ++ */ ++ yaffs_soft_del_chunk(dev, the_chunk); ++ yaffs_load_tnode_0(dev, tn, i, 0); ++ } ++ ++ } ++ return 1; ++ ++ } ++ ++ } ++ ++ return 1; ++ ++} ++ ++static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_obj *parent; ++ ++ yaffs_verify_obj_in_dir(obj); ++ parent = obj->parent; ++ ++ yaffs_verify_dir(parent); ++ ++ if (dev && dev->param.remove_obj_fn) ++ dev->param.remove_obj_fn(obj); ++ ++ list_del_init(&obj->siblings); ++ obj->parent = NULL; ++ ++ yaffs_verify_dir(parent); ++} ++ ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) ++{ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a null pointer directory" ++ ); ++ YBUG(); ++ return; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a non-directory" ++ ); ++ YBUG(); ++ } ++ ++ if (obj->siblings.prev == NULL) { ++ /* Not initialised */ ++ YBUG(); ++ } ++ ++ yaffs_verify_dir(directory); ++ ++ yaffs_remove_obj_from_dir(obj); ++ ++ /* Now add it */ ++ list_add(&obj->siblings, &directory->variant.dir_variant.children); ++ obj->parent = directory; ++ ++ if (directory == obj->my_dev->unlinked_dir ++ || directory == obj->my_dev->del_dir) { ++ obj->unlinked = 1; ++ obj->my_dev->n_unlinked_files++; ++ obj->rename_allowed = 0; ++ } ++ ++ yaffs_verify_dir(directory); ++ yaffs_verify_obj_in_dir(obj); ++} ++ ++static int yaffs_change_obj_name(struct yaffs_obj *obj, ++ struct yaffs_obj *new_dir, ++ const YCHAR * new_name, int force, int shadows) ++{ ++ int unlink_op; ++ int del_op; ++ ++ struct yaffs_obj *existing_target; ++ ++ if (new_dir == NULL) ++ new_dir = obj->parent; /* use the old directory */ ++ ++ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_change_obj_name: new_dir is not a directory" ++ ); ++ YBUG(); ++ } ++ ++ /* TODO: Do we need this different handling for YAFFS2 and YAFFS1?? */ ++ if (obj->my_dev->param.is_yaffs2) ++ unlink_op = (new_dir == obj->my_dev->unlinked_dir); ++ else ++ unlink_op = (new_dir == obj->my_dev->unlinked_dir ++ && obj->variant_type == YAFFS_OBJECT_TYPE_FILE); ++ ++ del_op = (new_dir == obj->my_dev->del_dir); ++ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ ++ /* If the object is a file going into the unlinked directory, ++ * then it is OK to just stuff it in since duplicate names are allowed. ++ * else only proceed if the new name does not exist and if we're putting ++ * it into a directory. ++ */ ++ if ((unlink_op || ++ del_op || ++ force || ++ (shadows > 0) || ++ !existing_target) && ++ new_dir->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_set_obj_name(obj, new_name); ++ obj->dirty = 1; ++ ++ yaffs_add_obj_to_dir(new_dir, obj); ++ ++ if (unlink_op) ++ obj->unlinked = 1; ++ ++ /* If it is a deletion then we mark it as a shrink for gc purposes. */ ++ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= ++ 0) ++ return YAFFS_OK; ++ } ++ ++ return YAFFS_FAIL; ++} ++ ++/*------------------------ Short Operations Cache ---------------------------------------- ++ * In many situations where there is no high level buffering a lot of ++ * reads might be short sequential reads, and a lot of writes may be short ++ * sequential writes. eg. scanning/writing a jpeg file. ++ * In these cases, a short read/write cache can provide a huge perfomance ++ * benefit with dumb-as-a-rock code. ++ * In Linux, the page cache provides read buffering and the short op cache ++ * provides write buffering. ++ * ++ * There are a limited number (~10) of cache chunks per device so that we don't ++ * need a very intelligent search. ++ */ ++ ++static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ struct yaffs_cache *cache; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ for (i = 0; i < n_caches; i++) { ++ cache = &dev->cache[i]; ++ if (cache->object == obj && cache->dirty) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static void yaffs_flush_file_cache(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int lowest = -99; /* Stop compiler whining. */ ++ int i; ++ struct yaffs_cache *cache; ++ int chunk_written = 0; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ if (n_caches > 0) { ++ do { ++ cache = NULL; ++ ++ /* Find the dirty cache for this object with the lowest chunk id. */ ++ for (i = 0; i < n_caches; i++) { ++ if (dev->cache[i].object == obj && ++ dev->cache[i].dirty) { ++ if (!cache ++ || dev->cache[i].chunk_id < ++ lowest) { ++ cache = &dev->cache[i]; ++ lowest = cache->chunk_id; ++ } ++ } ++ } ++ ++ if (cache && !cache->locked) { ++ /* Write it out and free it up */ ++ ++ chunk_written = ++ yaffs_wr_data_obj(cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, 1); ++ cache->dirty = 0; ++ cache->object = NULL; ++ } ++ ++ } while (cache && chunk_written > 0); ++ ++ if (cache) ++ /* Hoosterman, disk full while writing cache out. */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: no space during cache write"); ++ ++ } ++ ++} ++ ++/*yaffs_flush_whole_cache(dev) ++ * ++ * ++ */ ++ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ int n_caches = dev->param.n_caches; ++ int i; ++ ++ /* Find a dirty object in the cache and flush it... ++ * until there are no further dirty objects. ++ */ ++ do { ++ obj = NULL; ++ for (i = 0; i < n_caches && !obj; i++) { ++ if (dev->cache[i].object && dev->cache[i].dirty) ++ obj = dev->cache[i].object; ++ ++ } ++ if (obj) ++ yaffs_flush_file_cache(obj); ++ ++ } while (obj); ++ ++} ++ ++/* Grab us a cache chunk for use. ++ * First look for an empty one. ++ * Then look for the least recently used non-dirty one. ++ * Then look for the least recently used dirty one...., flush and look again. ++ */ ++static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->param.n_caches > 0) { ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (!dev->cache[i].object) ++ return &dev->cache[i]; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) ++{ ++ struct yaffs_cache *cache; ++ struct yaffs_obj *the_obj; ++ int usage; ++ int i; ++ int pushout; ++ ++ if (dev->param.n_caches > 0) { ++ /* Try find a non-dirty one... */ ++ ++ cache = yaffs_grab_chunk_worker(dev); ++ ++ if (!cache) { ++ /* They were all dirty, find the last recently used object and flush ++ * its cache, then find again. ++ * NB what's here is not very accurate, we actually flush the object ++ * the last recently used page. ++ */ ++ ++ /* With locking we can't assume we can use entry zero */ ++ ++ the_obj = NULL; ++ usage = -1; ++ cache = NULL; ++ pushout = -1; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object && ++ !dev->cache[i].locked && ++ (dev->cache[i].last_use < usage ++ || !cache)) { ++ usage = dev->cache[i].last_use; ++ the_obj = dev->cache[i].object; ++ cache = &dev->cache[i]; ++ pushout = i; ++ } ++ } ++ ++ if (!cache || cache->dirty) { ++ /* Flush and try again */ ++ yaffs_flush_file_cache(the_obj); ++ cache = yaffs_grab_chunk_worker(dev); ++ } ++ ++ } ++ return cache; ++ } else { ++ return NULL; ++ } ++} ++ ++/* Find a cached chunk */ ++static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, ++ int chunk_id) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ if (dev->param.n_caches > 0) { ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == obj && ++ dev->cache[i].chunk_id == chunk_id) { ++ dev->cache_hits++; ++ ++ return &dev->cache[i]; ++ } ++ } ++ } ++ return NULL; ++} ++ ++/* Mark the chunk for the least recently used algorithym */ ++static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, ++ int is_write) ++{ ++ ++ if (dev->param.n_caches > 0) { ++ if (dev->cache_last_use < 0 || dev->cache_last_use > 100000000) { ++ /* Reset the cache usages */ ++ int i; ++ for (i = 1; i < dev->param.n_caches; i++) ++ dev->cache[i].last_use = 0; ++ ++ dev->cache_last_use = 0; ++ } ++ ++ dev->cache_last_use++; ++ ++ cache->last_use = dev->cache_last_use; ++ ++ if (is_write) ++ cache->dirty = 1; ++ } ++} ++ ++/* Invalidate a single cache page. ++ * Do this when a whole page gets written, ++ * ie the short cache for this page is no longer valid. ++ */ ++static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) ++{ ++ if (object->my_dev->param.n_caches > 0) { ++ struct yaffs_cache *cache = ++ yaffs_find_chunk_cache(object, chunk_id); ++ ++ if (cache) ++ cache->object = NULL; ++ } ++} ++ ++/* Invalidate all the cache pages associated with this object ++ * Do this whenever ther file is deleted or resized. ++ */ ++static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.n_caches > 0) { ++ /* Invalidate it. */ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == in) ++ dev->cache[i].object = NULL; ++ } ++ } ++} ++ ++static void yaffs_unhash_obj(struct yaffs_obj *obj) ++{ ++ int bucket; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ /* If it is still linked into the bucket list, free from the list */ ++ if (!list_empty(&obj->hash_link)) { ++ list_del_init(&obj->hash_link); ++ bucket = yaffs_hash_fn(obj->obj_id); ++ dev->obj_bucket[bucket].count--; ++ } ++} ++ ++/* FreeObject frees up a Object and puts it back on the free list */ ++static void yaffs_free_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", ++ obj, obj->my_inode); ++ ++ if (!obj) ++ YBUG(); ++ if (obj->parent) ++ YBUG(); ++ if (!list_empty(&obj->siblings)) ++ YBUG(); ++ ++ if (obj->my_inode) { ++ /* We're still hooked up to a cached inode. ++ * Don't delete now, but mark for later deletion ++ */ ++ obj->defered_free = 1; ++ return; ++ } ++ ++ yaffs_unhash_obj(obj); ++ ++ yaffs_free_raw_obj(dev, obj); ++ dev->n_obj--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj) ++{ ++ if (obj->defered_free) ++ yaffs_free_obj(obj); ++} ++ ++static int yaffs_generic_obj_del(struct yaffs_obj *in) ++{ ++ ++ /* First off, invalidate the file's data in the cache, without flushing. */ ++ yaffs_invalidate_whole_cache(in); ++ ++ if (in->my_dev->param.is_yaffs2 && (in->parent != in->my_dev->del_dir)) { ++ /* Move to the unlinked directory so we have a record that it was deleted. */ ++ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, ++ 0); ++ ++ } ++ ++ yaffs_remove_obj_from_dir(in); ++ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); ++ in->hdr_chunk = 0; ++ ++ yaffs_free_obj(in); ++ return YAFFS_OK; ++ ++} ++ ++static void yaffs_soft_del_file(struct yaffs_obj *obj) ++{ ++ if (obj->deleted && ++ obj->variant_type == YAFFS_OBJECT_TYPE_FILE && !obj->soft_del) { ++ if (obj->n_data_chunks <= 0) { ++ /* Empty file with no duplicate object headers, ++ * just delete it immediately */ ++ yaffs_free_tnode(obj->my_dev, ++ obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: Deleting empty file %d", ++ obj->obj_id); ++ yaffs_generic_obj_del(obj); ++ } else { ++ yaffs_soft_del_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant. ++ file_variant.top_level, 0); ++ obj->soft_del = 1; ++ } ++ } ++} ++ ++/* Pruning removes any part of the file structure tree that is beyond the ++ * bounds of the file (ie that does not point to chunks). ++ * ++ * A file should only get pruned when its size is reduced. ++ * ++ * Before pruning, the chunks must be pulled from the tree and the ++ * level 0 tnode entries must be zeroed out. ++ * Could also use this for file deletion, but that's probably better handled ++ * by a special case. ++ * ++ * This function is recursive. For levels > 0 the function is called again on ++ * any sub-tree. For level == 0 we just check if the sub-tree has data. ++ * If there is no data in a subtree then it is pruned. ++ */ ++ ++static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, ++ struct yaffs_tnode *tn, u32 level, ++ int del0) ++{ ++ int i; ++ int has_data; ++ ++ if (tn) { ++ has_data = 0; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) { ++ tn->internal[i] = ++ yaffs_prune_worker(dev, ++ tn->internal[i], ++ level - 1, ++ (i == ++ 0) ? del0 : 1); ++ } ++ ++ if (tn->internal[i]) ++ has_data++; ++ } ++ } else { ++ int tnode_size_u32 = dev->tnode_size / sizeof(u32); ++ u32 *map = (u32 *) tn; ++ ++ for (i = 0; !has_data && i < tnode_size_u32; i++) { ++ if (map[i]) ++ has_data++; ++ } ++ } ++ ++ if (has_data == 0 && del0) { ++ /* Free and return NULL */ ++ ++ yaffs_free_tnode(dev, tn); ++ tn = NULL; ++ } ++ ++ } ++ ++ return tn; ++ ++} ++ ++static int yaffs_prune_tree(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct) ++{ ++ int i; ++ int has_data; ++ int done = 0; ++ struct yaffs_tnode *tn; ++ ++ if (file_struct->top_level > 0) { ++ file_struct->top = ++ yaffs_prune_worker(dev, file_struct->top, ++ file_struct->top_level, 0); ++ ++ /* Now we have a tree with all the non-zero branches NULL but the height ++ * is the same as it was. ++ * Let's see if we can trim internal tnodes to shorten the tree. ++ * We can do this if only the 0th element in the tnode is in use ++ * (ie all the non-zero are NULL) ++ */ ++ ++ while (file_struct->top_level && !done) { ++ tn = file_struct->top; ++ ++ has_data = 0; ++ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) ++ has_data++; ++ } ++ ++ if (!has_data) { ++ file_struct->top = tn->internal[0]; ++ file_struct->top_level--; ++ yaffs_free_tnode(dev, tn); ++ } else { ++ done = 1; ++ } ++ } ++ } ++ ++ return YAFFS_OK; ++} ++ ++/*-------------------- End of File Structure functions.-------------------*/ ++ ++/* AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out */ ++static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); ++ ++ if (obj) { ++ dev->n_obj++; ++ ++ /* Now sweeten it up... */ ++ ++ memset(obj, 0, sizeof(struct yaffs_obj)); ++ obj->being_created = 1; ++ ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; ++ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; ++ INIT_LIST_HEAD(&(obj->hard_links)); ++ INIT_LIST_HEAD(&(obj->hash_link)); ++ INIT_LIST_HEAD(&obj->siblings); ++ ++ /* Now make the directory sane */ ++ if (dev->root_dir) { ++ obj->parent = dev->root_dir; ++ list_add(&(obj->siblings), ++ &dev->root_dir->variant.dir_variant.children); ++ } ++ ++ /* Add it to the lost and found directory. ++ * NB Can't put root or lost-n-found in lost-n-found so ++ * check if lost-n-found exists first ++ */ ++ if (dev->lost_n_found) ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ ++ obj->being_created = 0; ++ } ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return obj; ++} ++ ++static int yaffs_find_nice_bucket(struct yaffs_dev *dev) ++{ ++ int i; ++ int l = 999; ++ int lowest = 999999; ++ ++ /* Search for the shortest list or one that ++ * isn't too long. ++ */ ++ ++ for (i = 0; i < 10 && lowest > 4; i++) { ++ dev->bucket_finder++; ++ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; ++ if (dev->obj_bucket[dev->bucket_finder].count < lowest) { ++ lowest = dev->obj_bucket[dev->bucket_finder].count; ++ l = dev->bucket_finder; ++ } ++ ++ } ++ ++ return l; ++} ++ ++static int yaffs_new_obj_id(struct yaffs_dev *dev) ++{ ++ int bucket = yaffs_find_nice_bucket(dev); ++ ++ /* Now find an object value that has not already been taken ++ * by scanning the list. ++ */ ++ ++ int found = 0; ++ struct list_head *i; ++ ++ u32 n = (u32) bucket; ++ ++ /* yaffs_check_obj_hash_sane(); */ ++ ++ while (!found) { ++ found = 1; ++ n += YAFFS_NOBJECT_BUCKETS; ++ if (1 || dev->obj_bucket[bucket].count > 0) { ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* If there is already one in the list */ ++ if (i && list_entry(i, struct yaffs_obj, ++ hash_link)->obj_id == n) { ++ found = 0; ++ } ++ } ++ } ++ } ++ ++ return n; ++} ++ ++static void yaffs_hash_obj(struct yaffs_obj *in) ++{ ++ int bucket = yaffs_hash_fn(in->obj_id); ++ struct yaffs_dev *dev = in->my_dev; ++ ++ list_add(&in->hash_link, &dev->obj_bucket[bucket].list); ++ dev->obj_bucket[bucket].count++; ++} ++ ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) ++{ ++ int bucket = yaffs_hash_fn(number); ++ struct list_head *i; ++ struct yaffs_obj *in; ++ ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* Look if it is in the list */ ++ if (i) { ++ in = list_entry(i, struct yaffs_obj, hash_link); ++ if (in->obj_id == number) { ++ ++ /* Don't tell the VFS about this one if it is defered free */ ++ if (in->defered_free) ++ return NULL; ++ ++ return in; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (number < 0) ++ number = yaffs_new_obj_id(dev); ++ ++ if (type == YAFFS_OBJECT_TYPE_FILE) { ++ tn = yaffs_get_tnode(dev); ++ if (!tn) ++ return NULL; ++ } ++ ++ the_obj = yaffs_alloc_empty_obj(dev); ++ if (!the_obj) { ++ if (tn) ++ yaffs_free_tnode(dev, tn); ++ return NULL; ++ } ++ ++ if (the_obj) { ++ the_obj->fake = 0; ++ the_obj->rename_allowed = 1; ++ the_obj->unlink_allowed = 1; ++ the_obj->obj_id = number; ++ yaffs_hash_obj(the_obj); ++ the_obj->variant_type = type; ++ yaffs_load_current_time(the_obj, 1, 1); ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ the_obj->variant.file_variant.file_size = 0; ++ the_obj->variant.file_variant.scanned_size = 0; ++ the_obj->variant.file_variant.shrink_size = ~0; /* max */ ++ the_obj->variant.file_variant.top_level = 0; ++ the_obj->variant.file_variant.top = tn; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* No action required */ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* todo this should not happen */ ++ break; ++ } ++ } ++ ++ return the_obj; ++} ++ ++static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, ++ int number, u32 mode) ++{ ++ ++ struct yaffs_obj *obj = ++ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); ++ if (obj) { ++ obj->fake = 1; /* it is fake so it might have no NAND presence... */ ++ obj->rename_allowed = 0; /* ... and we're not allowed to rename it... */ ++ obj->unlink_allowed = 0; /* ... or unlink it */ ++ obj->deleted = 0; ++ obj->unlinked = 0; ++ obj->yst_mode = mode; ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; /* Not a valid chunk. */ ++ } ++ ++ return obj; ++ ++} ++ ++ ++static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++ ++ yaffs_init_raw_tnodes_and_objs(dev); ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ INIT_LIST_HEAD(&dev->obj_bucket[i].list); ++ dev->obj_bucket[i].count = 0; ++ } ++} ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ ++ if (number > 0) ++ the_obj = yaffs_find_by_number(dev, number); ++ ++ if (!the_obj) ++ the_obj = yaffs_new_obj(dev, number, type); ++ ++ return the_obj; ++ ++} ++ ++YCHAR *yaffs_clone_str(const YCHAR * str) ++{ ++ YCHAR *new_str = NULL; ++ int len; ++ ++ if (!str) ++ str = _Y(""); ++ ++ len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); ++ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); ++ if (new_str) { ++ strncpy(new_str, str, len); ++ new_str[len] = 0; ++ } ++ return new_str; ++ ++} ++/* ++ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new ++ * link (ie. name) is created or deleted in the directory. ++ * ++ * ie. ++ * create dir/a : update dir's mtime/ctime ++ * rm dir/a: update dir's mtime/ctime ++ * modify dir/a: don't update dir's mtimme/ctime ++ * ++ * This can be handled immediately or defered. Defering helps reduce the number ++ * of updates when many files in a directory are changed within a brief period. ++ * ++ * If the directory updating is defered then yaffs_update_dirty_dirs must be ++ * called periodically. ++ */ ++ ++static void yaffs_update_parent(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ if (!obj) ++ return; ++ dev = obj->my_dev; ++ obj->dirty = 1; ++ yaffs_load_current_time(obj, 0, 1); ++ if (dev->param.defered_dir_update) { ++ struct list_head *link = &obj->variant.dir_variant.dirty; ++ ++ if (list_empty(link)) { ++ list_add(link, &dev->dirty_dirs); ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Added object %d to dirty directories", ++ obj->obj_id); ++ } ++ ++ } else { ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev) ++{ ++ struct list_head *link; ++ struct yaffs_obj *obj; ++ struct yaffs_dir_var *d_s; ++ union yaffs_obj_var *o_v; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); ++ ++ while (!list_empty(&dev->dirty_dirs)) { ++ link = dev->dirty_dirs.next; ++ list_del_init(link); ++ ++ d_s = list_entry(link, struct yaffs_dir_var, dirty); ++ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); ++ obj = list_entry(o_v, struct yaffs_obj, variant); ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", ++ obj->obj_id); ++ ++ if (obj->dirty) ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++/* ++ * Mknod (create) a new object. ++ * equiv_obj only has meaning for a hard link; ++ * alias_str only has meaning for a symlink. ++ * rdev only has meaning for devices (a subset of special objects) ++ */ ++ ++static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, ++ struct yaffs_obj *parent, ++ const YCHAR * name, ++ u32 mode, ++ u32 uid, ++ u32 gid, ++ struct yaffs_obj *equiv_obj, ++ const YCHAR * alias_str, u32 rdev) ++{ ++ struct yaffs_obj *in; ++ YCHAR *str = NULL; ++ ++ struct yaffs_dev *dev = parent->my_dev; ++ ++ /* Check if the entry exists. If it does then fail the call since we don't want a dup. */ ++ if (yaffs_find_by_name(parent, name)) ++ return NULL; ++ ++ if (type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ str = yaffs_clone_str(alias_str); ++ if (!str) ++ return NULL; ++ } ++ ++ in = yaffs_new_obj(dev, -1, type); ++ ++ if (!in) { ++ if (str) ++ kfree(str); ++ return NULL; ++ } ++ ++ if (in) { ++ in->hdr_chunk = 0; ++ in->valid = 1; ++ in->variant_type = type; ++ ++ in->yst_mode = mode; ++ ++ yaffs_attribs_init(in, gid, uid, rdev); ++ ++ in->n_data_chunks = 0; ++ ++ yaffs_set_obj_name(in, name); ++ in->dirty = 1; ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ in->my_dev = parent->my_dev; ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant.alias = str; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant.hardlink_variant.equiv_obj = equiv_obj; ++ in->variant.hardlink_variant.equiv_id = ++ equiv_obj->obj_id; ++ list_add(&in->hard_links, &equiv_obj->hard_links); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* do nothing */ ++ break; ++ } ++ ++ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { ++ /* Could not create the object header, fail the creation */ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ yaffs_update_parent(parent); ++ } ++ ++ return in; ++} ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR * name, u32 mode, u32 uid, ++ u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, ++ uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR * name, ++ u32 mode, u32 uid, u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, ++ mode, uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR * name, u32 mode, u32 uid, ++ u32 gid, u32 rdev) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, ++ uid, gid, NULL, NULL, rdev); ++} ++ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR * name, u32 mode, u32 uid, ++ u32 gid, const YCHAR * alias) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, ++ uid, gid, NULL, alias, 0); ++} ++ ++/* yaffs_link_obj returns the object id of the equivalent object.*/ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, ++ struct yaffs_obj *equiv_obj) ++{ ++ /* Get the real object in case we were fed a hard link as an equivalent object */ ++ equiv_obj = yaffs_get_equivalent_obj(equiv_obj); ++ ++ if (yaffs_create_obj ++ (YAFFS_OBJECT_TYPE_HARDLINK, parent, name, 0, 0, 0, ++ equiv_obj, NULL, 0)) { ++ return equiv_obj; ++ } else { ++ return NULL; ++ } ++ ++} ++ ++ ++ ++/*------------------------- Block Management and Page Allocation ----------------*/ ++ ++static int yaffs_init_blocks(struct yaffs_dev *dev) ++{ ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ dev->block_info = NULL; ++ dev->chunk_bits = NULL; ++ ++ dev->alloc_block = -1; /* force it to get a new one */ ++ ++ /* If the first allocation strategy fails, thry the alternate one */ ++ dev->block_info = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); ++ if (!dev->block_info) { ++ dev->block_info = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_info)); ++ dev->block_info_alt = 1; ++ } else { ++ dev->block_info_alt = 0; ++ } ++ ++ if (dev->block_info) { ++ /* Set up dynamic blockinfo stuff. Round up bytes. */ ++ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; ++ dev->chunk_bits = ++ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); ++ if (!dev->chunk_bits) { ++ dev->chunk_bits = ++ vmalloc(dev->chunk_bit_stride * n_blocks); ++ dev->chunk_bits_alt = 1; ++ } else { ++ dev->chunk_bits_alt = 0; ++ } ++ } ++ ++ if (dev->block_info && dev->chunk_bits) { ++ memset(dev->block_info, 0, ++ n_blocks * sizeof(struct yaffs_block_info)); ++ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); ++ return YAFFS_OK; ++ } ++ ++ return YAFFS_FAIL; ++} ++ ++static void yaffs_deinit_blocks(struct yaffs_dev *dev) ++{ ++ if (dev->block_info_alt && dev->block_info) ++ vfree(dev->block_info); ++ else if (dev->block_info) ++ kfree(dev->block_info); ++ ++ dev->block_info_alt = 0; ++ ++ dev->block_info = NULL; ++ ++ if (dev->chunk_bits_alt && dev->chunk_bits) ++ vfree(dev->chunk_bits); ++ else if (dev->chunk_bits) ++ kfree(dev->chunk_bits); ++ dev->chunk_bits_alt = 0; ++ dev->chunk_bits = NULL; ++} ++ ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); ++ ++ int erased_ok = 0; ++ ++ /* If the block is still healthy erase it and mark as clean. ++ * If the block has had a data failure, then retire it. ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, ++ "yaffs_block_became_dirty block %d state %d %s", ++ block_no, bi->block_state, ++ (bi->needs_retiring) ? "needs retiring" : ""); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DIRTY; ++ ++ /* If this is the block being garbage collected then stop gc'ing this block */ ++ if (block_no == dev->gc_block) ++ dev->gc_block = 0; ++ ++ /* If this block is currently the best candidate for gc then drop as a candidate */ ++ if (block_no == dev->gc_dirtiest) { ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ } ++ ++ if (!bi->needs_retiring) { ++ yaffs2_checkpt_invalidate(dev); ++ erased_ok = yaffs_erase_block(dev, block_no); ++ if (!erased_ok) { ++ dev->n_erase_failures++; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Erasure failed %d", block_no); ++ } ++ } ++ ++ if (erased_ok && ++ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) ++ || !yaffs_skip_verification(dev))) { ++ int i; ++ for (i = 0; i < dev->param.chunks_per_block; i++) { ++ if (!yaffs_check_chunk_erased ++ (dev, block_no * dev->param.chunks_per_block + i)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ ">>Block %d erasure supposedly OK, but chunk %d not erased", ++ block_no, i); ++ } ++ } ++ } ++ ++ if (erased_ok) { ++ /* Clean it up... */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ bi->seq_number = 0; ++ dev->n_erased_blocks++; ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ bi->has_shrink_hdr = 0; ++ bi->skip_erased_check = 1; /* Clean, so no need to check */ ++ bi->gc_prioritise = 0; ++ yaffs_clear_chunk_bits(dev, block_no); ++ ++ yaffs_trace(YAFFS_TRACE_ERASE, ++ "Erased block %d", block_no); ++ } else { ++ /* We lost a block of free space */ ++ dev->n_free_chunks -= dev->param.chunks_per_block; ++ yaffs_retire_block(dev, block_no); ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d retired", block_no); ++ } ++} ++ ++ ++ ++static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) ++{ ++ int old_chunk; ++ int new_chunk; ++ int mark_flash; ++ int ret_val = YAFFS_OK; ++ int i; ++ int is_checkpt_block; ++ int matching_chunk; ++ int max_copies; ++ ++ int chunks_before = yaffs_get_erased_chunks(dev); ++ int chunks_after; ++ ++ struct yaffs_ext_tags tags; ++ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); ++ ++ struct yaffs_obj *object; ++ ++ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d, in use %d, shrink %d, whole_block %d", ++ block, bi->pages_in_use, bi->has_shrink_hdr, ++ whole_block); ++ ++ /*yaffs_verify_free_chunks(dev); */ ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; ++ ++ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ ++ ++ dev->gc_disable = 1; ++ ++ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d that has no chunks in use", ++ block); ++ yaffs_block_became_dirty(dev, block); ++ } else { ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev, __LINE__); ++ ++ yaffs_verify_blk(dev, bi, block); ++ ++ max_copies = (whole_block) ? dev->param.chunks_per_block : 5; ++ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; ++ ++ for ( /* init already done */ ; ++ ret_val == YAFFS_OK && ++ dev->gc_chunk < dev->param.chunks_per_block && ++ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && ++ max_copies > 0; dev->gc_chunk++, old_chunk++) { ++ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { ++ ++ /* This page is in use and might need to be copied off */ ++ ++ max_copies--; ++ ++ mark_flash = 1; ++ ++ yaffs_init_tags(&tags); ++ ++ yaffs_rd_chunk_tags_nand(dev, old_chunk, ++ buffer, &tags); ++ ++ object = yaffs_find_by_number(dev, tags.obj_id); ++ ++ yaffs_trace(YAFFS_TRACE_GC_DETAIL, ++ "Collecting chunk in block %d, %d %d %d ", ++ dev->gc_chunk, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ ++ if (object && !yaffs_skip_verification(dev)) { ++ if (tags.chunk_id == 0) ++ matching_chunk = ++ object->hdr_chunk; ++ else if (object->soft_del) ++ matching_chunk = old_chunk; /* Defeat the test */ ++ else ++ matching_chunk = ++ yaffs_find_chunk_in_file ++ (object, tags.chunk_id, ++ NULL); ++ ++ if (old_chunk != matching_chunk) ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "gc: page in gc mismatch: %d %d %d %d", ++ old_chunk, ++ matching_chunk, ++ tags.obj_id, ++ tags.chunk_id); ++ ++ } ++ ++ if (!object) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "page %d in gc has no object: %d %d %d ", ++ old_chunk, ++ tags.obj_id, tags.chunk_id, ++ tags.n_bytes); ++ } ++ ++ if (object && ++ object->deleted && ++ object->soft_del && tags.chunk_id != 0) { ++ /* Data chunk in a soft deleted file, throw it away ++ * It's a soft deleted data chunk, ++ * No need to copy this, just forget about it and ++ * fix up the object. ++ */ ++ ++ /* Free chunks already includes softdeleted chunks. ++ * How ever this chunk is going to soon be really deleted ++ * which will increment free chunks. ++ * We have to decrement free chunks so this works out properly. ++ */ ++ dev->n_free_chunks--; ++ bi->soft_del_pages--; ++ ++ object->n_data_chunks--; ++ ++ if (object->n_data_chunks <= 0) { ++ /* remeber to clean up the object */ ++ dev->gc_cleanup_list[dev-> ++ n_clean_ups] ++ = tags.obj_id; ++ dev->n_clean_ups++; ++ } ++ mark_flash = 0; ++ } else if (0) { ++ /* Todo object && object->deleted && object->n_data_chunks == 0 */ ++ /* Deleted object header with no data chunks. ++ * Can be discarded and the file deleted. ++ */ ++ object->hdr_chunk = 0; ++ yaffs_free_tnode(object->my_dev, ++ object-> ++ variant.file_variant. ++ top); ++ object->variant.file_variant.top = NULL; ++ yaffs_generic_obj_del(object); ++ ++ } else if (object) { ++ /* It's either a data chunk in a live file or ++ * an ObjectHeader, so we're interested in it. ++ * NB Need to keep the ObjectHeaders of deleted files ++ * until the whole file has been deleted off ++ */ ++ tags.serial_number++; ++ ++ dev->n_gc_copies++; ++ ++ if (tags.chunk_id == 0) { ++ /* It is an object Id, ++ * We need to nuke the shrinkheader flags first ++ * Also need to clean up shadowing. ++ * We no longer want the shrink_header flag since its work is done ++ * and if it is left in place it will mess up scanning. ++ */ ++ ++ struct yaffs_obj_hdr *oh; ++ oh = (struct yaffs_obj_hdr *) ++ buffer; ++ ++ oh->is_shrink = 0; ++ tags.extra_is_shrink = 0; ++ ++ oh->shadows_obj = 0; ++ oh->inband_shadowed_obj_id = 0; ++ tags.extra_shadows = 0; ++ ++ /* Update file size */ ++ if (object->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) { ++ oh->file_size = ++ object->variant. ++ file_variant. ++ file_size; ++ tags.extra_length = ++ oh->file_size; ++ } ++ ++ yaffs_verify_oh(object, oh, ++ &tags, 1); ++ new_chunk = ++ yaffs_write_new_chunk(dev, ++ (u8 *) ++ oh, ++ &tags, ++ 1); ++ } else { ++ new_chunk = ++ yaffs_write_new_chunk(dev, ++ buffer, ++ &tags, ++ 1); ++ } ++ ++ if (new_chunk < 0) { ++ ret_val = YAFFS_FAIL; ++ } else { ++ ++ /* Ok, now fix up the Tnodes etc. */ ++ ++ if (tags.chunk_id == 0) { ++ /* It's a header */ ++ object->hdr_chunk = ++ new_chunk; ++ object->serial = ++ tags.serial_number; ++ } else { ++ /* It's a data chunk */ ++ int ok; ++ ok = yaffs_put_chunk_in_file(object, tags.chunk_id, new_chunk, 0); ++ } ++ } ++ } ++ ++ if (ret_val == YAFFS_OK) ++ yaffs_chunk_del(dev, old_chunk, ++ mark_flash, __LINE__); ++ ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, buffer, __LINE__); ++ ++ } ++ ++ yaffs_verify_collected_blk(dev, bi, block); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ /* ++ * The gc did not complete. Set block state back to FULL ++ * because checkpointing does not restore gc. ++ */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } else { ++ /* The gc completed. */ ++ /* Do any required cleanups */ ++ for (i = 0; i < dev->n_clean_ups; i++) { ++ /* Time to delete the file too */ ++ object = ++ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); ++ if (object) { ++ yaffs_free_tnode(dev, ++ object->variant. ++ file_variant.top); ++ object->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: About to finally delete object %d", ++ object->obj_id); ++ yaffs_generic_obj_del(object); ++ object->my_dev->n_deleted_files--; ++ } ++ ++ } ++ ++ chunks_after = yaffs_get_erased_chunks(dev); ++ if (chunks_before >= chunks_after) ++ yaffs_trace(YAFFS_TRACE_GC, ++ "gc did not increase free chunks before %d after %d", ++ chunks_before, chunks_after); ++ dev->gc_block = 0; ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ dev->gc_disable = 0; ++ ++ return ret_val; ++} ++ ++/* ++ * FindBlockForgarbageCollection is used to select the dirtiest block (or close enough) ++ * for garbage collection. ++ */ ++ ++static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, ++ int aggressive, int background) ++{ ++ int i; ++ int iterations; ++ unsigned selected = 0; ++ int prioritised = 0; ++ int prioritised_exist = 0; ++ struct yaffs_block_info *bi; ++ int threshold; ++ ++ /* First let's see if we need to grab a prioritised block */ ++ if (dev->has_pending_prioritised_gc && !aggressive) { ++ dev->gc_dirtiest = 0; ++ bi = dev->block_info; ++ for (i = dev->internal_start_block; ++ i <= dev->internal_end_block && !selected; i++) { ++ ++ if (bi->gc_prioritise) { ++ prioritised_exist = 1; ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ selected = i; ++ prioritised = 1; ++ } ++ } ++ bi++; ++ } ++ ++ /* ++ * If there is a prioritised block and none was selected then ++ * this happened because there is at least one old dirty block gumming ++ * up the works. Let's gc the oldest dirty block. ++ */ ++ ++ if (prioritised_exist && ++ !selected && dev->oldest_dirty_block > 0) ++ selected = dev->oldest_dirty_block; ++ ++ if (!prioritised_exist) /* None found, so we can clear this */ ++ dev->has_pending_prioritised_gc = 0; ++ } ++ ++ /* If we're doing aggressive GC then we are happy to take a less-dirty block, and ++ * search harder. ++ * else (we're doing a leasurely gc), then we only bother to do this if the ++ * block has only a few pages in use. ++ */ ++ ++ if (!selected) { ++ int pages_used; ++ int n_blocks = ++ dev->internal_end_block - dev->internal_start_block + 1; ++ if (aggressive) { ++ threshold = dev->param.chunks_per_block; ++ iterations = n_blocks; ++ } else { ++ int max_threshold; ++ ++ if (background) ++ max_threshold = dev->param.chunks_per_block / 2; ++ else ++ max_threshold = dev->param.chunks_per_block / 8; ++ ++ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ ++ threshold = background ? (dev->gc_not_done + 2) * 2 : 0; ++ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ if (threshold > max_threshold) ++ threshold = max_threshold; ++ ++ iterations = n_blocks / 16 + 1; ++ if (iterations > 100) ++ iterations = 100; ++ } ++ ++ for (i = 0; ++ i < iterations && ++ (dev->gc_dirtiest < 1 || ++ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); i++) { ++ dev->gc_block_finder++; ++ if (dev->gc_block_finder < dev->internal_start_block || ++ dev->gc_block_finder > dev->internal_end_block) ++ dev->gc_block_finder = ++ dev->internal_start_block; ++ ++ bi = yaffs_get_block_info(dev, dev->gc_block_finder); ++ ++ pages_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ pages_used < dev->param.chunks_per_block && ++ (dev->gc_dirtiest < 1 ++ || pages_used < dev->gc_pages_in_use) ++ && yaffs_block_ok_for_gc(dev, bi)) { ++ dev->gc_dirtiest = dev->gc_block_finder; ++ dev->gc_pages_in_use = pages_used; ++ } ++ } ++ ++ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) ++ selected = dev->gc_dirtiest; ++ } ++ ++ /* ++ * If nothing has been selected for a while, try selecting the oldest dirty ++ * because that's gumming up the works. ++ */ ++ ++ if (!selected && dev->param.is_yaffs2 && ++ dev->gc_not_done >= (background ? 10 : 20)) { ++ yaffs2_find_oldest_dirty_seq(dev); ++ if (dev->oldest_dirty_block > 0) { ++ selected = dev->oldest_dirty_block; ++ dev->gc_dirtiest = selected; ++ dev->oldest_dirty_gc_count++; ++ bi = yaffs_get_block_info(dev, selected); ++ dev->gc_pages_in_use = ++ bi->pages_in_use - bi->soft_del_pages; ++ } else { ++ dev->gc_not_done = 0; ++ } ++ } ++ ++ if (selected) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC Selected block %d with %d free, prioritised:%d", ++ selected, ++ dev->param.chunks_per_block - dev->gc_pages_in_use, ++ prioritised); ++ ++ dev->n_gc_blocks++; ++ if (background) ++ dev->bg_gcs++; ++ ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ dev->gc_not_done = 0; ++ if (dev->refresh_skip > 0) ++ dev->refresh_skip--; ++ } else { ++ dev->gc_not_done++; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", ++ dev->gc_block_finder, dev->gc_not_done, threshold, ++ dev->gc_dirtiest, dev->gc_pages_in_use, ++ dev->oldest_dirty_block, background ? " bg" : ""); ++ } ++ ++ return selected; ++} ++ ++/* New garbage collector ++ * If we're very low on erased blocks then we do aggressive garbage collection ++ * otherwise we do "leasurely" garbage collection. ++ * Aggressive gc looks further (whole array) and will accept less dirty blocks. ++ * Passive gc only inspects smaller areas and will only accept more dirty blocks. ++ * ++ * The idea is to help clear out space in a more spread-out manner. ++ * Dunno if it really does anything useful. ++ */ ++static int yaffs_check_gc(struct yaffs_dev *dev, int background) ++{ ++ int aggressive = 0; ++ int gc_ok = YAFFS_OK; ++ int max_tries = 0; ++ int min_erased; ++ int erased_chunks; ++ int checkpt_block_adjust; ++ ++ if (dev->param.gc_control && (dev->param.gc_control(dev) & 1) == 0) ++ return YAFFS_OK; ++ ++ if (dev->gc_disable) { ++ /* Bail out so we don't get recursive gc */ ++ return YAFFS_OK; ++ } ++ ++ /* This loop should pass the first time. ++ * We'll only see looping here if the collection does not increase space. ++ */ ++ ++ do { ++ max_tries++; ++ ++ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); ++ ++ min_erased = ++ dev->param.n_reserved_blocks + checkpt_block_adjust + 1; ++ erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ /* If we need a block soon then do aggressive gc. */ ++ if (dev->n_erased_blocks < min_erased) ++ aggressive = 1; ++ else { ++ if (!background ++ && erased_chunks > (dev->n_free_chunks / 4)) ++ break; ++ ++ if (dev->gc_skip > 20) ++ dev->gc_skip = 20; ++ if (erased_chunks < dev->n_free_chunks / 2 || ++ dev->gc_skip < 1 || background) ++ aggressive = 0; ++ else { ++ dev->gc_skip--; ++ break; ++ } ++ } ++ ++ dev->gc_skip = 5; ++ ++ /* If we don't already have a block being gc'd then see if we should start another */ ++ ++ if (dev->gc_block < 1 && !aggressive) { ++ dev->gc_block = yaffs2_find_refresh_block(dev); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ if (dev->gc_block < 1) { ++ dev->gc_block = ++ yaffs_find_gc_block(dev, aggressive, background); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ if (dev->gc_block > 0) { ++ dev->all_gcs++; ++ if (!aggressive) ++ dev->passive_gc_count++; ++ ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC n_erased_blocks %d aggressive %d", ++ dev->n_erased_blocks, aggressive); ++ ++ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); ++ } ++ ++ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) ++ && dev->gc_block > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", ++ dev->n_erased_blocks, max_tries, ++ dev->gc_block); ++ } ++ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && ++ (dev->gc_block > 0) && (max_tries < 2)); ++ ++ return aggressive ? gc_ok : YAFFS_OK; ++} ++ ++/* ++ * yaffs_bg_gc() ++ * Garbage collects. Intended to be called from a background thread. ++ * Returns non-zero if at least half the free chunks are erased. ++ */ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) ++{ ++ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); ++ ++ yaffs_check_gc(dev, 1); ++ return erased_chunks > dev->n_free_chunks / 2; ++} ++ ++/*-------------------- Data file manipulation -----------------*/ ++ ++static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) ++{ ++ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); ++ ++ if (nand_chunk >= 0) ++ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, ++ buffer, NULL); ++ else { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not found zero instead", ++ nand_chunk); ++ /* get sane (zero) data if you read a hole */ ++ memset(buffer, 0, in->my_dev->data_bytes_per_chunk); ++ return 0; ++ } ++ ++} ++ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn) ++{ ++ int block; ++ int page; ++ struct yaffs_ext_tags tags; ++ struct yaffs_block_info *bi; ++ ++ if (chunk_id <= 0) ++ return; ++ ++ dev->n_deletions++; ++ block = chunk_id / dev->param.chunks_per_block; ++ page = chunk_id % dev->param.chunks_per_block; ++ ++ if (!yaffs_check_chunk_bit(dev, block, page)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Deleting invalid chunk %d", chunk_id); ++ ++ bi = yaffs_get_block_info(dev, block); ++ ++ yaffs2_update_oldest_dirty_seq(dev, block, bi); ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, ++ "line %d delete of chunk %d", ++ lyn, chunk_id); ++ ++ if (!dev->param.is_yaffs2 && mark_flash && ++ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { ++ ++ yaffs_init_tags(&tags); ++ ++ tags.is_deleted = 1; ++ ++ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); ++ yaffs_handle_chunk_update(dev, chunk_id, &tags); ++ } else { ++ dev->n_unmarked_deletions++; ++ } ++ ++ /* Pull out of the management area. ++ * If the whole block became dirty, this will kick off an erasure. ++ */ ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || ++ bi->block_state == YAFFS_BLOCK_STATE_FULL || ++ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || ++ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ dev->n_free_chunks++; ++ ++ yaffs_clear_chunk_bit(dev, block, page); ++ ++ bi->pages_in_use--; ++ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && ++ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCANNING) { ++ yaffs_block_became_dirty(dev, block); ++ } ++ ++ } ++ ++} ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 * buffer, int n_bytes, int use_reserve) ++{ ++ /* Find old chunk Need to do this to get serial number ++ * Write new one and patch into tree. ++ * Invalidate old tags. ++ */ ++ ++ int prev_chunk_id; ++ struct yaffs_ext_tags prev_tags; ++ ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ ++ struct yaffs_dev *dev = in->my_dev; ++ ++ yaffs_check_gc(dev, 0); ++ ++ /* Get the previous chunk at this location in the file if it exists. ++ * If it does not exist then put a zero into the tree. This creates ++ * the tnode now, rather than later when it is harder to clean up. ++ */ ++ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); ++ if (prev_chunk_id < 1 && ++ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) ++ return 0; ++ ++ /* Set up new tags */ ++ yaffs_init_tags(&new_tags); ++ ++ new_tags.chunk_id = inode_chunk; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = ++ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; ++ new_tags.n_bytes = n_bytes; ++ ++ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Writing %d bytes to chunk!!!!!!!!!", ++ n_bytes); ++ YBUG(); ++ } ++ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); ++ ++ if (new_chunk_id > 0) { ++ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ yaffs_verify_file_sane(in); ++ } ++ return new_chunk_id; ++ ++} ++ ++ ++ ++static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, ++ const YCHAR * name, const void *value, int size, ++ int flags) ++{ ++ struct yaffs_xattr_mod xmod; ++ ++ int result; ++ ++ xmod.set = set; ++ xmod.name = name; ++ xmod.data = value; ++ xmod.size = size; ++ xmod.flags = flags; ++ xmod.result = -ENOSPC; ++ ++ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); ++ ++ if (result > 0) ++ return xmod.result; ++ else ++ return -ENOSPC; ++} ++ ++static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, ++ struct yaffs_xattr_mod *xmod) ++{ ++ int retval = 0; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ ++ char *x_buffer = buffer + x_offs; ++ ++ if (xmod->set) ++ retval = ++ nval_set(x_buffer, x_size, xmod->name, xmod->data, ++ xmod->size, xmod->flags); ++ else ++ retval = nval_del(x_buffer, x_size, xmod->name); ++ ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ ++ xmod->result = retval; ++ ++ return retval; ++} ++ ++static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR * name, ++ void *value, int size) ++{ ++ char *buffer = NULL; ++ int result; ++ struct yaffs_ext_tags tags; ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ ++ char *x_buffer; ++ ++ int retval = 0; ++ ++ if (obj->hdr_chunk < 1) ++ return -ENODATA; ++ ++ /* If we know that the object has no xattribs then don't do all the ++ * reading and parsing. ++ */ ++ if (obj->xattr_known && !obj->has_xattr) { ++ if (name) ++ return -ENODATA; ++ else ++ return 0; ++ } ++ ++ buffer = (char *)yaffs_get_temp_buffer(dev, __LINE__); ++ if (!buffer) ++ return -ENOMEM; ++ ++ result = ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ retval = -ENOENT; ++ else { ++ x_buffer = buffer + x_offs; ++ ++ if (!obj->xattr_known) { ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ } ++ ++ if (name) ++ retval = nval_get(x_buffer, x_size, name, value, size); ++ else ++ retval = nval_list(x_buffer, x_size, value, size); ++ } ++ yaffs_release_temp_buffer(dev, (u8 *) buffer, __LINE__); ++ return retval; ++} ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, ++ const void *value, int size, int flags) ++{ ++ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); ++} ++ ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); ++} ++ ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, ++ int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, name, value, size); ++} ++ ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); ++} ++ ++static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) ++{ ++ u8 *chunk_data; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ ++ if (!in) ++ return; ++ ++ dev = in->my_dev; ++ ++ if (in->lazy_loaded && in->hdr_chunk > 0) { ++ in->lazy_loaded = 0; ++ chunk_data = yaffs_get_temp_buffer(dev, __LINE__); ++ ++ result = ++ yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, chunk_data, ++ &tags); ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ yaffs_set_obj_name_from_oh(in, oh); ++ ++ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ in->variant.symlink_variant.alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant.symlink_variant.alias) ++ alloc_failed = 1; /* Not returned to caller */ ++ } ++ ++ yaffs_release_temp_buffer(dev, chunk_data, __LINE__); ++ } ++} ++ ++static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR * name, ++ const YCHAR * oh_name, int buff_size) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ if (dev->param.auto_unicode) { ++ if (*oh_name) { ++ /* It is an ASCII name, do an ASCII to ++ * unicode conversion */ ++ const char *ascii_oh_name = (const char *)oh_name; ++ int n = buff_size - 1; ++ while (n > 0 && *ascii_oh_name) { ++ *name = *ascii_oh_name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ strncpy(name, oh_name + 1, buff_size - 1); ++ } ++ } else { ++#else ++ { ++#endif ++ strncpy(name, oh_name, buff_size - 1); ++ } ++} ++ ++static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR * oh_name, ++ const YCHAR * name) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ ++ int is_ascii; ++ YCHAR *w; ++ ++ if (dev->param.auto_unicode) { ++ ++ is_ascii = 1; ++ w = name; ++ ++ /* Figure out if the name will fit in ascii character set */ ++ while (is_ascii && *w) { ++ if ((*w) & 0xff00) ++ is_ascii = 0; ++ w++; ++ } ++ ++ if (is_ascii) { ++ /* It is an ASCII name, so do a unicode to ascii conversion */ ++ char *ascii_oh_name = (char *)oh_name; ++ int n = YAFFS_MAX_NAME_LENGTH - 1; ++ while (n > 0 && *name) { ++ *ascii_oh_name = *name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ /* It is a unicode name, so save starting at the second YCHAR */ ++ *oh_name = 0; ++ strncpy(oh_name + 1, name, ++ YAFFS_MAX_NAME_LENGTH - 2); ++ } ++ } else { ++#else ++ { ++#endif ++ strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); ++ } ++ ++} ++ ++/* UpdateObjectHeader updates the header on NAND for an object. ++ * If name is not NULL, then that new name is used. ++ */ ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR * name, int force, ++ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) ++{ ++ ++ struct yaffs_block_info *bi; ++ ++ struct yaffs_dev *dev = in->my_dev; ++ ++ int prev_chunk_id; ++ int ret_val = 0; ++ int result = 0; ++ ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_ext_tags old_tags; ++ const YCHAR *alias = NULL; ++ ++ u8 *buffer = NULL; ++ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ struct yaffs_obj_hdr *oh = NULL; ++ ++ strcpy(old_name, _Y("silly old name")); ++ ++ if (!in->fake || in == dev->root_dir || ++ force || xmod) { ++ ++ yaffs_check_gc(dev, 0); ++ yaffs_check_obj_details_loaded(in); ++ ++ buffer = yaffs_get_temp_buffer(in->my_dev, __LINE__); ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ prev_chunk_id = in->hdr_chunk; ++ ++ if (prev_chunk_id > 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, ++ buffer, &old_tags); ++ ++ yaffs_verify_oh(in, oh, &old_tags, 0); ++ ++ memcpy(old_name, oh->name, sizeof(oh->name)); ++ memset(buffer, 0xFF, sizeof(struct yaffs_obj_hdr)); ++ } else { ++ memset(buffer, 0xFF, dev->data_bytes_per_chunk); ++ } ++ ++ oh->type = in->variant_type; ++ oh->yst_mode = in->yst_mode; ++ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; ++ ++ yaffs_load_attribs_oh(oh, in); ++ ++ if (in->parent) ++ oh->parent_obj_id = in->parent->obj_id; ++ else ++ oh->parent_obj_id = 0; ++ ++ if (name && *name) { ++ memset(oh->name, 0, sizeof(oh->name)); ++ yaffs_load_oh_from_name(dev, oh->name, name); ++ } else if (prev_chunk_id > 0) { ++ memcpy(oh->name, old_name, sizeof(oh->name)); ++ } else { ++ memset(oh->name, 0, sizeof(oh->name)); ++ } ++ ++ oh->is_shrink = is_shrink; ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Should not happen */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ oh->file_size = ++ (oh->parent_obj_id == YAFFS_OBJECTID_DELETED ++ || oh->parent_obj_id == ++ YAFFS_OBJECTID_UNLINKED) ? 0 : in-> ++ variant.file_variant.file_size; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ oh->equiv_id = in->variant.hardlink_variant.equiv_id; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = in->variant.symlink_variant.alias; ++ if (!alias) ++ alias = _Y("no alias"); ++ strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); ++ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; ++ break; ++ } ++ ++ /* process any xattrib modifications */ ++ if (xmod) ++ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); ++ ++ /* Tags */ ++ yaffs_init_tags(&new_tags); ++ in->serial++; ++ new_tags.chunk_id = 0; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = in->serial; ++ ++ /* Add extra info for file header */ ++ ++ new_tags.extra_available = 1; ++ new_tags.extra_parent_id = oh->parent_obj_id; ++ new_tags.extra_length = oh->file_size; ++ new_tags.extra_is_shrink = oh->is_shrink; ++ new_tags.extra_equiv_id = oh->equiv_id; ++ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; ++ new_tags.extra_obj_type = in->variant_type; ++ ++ yaffs_verify_oh(in, oh, &new_tags, 1); ++ ++ /* Create new chunk in NAND */ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, ++ (prev_chunk_id > 0) ? 1 : 0); ++ ++ if (new_chunk_id >= 0) { ++ ++ in->hdr_chunk = new_chunk_id; ++ ++ if (prev_chunk_id > 0) { ++ yaffs_chunk_del(dev, prev_chunk_id, 1, ++ __LINE__); ++ } ++ ++ if (!yaffs_obj_cache_dirty(in)) ++ in->dirty = 0; ++ ++ /* If this was a shrink, then mark the block that the chunk lives on */ ++ if (is_shrink) { ++ bi = yaffs_get_block_info(in->my_dev, ++ new_chunk_id / ++ in->my_dev->param. ++ chunks_per_block); ++ bi->has_shrink_hdr = 1; ++ } ++ ++ } ++ ++ ret_val = new_chunk_id; ++ ++ } ++ ++ if (buffer) ++ yaffs_release_temp_buffer(dev, buffer, __LINE__); ++ ++ return ret_val; ++} ++ ++/*--------------------- File read/write ------------------------ ++ * Read and write have very similar structures. ++ * In general the read/write has three parts to it ++ * An incomplete chunk to start with (if the read/write is not chunk-aligned) ++ * Some complete chunks ++ * An incomplete chunk to end off with ++ * ++ * Curve-balls: the first chunk might also be the last chunk. ++ */ ++ ++int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) ++{ ++ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ struct yaffs_cache *cache; ++ ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0) { ++ /* chunk = offset / dev->data_bytes_per_chunk + 1; */ ++ /* start = offset % dev->data_bytes_per_chunk; */ ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ chunk++; ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ if ((start + n) < dev->data_bytes_per_chunk) ++ n_copy = n; ++ else ++ n_copy = dev->data_bytes_per_chunk - start; ++ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ /* If the chunk is already in the cache or it is less than a whole chunk ++ * or we're using inband tags then use the cache (if there is caching) ++ * else bypass the cache. ++ */ ++ if (cache || n_copy != dev->data_bytes_per_chunk ++ || dev->param.inband_tags) { ++ if (dev->param.n_caches > 0) { ++ ++ /* If we can't find the data in the cache, then load it up. */ ++ ++ if (!cache) { ++ cache = ++ yaffs_grab_chunk_cache(in->my_dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ cache->n_bytes = 0; ++ } ++ ++ yaffs_use_cache(dev, cache, 0); ++ ++ cache->locked = 1; ++ ++ memcpy(buffer, &cache->data[start], n_copy); ++ ++ cache->locked = 0; ++ } else { ++ /* Read into the local buffer then copy.. */ ++ ++ u8 *local_buffer = ++ yaffs_get_temp_buffer(dev, __LINE__); ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ ++ memcpy(buffer, &local_buffer[start], n_copy); ++ ++ yaffs_release_temp_buffer(dev, local_buffer, ++ __LINE__); ++ } ++ ++ } else { ++ ++ /* A full chunk. Read directly into the supplied buffer. */ ++ yaffs_rd_data_obj(in, chunk, buffer); ++ ++ } ++ ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ ++ } ++ ++ return n_done; ++} ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 * buffer, loff_t offset, ++ int n_bytes, int write_trhrough) ++{ ++ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ int n_writeback; ++ int start_write = offset; ++ int chunk_written = 0; ++ u32 n_bytes_read; ++ u32 chunk_start; ++ ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0 && chunk_written >= 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ ++ if (chunk * dev->data_bytes_per_chunk + start != offset || ++ start >= dev->data_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "AddrToChunk of offset %d gives chunk %d start %d", ++ (int)offset, chunk, start); ++ } ++ chunk++; /* File pos to chunk in file offset */ ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ ++ if ((start + n) < dev->data_bytes_per_chunk) { ++ n_copy = n; ++ ++ /* Now folks, to calculate how many bytes to write back.... ++ * If we're overwriting and not writing to then end of file then ++ * we need to write back as much as was there before. ++ */ ++ ++ chunk_start = ((chunk - 1) * dev->data_bytes_per_chunk); ++ ++ if (chunk_start > in->variant.file_variant.file_size) ++ n_bytes_read = 0; /* Past end of file */ ++ else ++ n_bytes_read = ++ in->variant.file_variant.file_size - ++ chunk_start; ++ ++ if (n_bytes_read > dev->data_bytes_per_chunk) ++ n_bytes_read = dev->data_bytes_per_chunk; ++ ++ n_writeback = ++ (n_bytes_read > ++ (start + n)) ? n_bytes_read : (start + n); ++ ++ if (n_writeback < 0 ++ || n_writeback > dev->data_bytes_per_chunk) ++ YBUG(); ++ ++ } else { ++ n_copy = dev->data_bytes_per_chunk - start; ++ n_writeback = dev->data_bytes_per_chunk; ++ } ++ ++ if (n_copy != dev->data_bytes_per_chunk ++ || dev->param.inband_tags) { ++ /* An incomplete start or end chunk (or maybe both start and end chunk), ++ * or we're using inband tags, so we want to use the cache buffers. ++ */ ++ if (dev->param.n_caches > 0) { ++ struct yaffs_cache *cache; ++ /* If we can't find the data in the cache, then load the cache */ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ if (!cache ++ && yaffs_check_alloc_available(dev, 1)) { ++ cache = yaffs_grab_chunk_cache(dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ } else if (cache && ++ !cache->dirty && ++ !yaffs_check_alloc_available(dev, ++ 1)) { ++ /* Drop the cache if it was a read cache item and ++ * no space check has been made for it. ++ */ ++ cache = NULL; ++ } ++ ++ if (cache) { ++ yaffs_use_cache(dev, cache, 1); ++ cache->locked = 1; ++ ++ memcpy(&cache->data[start], buffer, ++ n_copy); ++ ++ cache->locked = 0; ++ cache->n_bytes = n_writeback; ++ ++ if (write_trhrough) { ++ chunk_written = ++ yaffs_wr_data_obj ++ (cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, 1); ++ cache->dirty = 0; ++ } ++ ++ } else { ++ chunk_written = -1; /* fail the write */ ++ } ++ } else { ++ /* An incomplete start or end chunk (or maybe both start and end chunk) ++ * Read into the local buffer then copy, then copy over and write back. ++ */ ++ ++ u8 *local_buffer = ++ yaffs_get_temp_buffer(dev, __LINE__); ++ ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ ++ memcpy(&local_buffer[start], buffer, n_copy); ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, ++ local_buffer, ++ n_writeback, 0); ++ ++ yaffs_release_temp_buffer(dev, local_buffer, ++ __LINE__); ++ ++ } ++ ++ } else { ++ /* A full chunk. Write directly from the supplied buffer. */ ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, buffer, ++ dev->data_bytes_per_chunk, 0); ++ ++ /* Since we've overwritten the cached data, we better invalidate it. */ ++ yaffs_invalidate_chunk_cache(in, chunk); ++ } ++ ++ if (chunk_written >= 0) { ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ ++ } ++ ++ /* Update file object */ ++ ++ if ((start_write + n_done) > in->variant.file_variant.file_size) ++ in->variant.file_variant.file_size = (start_write + n_done); ++ ++ in->dirty = 1; ++ ++ return n_done; ++} ++ ++int yaffs_wr_file(struct yaffs_obj *in, const u8 * buffer, loff_t offset, ++ int n_bytes, int write_trhrough) ++{ ++ yaffs2_handle_hole(in, offset); ++ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_trhrough); ++} ++ ++/* ---------------------- File resizing stuff ------------------ */ ++ ++static void yaffs_prune_chunks(struct yaffs_obj *in, int new_size) ++{ ++ ++ struct yaffs_dev *dev = in->my_dev; ++ int old_size = in->variant.file_variant.file_size; ++ ++ int last_del = 1 + (old_size - 1) / dev->data_bytes_per_chunk; ++ ++ int start_del = 1 + (new_size + dev->data_bytes_per_chunk - 1) / ++ dev->data_bytes_per_chunk; ++ int i; ++ int chunk_id; ++ ++ /* Delete backwards so that we don't end up with holes if ++ * power is lost part-way through the operation. ++ */ ++ for (i = last_del; i >= start_del; i--) { ++ /* NB this could be optimised somewhat, ++ * eg. could retrieve the tags and write them without ++ * using yaffs_chunk_del ++ */ ++ ++ chunk_id = yaffs_find_del_file_chunk(in, i, NULL); ++ if (chunk_id > 0) { ++ if (chunk_id < ++ (dev->internal_start_block * ++ dev->param.chunks_per_block) ++ || chunk_id >= ++ ((dev->internal_end_block + ++ 1) * dev->param.chunks_per_block)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Found daft chunk_id %d for %d", ++ chunk_id, i); ++ } else { ++ in->n_data_chunks--; ++ yaffs_chunk_del(dev, chunk_id, 1, __LINE__); ++ } ++ } ++ } ++ ++} ++ ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) ++{ ++ int new_full; ++ u32 new_partial; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); ++ ++ yaffs_prune_chunks(obj, new_size); ++ ++ if (new_partial != 0) { ++ int last_chunk = 1 + new_full; ++ u8 *local_buffer = yaffs_get_temp_buffer(dev, __LINE__); ++ ++ /* Rewrite the last chunk with its new size and zero pad */ ++ yaffs_rd_data_obj(obj, last_chunk, local_buffer); ++ memset(local_buffer + new_partial, 0, ++ dev->data_bytes_per_chunk - new_partial); ++ ++ yaffs_wr_data_obj(obj, last_chunk, local_buffer, ++ new_partial, 1); ++ ++ yaffs_release_temp_buffer(dev, local_buffer, __LINE__); ++ } ++ ++ obj->variant.file_variant.file_size = new_size; ++ ++ yaffs_prune_tree(dev, &obj->variant.file_variant); ++} ++ ++int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) ++{ ++ struct yaffs_dev *dev = in->my_dev; ++ int old_size = in->variant.file_variant.file_size; ++ ++ yaffs_flush_file_cache(in); ++ yaffs_invalidate_whole_cache(in); ++ ++ yaffs_check_gc(dev, 0); ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ if (new_size == old_size) ++ return YAFFS_OK; ++ ++ if (new_size > old_size) { ++ yaffs2_handle_hole(in, new_size); ++ in->variant.file_variant.file_size = new_size; ++ } else { ++ /* new_size < old_size */ ++ yaffs_resize_file_down(in, new_size); ++ } ++ ++ /* Write a new object header to reflect the resize. ++ * show we've shrunk the file, if need be ++ * Do this only if the file is not in the deleted directories ++ * and is not shadowed. ++ */ ++ if (in->parent && ++ !in->is_shadowed && ++ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ in->parent->obj_id != YAFFS_OBJECTID_DELETED) ++ yaffs_update_oh(in, NULL, 0, 0, 0, NULL); ++ ++ return YAFFS_OK; ++} ++ ++int yaffs_flush_file(struct yaffs_obj *in, int update_time, int data_sync) ++{ ++ int ret_val; ++ if (in->dirty) { ++ yaffs_flush_file_cache(in); ++ if (data_sync) /* Only sync data */ ++ ret_val = YAFFS_OK; ++ else { ++ if (update_time) ++ yaffs_load_current_time(in, 0, 0); ++ ++ ret_val = (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= ++ 0) ? YAFFS_OK : YAFFS_FAIL; ++ } ++ } else { ++ ret_val = YAFFS_OK; ++ } ++ ++ return ret_val; ++ ++} ++ ++ ++/* yaffs_del_file deletes the whole file data ++ * and the inode associated with the file. ++ * It does not delete the links associated with the file. ++ */ ++static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) ++{ ++ ++ int ret_val; ++ int del_now = 0; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!in->my_inode) ++ del_now = 1; ++ ++ if (del_now) { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->del_dir, ++ _Y("deleted"), 0, 0); ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: immediate deletion of file %d", ++ in->obj_id); ++ in->deleted = 1; ++ in->my_dev->n_deleted_files++; ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ yaffs_soft_del_file(in); ++ } else { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++ ++ return ret_val; ++} ++ ++int yaffs_del_file(struct yaffs_obj *in) ++{ ++ int ret_val = YAFFS_OK; ++ int deleted; /* Need to cache value on stack if in is freed */ ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ ++ if (in->n_data_chunks > 0) { ++ /* Use soft deletion if there is data in the file. ++ * That won't be the case if it has been resized to zero. ++ */ ++ if (!in->unlinked) ++ ret_val = yaffs_unlink_file_if_needed(in); ++ ++ deleted = in->deleted; ++ ++ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { ++ in->deleted = 1; ++ deleted = 1; ++ in->my_dev->n_deleted_files++; ++ yaffs_soft_del_file(in); ++ } ++ return deleted ? YAFFS_OK : YAFFS_FAIL; ++ } else { ++ /* The file has no data chunks so we toss it immediately */ ++ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); ++ in->variant.file_variant.top = NULL; ++ yaffs_generic_obj_del(in); ++ ++ return YAFFS_OK; ++ } ++} ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj) ++{ ++ return (obj && ++ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && ++ !(list_empty(&obj->variant.dir_variant.children)); ++} ++ ++static int yaffs_del_dir(struct yaffs_obj *obj) ++{ ++ /* First check that the directory is empty. */ ++ if (yaffs_is_non_empty_dir(obj)) ++ return YAFFS_FAIL; ++ ++ return yaffs_generic_obj_del(obj); ++} ++ ++static int yaffs_del_symlink(struct yaffs_obj *in) ++{ ++ if (in->variant.symlink_variant.alias) ++ kfree(in->variant.symlink_variant.alias); ++ in->variant.symlink_variant.alias = NULL; ++ ++ return yaffs_generic_obj_del(in); ++} ++ ++static int yaffs_del_link(struct yaffs_obj *in) ++{ ++ /* remove this hardlink from the list assocaited with the equivalent ++ * object ++ */ ++ list_del_init(&in->hard_links); ++ return yaffs_generic_obj_del(in); ++} ++ ++int yaffs_del_obj(struct yaffs_obj *obj) ++{ ++ int ret_val = -1; ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ ret_val = yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!list_empty(&obj->variant.dir_variant.dirty)) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Remove object %d from dirty directories", ++ obj->obj_id); ++ list_del_init(&obj->variant.dir_variant.dirty); ++ } ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ ret_val = yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ ret_val = yaffs_del_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ ret_val = yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ ret_val = 0; ++ break; /* should not happen. */ ++ } ++ ++ return ret_val; ++} ++ ++static int yaffs_unlink_worker(struct yaffs_obj *obj) ++{ ++ ++ int del_now = 0; ++ ++ if (!obj->my_inode) ++ del_now = 1; ++ ++ if (obj) ++ yaffs_update_parent(obj->parent); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ return yaffs_del_link(obj); ++ } else if (!list_empty(&obj->hard_links)) { ++ /* Curve ball: We're unlinking an object that has a hardlink. ++ * ++ * This problem arises because we are not strictly following ++ * The Linux link/inode model. ++ * ++ * We can't really delete the object. ++ * Instead, we do the following: ++ * - Select a hardlink. ++ * - Unhook it from the hard links ++ * - Move it from its parent directory (so that the rename can work) ++ * - Rename the object to the hardlink's name. ++ * - Delete the hardlink ++ */ ++ ++ struct yaffs_obj *hl; ++ struct yaffs_obj *parent; ++ int ret_val; ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ hl = list_entry(obj->hard_links.next, struct yaffs_obj, ++ hard_links); ++ ++ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); ++ parent = hl->parent; ++ ++ list_del_init(&hl->hard_links); ++ ++ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); ++ ++ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); ++ ++ if (ret_val == YAFFS_OK) ++ ret_val = yaffs_generic_obj_del(hl); ++ ++ return ret_val; ++ ++ } else if (del_now) { ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ list_del_init(&obj->variant.dir_variant.dirty); ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ return yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ return YAFFS_FAIL; ++ } ++ } else if (yaffs_is_non_empty_dir(obj)) { ++ return YAFFS_FAIL; ++ } else { ++ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++} ++ ++static int yaffs_unlink_obj(struct yaffs_obj *obj) ++{ ++ ++ if (obj && obj->unlink_allowed) ++ return yaffs_unlink_worker(obj); ++ ++ return YAFFS_FAIL; ++ ++} ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name) ++{ ++ struct yaffs_obj *obj; ++ ++ obj = yaffs_find_by_name(dir, name); ++ return yaffs_unlink_obj(obj); ++} ++ ++/* Note: ++ * If old_name is NULL then we take old_dir as the object to be renamed. ++ */ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, ++ struct yaffs_obj *new_dir, const YCHAR * new_name) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *existing_target = NULL; ++ int force = 0; ++ int result; ++ struct yaffs_dev *dev; ++ ++ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ YBUG(); ++ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ YBUG(); ++ ++ dev = old_dir->my_dev; ++ ++#ifdef CONFIG_YAFFS_CASE_INSENSITIVE ++ /* Special case for case insemsitive systems. ++ * While look-up is case insensitive, the name isn't. ++ * Therefore we might want to change x.txt to X.txt ++ */ ++ if (old_dir == new_dir && ++ old_name && new_name && ++ strcmp(old_name, new_name) == 0) ++ force = 1; ++#endif ++ ++ if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ /* ENAMETOOLONG */ ++ return YAFFS_FAIL; ++ ++ if(old_name) ++ obj = yaffs_find_by_name(old_dir, old_name); ++ else{ ++ obj = old_dir; ++ old_dir = obj->parent; ++ } ++ ++ ++ if (obj && obj->rename_allowed) { ++ ++ /* Now do the handling for an existing target, if there is one */ ++ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ if (yaffs_is_non_empty_dir(existing_target)){ ++ return YAFFS_FAIL; /* ENOTEMPTY */ ++ } else if (existing_target && existing_target != obj) { ++ /* Nuke the target first, using shadowing, ++ * but only if it isn't the same object. ++ * ++ * Note we must disable gc otherwise it can mess up the shadowing. ++ * ++ */ ++ dev->gc_disable = 1; ++ yaffs_change_obj_name(obj, new_dir, new_name, force, ++ existing_target->obj_id); ++ existing_target->is_shadowed = 1; ++ yaffs_unlink_obj(existing_target); ++ dev->gc_disable = 0; ++ } ++ ++ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); ++ ++ yaffs_update_parent(old_dir); ++ if (new_dir != old_dir) ++ yaffs_update_parent(new_dir); ++ ++ return result; ++ } ++ return YAFFS_FAIL; ++} ++ ++/*----------------------- Initialisation Scanning ---------------------- */ ++ ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning) ++{ ++ struct yaffs_obj *obj; ++ ++ if (!backward_scanning) { ++ /* Handle YAFFS1 forward scanning case ++ * For YAFFS1 we always do the deletion ++ */ ++ ++ } else { ++ /* Handle YAFFS2 case (backward scanning) ++ * If the shadowed object exists then ignore. ++ */ ++ obj = yaffs_find_by_number(dev, obj_id); ++ if (obj) ++ return; ++ } ++ ++ /* Let's create it (if it does not exist) assuming it is a file so that it can do shrinking etc. ++ * We put it in unlinked dir to be cleaned up after the scanning ++ */ ++ obj = ++ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); ++ if (!obj) ++ return; ++ obj->is_shadowed = 1; ++ yaffs_add_obj_to_dir(dev->unlinked_dir, obj); ++ obj->variant.file_variant.shrink_size = 0; ++ obj->valid = 1; /* So that we don't read any other info for this file */ ++ ++} ++ ++void yaffs_link_fixup(struct yaffs_dev *dev, struct yaffs_obj *hard_list) ++{ ++ struct yaffs_obj *hl; ++ struct yaffs_obj *in; ++ ++ while (hard_list) { ++ hl = hard_list; ++ hard_list = (struct yaffs_obj *)(hard_list->hard_links.next); ++ ++ in = yaffs_find_by_number(dev, ++ hl->variant. ++ hardlink_variant.equiv_id); ++ ++ if (in) { ++ /* Add the hardlink pointers */ ++ hl->variant.hardlink_variant.equiv_obj = in; ++ list_add(&hl->hard_links, &in->hard_links); ++ } else { ++ /* Todo Need to report/handle this better. ++ * Got a problem... hardlink to a non-existant object ++ */ ++ hl->variant.hardlink_variant.equiv_obj = NULL; ++ INIT_LIST_HEAD(&hl->hard_links); ++ ++ } ++ } ++} ++ ++static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) ++{ ++ /* ++ * Sort out state of unlinked and deleted objects after scanning. ++ */ ++ struct list_head *i; ++ struct list_head *n; ++ struct yaffs_obj *l; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Soft delete all the unlinked files */ ++ list_for_each_safe(i, n, ++ &dev->unlinked_dir->variant.dir_variant.children) { ++ if (i) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++ } ++ ++ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { ++ if (i) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++ } ++ ++} ++ ++/* ++ * This code iterates through all the objects making sure that they are rooted. ++ * Any unrooted objects are re-rooted in lost+found. ++ * An object needs to be in one of: ++ * - Directly under deleted, unlinked ++ * - Directly or indirectly under root. ++ * ++ * Note: ++ * This code assumes that we don't ever change the current relationships between ++ * directories: ++ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL ++ * lost-n-found->parent == root_dir ++ * ++ * This fixes the problem where directories might have inadvertently been deleted ++ * leaving the object "hanging" without being rooted in the directory tree. ++ */ ++ ++static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ return (obj == dev->del_dir || ++ obj == dev->unlinked_dir || obj == dev->root_dir); ++} ++ ++static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_obj *parent; ++ int i; ++ struct list_head *lh; ++ struct list_head *n; ++ int depth_limit; ++ int hanging; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Iterate through the objects in each hash entry, ++ * looking at each object. ++ * Make sure it is rooted. ++ */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { ++ if (lh) { ++ obj = ++ list_entry(lh, struct yaffs_obj, hash_link); ++ parent = obj->parent; ++ ++ if (yaffs_has_null_parent(dev, obj)) { ++ /* These directories are not hanging */ ++ hanging = 0; ++ } else if (!parent ++ || parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ hanging = 1; ++ } else if (yaffs_has_null_parent(dev, parent)) { ++ hanging = 0; ++ } else { ++ /* ++ * Need to follow the parent chain to see if it is hanging. ++ */ ++ hanging = 0; ++ depth_limit = 100; ++ ++ while (parent != dev->root_dir && ++ parent->parent && ++ parent->parent->variant_type == ++ YAFFS_OBJECT_TYPE_DIRECTORY ++ && depth_limit > 0) { ++ parent = parent->parent; ++ depth_limit--; ++ } ++ if (parent != dev->root_dir) ++ hanging = 1; ++ } ++ if (hanging) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Hanging object %d moved to lost and found", ++ obj->obj_id); ++ yaffs_add_obj_to_dir(dev->lost_n_found, ++ obj); ++ } ++ } ++ } ++ } ++} ++ ++/* ++ * Delete directory contents for cleaning up lost and found. ++ */ ++static void yaffs_del_dir_contents(struct yaffs_obj *dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ YBUG(); ++ ++ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { ++ if (lh) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) ++ yaffs_del_dir_contents(obj); ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Deleting lost_found object %d", ++ obj->obj_id); ++ ++ /* Need to use UnlinkObject since Delete would not handle ++ * hardlinked objects correctly. ++ */ ++ yaffs_unlink_obj(obj); ++ } ++ } ++ ++} ++ ++static void yaffs_empty_l_n_f(struct yaffs_dev *dev) ++{ ++ yaffs_del_dir_contents(dev->lost_n_found); ++} ++ ++ ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, ++ const YCHAR * name) ++{ ++ int sum; ++ ++ struct list_head *i; ++ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ struct yaffs_obj *l; ++ ++ if (!name) ++ return NULL; ++ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: null pointer directory" ++ ); ++ YBUG(); ++ return NULL; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: non-directory" ++ ); ++ YBUG(); ++ } ++ ++ sum = yaffs_calc_name_sum(name); ++ ++ list_for_each(i, &directory->variant.dir_variant.children) { ++ if (i) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ ++ if (l->parent != directory) ++ YBUG(); ++ ++ yaffs_check_obj_details_loaded(l); ++ ++ /* Special case for lost-n-found */ ++ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) ++ return l; ++ } else if (l->sum == sum ++ || l->hdr_chunk <= 0) { ++ /* LostnFound chunk called Objxxx ++ * Do a real check ++ */ ++ yaffs_get_obj_name(l, buffer, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ if (strncmp ++ (name, buffer, YAFFS_MAX_NAME_LENGTH) == 0) ++ return l; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++/* GetEquivalentObject dereferences any hard links to get to the ++ * actual object. ++ */ ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ /* We want the object id of the equivalent object, not this one */ ++ obj = obj->variant.hardlink_variant.equiv_obj; ++ yaffs_check_obj_details_loaded(obj); ++ } ++ return obj; ++} ++ ++/* ++ * A note or two on object names. ++ * * If the object name is missing, we then make one up in the form objnnn ++ * ++ * * ASCII names are stored in the object header's name field from byte zero ++ * * Unicode names are historically stored starting from byte zero. ++ * ++ * Then there are automatic Unicode names... ++ * The purpose of these is to save names in a way that can be read as ++ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII ++ * system to share files. ++ * ++ * These automatic unicode are stored slightly differently... ++ * - If the name can fit in the ASCII character space then they are saved as ++ * ascii names as per above. ++ * - If the name needs Unicode then the name is saved in Unicode ++ * starting at oh->name[1]. ++ ++ */ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR * name, ++ int buffer_size) ++{ ++ /* Create an object name if we could not find one. */ ++ if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { ++ YCHAR local_name[20]; ++ YCHAR num_string[20]; ++ YCHAR *x = &num_string[19]; ++ unsigned v = obj->obj_id; ++ num_string[19] = 0; ++ while (v > 0) { ++ x--; ++ *x = '0' + (v % 10); ++ v /= 10; ++ } ++ /* make up a name */ ++ strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); ++ strcat(local_name, x); ++ strncpy(name, local_name, buffer_size - 1); ++ } ++} ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size) ++{ ++ memset(name, 0, buffer_size * sizeof(YCHAR)); ++ ++ yaffs_check_obj_details_loaded(obj); ++ ++ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); ++ } ++#ifndef CONFIG_YAFFS_NO_SHORT_NAMES ++ else if (obj->short_name[0]) { ++ strcpy(name, obj->short_name); ++ } ++#endif ++ else if (obj->hdr_chunk > 0) { ++ int result; ++ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev, __LINE__); ++ ++ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; ++ ++ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); ++ ++ if (obj->hdr_chunk > 0) { ++ result = yaffs_rd_chunk_tags_nand(obj->my_dev, ++ obj->hdr_chunk, ++ buffer, NULL); ++ } ++ yaffs_load_name_from_oh(obj->my_dev, name, oh->name, ++ buffer_size); ++ ++ yaffs_release_temp_buffer(obj->my_dev, buffer, __LINE__); ++ } ++ ++ yaffs_fix_null_name(obj, name, buffer_size); ++ ++ return strnlen(name, YAFFS_MAX_NAME_LENGTH); ++} ++ ++int yaffs_get_obj_length(struct yaffs_obj *obj) ++{ ++ /* Dereference any hard linking */ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ return obj->variant.file_variant.file_size; ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ if (!obj->variant.symlink_variant.alias) ++ return 0; ++ return strnlen(obj->variant.symlink_variant.alias, ++ YAFFS_MAX_ALIAS_LENGTH); ++ } else { ++ /* Only a directory should drop through to here */ ++ return obj->my_dev->data_bytes_per_chunk; ++ } ++} ++ ++int yaffs_get_obj_link_count(struct yaffs_obj *obj) ++{ ++ int count = 0; ++ struct list_head *i; ++ ++ if (!obj->unlinked) ++ count++; /* the object itself */ ++ ++ list_for_each(i, &obj->hard_links) ++ count++; /* add the hard links; */ ++ ++ return count; ++} ++ ++int yaffs_get_obj_inode(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ return obj->obj_id; ++} ++ ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ return DT_DIR; ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return DT_LNK; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ if (S_ISFIFO(obj->yst_mode)) ++ return DT_FIFO; ++ if (S_ISCHR(obj->yst_mode)) ++ return DT_CHR; ++ if (S_ISBLK(obj->yst_mode)) ++ return DT_BLK; ++ if (S_ISSOCK(obj->yst_mode)) ++ return DT_SOCK; ++ default: ++ return DT_REG; ++ break; ++ } ++} ++ ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) ++ return yaffs_clone_str(obj->variant.symlink_variant.alias); ++ else ++ return yaffs_clone_str(_Y("")); ++} ++ ++/*--------------------------- Initialisation code -------------------------- */ ++ ++static int yaffs_check_dev_fns(const struct yaffs_dev *dev) ++{ ++ ++ /* Common functions, gotta have */ ++ if (!dev->param.erase_fn || !dev->param.initialise_flash_fn) ++ return 0; ++ ++#ifdef CONFIG_YAFFS_YAFFS2 ++ ++ /* Can use the "with tags" style interface for yaffs1 or yaffs2 */ ++ if (dev->param.write_chunk_tags_fn && ++ dev->param.read_chunk_tags_fn && ++ !dev->param.write_chunk_fn && ++ !dev->param.read_chunk_fn && ++ dev->param.bad_block_fn && dev->param.query_block_fn) ++ return 1; ++#endif ++ ++ /* Can use the "spare" style interface for yaffs1 */ ++ if (!dev->param.is_yaffs2 && ++ !dev->param.write_chunk_tags_fn && ++ !dev->param.read_chunk_tags_fn && ++ dev->param.write_chunk_fn && ++ dev->param.read_chunk_fn && ++ !dev->param.bad_block_fn && !dev->param.query_block_fn) ++ return 1; ++ ++ return 0; /* bad */ ++} ++ ++static int yaffs_create_initial_dir(struct yaffs_dev *dev) ++{ ++ /* Initialise the unlinked, deleted, root and lost and found directories */ ++ ++ dev->lost_n_found = dev->root_dir = NULL; ++ dev->unlinked_dir = dev->del_dir = NULL; ++ ++ dev->unlinked_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); ++ ++ dev->del_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); ++ ++ dev->root_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, ++ YAFFS_ROOT_MODE | S_IFDIR); ++ dev->lost_n_found = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, ++ YAFFS_LOSTNFOUND_MODE | S_IFDIR); ++ ++ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir ++ && dev->del_dir) { ++ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); ++ return YAFFS_OK; ++ } ++ ++ return YAFFS_FAIL; ++} ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev) ++{ ++ int init_failed = 0; ++ unsigned x; ++ int bits; ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_guts_initialise()" ); ++ ++ /* Check stuff that must be set */ ++ ++ if (!dev) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Need a device" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ dev->internal_start_block = dev->param.start_block; ++ dev->internal_end_block = dev->param.end_block; ++ dev->block_offset = 0; ++ dev->chunk_offset = 0; ++ dev->n_free_chunks = 0; ++ ++ dev->gc_block = 0; ++ ++ if (dev->param.start_block == 0) { ++ dev->internal_start_block = dev->param.start_block + 1; ++ dev->internal_end_block = dev->param.end_block + 1; ++ dev->block_offset = 1; ++ dev->chunk_offset = dev->param.chunks_per_block; ++ } ++ ++ /* Check geometry parameters. */ ++ ++ if ((!dev->param.inband_tags && dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 1024) || ++ (!dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 512) || ++ (dev->param.inband_tags && !dev->param.is_yaffs2) || ++ dev->param.chunks_per_block < 2 || ++ dev->param.n_reserved_blocks < 2 || ++ dev->internal_start_block <= 0 || ++ dev->internal_end_block <= 0 || ++ dev->internal_end_block <= ++ (dev->internal_start_block + dev->param.n_reserved_blocks + 2) ++ ) { ++ /* otherwise it is too small */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", ++ dev->param.total_bytes_per_chunk, ++ dev->param.is_yaffs2 ? "2" : "", ++ dev->param.inband_tags); ++ return YAFFS_FAIL; ++ } ++ ++ if (yaffs_init_nand(dev) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); ++ return YAFFS_FAIL; ++ } ++ ++ /* Sort out space for inband tags, if required */ ++ if (dev->param.inband_tags) ++ dev->data_bytes_per_chunk = ++ dev->param.total_bytes_per_chunk - ++ sizeof(struct yaffs_packed_tags2_tags_only); ++ else ++ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; ++ ++ /* Got the right mix of functions? */ ++ if (!yaffs_check_dev_fns(dev)) { ++ /* Function missing */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "device function(s) missing or wrong"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ if (dev->is_mounted) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); ++ return YAFFS_FAIL; ++ } ++ ++ /* Finished with most checks. One or two more checks happen later on too. */ ++ ++ dev->is_mounted = 1; ++ ++ /* OK now calculate a few things for the device */ ++ ++ /* ++ * Calculate all the chunk size manipulation numbers: ++ */ ++ x = dev->data_bytes_per_chunk; ++ /* We always use dev->chunk_shift and dev->chunk_div */ ++ dev->chunk_shift = calc_shifts(x); ++ x >>= dev->chunk_shift; ++ dev->chunk_div = x; ++ /* We only use chunk mask if chunk_div is 1 */ ++ dev->chunk_mask = (1 << dev->chunk_shift) - 1; ++ ++ /* ++ * Calculate chunk_grp_bits. ++ * We need to find the next power of 2 > than internal_end_block ++ */ ++ ++ x = dev->param.chunks_per_block * (dev->internal_end_block + 1); ++ ++ bits = calc_shifts_ceiling(x); ++ ++ /* Set up tnode width if wide tnodes are enabled. */ ++ if (!dev->param.wide_tnodes_disabled) { ++ /* bits must be even so that we end up with 32-bit words */ ++ if (bits & 1) ++ bits++; ++ if (bits < 16) ++ dev->tnode_width = 16; ++ else ++ dev->tnode_width = bits; ++ } else { ++ dev->tnode_width = 16; ++ } ++ ++ dev->tnode_mask = (1 << dev->tnode_width) - 1; ++ ++ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), ++ * so if the bitwidth of the ++ * chunk range we're using is greater than 16 we need ++ * to figure out chunk shift and chunk_grp_size ++ */ ++ ++ if (bits <= dev->tnode_width) ++ dev->chunk_grp_bits = 0; ++ else ++ dev->chunk_grp_bits = bits - dev->tnode_width; ++ ++ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; ++ if (dev->tnode_size < sizeof(struct yaffs_tnode)) ++ dev->tnode_size = sizeof(struct yaffs_tnode); ++ ++ dev->chunk_grp_size = 1 << dev->chunk_grp_bits; ++ ++ if (dev->param.chunks_per_block < dev->chunk_grp_size) { ++ /* We have a problem because the soft delete won't work if ++ * the chunk group size > chunks per block. ++ * This can be remedied by using larger "virtual blocks". ++ */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ /* OK, we've finished verifying the device, lets continue with initialisation */ ++ ++ /* More device initialisation */ ++ dev->all_gcs = 0; ++ dev->passive_gc_count = 0; ++ dev->oldest_dirty_gc_count = 0; ++ dev->bg_gcs = 0; ++ dev->gc_block_finder = 0; ++ dev->buffered_block = -1; ++ dev->doing_buffered_block_rewrite = 0; ++ dev->n_deleted_files = 0; ++ dev->n_bg_deletions = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_ecc_fixed = 0; ++ dev->n_ecc_unfixed = 0; ++ dev->n_tags_ecc_fixed = 0; ++ dev->n_tags_ecc_unfixed = 0; ++ dev->n_erase_failures = 0; ++ dev->n_erased_blocks = 0; ++ dev->gc_disable = 0; ++ dev->has_pending_prioritised_gc = 1; /* Assume the worst for now, will get fixed on first GC */ ++ INIT_LIST_HEAD(&dev->dirty_dirs); ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ ++ /* Initialise temporary buffers and caches. */ ++ if (!yaffs_init_tmp_buffers(dev)) ++ init_failed = 1; ++ ++ dev->cache = NULL; ++ dev->gc_cleanup_list = NULL; ++ ++ if (!init_failed && dev->param.n_caches > 0) { ++ int i; ++ void *buf; ++ int cache_bytes = ++ dev->param.n_caches * sizeof(struct yaffs_cache); ++ ++ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) ++ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; ++ ++ dev->cache = kmalloc(cache_bytes, GFP_NOFS); ++ ++ buf = (u8 *) dev->cache; ++ ++ if (dev->cache) ++ memset(dev->cache, 0, cache_bytes); ++ ++ for (i = 0; i < dev->param.n_caches && buf; i++) { ++ dev->cache[i].object = NULL; ++ dev->cache[i].last_use = 0; ++ dev->cache[i].dirty = 0; ++ dev->cache[i].data = buf = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ } ++ if (!buf) ++ init_failed = 1; ++ ++ dev->cache_last_use = 0; ++ } ++ ++ dev->cache_hits = 0; ++ ++ if (!init_failed) { ++ dev->gc_cleanup_list = ++ kmalloc(dev->param.chunks_per_block * sizeof(u32), ++ GFP_NOFS); ++ if (!dev->gc_cleanup_list) ++ init_failed = 1; ++ } ++ ++ if (dev->param.is_yaffs2) ++ dev->param.use_header_file_size = 1; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed) { ++ /* Now scan the flash. */ ++ if (dev->param.is_yaffs2) { ++ if (yaffs2_checkpt_restore(dev)) { ++ yaffs_check_obj_details_loaded(dev->root_dir); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, ++ "yaffs: restored from checkpoint" ++ ); ++ } else { ++ ++ /* Clean up the mess caused by an aborted checkpoint load ++ * and scan backwards. ++ */ ++ yaffs_deinit_blocks(dev); ++ ++ yaffs_deinit_tnodes_and_objs(dev); ++ ++ dev->n_erased_blocks = 0; ++ dev->n_free_chunks = 0; ++ dev->alloc_block = -1; ++ dev->alloc_page = -1; ++ dev->n_deleted_files = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_bg_deletions = 0; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed ++ && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && !yaffs2_scan_backwards(dev)) ++ init_failed = 1; ++ } ++ } else if (!yaffs1_scan(dev)) { ++ init_failed = 1; ++ } ++ ++ yaffs_strip_deleted_objs(dev); ++ yaffs_fix_hanging_objs(dev); ++ if (dev->param.empty_lost_n_found) ++ yaffs_empty_l_n_f(dev); ++ } ++ ++ if (init_failed) { ++ /* Clean up the mess */ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() aborted."); ++ ++ yaffs_deinitialise(dev); ++ return YAFFS_FAIL; ++ } ++ ++ /* Zero out stats */ ++ dev->n_page_reads = 0; ++ dev->n_page_writes = 0; ++ dev->n_erasures = 0; ++ dev->n_gc_copies = 0; ++ dev->n_retired_writes = 0; ++ ++ dev->n_retired_blocks = 0; ++ ++ yaffs_verify_free_chunks(dev); ++ yaffs_verify_blocks(dev); ++ ++ /* Clean up any aborted checkpoint data */ ++ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() done."); ++ return YAFFS_OK; ++ ++} ++ ++void yaffs_deinitialise(struct yaffs_dev *dev) ++{ ++ if (dev->is_mounted) { ++ int i; ++ ++ yaffs_deinit_blocks(dev); ++ yaffs_deinit_tnodes_and_objs(dev); ++ if (dev->param.n_caches > 0 && dev->cache) { ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].data) ++ kfree(dev->cache[i].data); ++ dev->cache[i].data = NULL; ++ } ++ ++ kfree(dev->cache); ++ dev->cache = NULL; ++ } ++ ++ kfree(dev->gc_cleanup_list); ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) ++ kfree(dev->temp_buffer[i].buffer); ++ ++ dev->is_mounted = 0; ++ ++ if (dev->param.deinitialise_flash_fn) ++ dev->param.deinitialise_flash_fn(dev); ++ } ++} ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev) ++{ ++ int n_free = 0; ++ int b; ++ ++ struct yaffs_block_info *blk; ++ ++ blk = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ switch (blk->block_state) { ++ case YAFFS_BLOCK_STATE_EMPTY: ++ case YAFFS_BLOCK_STATE_ALLOCATING: ++ case YAFFS_BLOCK_STATE_COLLECTING: ++ case YAFFS_BLOCK_STATE_FULL: ++ n_free += ++ (dev->param.chunks_per_block - blk->pages_in_use + ++ blk->soft_del_pages); ++ break; ++ default: ++ break; ++ } ++ blk++; ++ } ++ ++ return n_free; ++} ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev) ++{ ++ /* This is what we report to the outside world */ ++ ++ int n_free; ++ int n_dirty_caches; ++ int blocks_for_checkpt; ++ int i; ++ ++ n_free = dev->n_free_chunks; ++ n_free += dev->n_deleted_files; ++ ++ /* Now count the number of dirty chunks in the cache and subtract those */ ++ ++ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].dirty) ++ n_dirty_caches++; ++ } ++ ++ n_free -= n_dirty_caches; ++ ++ n_free -= ++ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); ++ ++ /* Now we figure out how much to reserve for the checkpoint and report that... */ ++ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); ++ ++ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); ++ ++ if (n_free < 0) ++ n_free = 0; ++ ++ return n_free; ++ ++} +diff --git a/fs/yaffs2/yaffs_guts.h b/fs/yaffs2/yaffs_guts.h +new file mode 100644 +index 00000000..307eba28 +--- /dev/null ++++ b/fs/yaffs2/yaffs_guts.h +@@ -0,0 +1,915 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GUTS_H__ ++#define __YAFFS_GUTS_H__ ++ ++#include "yportenv.h" ++ ++#define YAFFS_OK 1 ++#define YAFFS_FAIL 0 ++ ++/* Give us a Y=0x59, ++ * Give us an A=0x41, ++ * Give us an FF=0xFF ++ * Give us an S=0x53 ++ * And what have we got... ++ */ ++#define YAFFS_MAGIC 0x5941FF53 ++ ++#define YAFFS_NTNODES_LEVEL0 16 ++#define YAFFS_TNODES_LEVEL0_BITS 4 ++#define YAFFS_TNODES_LEVEL0_MASK 0xf ++ ++#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) ++#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) ++#define YAFFS_TNODES_INTERNAL_MASK 0x7 ++#define YAFFS_TNODES_MAX_LEVEL 6 ++ ++#ifndef CONFIG_YAFFS_NO_YAFFS1 ++#define YAFFS_BYTES_PER_SPARE 16 ++#define YAFFS_BYTES_PER_CHUNK 512 ++#define YAFFS_CHUNK_SIZE_SHIFT 9 ++#define YAFFS_CHUNKS_PER_BLOCK 32 ++#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) ++#endif ++ ++#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 ++#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 ++ ++#define YAFFS_MAX_CHUNK_ID 0x000FFFFF ++ ++#define YAFFS_ALLOCATION_NOBJECTS 100 ++#define YAFFS_ALLOCATION_NTNODES 100 ++#define YAFFS_ALLOCATION_NLINKS 100 ++ ++#define YAFFS_NOBJECT_BUCKETS 256 ++ ++#define YAFFS_OBJECT_SPACE 0x40000 ++#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE -1) ++ ++#define YAFFS_CHECKPOINT_VERSION 4 ++ ++#ifdef CONFIG_YAFFS_UNICODE ++#define YAFFS_MAX_NAME_LENGTH 127 ++#define YAFFS_MAX_ALIAS_LENGTH 79 ++#else ++#define YAFFS_MAX_NAME_LENGTH 255 ++#define YAFFS_MAX_ALIAS_LENGTH 159 ++#endif ++ ++#define YAFFS_SHORT_NAME_LENGTH 15 ++ ++/* Some special object ids for pseudo objects */ ++#define YAFFS_OBJECTID_ROOT 1 ++#define YAFFS_OBJECTID_LOSTNFOUND 2 ++#define YAFFS_OBJECTID_UNLINKED 3 ++#define YAFFS_OBJECTID_DELETED 4 ++ ++/* Pseudo object ids for checkpointing */ ++#define YAFFS_OBJECTID_SB_HEADER 0x10 ++#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 ++#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 ++ ++#define YAFFS_MAX_SHORT_OP_CACHES 20 ++ ++#define YAFFS_N_TEMP_BUFFERS 6 ++ ++/* We limit the number attempts at sucessfully saving a chunk of data. ++ * Small-page devices have 32 pages per block; large-page devices have 64. ++ * Default to something in the order of 5 to 10 blocks worth of chunks. ++ */ ++#define YAFFS_WR_ATTEMPTS (5*64) ++ ++/* Sequence numbers are used in YAFFS2 to determine block allocation order. ++ * The range is limited slightly to help distinguish bad numbers from good. ++ * This also allows us to perhaps in the future use special numbers for ++ * special purposes. ++ * EFFFFF00 allows the allocation of 8 blocks per second (~1Mbytes) for 15 years, ++ * and is a larger number than the lifetime of a 2GB device. ++ */ ++#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 ++#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xEFFFFF00 ++ ++/* Special sequence number for bad block that failed to be marked bad */ ++#define YAFFS_SEQUENCE_BAD_BLOCK 0xFFFF0000 ++ ++/* ChunkCache is used for short read/write operations.*/ ++struct yaffs_cache { ++ struct yaffs_obj *object; ++ int chunk_id; ++ int last_use; ++ int dirty; ++ int n_bytes; /* Only valid if the cache is dirty */ ++ int locked; /* Can't push out or flush while locked. */ ++ u8 *data; ++}; ++ ++/* Tags structures in RAM ++ * NB This uses bitfield. Bitfields should not straddle a u32 boundary otherwise ++ * the structure size will get blown out. ++ */ ++ ++#ifndef CONFIG_YAFFS_NO_YAFFS1 ++struct yaffs_tags { ++ unsigned chunk_id:20; ++ unsigned serial_number:2; ++ unsigned n_bytes_lsb:10; ++ unsigned obj_id:18; ++ unsigned ecc:12; ++ unsigned n_bytes_msb:2; ++}; ++ ++union yaffs_tags_union { ++ struct yaffs_tags as_tags; ++ u8 as_bytes[8]; ++}; ++ ++#endif ++ ++/* Stuff used for extended tags in YAFFS2 */ ++ ++enum yaffs_ecc_result { ++ YAFFS_ECC_RESULT_UNKNOWN, ++ YAFFS_ECC_RESULT_NO_ERROR, ++ YAFFS_ECC_RESULT_FIXED, ++ YAFFS_ECC_RESULT_UNFIXED ++}; ++ ++enum yaffs_obj_type { ++ YAFFS_OBJECT_TYPE_UNKNOWN, ++ YAFFS_OBJECT_TYPE_FILE, ++ YAFFS_OBJECT_TYPE_SYMLINK, ++ YAFFS_OBJECT_TYPE_DIRECTORY, ++ YAFFS_OBJECT_TYPE_HARDLINK, ++ YAFFS_OBJECT_TYPE_SPECIAL ++}; ++ ++#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL ++ ++struct yaffs_ext_tags { ++ ++ unsigned validity0; ++ unsigned chunk_used; /* Status of the chunk: used or unused */ ++ unsigned obj_id; /* If 0 then this is not part of an object (unused) */ ++ unsigned chunk_id; /* If 0 then this is a header, else a data chunk */ ++ unsigned n_bytes; /* Only valid for data chunks */ ++ ++ /* The following stuff only has meaning when we read */ ++ enum yaffs_ecc_result ecc_result; ++ unsigned block_bad; ++ ++ /* YAFFS 1 stuff */ ++ unsigned is_deleted; /* The chunk is marked deleted */ ++ unsigned serial_number; /* Yaffs1 2-bit serial number */ ++ ++ /* YAFFS2 stuff */ ++ unsigned seq_number; /* The sequence number of this block */ ++ ++ /* Extra info if this is an object header (YAFFS2 only) */ ++ ++ unsigned extra_available; /* There is extra info available if this is not zero */ ++ unsigned extra_parent_id; /* The parent object */ ++ unsigned extra_is_shrink; /* Is it a shrink header? */ ++ unsigned extra_shadows; /* Does this shadow another object? */ ++ ++ enum yaffs_obj_type extra_obj_type; /* What object type? */ ++ ++ unsigned extra_length; /* Length if it is a file */ ++ unsigned extra_equiv_id; /* Equivalent object Id if it is a hard link */ ++ ++ unsigned validity1; ++ ++}; ++ ++/* Spare structure for YAFFS1 */ ++struct yaffs_spare { ++ u8 tb0; ++ u8 tb1; ++ u8 tb2; ++ u8 tb3; ++ u8 page_status; /* set to 0 to delete the chunk */ ++ u8 block_status; ++ u8 tb4; ++ u8 tb5; ++ u8 ecc1[3]; ++ u8 tb6; ++ u8 tb7; ++ u8 ecc2[3]; ++}; ++ ++/*Special structure for passing through to mtd */ ++struct yaffs_nand_spare { ++ struct yaffs_spare spare; ++ int eccres1; ++ int eccres2; ++}; ++ ++/* Block data in RAM */ ++ ++enum yaffs_block_state { ++ YAFFS_BLOCK_STATE_UNKNOWN = 0, ++ ++ YAFFS_BLOCK_STATE_SCANNING, ++ /* Being scanned */ ++ ++ YAFFS_BLOCK_STATE_NEEDS_SCANNING, ++ /* The block might have something on it (ie it is allocating or full, perhaps empty) ++ * but it needs to be scanned to determine its true state. ++ * This state is only valid during scanning. ++ * NB We tolerate empty because the pre-scanner might be incapable of deciding ++ * However, if this state is returned on a YAFFS2 device, then we expect a sequence number ++ */ ++ ++ YAFFS_BLOCK_STATE_EMPTY, ++ /* This block is empty */ ++ ++ YAFFS_BLOCK_STATE_ALLOCATING, ++ /* This block is partially allocated. ++ * At least one page holds valid data. ++ * This is the one currently being used for page ++ * allocation. Should never be more than one of these. ++ * If a block is only partially allocated at mount it is treated as full. ++ */ ++ ++ YAFFS_BLOCK_STATE_FULL, ++ /* All the pages in this block have been allocated. ++ * If a block was only partially allocated when mounted we treat ++ * it as fully allocated. ++ */ ++ ++ YAFFS_BLOCK_STATE_DIRTY, ++ /* The block was full and now all chunks have been deleted. ++ * Erase me, reuse me. ++ */ ++ ++ YAFFS_BLOCK_STATE_CHECKPOINT, ++ /* This block is assigned to holding checkpoint data. */ ++ ++ YAFFS_BLOCK_STATE_COLLECTING, ++ /* This block is being garbage collected */ ++ ++ YAFFS_BLOCK_STATE_DEAD ++ /* This block has failed and is not in use */ ++}; ++ ++#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) ++ ++struct yaffs_block_info { ++ ++ int soft_del_pages:10; /* number of soft deleted pages */ ++ int pages_in_use:10; /* number of pages in use */ ++ unsigned block_state:4; /* One of the above block states. NB use unsigned because enum is sometimes an int */ ++ u32 needs_retiring:1; /* Data has failed on this block, need to get valid data off */ ++ /* and retire the block. */ ++ u32 skip_erased_check:1; /* If this is set we can skip the erased check on this block */ ++ u32 gc_prioritise:1; /* An ECC check or blank check has failed on this block. ++ It should be prioritised for GC */ ++ u32 chunk_error_strikes:3; /* How many times we've had ecc etc failures on this block and tried to reuse it */ ++ ++#ifdef CONFIG_YAFFS_YAFFS2 ++ u32 has_shrink_hdr:1; /* This block has at least one shrink object header */ ++ u32 seq_number; /* block sequence number for yaffs2 */ ++#endif ++ ++}; ++ ++/* -------------------------- Object structure -------------------------------*/ ++/* This is the object structure as stored on NAND */ ++ ++struct yaffs_obj_hdr { ++ enum yaffs_obj_type type; ++ ++ /* Apply to everything */ ++ int parent_obj_id; ++ u16 sum_no_longer_used; /* checksum of name. No longer used */ ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ /* The following apply to directories, files, symlinks - not hard links */ ++ u32 yst_mode; /* protection */ ++ ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++ ++ /* File size applies to files only */ ++ int file_size; ++ ++ /* Equivalent object id applies to hard links only. */ ++ int equiv_id; ++ ++ /* Alias is for symlinks only. */ ++ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; ++ ++ u32 yst_rdev; /* device stuff for block and char devices (major/min) */ ++ ++ u32 win_ctime[2]; ++ u32 win_atime[2]; ++ u32 win_mtime[2]; ++ ++ u32 inband_shadowed_obj_id; ++ u32 inband_is_shrink; ++ ++ u32 reserved[2]; ++ int shadows_obj; /* This object header shadows the specified object if > 0 */ ++ ++ /* is_shrink applies to object headers written when we shrink the file (ie resize) */ ++ u32 is_shrink; ++ ++}; ++ ++/*--------------------------- Tnode -------------------------- */ ++ ++struct yaffs_tnode { ++ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; ++}; ++ ++/*------------------------ Object -----------------------------*/ ++/* An object can be one of: ++ * - a directory (no data, has children links ++ * - a regular file (data.... not prunes :->). ++ * - a symlink [symbolic link] (the alias). ++ * - a hard link ++ */ ++ ++struct yaffs_file_var { ++ u32 file_size; ++ u32 scanned_size; ++ u32 shrink_size; ++ int top_level; ++ struct yaffs_tnode *top; ++}; ++ ++struct yaffs_dir_var { ++ struct list_head children; /* list of child links */ ++ struct list_head dirty; /* Entry for list of dirty directories */ ++}; ++ ++struct yaffs_symlink_var { ++ YCHAR *alias; ++}; ++ ++struct yaffs_hardlink_var { ++ struct yaffs_obj *equiv_obj; ++ u32 equiv_id; ++}; ++ ++union yaffs_obj_var { ++ struct yaffs_file_var file_variant; ++ struct yaffs_dir_var dir_variant; ++ struct yaffs_symlink_var symlink_variant; ++ struct yaffs_hardlink_var hardlink_variant; ++}; ++ ++struct yaffs_obj { ++ u8 deleted:1; /* This should only apply to unlinked files. */ ++ u8 soft_del:1; /* it has also been soft deleted */ ++ u8 unlinked:1; /* An unlinked file. The file should be in the unlinked directory. */ ++ u8 fake:1; /* A fake object has no presence on NAND. */ ++ u8 rename_allowed:1; /* Some objects are not allowed to be renamed. */ ++ u8 unlink_allowed:1; ++ u8 dirty:1; /* the object needs to be written to flash */ ++ u8 valid:1; /* When the file system is being loaded up, this ++ * object might be created before the data ++ * is available (ie. file data records appear before the header). ++ */ ++ u8 lazy_loaded:1; /* This object has been lazy loaded and is missing some detail */ ++ ++ u8 defered_free:1; /* For Linux kernel. Object is removed from NAND, but is ++ * still in the inode cache. Free of object is defered. ++ * until the inode is released. ++ */ ++ u8 being_created:1; /* This object is still being created so skip some checks. */ ++ u8 is_shadowed:1; /* This object is shadowed on the way to being renamed. */ ++ ++ u8 xattr_known:1; /* We know if this has object has xattribs or not. */ ++ u8 has_xattr:1; /* This object has xattribs. Valid if xattr_known. */ ++ ++ u8 serial; /* serial number of chunk in NAND. Cached here */ ++ u16 sum; /* sum of the name to speed searching */ ++ ++ struct yaffs_dev *my_dev; /* The device I'm on */ ++ ++ struct list_head hash_link; /* list of objects in this hash bucket */ ++ ++ struct list_head hard_links; /* all the equivalent hard linked objects */ ++ ++ /* directory structure stuff */ ++ /* also used for linking up the free list */ ++ struct yaffs_obj *parent; ++ struct list_head siblings; ++ ++ /* Where's my object header in NAND? */ ++ int hdr_chunk; ++ ++ int n_data_chunks; /* Number of data chunks attached to the file. */ ++ ++ u32 obj_id; /* the object id value */ ++ ++ u32 yst_mode; ++ ++#ifndef CONFIG_YAFFS_NO_SHORT_NAMES ++ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; ++#endif ++ ++#ifdef CONFIG_YAFFS_WINCE ++ u32 win_ctime[2]; ++ u32 win_mtime[2]; ++ u32 win_atime[2]; ++#else ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++#endif ++ ++ u32 yst_rdev; ++ ++ void *my_inode; ++ ++ enum yaffs_obj_type variant_type; ++ ++ union yaffs_obj_var variant; ++ ++}; ++ ++struct yaffs_obj_bucket { ++ struct list_head list; ++ int count; ++}; ++ ++/* yaffs_checkpt_obj holds the definition of an object as dumped ++ * by checkpointing. ++ */ ++ ++struct yaffs_checkpt_obj { ++ int struct_type; ++ u32 obj_id; ++ u32 parent_id; ++ int hdr_chunk; ++ enum yaffs_obj_type variant_type:3; ++ u8 deleted:1; ++ u8 soft_del:1; ++ u8 unlinked:1; ++ u8 fake:1; ++ u8 rename_allowed:1; ++ u8 unlink_allowed:1; ++ u8 serial; ++ int n_data_chunks; ++ u32 size_or_equiv_obj; ++}; ++ ++/*--------------------- Temporary buffers ---------------- ++ * ++ * These are chunk-sized working buffers. Each device has a few ++ */ ++ ++struct yaffs_buffer { ++ u8 *buffer; ++ int line; /* track from whence this buffer was allocated */ ++ int max_line; ++}; ++ ++/*----------------- Device ---------------------------------*/ ++ ++struct yaffs_param { ++ const YCHAR *name; ++ ++ /* ++ * Entry parameters set up way early. Yaffs sets up the rest. ++ * The structure should be zeroed out before use so that unused ++ * and defualt values are zero. ++ */ ++ ++ int inband_tags; /* Use unband tags */ ++ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to be a power of 2 */ ++ int chunks_per_block; /* does not need to be a power of 2 */ ++ int spare_bytes_per_chunk; /* spare area size */ ++ int start_block; /* Start block we're allowed to use */ ++ int end_block; /* End block we're allowed to use */ ++ int n_reserved_blocks; /* We want this tuneable so that we can reduce */ ++ /* reserved blocks on NOR and RAM. */ ++ ++ int n_caches; /* If <= 0, then short op caching is disabled, else ++ * the number of short op caches (don't use too many). ++ * 10 to 20 is a good bet. ++ */ ++ int use_nand_ecc; /* Flag to decide whether or not to use NANDECC on data (yaffs1) */ ++ int no_tags_ecc; /* Flag to decide whether or not to do ECC on packed tags (yaffs2) */ ++ ++ int is_yaffs2; /* Use yaffs2 mode on this device */ ++ ++ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ ++ ++ int refresh_period; /* How often we should check to do a block refresh */ ++ ++ /* Checkpoint control. Can be set before or after initialisation */ ++ u8 skip_checkpt_rd; ++ u8 skip_checkpt_wr; ++ ++ int enable_xattr; /* Enable xattribs */ ++ ++ /* NAND access functions (Must be set before calling YAFFS) */ ++ ++ int (*write_chunk_fn) (struct yaffs_dev * dev, ++ int nand_chunk, const u8 * data, ++ const struct yaffs_spare * spare); ++ int (*read_chunk_fn) (struct yaffs_dev * dev, ++ int nand_chunk, u8 * data, ++ struct yaffs_spare * spare); ++ int (*erase_fn) (struct yaffs_dev * dev, int flash_block); ++ int (*initialise_flash_fn) (struct yaffs_dev * dev); ++ int (*deinitialise_flash_fn) (struct yaffs_dev * dev); ++ ++#ifdef CONFIG_YAFFS_YAFFS2 ++ int (*write_chunk_tags_fn) (struct yaffs_dev * dev, ++ int nand_chunk, const u8 * data, ++ const struct yaffs_ext_tags * tags); ++ int (*read_chunk_tags_fn) (struct yaffs_dev * dev, ++ int nand_chunk, u8 * data, ++ struct yaffs_ext_tags * tags); ++ int (*bad_block_fn) (struct yaffs_dev * dev, int block_no); ++ int (*query_block_fn) (struct yaffs_dev * dev, int block_no, ++ enum yaffs_block_state * state, ++ u32 * seq_number); ++#endif ++ ++ /* The remove_obj_fn function must be supplied by OS flavours that ++ * need it. ++ * yaffs direct uses it to implement the faster readdir. ++ * Linux uses it to protect the directory during unlocking. ++ */ ++ void (*remove_obj_fn) (struct yaffs_obj * obj); ++ ++ /* Callback to mark the superblock dirty */ ++ void (*sb_dirty_fn) (struct yaffs_dev * dev); ++ ++ /* Callback to control garbage collection. */ ++ unsigned (*gc_control) (struct yaffs_dev * dev); ++ ++ /* Debug control flags. Don't use unless you know what you're doing */ ++ int use_header_file_size; /* Flag to determine if we should use file sizes from the header */ ++ int disable_lazy_load; /* Disable lazy loading on this device */ ++ int wide_tnodes_disabled; /* Set to disable wide tnodes */ ++ int disable_soft_del; /* yaffs 1 only: Set to disable the use of softdeletion. */ ++ ++ int defered_dir_update; /* Set to defer directory updates */ ++ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ int auto_unicode; ++#endif ++ int always_check_erased; /* Force chunk erased check always on */ ++}; ++ ++struct yaffs_dev { ++ struct yaffs_param param; ++ ++ /* Context storage. Holds extra OS specific data for this device */ ++ ++ void *os_context; ++ void *driver_context; ++ ++ struct list_head dev_list; ++ ++ /* Runtime parameters. Set up by YAFFS. */ ++ int data_bytes_per_chunk; ++ ++ /* Non-wide tnode stuff */ ++ u16 chunk_grp_bits; /* Number of bits that need to be resolved if ++ * the tnodes are not wide enough. ++ */ ++ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ ++ ++ /* Stuff to support wide tnodes */ ++ u32 tnode_width; ++ u32 tnode_mask; ++ u32 tnode_size; ++ ++ /* Stuff for figuring out file offset to chunk conversions */ ++ u32 chunk_shift; /* Shift value */ ++ u32 chunk_div; /* Divisor after shifting: 1 for power-of-2 sizes */ ++ u32 chunk_mask; /* Mask to use for power-of-2 case */ ++ ++ int is_mounted; ++ int read_only; ++ int is_checkpointed; ++ ++ /* Stuff to support block offsetting to support start block zero */ ++ int internal_start_block; ++ int internal_end_block; ++ int block_offset; ++ int chunk_offset; ++ ++ /* Runtime checkpointing stuff */ ++ int checkpt_page_seq; /* running sequence number of checkpoint pages */ ++ int checkpt_byte_count; ++ int checkpt_byte_offs; ++ u8 *checkpt_buffer; ++ int checkpt_open_write; ++ int blocks_in_checkpt; ++ int checkpt_cur_chunk; ++ int checkpt_cur_block; ++ int checkpt_next_block; ++ int *checkpt_block_list; ++ int checkpt_max_blocks; ++ u32 checkpt_sum; ++ u32 checkpt_xor; ++ ++ int checkpoint_blocks_required; /* Number of blocks needed to store current checkpoint set */ ++ ++ /* Block Info */ ++ struct yaffs_block_info *block_info; ++ u8 *chunk_bits; /* bitmap of chunks in use */ ++ unsigned block_info_alt:1; /* was allocated using alternative strategy */ ++ unsigned chunk_bits_alt:1; /* was allocated using alternative strategy */ ++ int chunk_bit_stride; /* Number of bytes of chunk_bits per block. ++ * Must be consistent with chunks_per_block. ++ */ ++ ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int alloc_block_finder; /* Used to search for next allocation block */ ++ ++ /* Object and Tnode memory management */ ++ void *allocator; ++ int n_obj; ++ int n_tnodes; ++ ++ int n_hardlinks; ++ ++ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; ++ u32 bucket_finder; ++ ++ int n_free_chunks; ++ ++ /* Garbage collection control */ ++ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ ++ u32 n_clean_ups; ++ ++ unsigned has_pending_prioritised_gc; /* We think this device might have pending prioritised gcs */ ++ unsigned gc_disable; ++ unsigned gc_block_finder; ++ unsigned gc_dirtiest; ++ unsigned gc_pages_in_use; ++ unsigned gc_not_done; ++ unsigned gc_block; ++ unsigned gc_chunk; ++ unsigned gc_skip; ++ ++ /* Special directories */ ++ struct yaffs_obj *root_dir; ++ struct yaffs_obj *lost_n_found; ++ ++ /* Buffer areas for storing data to recover from write failures TODO ++ * u8 buffered_data[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK]; ++ * struct yaffs_spare buffered_spare[YAFFS_CHUNKS_PER_BLOCK]; ++ */ ++ ++ int buffered_block; /* Which block is buffered here? */ ++ int doing_buffered_block_rewrite; ++ ++ struct yaffs_cache *cache; ++ int cache_last_use; ++ ++ /* Stuff for background deletion and unlinked files. */ ++ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted files live. */ ++ struct yaffs_obj *del_dir; /* Directory where deleted objects are sent to disappear. */ ++ struct yaffs_obj *unlinked_deletion; /* Current file being background deleted. */ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* Temporary buffer management */ ++ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; ++ int max_temp; ++ int temp_in_use; ++ int unmanaged_buffer_allocs; ++ int unmanaged_buffer_deallocs; ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently allocating block */ ++ unsigned oldest_dirty_seq; ++ unsigned oldest_dirty_block; ++ ++ /* Block refreshing */ ++ int refresh_skip; /* A skip down counter. Refresh happens when this gets to zero. */ ++ ++ /* Dirty directory handling */ ++ struct list_head dirty_dirs; /* List of dirty directories */ ++ ++ /* Statistcs */ ++ u32 n_page_writes; ++ u32 n_page_reads; ++ u32 n_erasures; ++ u32 n_erase_failures; ++ u32 n_gc_copies; ++ u32 all_gcs; ++ u32 passive_gc_count; ++ u32 oldest_dirty_gc_count; ++ u32 n_gc_blocks; ++ u32 bg_gcs; ++ u32 n_retired_writes; ++ u32 n_retired_blocks; ++ u32 n_ecc_fixed; ++ u32 n_ecc_unfixed; ++ u32 n_tags_ecc_fixed; ++ u32 n_tags_ecc_unfixed; ++ u32 n_deletions; ++ u32 n_unmarked_deletions; ++ u32 refresh_count; ++ u32 cache_hits; ++ ++}; ++ ++/* The CheckpointDevice structure holds the device information that changes at runtime and ++ * must be preserved over unmount/mount cycles. ++ */ ++struct yaffs_checkpt_dev { ++ int struct_type; ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int n_free_chunks; ++ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently allocating block */ ++ ++}; ++ ++struct yaffs_checkpt_validity { ++ int struct_type; ++ u32 magic; ++ u32 version; ++ u32 head; ++}; ++ ++struct yaffs_shadow_fixer { ++ int obj_id; ++ int shadowed_id; ++ struct yaffs_shadow_fixer *next; ++}; ++ ++/* Structure for doing xattr modifications */ ++struct yaffs_xattr_mod { ++ int set; /* If 0 then this is a deletion */ ++ const YCHAR *name; ++ const void *data; ++ int size; ++ int flags; ++ int result; ++}; ++ ++/*----------------------- YAFFS Functions -----------------------*/ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev); ++void yaffs_deinitialise(struct yaffs_dev *dev); ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, ++ struct yaffs_obj *new_dir, const YCHAR * new_name); ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); ++int yaffs_del_obj(struct yaffs_obj *obj); ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); ++int yaffs_get_obj_length(struct yaffs_obj *obj); ++int yaffs_get_obj_inode(struct yaffs_obj *obj); ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj); ++int yaffs_get_obj_link_count(struct yaffs_obj *obj); ++ ++/* File operations */ ++int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, ++ int n_bytes); ++int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR * name, u32 mode, u32 uid, ++ u32 gid); ++ ++int yaffs_flush_file(struct yaffs_obj *obj, int update_time, int data_sync); ++ ++/* Flushing and checkpointing */ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev); ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev); ++int yaffs_checkpoint_restore(struct yaffs_dev *dev); ++ ++/* Directory operations */ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR * name, ++ u32 mode, u32 uid, u32 gid); ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, ++ const YCHAR * name); ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); ++ ++/* Link operations */ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, ++ struct yaffs_obj *equiv_obj); ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); ++ ++/* Symlink operations */ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR * name, u32 mode, u32 uid, ++ u32 gid, const YCHAR * alias); ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); ++ ++/* Special inodes (fifos, sockets and devices) */ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR * name, u32 mode, u32 uid, ++ u32 gid, u32 rdev); ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, ++ const void *value, int size, int flags); ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, ++ int size); ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name); ++ ++/* Special directories */ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj); ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev); ++ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); ++ ++/* Debug dump */ ++int yaffs_dump_obj(struct yaffs_obj *obj); ++ ++void yaffs_guts_test(struct yaffs_dev *dev); ++ ++/* A few useful functions to be used within the core files*/ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn); ++int yaffs_check_ff(u8 * buffer, int n_bytes); ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev, int line_no); ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 * buffer, int line_no); ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type); ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan); ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name); ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh); ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); ++YCHAR *yaffs_clone_str(const YCHAR * str); ++void yaffs_link_fixup(struct yaffs_dev *dev, struct yaffs_obj *hard_list); ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR * name, ++ int force, int is_shrink, int shadows, ++ struct yaffs_xattr_mod *xop); ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning); ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn); ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 * buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev); ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id); ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos); ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj); ++#endif +diff --git a/fs/yaffs2/yaffs_linux.h b/fs/yaffs2/yaffs_linux.h +new file mode 100644 +index 00000000..3b508cbc +--- /dev/null ++++ b/fs/yaffs2/yaffs_linux.h +@@ -0,0 +1,41 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_LINUX_H__ ++#define __YAFFS_LINUX_H__ ++ ++#include "yportenv.h" ++ ++struct yaffs_linux_context { ++ struct list_head context_list; /* List of these we have mounted */ ++ struct yaffs_dev *dev; ++ struct super_block *super; ++ struct task_struct *bg_thread; /* Background thread for this device */ ++ int bg_running; ++ struct mutex gross_lock; /* Gross locking mutex*/ ++ u8 *spare_buffer; /* For mtdif2 use. Don't know the size of the buffer ++ * at compile time so we have to allocate it. ++ */ ++ struct list_head search_contexts; ++ void (*put_super_fn) (struct super_block * sb); ++ ++ struct task_struct *readdir_process; ++ unsigned mount_id; ++}; ++ ++#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) ++#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) ++ ++#endif +diff --git a/fs/yaffs2/yaffs_mtdif.c b/fs/yaffs2/yaffs_mtdif.c +new file mode 100644 +index 00000000..7cf53b3d +--- /dev/null ++++ b/fs/yaffs2/yaffs_mtdif.c +@@ -0,0 +1,54 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_mtdif.h" ++ ++#include "linux/mtd/mtd.h" ++#include "linux/types.h" ++#include "linux/time.h" ++#include "linux/mtd/nand.h" ++ ++#include "yaffs_linux.h" ++ ++int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ u32 addr = ++ ((loff_t) block_no) * dev->param.total_bytes_per_chunk ++ * dev->param.chunks_per_block; ++ struct erase_info ei; ++ ++ int retval = 0; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd->erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++int nandmtd_initialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_mtdif.h b/fs/yaffs2/yaffs_mtdif.h +new file mode 100644 +index 00000000..66650741 +--- /dev/null ++++ b/fs/yaffs2/yaffs_mtdif.h +@@ -0,0 +1,23 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_MTDIF_H__ ++#define __YAFFS_MTDIF_H__ ++ ++#include "yaffs_guts.h" ++ ++int nandmtd_erase_block(struct yaffs_dev *dev, int block_no); ++int nandmtd_initialise(struct yaffs_dev *dev); ++#endif +diff --git a/fs/yaffs2/yaffs_mtdif1.c b/fs/yaffs2/yaffs_mtdif1.c +new file mode 100644 +index 00000000..51083695 +--- /dev/null ++++ b/fs/yaffs2/yaffs_mtdif1.c +@@ -0,0 +1,330 @@ ++/* ++ * YAFFS: Yet another FFS. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This module provides the interface between yaffs_nand.c and the ++ * MTD API. This version is used when the MTD interface supports the ++ * 'mtd_oob_ops' style calls to read_oob and write_oob, circa 2.6.17, ++ * and we have small-page NAND device. ++ * ++ * These functions are invoked via function pointers in yaffs_nand.c. ++ * This replaces functionality provided by functions in yaffs_mtdif.c ++ * and the yaffs_tags compatability functions in yaffs_tagscompat.c that are ++ * called in yaffs_mtdif.c when the function pointers are NULL. ++ * We assume the MTD layer is performing ECC (use_nand_ecc is true). ++ */ ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_packedtags1.h" ++#include "yaffs_tagscompat.h" /* for yaffs_calc_tags_ecc */ ++#include "yaffs_linux.h" ++ ++#include "linux/kernel.h" ++#include "linux/version.h" ++#include "linux/types.h" ++#include "linux/mtd/mtd.h" ++ ++#ifndef CONFIG_YAFFS_9BYTE_TAGS ++# define YTAG1_SIZE 8 ++#else ++# define YTAG1_SIZE 9 ++#endif ++ ++/* Write a chunk (page) of data to NAND. ++ * ++ * Caller always provides ExtendedTags data which are converted to a more ++ * compact (packed) form for storage in NAND. A mini-ECC runs over the ++ * contents of the tags meta-data; used to valid the tags when read. ++ * ++ * - Pack ExtendedTags to packed_tags1 form ++ * - Compute mini-ECC for packed_tags1 ++ * - Write data and packed tags to NAND. ++ * ++ * Note: Due to the use of the packed_tags1 meta-data which does not include ++ * a full sequence number (as found in the larger packed_tags2 form) it is ++ * necessary for Yaffs to re-write a chunk/page (just once) to mark it as ++ * discarded and dirty. This is not ideal: newer NAND parts are supposed ++ * to be written just once. When Yaffs performs this operation, this ++ * function is called with a NULL data pointer -- calling MTD write_oob ++ * without data is valid usage (2.6.17). ++ * ++ * Any underlying MTD error results in YAFFS_FAIL. ++ * Returns YAFFS_OK or YAFFS_FAIL. ++ */ ++int nandmtd1_write_chunk_tags(struct yaffs_dev *dev, ++ int nand_chunk, const u8 * data, ++ const struct yaffs_ext_tags *etags) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int chunk_bytes = dev->data_bytes_per_chunk; ++ loff_t addr = ((loff_t) nand_chunk) * chunk_bytes; ++ struct mtd_oob_ops ops; ++ struct yaffs_packed_tags1 pt1; ++ int retval; ++ ++ /* we assume that packed_tags1 and struct yaffs_tags are compatible */ ++ compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12); ++ compile_time_assertion(sizeof(struct yaffs_tags) == 8); ++ ++ yaffs_pack_tags1(&pt1, etags); ++ yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1); ++ ++ /* When deleting a chunk, the upper layer provides only skeletal ++ * etags, one with is_deleted set. However, we need to update the ++ * tags, not erase them completely. So we use the NAND write property ++ * that only zeroed-bits stick and set tag bytes to all-ones and ++ * zero just the (not) deleted bit. ++ */ ++#ifndef CONFIG_YAFFS_9BYTE_TAGS ++ if (etags->is_deleted) { ++ memset(&pt1, 0xff, 8); ++ /* clear delete status bit to indicate deleted */ ++ pt1.deleted = 0; ++ } ++#else ++ ((u8 *) & pt1)[8] = 0xff; ++ if (etags->is_deleted) { ++ memset(&pt1, 0xff, 8); ++ /* zero page_status byte to indicate deleted */ ++ ((u8 *) & pt1)[8] = 0; ++ } ++#endif ++ ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OOB_AUTO; ++ ops.len = (data) ? chunk_bytes : 0; ++ ops.ooblen = YTAG1_SIZE; ++ ops.datbuf = (u8 *) data; ++ ops.oobbuf = (u8 *) & pt1; ++ ++ retval = mtd->write_oob(mtd, addr, &ops); ++ if (retval) { ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "write_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ } ++ return retval ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++/* Return with empty ExtendedTags but add ecc_result. ++ */ ++static int rettags(struct yaffs_ext_tags *etags, int ecc_result, int retval) ++{ ++ if (etags) { ++ memset(etags, 0, sizeof(*etags)); ++ etags->ecc_result = ecc_result; ++ } ++ return retval; ++} ++ ++/* Read a chunk (page) from NAND. ++ * ++ * Caller expects ExtendedTags data to be usable even on error; that is, ++ * all members except ecc_result and block_bad are zeroed. ++ * ++ * - Check ECC results for data (if applicable) ++ * - Check for blank/erased block (return empty ExtendedTags if blank) ++ * - Check the packed_tags1 mini-ECC (correct if necessary/possible) ++ * - Convert packed_tags1 to ExtendedTags ++ * - Update ecc_result and block_bad members to refect state. ++ * ++ * Returns YAFFS_OK or YAFFS_FAIL. ++ */ ++int nandmtd1_read_chunk_tags(struct yaffs_dev *dev, ++ int nand_chunk, u8 * data, ++ struct yaffs_ext_tags *etags) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int chunk_bytes = dev->data_bytes_per_chunk; ++ loff_t addr = ((loff_t) nand_chunk) * chunk_bytes; ++ int eccres = YAFFS_ECC_RESULT_NO_ERROR; ++ struct mtd_oob_ops ops; ++ struct yaffs_packed_tags1 pt1; ++ int retval; ++ int deleted; ++ ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OOB_AUTO; ++ ops.len = (data) ? chunk_bytes : 0; ++ ops.ooblen = YTAG1_SIZE; ++ ops.datbuf = data; ++ ops.oobbuf = (u8 *) & pt1; ++ ++ /* Read page and oob using MTD. ++ * Check status and determine ECC result. ++ */ ++ retval = mtd->read_oob(mtd, addr, &ops); ++ if (retval) { ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "read_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ } ++ ++ switch (retval) { ++ case 0: ++ /* no error */ ++ break; ++ ++ case -EUCLEAN: ++ /* MTD's ECC fixed the data */ ++ eccres = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ break; ++ ++ case -EBADMSG: ++ /* MTD's ECC could not fix the data */ ++ dev->n_ecc_unfixed++; ++ /* fall into... */ ++ default: ++ rettags(etags, YAFFS_ECC_RESULT_UNFIXED, 0); ++ etags->block_bad = (mtd->block_isbad) (mtd, addr); ++ return YAFFS_FAIL; ++ } ++ ++ /* Check for a blank/erased chunk. ++ */ ++ if (yaffs_check_ff((u8 *) & pt1, 8)) { ++ /* when blank, upper layers want ecc_result to be <= NO_ERROR */ ++ return rettags(etags, YAFFS_ECC_RESULT_NO_ERROR, YAFFS_OK); ++ } ++#ifndef CONFIG_YAFFS_9BYTE_TAGS ++ /* Read deleted status (bit) then return it to it's non-deleted ++ * state before performing tags mini-ECC check. pt1.deleted is ++ * inverted. ++ */ ++ deleted = !pt1.deleted; ++ pt1.deleted = 1; ++#else ++ deleted = (yaffs_count_bits(((u8 *) & pt1)[8]) < 7); ++#endif ++ ++ /* Check the packed tags mini-ECC and correct if necessary/possible. ++ */ ++ retval = yaffs_check_tags_ecc((struct yaffs_tags *)&pt1); ++ switch (retval) { ++ case 0: ++ /* no tags error, use MTD result */ ++ break; ++ case 1: ++ /* recovered tags-ECC error */ ++ dev->n_tags_ecc_fixed++; ++ if (eccres == YAFFS_ECC_RESULT_NO_ERROR) ++ eccres = YAFFS_ECC_RESULT_FIXED; ++ break; ++ default: ++ /* unrecovered tags-ECC error */ ++ dev->n_tags_ecc_unfixed++; ++ return rettags(etags, YAFFS_ECC_RESULT_UNFIXED, YAFFS_FAIL); ++ } ++ ++ /* Unpack the tags to extended form and set ECC result. ++ * [set should_be_ff just to keep yaffs_unpack_tags1 happy] ++ */ ++ pt1.should_be_ff = 0xFFFFFFFF; ++ yaffs_unpack_tags1(etags, &pt1); ++ etags->ecc_result = eccres; ++ ++ /* Set deleted state */ ++ etags->is_deleted = deleted; ++ return YAFFS_OK; ++} ++ ++/* Mark a block bad. ++ * ++ * This is a persistant state. ++ * Use of this function should be rare. ++ * ++ * Returns YAFFS_OK or YAFFS_FAIL. ++ */ ++int nandmtd1_mark_block_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->data_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "marking block %d bad", block_no); ++ ++ retval = mtd->block_markbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++/* Check any MTD prerequists. ++ * ++ * Returns YAFFS_OK or YAFFS_FAIL. ++ */ ++static int nandmtd1_test_prerequists(struct mtd_info *mtd) ++{ ++ /* 2.6.18 has mtd->ecclayout->oobavail */ ++ /* 2.6.21 has mtd->ecclayout->oobavail and mtd->oobavail */ ++ int oobavail = mtd->ecclayout->oobavail; ++ ++ if (oobavail < YTAG1_SIZE) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "mtd device has only %d bytes for tags, need %d", ++ oobavail, YTAG1_SIZE); ++ return YAFFS_FAIL; ++ } ++ return YAFFS_OK; ++} ++ ++/* Query for the current state of a specific block. ++ * ++ * Examine the tags of the first chunk of the block and return the state: ++ * - YAFFS_BLOCK_STATE_DEAD, the block is marked bad ++ * - YAFFS_BLOCK_STATE_NEEDS_SCANNING, the block is in use ++ * - YAFFS_BLOCK_STATE_EMPTY, the block is clean ++ * ++ * Always returns YAFFS_OK. ++ */ ++int nandmtd1_query_block(struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state_ptr, u32 * seq_ptr) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int chunk_num = block_no * dev->param.chunks_per_block; ++ loff_t addr = (loff_t) chunk_num * dev->data_bytes_per_chunk; ++ struct yaffs_ext_tags etags; ++ int state = YAFFS_BLOCK_STATE_DEAD; ++ int seqnum = 0; ++ int retval; ++ ++ /* We don't yet have a good place to test for MTD config prerequists. ++ * Do it here as we are called during the initial scan. ++ */ ++ if (nandmtd1_test_prerequists(mtd) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ retval = nandmtd1_read_chunk_tags(dev, chunk_num, NULL, &etags); ++ etags.block_bad = (mtd->block_isbad) (mtd, addr); ++ if (etags.block_bad) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is marked bad", block_no); ++ state = YAFFS_BLOCK_STATE_DEAD; ++ } else if (etags.ecc_result != YAFFS_ECC_RESULT_NO_ERROR) { ++ /* bad tags, need to look more closely */ ++ state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; ++ } else if (etags.chunk_used) { ++ state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; ++ seqnum = etags.seq_number; ++ } else { ++ state = YAFFS_BLOCK_STATE_EMPTY; ++ } ++ ++ *state_ptr = state; ++ *seq_ptr = seqnum; ++ ++ /* query always succeeds */ ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_mtdif1.h b/fs/yaffs2/yaffs_mtdif1.h +new file mode 100644 +index 00000000..07ce4524 +--- /dev/null ++++ b/fs/yaffs2/yaffs_mtdif1.h +@@ -0,0 +1,29 @@ ++/* ++ * YAFFS: Yet another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_MTDIF1_H__ ++#define __YAFFS_MTDIF1_H__ ++ ++int nandmtd1_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk, ++ const u8 * data, ++ const struct yaffs_ext_tags *tags); ++ ++int nandmtd1_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk, ++ u8 * data, struct yaffs_ext_tags *tags); ++ ++int nandmtd1_mark_block_bad(struct yaffs_dev *dev, int block_no); ++ ++int nandmtd1_query_block(struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, u32 * seq_number); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_mtdif2.c b/fs/yaffs2/yaffs_mtdif2.c +new file mode 100644 +index 00000000..d1643df2 +--- /dev/null ++++ b/fs/yaffs2/yaffs_mtdif2.c +@@ -0,0 +1,225 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* mtd interface for YAFFS2 */ ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++#include "yaffs_mtdif2.h" ++ ++#include "linux/mtd/mtd.h" ++#include "linux/types.h" ++#include "linux/time.h" ++ ++#include "yaffs_packedtags2.h" ++ ++#include "yaffs_linux.h" ++ ++/* NB For use with inband tags.... ++ * We assume that the data buffer is of size total_bytes_per_chunk so that we can also ++ * use it to load the tags. ++ */ ++int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk, ++ const u8 * data, ++ const struct yaffs_ext_tags *tags) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ struct mtd_oob_ops ops; ++ int retval = 0; ++ ++ loff_t addr; ++ ++ struct yaffs_packed_tags2 pt; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "nandmtd2_write_chunk_tags chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ ++ /* For yaffs2 writing there must be both data and tags. ++ * If we're using inband tags, then the tags are stuffed into ++ * the end of the data buffer. ++ */ ++ if (!data || !tags) ++ BUG(); ++ else if (dev->param.inband_tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *)(data + ++ dev-> ++ data_bytes_per_chunk); ++ yaffs_pack_tags2_tags_only(pt2tp, tags); ++ } else { ++ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); ++ } ++ ++ ops.mode = MTD_OOB_AUTO; ++ ops.ooblen = (dev->param.inband_tags) ? 0 : packed_tags_size; ++ ops.len = dev->param.total_bytes_per_chunk; ++ ops.ooboffs = 0; ++ ops.datbuf = (u8 *) data; ++ ops.oobbuf = (dev->param.inband_tags) ? NULL : packed_tags_ptr; ++ retval = mtd->write_oob(mtd, addr, &ops); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++int nandmtd2_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk, ++ u8 * data, struct yaffs_ext_tags *tags) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ struct mtd_oob_ops ops; ++ ++ size_t dummy; ++ int retval = 0; ++ int local_data = 0; ++ ++ loff_t addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ ++ struct yaffs_packed_tags2 pt; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "nandmtd2_read_chunk_tags chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ if (dev->param.inband_tags) { ++ ++ if (!data) { ++ local_data = 1; ++ data = yaffs_get_temp_buffer(dev, __LINE__); ++ } ++ ++ } ++ ++ if (dev->param.inband_tags || (data && !tags)) ++ retval = mtd->read(mtd, addr, dev->param.total_bytes_per_chunk, ++ &dummy, data); ++ else if (tags) { ++ ops.mode = MTD_OOB_AUTO; ++ ops.ooblen = packed_tags_size; ++ ops.len = data ? dev->data_bytes_per_chunk : packed_tags_size; ++ ops.ooboffs = 0; ++ ops.datbuf = data; ++ ops.oobbuf = yaffs_dev_to_lc(dev)->spare_buffer; ++ retval = mtd->read_oob(mtd, addr, &ops); ++ } ++ ++ if (dev->param.inband_tags) { ++ if (tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *)&data[dev-> ++ data_bytes_per_chunk]; ++ yaffs_unpack_tags2_tags_only(tags, pt2tp); ++ } ++ } else { ++ if (tags) { ++ memcpy(packed_tags_ptr, ++ yaffs_dev_to_lc(dev)->spare_buffer, ++ packed_tags_size); ++ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); ++ } ++ } ++ ++ if (local_data) ++ yaffs_release_temp_buffer(dev, data, __LINE__); ++ ++ if (tags && retval == -EBADMSG ++ && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) { ++ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ dev->n_ecc_unfixed++; ++ } ++ if (tags && retval == -EUCLEAN ++ && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) { ++ tags->ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ } ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++int nandmtd2_mark_block_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int retval; ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "nandmtd2_mark_block_bad %d", block_no); ++ ++ retval = ++ mtd->block_markbad(mtd, ++ block_no * dev->param.chunks_per_block * ++ dev->param.total_bytes_per_chunk); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++ ++} ++ ++int nandmtd2_query_block(struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, u32 * seq_number) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "nandmtd2_query_block %d", block_no); ++ retval = ++ mtd->block_isbad(mtd, ++ block_no * dev->param.chunks_per_block * ++ dev->param.total_bytes_per_chunk); ++ ++ if (retval) { ++ yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); ++ ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ *seq_number = 0; ++ } else { ++ struct yaffs_ext_tags t; ++ nandmtd2_read_chunk_tags(dev, block_no * ++ dev->param.chunks_per_block, NULL, &t); ++ ++ if (t.chunk_used) { ++ *seq_number = t.seq_number; ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; ++ } else { ++ *seq_number = 0; ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ } ++ } ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "block is bad seq %d state %d", *seq_number, *state); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ +diff --git a/fs/yaffs2/yaffs_mtdif2.h b/fs/yaffs2/yaffs_mtdif2.h +new file mode 100644 +index 00000000..d8211261 +--- /dev/null ++++ b/fs/yaffs2/yaffs_mtdif2.h +@@ -0,0 +1,29 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_MTDIF2_H__ ++#define __YAFFS_MTDIF2_H__ ++ ++#include "yaffs_guts.h" ++int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk, ++ const u8 * data, ++ const struct yaffs_ext_tags *tags); ++int nandmtd2_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk, ++ u8 * data, struct yaffs_ext_tags *tags); ++int nandmtd2_mark_block_bad(struct yaffs_dev *dev, int block_no); ++int nandmtd2_query_block(struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, u32 * seq_number); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_nameval.c b/fs/yaffs2/yaffs_nameval.c +new file mode 100644 +index 00000000..daa36f98 +--- /dev/null ++++ b/fs/yaffs2/yaffs_nameval.c +@@ -0,0 +1,201 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This simple implementation of a name-value store assumes a small number of values and fits ++ * into a small finite buffer. ++ * ++ * Each attribute is stored as a record: ++ * sizeof(int) bytes record size. ++ * strnlen+1 bytes name null terminated. ++ * nbytes value. ++ * ---------- ++ * total size stored in record size ++ * ++ * This code has not been tested with unicode yet. ++ */ ++ ++#include "yaffs_nameval.h" ++ ++#include "yportenv.h" ++ ++static int nval_find(const char *xb, int xb_size, const YCHAR * name, ++ int *exist_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ if (strncmp ++ ((YCHAR *) (xb + pos + sizeof(int)), name, size) == 0) { ++ if (exist_size) ++ *exist_size = size; ++ return pos; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ if (exist_size) ++ *exist_size = 0; ++ return -1; ++} ++ ++static int nval_used(const char *xb, int xb_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return pos; ++} ++ ++int nval_del(char *xb, int xb_size, const YCHAR * name) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos >= 0 && pos < xb_size) { ++ /* Find size, shift rest over this record, then zero out the rest of buffer */ ++ memcpy(&size, xb + pos, sizeof(int)); ++ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); ++ memset(xb + (xb_size - size), 0, size); ++ return 0; ++ } else { ++ return -ENODATA; ++ } ++} ++ ++int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, ++ int bsize, int flags) ++{ ++ int pos; ++ int namelen = strnlen(name, xb_size); ++ int reclen; ++ int size_exist = 0; ++ int space; ++ int start; ++ ++ pos = nval_find(xb, xb_size, name, &size_exist); ++ ++ if (flags & XATTR_CREATE && pos >= 0) ++ return -EEXIST; ++ if (flags & XATTR_REPLACE && pos < 0) ++ return -ENODATA; ++ ++ start = nval_used(xb, xb_size); ++ space = xb_size - start + size_exist; ++ ++ reclen = (sizeof(int) + namelen + 1 + bsize); ++ ++ if (reclen > space) ++ return -ENOSPC; ++ ++ if (pos >= 0) { ++ nval_del(xb, xb_size, name); ++ start = nval_used(xb, xb_size); ++ } ++ ++ pos = start; ++ ++ memcpy(xb + pos, &reclen, sizeof(int)); ++ pos += sizeof(int); ++ strncpy((YCHAR *) (xb + pos), name, reclen); ++ pos += (namelen + 1); ++ memcpy(xb + pos, buf, bsize); ++ return 0; ++} ++ ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos >= 0 && pos < xb_size) { ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ pos += sizeof(int); /* advance past record length */ ++ size -= sizeof(int); ++ ++ /* Advance over name string */ ++ while (xb[pos] && size > 0 && pos < xb_size) { ++ pos++; ++ size--; ++ } ++ /*Advance over NUL */ ++ pos++; ++ size--; ++ ++ if (size <= bsize) { ++ memcpy(buf, xb + pos, size); ++ return size; ++ } ++ ++ } ++ if (pos >= 0) ++ return -ERANGE; ++ else ++ return -ENODATA; ++} ++ ++int nval_list(const char *xb, int xb_size, char *buf, int bsize) ++{ ++ int pos = 0; ++ int size; ++ int name_len; ++ int ncopied = 0; ++ int filled = 0; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > sizeof(int) && size <= xb_size && (pos + size) < xb_size ++ && !filled) { ++ pos += sizeof(int); ++ size -= sizeof(int); ++ name_len = strnlen((YCHAR *) (xb + pos), size); ++ if (ncopied + name_len + 1 < bsize) { ++ memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); ++ buf += name_len; ++ *buf = '\0'; ++ buf++; ++ if (sizeof(YCHAR) > 1) { ++ *buf = '\0'; ++ buf++; ++ } ++ ncopied += (name_len + 1); ++ } else { ++ filled = 1; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return ncopied; ++} ++ ++int nval_hasvalues(const char *xb, int xb_size) ++{ ++ return nval_used(xb, xb_size) > 0; ++} +diff --git a/fs/yaffs2/yaffs_nameval.h b/fs/yaffs2/yaffs_nameval.h +new file mode 100644 +index 00000000..2bb02b62 +--- /dev/null ++++ b/fs/yaffs2/yaffs_nameval.h +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __NAMEVAL_H__ ++#define __NAMEVAL_H__ ++ ++#include "yportenv.h" ++ ++int nval_del(char *xb, int xb_size, const YCHAR * name); ++int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, ++ int bsize, int flags); ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize); ++int nval_list(const char *xb, int xb_size, char *buf, int bsize); ++int nval_hasvalues(const char *xb, int xb_size); ++#endif +diff --git a/fs/yaffs2/yaffs_nand.c b/fs/yaffs2/yaffs_nand.c +new file mode 100644 +index 00000000..e816cabf +--- /dev/null ++++ b/fs/yaffs2/yaffs_nand.c +@@ -0,0 +1,127 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_nand.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_tagsvalidity.h" ++ ++#include "yaffs_getblockinfo.h" ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 * buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ struct yaffs_ext_tags local_tags; ++ ++ int realigned_chunk = nand_chunk - dev->chunk_offset; ++ ++ dev->n_page_reads++; ++ ++ /* If there are no tags provided, use local tags to get prioritised gc working */ ++ if (!tags) ++ tags = &local_tags; ++ ++ if (dev->param.read_chunk_tags_fn) ++ result = ++ dev->param.read_chunk_tags_fn(dev, realigned_chunk, buffer, ++ tags); ++ else ++ result = yaffs_tags_compat_rd(dev, ++ realigned_chunk, buffer, tags); ++ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { ++ ++ struct yaffs_block_info *bi; ++ bi = yaffs_get_block_info(dev, ++ nand_chunk / ++ dev->param.chunks_per_block); ++ yaffs_handle_chunk_error(dev, bi); ++ } ++ ++ return result; ++} ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 * buffer, struct yaffs_ext_tags *tags) ++{ ++ ++ dev->n_page_writes++; ++ ++ nand_chunk -= dev->chunk_offset; ++ ++ if (tags) { ++ tags->seq_number = dev->seq_number; ++ tags->chunk_used = 1; ++ if (!yaffs_validate_tags(tags)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, "Writing uninitialised tags"); ++ YBUG(); ++ } ++ yaffs_trace(YAFFS_TRACE_WRITE, ++ "Writing chunk %d tags %d %d", ++ nand_chunk, tags->obj_id, tags->chunk_id); ++ } else { ++ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); ++ YBUG(); ++ } ++ ++ if (dev->param.write_chunk_tags_fn) ++ return dev->param.write_chunk_tags_fn(dev, nand_chunk, buffer, ++ tags); ++ else ++ return yaffs_tags_compat_wr(dev, nand_chunk, buffer, tags); ++} ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ block_no -= dev->block_offset; ++ ++ if (dev->param.bad_block_fn) ++ return dev->param.bad_block_fn(dev, block_no); ++ else ++ return yaffs_tags_compat_mark_bad(dev, block_no); ++} ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 * seq_number) ++{ ++ block_no -= dev->block_offset; ++ ++ if (dev->param.query_block_fn) ++ return dev->param.query_block_fn(dev, block_no, state, ++ seq_number); ++ else ++ return yaffs_tags_compat_query_block(dev, block_no, ++ state, seq_number); ++} ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int flash_block) ++{ ++ int result; ++ ++ flash_block -= dev->block_offset; ++ ++ dev->n_erasures++; ++ ++ result = dev->param.erase_fn(dev, flash_block); ++ ++ return result; ++} ++ ++int yaffs_init_nand(struct yaffs_dev *dev) ++{ ++ if (dev->param.initialise_flash_fn) ++ return dev->param.initialise_flash_fn(dev); ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_nand.h b/fs/yaffs2/yaffs_nand.h +new file mode 100644 +index 00000000..543f1987 +--- /dev/null ++++ b/fs/yaffs2/yaffs_nand.h +@@ -0,0 +1,38 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_NAND_H__ ++#define __YAFFS_NAND_H__ ++#include "yaffs_guts.h" ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 * buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 * buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ unsigned *seq_number); ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); ++ ++int yaffs_init_nand(struct yaffs_dev *dev); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_packedtags1.c b/fs/yaffs2/yaffs_packedtags1.c +new file mode 100644 +index 00000000..a77f0954 +--- /dev/null ++++ b/fs/yaffs2/yaffs_packedtags1.c +@@ -0,0 +1,53 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags1.h" ++#include "yportenv.h" ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t) ++{ ++ pt->chunk_id = t->chunk_id; ++ pt->serial_number = t->serial_number; ++ pt->n_bytes = t->n_bytes; ++ pt->obj_id = t->obj_id; ++ pt->ecc = 0; ++ pt->deleted = (t->is_deleted) ? 0 : 1; ++ pt->unused_stuff = 0; ++ pt->should_be_ff = 0xFFFFFFFF; ++ ++} ++ ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt) ++{ ++ static const u8 all_ff[] = ++ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff ++ }; ++ ++ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { ++ t->block_bad = 0; ++ if (pt->should_be_ff != 0xFFFFFFFF) ++ t->block_bad = 1; ++ t->chunk_used = 1; ++ t->obj_id = pt->obj_id; ++ t->chunk_id = pt->chunk_id; ++ t->n_bytes = pt->n_bytes; ++ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ t->is_deleted = (pt->deleted) ? 0 : 1; ++ t->serial_number = pt->serial_number; ++ } else { ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ } ++} +diff --git a/fs/yaffs2/yaffs_packedtags1.h b/fs/yaffs2/yaffs_packedtags1.h +new file mode 100644 +index 00000000..d6861ff5 +--- /dev/null ++++ b/fs/yaffs2/yaffs_packedtags1.h +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS1_H__ ++#define __YAFFS_PACKEDTAGS1_H__ ++ ++#include "yaffs_guts.h" ++ ++struct yaffs_packed_tags1 { ++ unsigned chunk_id:20; ++ unsigned serial_number:2; ++ unsigned n_bytes:10; ++ unsigned obj_id:18; ++ unsigned ecc:12; ++ unsigned deleted:1; ++ unsigned unused_stuff:1; ++ unsigned should_be_ff; ++ ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt); ++#endif +diff --git a/fs/yaffs2/yaffs_packedtags2.c b/fs/yaffs2/yaffs_packedtags2.c +new file mode 100644 +index 00000000..8e7fea3d +--- /dev/null ++++ b/fs/yaffs2/yaffs_packedtags2.c +@@ -0,0 +1,196 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags2.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_tagsvalidity.h" ++ ++/* This code packs a set of extended tags into a binary structure for ++ * NAND storage ++ */ ++ ++/* Some of the information is "extra" struff which can be packed in to ++ * speed scanning ++ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. ++ */ ++ ++/* Extra flags applied to chunk_id */ ++ ++#define EXTRA_HEADER_INFO_FLAG 0x80000000 ++#define EXTRA_SHRINK_FLAG 0x40000000 ++#define EXTRA_SHADOWS_FLAG 0x20000000 ++#define EXTRA_SPARE_FLAGS 0x10000000 ++ ++#define ALL_EXTRA_FLAGS 0xF0000000 ++ ++/* Also, the top 4 bits of the object Id are set to the object type. */ ++#define EXTRA_OBJECT_TYPE_SHIFT (28) ++#define EXTRA_OBJECT_TYPE_MASK ((0x0F) << EXTRA_OBJECT_TYPE_SHIFT) ++ ++static void yaffs_dump_packed_tags2_tags_only(const struct ++ yaffs_packed_tags2_tags_only *ptt) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "packed tags obj %d chunk %d byte %d seq %d", ++ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); ++} ++ ++static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) ++{ ++ yaffs_dump_packed_tags2_tags_only(&pt->t); ++} ++ ++static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", ++ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, ++ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, ++ t->seq_number); ++ ++} ++ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, ++ const struct yaffs_ext_tags *t) ++{ ++ ptt->chunk_id = t->chunk_id; ++ ptt->seq_number = t->seq_number; ++ ptt->n_bytes = t->n_bytes; ++ ptt->obj_id = t->obj_id; ++ ++ if (t->chunk_id == 0 && t->extra_available) { ++ /* Store the extra header info instead */ ++ /* We save the parent object in the chunk_id */ ++ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; ++ if (t->extra_is_shrink) ++ ptt->chunk_id |= EXTRA_SHRINK_FLAG; ++ if (t->extra_shadows) ++ ptt->chunk_id |= EXTRA_SHADOWS_FLAG; ++ ++ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ ptt->n_bytes = t->extra_equiv_id; ++ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ ptt->n_bytes = t->extra_length; ++ else ++ ptt->n_bytes = 0; ++ } ++ ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc) ++{ ++ yaffs_pack_tags2_tags_only(&pt->t, t); ++ ++ if (tags_ecc) ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct ++ yaffs_packed_tags2_tags_only), ++ &pt->ecc); ++} ++ ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ ++ yaffs_init_tags(t); ++ ++ if (ptt->seq_number != 0xFFFFFFFF) { ++ t->block_bad = 0; ++ t->chunk_used = 1; ++ t->obj_id = ptt->obj_id; ++ t->chunk_id = ptt->chunk_id; ++ t->n_bytes = ptt->n_bytes; ++ t->is_deleted = 0; ++ t->serial_number = 0; ++ t->seq_number = ptt->seq_number; ++ ++ /* Do extra header info stuff */ ++ ++ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { ++ t->chunk_id = 0; ++ t->n_bytes = 0; ++ ++ t->extra_available = 1; ++ t->extra_parent_id = ++ ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); ++ t->extra_is_shrink = ++ (ptt->chunk_id & EXTRA_SHRINK_FLAG) ? 1 : 0; ++ t->extra_shadows = ++ (ptt->chunk_id & EXTRA_SHADOWS_FLAG) ? 1 : 0; ++ t->extra_obj_type = ++ ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; ++ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ t->extra_equiv_id = ptt->n_bytes; ++ else ++ t->extra_length = ptt->n_bytes; ++ } ++ } ++ ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++ ++} ++ ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc) ++{ ++ ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ if (pt->t.seq_number != 0xFFFFFFFF && tags_ecc) { ++ /* Chunk is in use and we need to do ECC */ ++ ++ struct yaffs_ecc_other ecc; ++ int result; ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct ++ yaffs_packed_tags2_tags_only), ++ &ecc); ++ result = ++ yaffs_ecc_correct_other((unsigned char *)&pt->t, ++ sizeof(struct ++ yaffs_packed_tags2_tags_only), ++ &pt->ecc, &ecc); ++ switch (result) { ++ case 0: ++ ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ case 1: ++ ecc_result = YAFFS_ECC_RESULT_FIXED; ++ break; ++ case -1: ++ ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ break; ++ default: ++ ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ } ++ } ++ ++ yaffs_unpack_tags2_tags_only(t, &pt->t); ++ ++ t->ecc_result = ecc_result; ++ ++ yaffs_dump_packed_tags2(pt); ++ yaffs_dump_tags2(t); ++} +diff --git a/fs/yaffs2/yaffs_packedtags2.h b/fs/yaffs2/yaffs_packedtags2.h +new file mode 100644 +index 00000000..f3296697 +--- /dev/null ++++ b/fs/yaffs2/yaffs_packedtags2.h +@@ -0,0 +1,47 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS2_H__ ++#define __YAFFS_PACKEDTAGS2_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_ecc.h" ++ ++struct yaffs_packed_tags2_tags_only { ++ unsigned seq_number; ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++struct yaffs_packed_tags2 { ++ struct yaffs_packed_tags2_tags_only t; ++ struct yaffs_ecc_other ecc; ++}; ++ ++/* Full packed tags with ECC, used for oob tags */ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc); ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc); ++ ++/* Only the tags part (no ECC for use with inband tags */ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *pt); ++#endif +diff --git a/fs/yaffs2/yaffs_tagscompat.c b/fs/yaffs2/yaffs_tagscompat.c +new file mode 100644 +index 00000000..7578075d +--- /dev/null ++++ b/fs/yaffs2/yaffs_tagscompat.c +@@ -0,0 +1,422 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_ecc.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_trace.h" ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); ++ ++ ++/********** Tags ECC calculations *********/ ++ ++void yaffs_calc_ecc(const u8 * data, struct yaffs_spare *spare) ++{ ++ yaffs_ecc_cacl(data, spare->ecc1); ++ yaffs_ecc_cacl(&data[256], spare->ecc2); ++} ++ ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags) ++{ ++ /* Calculate an ecc */ ++ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ unsigned i, j; ++ unsigned ecc = 0; ++ unsigned bit = 0; ++ ++ tags->ecc = 0; ++ ++ for (i = 0; i < 8; i++) { ++ for (j = 1; j & 0xff; j <<= 1) { ++ bit++; ++ if (b[i] & j) ++ ecc ^= bit; ++ } ++ } ++ ++ tags->ecc = ecc; ++ ++} ++ ++int yaffs_check_tags_ecc(struct yaffs_tags *tags) ++{ ++ unsigned ecc = tags->ecc; ++ ++ yaffs_calc_tags_ecc(tags); ++ ++ ecc ^= tags->ecc; ++ ++ if (ecc && ecc <= 64) { ++ /* TODO: Handle the failure better. Retire? */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ ++ ecc--; ++ ++ b[ecc / 8] ^= (1 << (ecc & 7)); ++ ++ /* Now recvalc the ecc */ ++ yaffs_calc_tags_ecc(tags); ++ ++ return 1; /* recovered error */ ++ } else if (ecc) { ++ /* Wierd ecc failure value */ ++ /* TODO Need to do somethiong here */ ++ return -1; /* unrecovered error */ ++ } ++ ++ return 0; ++} ++ ++/********** Tags **********/ ++ ++static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ ++ yaffs_calc_tags_ecc(tags_ptr); ++ ++ spare_ptr->tb0 = tu->as_bytes[0]; ++ spare_ptr->tb1 = tu->as_bytes[1]; ++ spare_ptr->tb2 = tu->as_bytes[2]; ++ spare_ptr->tb3 = tu->as_bytes[3]; ++ spare_ptr->tb4 = tu->as_bytes[4]; ++ spare_ptr->tb5 = tu->as_bytes[5]; ++ spare_ptr->tb6 = tu->as_bytes[6]; ++ spare_ptr->tb7 = tu->as_bytes[7]; ++} ++ ++static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, ++ struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ int result; ++ ++ tu->as_bytes[0] = spare_ptr->tb0; ++ tu->as_bytes[1] = spare_ptr->tb1; ++ tu->as_bytes[2] = spare_ptr->tb2; ++ tu->as_bytes[3] = spare_ptr->tb3; ++ tu->as_bytes[4] = spare_ptr->tb4; ++ tu->as_bytes[5] = spare_ptr->tb5; ++ tu->as_bytes[6] = spare_ptr->tb6; ++ tu->as_bytes[7] = spare_ptr->tb7; ++ ++ result = yaffs_check_tags_ecc(tags_ptr); ++ if (result > 0) ++ dev->n_tags_ecc_fixed++; ++ else if (result < 0) ++ dev->n_tags_ecc_unfixed++; ++} ++ ++static void yaffs_spare_init(struct yaffs_spare *spare) ++{ ++ memset(spare, 0xFF, sizeof(struct yaffs_spare)); ++} ++ ++static int yaffs_wr_nand(struct yaffs_dev *dev, ++ int nand_chunk, const u8 * data, ++ struct yaffs_spare *spare) ++{ ++ if (nand_chunk < dev->param.start_block * dev->param.chunks_per_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs chunk %d is not valid", ++ nand_chunk); ++ return YAFFS_FAIL; ++ } ++ ++ return dev->param.write_chunk_fn(dev, nand_chunk, data, spare); ++} ++ ++static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 * data, ++ struct yaffs_spare *spare, ++ enum yaffs_ecc_result *ecc_result, ++ int correct_errors) ++{ ++ int ret_val; ++ struct yaffs_spare local_spare; ++ ++ if (!spare && data) { ++ /* If we don't have a real spare, then we use a local one. */ ++ /* Need this for the calculation of the ecc */ ++ spare = &local_spare; ++ } ++ ++ if (!dev->param.use_nand_ecc) { ++ ret_val = ++ dev->param.read_chunk_fn(dev, nand_chunk, data, spare); ++ if (data && correct_errors) { ++ /* Do ECC correction */ ++ /* Todo handle any errors */ ++ int ecc_result1, ecc_result2; ++ u8 calc_ecc[3]; ++ ++ yaffs_ecc_cacl(data, calc_ecc); ++ ecc_result1 = ++ yaffs_ecc_correct(data, spare->ecc1, calc_ecc); ++ yaffs_ecc_cacl(&data[256], calc_ecc); ++ ecc_result2 = ++ yaffs_ecc_correct(&data[256], spare->ecc2, ++ calc_ecc); ++ ++ if (ecc_result1 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result1 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result2 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result2 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result1 || ecc_result2) { ++ /* We had a data problem on this page */ ++ yaffs_handle_rd_data_error(dev, nand_chunk); ++ } ++ ++ if (ecc_result1 < 0 || ecc_result2 < 0) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ else if (ecc_result1 > 0 || ecc_result2 > 0) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ else ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ } ++ } else { ++ /* Must allocate enough memory for spare+2*sizeof(int) */ ++ /* for ecc results from device. */ ++ struct yaffs_nand_spare nspare; ++ ++ memset(&nspare, 0, sizeof(nspare)); ++ ++ ret_val = dev->param.read_chunk_fn(dev, nand_chunk, data, ++ (struct yaffs_spare *) ++ &nspare); ++ memcpy(spare, &nspare, sizeof(struct yaffs_spare)); ++ if (data && correct_errors) { ++ if (nspare.eccres1 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>mtd ecc error fix performed on chunk %d:0", ++ nand_chunk); ++ } else if (nspare.eccres1 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>mtd ecc error unfixed on chunk %d:0", ++ nand_chunk); ++ } ++ ++ if (nspare.eccres2 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>mtd ecc error fix performed on chunk %d:1", ++ nand_chunk); ++ } else if (nspare.eccres2 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>mtd ecc error unfixed on chunk %d:1", ++ nand_chunk); ++ } ++ ++ if (nspare.eccres1 || nspare.eccres2) { ++ /* We had a data problem on this page */ ++ yaffs_handle_rd_data_error(dev, nand_chunk); ++ } ++ ++ if (nspare.eccres1 < 0 || nspare.eccres2 < 0) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ else if (nspare.eccres1 > 0 || nspare.eccres2 > 0) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ else ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ } ++ } ++ return ret_val; ++} ++ ++/* ++ * Functions for robustisizing ++ */ ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ ++ /* Mark the block for retirement */ ++ yaffs_get_block_info(dev, ++ flash_block + dev->block_offset)->needs_retiring = ++ 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>>Block %d marked for retirement", ++ flash_block); ++ ++ /* TODO: ++ * Just do a garbage collection on the affected block ++ * then retire the block ++ * NB recursion ++ */ ++} ++ ++int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 * data, const struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ ++ yaffs_spare_init(&spare); ++ ++ if (ext_tags->is_deleted) ++ spare.page_status = 0; ++ else { ++ tags.obj_id = ext_tags->obj_id; ++ tags.chunk_id = ext_tags->chunk_id; ++ ++ tags.n_bytes_lsb = ext_tags->n_bytes & 0x3ff; ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; ++ else ++ tags.n_bytes_msb = 3; ++ ++ tags.serial_number = ext_tags->serial_number; ++ ++ if (!dev->param.use_nand_ecc && data) ++ yaffs_calc_ecc(data, &spare); ++ ++ yaffs_load_tags_to_spare(&spare, &tags); ++ ++ } ++ ++ return yaffs_wr_nand(dev, nand_chunk, data, &spare); ++} ++ ++int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 * data, struct yaffs_ext_tags *ext_tags) ++{ ++ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ ++ static struct yaffs_spare spare_ff; ++ static int init; ++ ++ if (!init) { ++ memset(&spare_ff, 0xFF, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ if (yaffs_rd_chunk_nand(dev, nand_chunk, data, &spare, &ecc_result, 1)) { ++ /* ext_tags may be NULL */ ++ if (ext_tags) { ++ ++ int deleted = ++ (hweight8(spare.page_status) < 7) ? 1 : 0; ++ ++ ext_tags->is_deleted = deleted; ++ ext_tags->ecc_result = ecc_result; ++ ext_tags->block_bad = 0; /* We're reading it */ ++ /* therefore it is not a bad block */ ++ ext_tags->chunk_used = ++ (memcmp(&spare_ff, &spare, sizeof(spare_ff)) != ++ 0) ? 1 : 0; ++ ++ if (ext_tags->chunk_used) { ++ yaffs_get_tags_from_spare(dev, &spare, &tags); ++ ++ ext_tags->obj_id = tags.obj_id; ++ ext_tags->chunk_id = tags.chunk_id; ++ ext_tags->n_bytes = tags.n_bytes_lsb; ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ ext_tags->n_bytes |= ++ (((unsigned)tags. ++ n_bytes_msb) << 10); ++ ++ ext_tags->serial_number = tags.serial_number; ++ } ++ } ++ ++ return YAFFS_OK; ++ } else { ++ return YAFFS_FAIL; ++ } ++} ++ ++int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) ++{ ++ ++ struct yaffs_spare spare; ++ ++ memset(&spare, 0xff, sizeof(struct yaffs_spare)); ++ ++ spare.block_status = 'Y'; ++ ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, ++ &spare); ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, ++ NULL, &spare); ++ ++ return YAFFS_OK; ++ ++} ++ ++int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 * seq_number) ++{ ++ ++ struct yaffs_spare spare0, spare1; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ enum yaffs_ecc_result dummy; ++ ++ if (!init) { ++ memset(&spare_ff, 0xFF, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ *seq_number = 0; ++ ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, NULL, ++ &spare0, &dummy, 1); ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, ++ NULL, &spare1, &dummy, 1); ++ ++ if (hweight8(spare0.block_status & spare1.block_status) < 7) ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ else ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; ++ ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_tagscompat.h b/fs/yaffs2/yaffs_tagscompat.h +new file mode 100644 +index 00000000..8cd35dcd +--- /dev/null ++++ b/fs/yaffs2/yaffs_tagscompat.h +@@ -0,0 +1,36 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSCOMPAT_H__ ++#define __YAFFS_TAGSCOMPAT_H__ ++ ++#include "yaffs_guts.h" ++int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 * data, const struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 * data, struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); ++int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 * seq_number); ++ ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags); ++int yaffs_check_tags_ecc(struct yaffs_tags *tags); ++int yaffs_count_bits(u8 byte); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_tagsvalidity.c b/fs/yaffs2/yaffs_tagsvalidity.c +new file mode 100644 +index 00000000..4358d79d +--- /dev/null ++++ b/fs/yaffs2/yaffs_tagsvalidity.c +@@ -0,0 +1,27 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_tagsvalidity.h" ++ ++void yaffs_init_tags(struct yaffs_ext_tags *tags) ++{ ++ memset(tags, 0, sizeof(struct yaffs_ext_tags)); ++ tags->validity0 = 0xAAAAAAAA; ++ tags->validity1 = 0x55555555; ++} ++ ++int yaffs_validate_tags(struct yaffs_ext_tags *tags) ++{ ++ return (tags->validity0 == 0xAAAAAAAA && tags->validity1 == 0x55555555); ++ ++} +diff --git a/fs/yaffs2/yaffs_tagsvalidity.h b/fs/yaffs2/yaffs_tagsvalidity.h +new file mode 100644 +index 00000000..36a021fc +--- /dev/null ++++ b/fs/yaffs2/yaffs_tagsvalidity.h +@@ -0,0 +1,23 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGS_VALIDITY_H__ ++#define __YAFFS_TAGS_VALIDITY_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_init_tags(struct yaffs_ext_tags *tags); ++int yaffs_validate_tags(struct yaffs_ext_tags *tags); ++#endif +diff --git a/fs/yaffs2/yaffs_trace.h b/fs/yaffs2/yaffs_trace.h +new file mode 100644 +index 00000000..6273dbf9 +--- /dev/null ++++ b/fs/yaffs2/yaffs_trace.h +@@ -0,0 +1,57 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YTRACE_H__ ++#define __YTRACE_H__ ++ ++extern unsigned int yaffs_trace_mask; ++extern unsigned int yaffs_wr_attempts; ++ ++/* ++ * Tracing flags. ++ * The flags masked in YAFFS_TRACE_ALWAYS are always traced. ++ */ ++ ++#define YAFFS_TRACE_OS 0x00000002 ++#define YAFFS_TRACE_ALLOCATE 0x00000004 ++#define YAFFS_TRACE_SCAN 0x00000008 ++#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 ++#define YAFFS_TRACE_ERASE 0x00000020 ++#define YAFFS_TRACE_GC 0x00000040 ++#define YAFFS_TRACE_WRITE 0x00000080 ++#define YAFFS_TRACE_TRACING 0x00000100 ++#define YAFFS_TRACE_DELETION 0x00000200 ++#define YAFFS_TRACE_BUFFERS 0x00000400 ++#define YAFFS_TRACE_NANDACCESS 0x00000800 ++#define YAFFS_TRACE_GC_DETAIL 0x00001000 ++#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 ++#define YAFFS_TRACE_MTD 0x00004000 ++#define YAFFS_TRACE_CHECKPOINT 0x00008000 ++ ++#define YAFFS_TRACE_VERIFY 0x00010000 ++#define YAFFS_TRACE_VERIFY_NAND 0x00020000 ++#define YAFFS_TRACE_VERIFY_FULL 0x00040000 ++#define YAFFS_TRACE_VERIFY_ALL 0x000F0000 ++ ++#define YAFFS_TRACE_SYNC 0x00100000 ++#define YAFFS_TRACE_BACKGROUND 0x00200000 ++#define YAFFS_TRACE_LOCK 0x00400000 ++#define YAFFS_TRACE_MOUNT 0x00800000 ++ ++#define YAFFS_TRACE_ERROR 0x40000000 ++#define YAFFS_TRACE_BUG 0x80000000 ++#define YAFFS_TRACE_ALWAYS 0xF0000000 ++ ++#endif +diff --git a/fs/yaffs2/yaffs_verify.c b/fs/yaffs2/yaffs_verify.c +new file mode 100644 +index 00000000..738c7f69 +--- /dev/null ++++ b/fs/yaffs2/yaffs_verify.c +@@ -0,0 +1,535 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_verify.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++ ++int yaffs_skip_verification(struct yaffs_dev *dev) ++{ ++ dev = dev; ++ return !(yaffs_trace_mask & ++ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_full_verification(struct yaffs_dev *dev) ++{ ++ dev = dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_nand_verification(struct yaffs_dev *dev) ++{ ++ dev = dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); ++} ++ ++static const char *block_state_name[] = { ++ "Unknown", ++ "Needs scanning", ++ "Scanning", ++ "Empty", ++ "Allocating", ++ "Full", ++ "Dirty", ++ "Checkpoint", ++ "Collecting", ++ "Dead" ++}; ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) ++{ ++ int actually_used; ++ int in_use; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Report illegal runtime states */ ++ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has undefined state %d", ++ n, bi->block_state); ++ ++ switch (bi->block_state) { ++ case YAFFS_BLOCK_STATE_UNKNOWN: ++ case YAFFS_BLOCK_STATE_SCANNING: ++ case YAFFS_BLOCK_STATE_NEEDS_SCANNING: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has bad run-state %s", ++ n, block_state_name[bi->block_state]); ++ } ++ ++ /* Check pages in use and soft deletions are legal */ ++ ++ actually_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->pages_in_use < 0 ++ || bi->pages_in_use > dev->param.chunks_per_block ++ || bi->soft_del_pages < 0 ++ || bi->soft_del_pages > dev->param.chunks_per_block ++ || actually_used < 0 || actually_used > dev->param.chunks_per_block) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has illegal values pages_in_used %d soft_del_pages %d", ++ n, bi->pages_in_use, bi->soft_del_pages); ++ ++ /* Check chunk bitmap legal */ ++ in_use = yaffs_count_chunk_bits(dev, n); ++ if (in_use != bi->pages_in_use) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", ++ n, bi->pages_in_use, in_use); ++ ++} ++ ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n) ++{ ++ yaffs_verify_blk(dev, bi, n); ++ ++ /* After collection the block should be in the erased state */ ++ ++ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && ++ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Block %d is in state %d after gc, should be erased", ++ n, bi->block_state); ++ } ++} ++ ++void yaffs_verify_blocks(struct yaffs_dev *dev) ++{ ++ int i; ++ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; ++ int illegal_states = 0; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ memset(state_count, 0, sizeof(state_count)); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ yaffs_verify_blk(dev, bi, i); ++ ++ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) ++ state_count[bi->block_state]++; ++ else ++ illegal_states++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%d blocks have illegal states", ++ illegal_states); ++ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many allocating blocks"); ++ ++ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%s %d blocks", ++ block_state_name[i], state_count[i]); ++ ++ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Checkpoint block count wrong dev %d count %d", ++ dev->blocks_in_checkpt, ++ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); ++ ++ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Erased block count wrong dev %d count %d", ++ dev->n_erased_blocks, ++ state_count[YAFFS_BLOCK_STATE_EMPTY]); ++ ++ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many collecting blocks %d (max is 1)", ++ state_count[YAFFS_BLOCK_STATE_COLLECTING]); ++} ++ ++/* ++ * Verify the object header. oh must be valid, but obj and tags may be NULL in which ++ * case those tests will not be performed. ++ */ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!(tags && obj && oh)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Verifying object header tags %p obj %p oh %p", ++ tags, obj, oh); ++ return; ++ } ++ ++ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || ++ oh->type > YAFFS_OBJECT_TYPE_MAX) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header type is illegal value 0x%x", ++ tags->obj_id, oh->type); ++ ++ if (tags->obj_id != obj->obj_id) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch obj_id %d", ++ tags->obj_id, obj->obj_id); ++ ++ /* ++ * Check that the object's parent ids match if parent_check requested. ++ * ++ * Tests do not apply to the root object. ++ */ ++ ++ if (parent_check && tags->obj_id > 1 && !obj->parent) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d obj->parent is NULL", ++ tags->obj_id, oh->parent_obj_id); ++ ++ if (parent_check && obj->parent && ++ oh->parent_obj_id != obj->parent->obj_id && ++ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d parent_obj_id %d", ++ tags->obj_id, oh->parent_obj_id, ++ obj->parent->obj_id); ++ ++ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is NULL", ++ obj->obj_id); ++ ++ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Trashed name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is 0xFF", ++ obj->obj_id); ++} ++ ++void yaffs_verify_file(struct yaffs_obj *obj) ++{ ++ int required_depth; ++ int actual_depth; ++ u32 last_chunk; ++ u32 x; ++ u32 i; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ struct yaffs_tnode *tn; ++ u32 obj_id; ++ ++ if (!obj) ++ return; ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ dev = obj->my_dev; ++ obj_id = obj->obj_id; ++ ++ /* Check file size is consistent with tnode depth */ ++ last_chunk = ++ obj->variant.file_variant.file_size / dev->data_bytes_per_chunk + 1; ++ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x > 0) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ actual_depth = obj->variant.file_variant.top_level; ++ ++ /* Check that the chunks in the tnode tree are all correct. ++ * We do this by scanning through the tnode tree and ++ * checking the tags for every chunk match. ++ */ ++ ++ if (yaffs_skip_nand_verification(dev)) ++ return; ++ ++ for (i = 1; i <= last_chunk; i++) { ++ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); ++ ++ if (tn) { ++ u32 the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk > 0) { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ &tags); ++ if (tags.obj_id != obj_id || tags.chunk_id != i) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", ++ obj_id, i, the_chunk, ++ tags.obj_id, tags.chunk_id); ++ } ++ } ++ } ++} ++ ++void yaffs_verify_link(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify sane equivalent object */ ++} ++ ++void yaffs_verify_symlink(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify symlink string */ ++} ++ ++void yaffs_verify_special(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++} ++ ++void yaffs_verify_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ u32 chunk_min; ++ u32 chunk_max; ++ ++ u32 chunk_id_ok; ++ u32 chunk_in_range; ++ u32 chunk_wrongly_deleted; ++ u32 chunk_valid; ++ ++ if (!obj) ++ return; ++ ++ if (obj->being_created) ++ return; ++ ++ dev = obj->my_dev; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Check sane object header chunk */ ++ ++ chunk_min = dev->internal_start_block * dev->param.chunks_per_block; ++ chunk_max = ++ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; ++ ++ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && ++ ((unsigned)(obj->hdr_chunk)) <= chunk_max); ++ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); ++ chunk_valid = chunk_in_range && ++ yaffs_check_chunk_bit(dev, ++ obj->hdr_chunk / dev->param.chunks_per_block, ++ obj->hdr_chunk % dev->param.chunks_per_block); ++ chunk_wrongly_deleted = chunk_in_range && !chunk_valid; ++ ++ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has chunk_id %d %s %s", ++ obj->obj_id, obj->hdr_chunk, ++ chunk_id_ok ? "" : ",out of range", ++ chunk_wrongly_deleted ? ",marked as deleted" : ""); ++ ++ if (chunk_valid && !yaffs_skip_nand_verification(dev)) { ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj_hdr *oh; ++ u8 *buffer = yaffs_get_temp_buffer(dev, __LINE__); ++ ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); ++ ++ yaffs_verify_oh(obj, oh, &tags, 1); ++ ++ yaffs_release_temp_buffer(dev, buffer, __LINE__); ++ } ++ ++ /* Verify it has a parent */ ++ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has parent pointer %p which does not look like an object", ++ obj->obj_id, obj->parent); ++ } ++ ++ /* Verify parent is a directory */ ++ if (obj->parent ++ && obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d's parent is not a directory (type %d)", ++ obj->obj_id, obj->parent->variant_type); ++ } ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ yaffs_verify_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ yaffs_verify_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ yaffs_verify_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ yaffs_verify_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ yaffs_verify_special(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has illegaltype %d", ++ obj->obj_id, obj->variant_type); ++ break; ++ } ++} ++ ++void yaffs_verify_objects(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ int i; ++ struct list_head *lh; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ if (lh) { ++ obj = ++ list_entry(lh, struct yaffs_obj, hash_link); ++ yaffs_verify_obj(obj); ++ } ++ } ++ } ++} ++ ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ ++ int count = 0; ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); ++ YBUG(); ++ return; ++ } ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!obj->parent) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent" ); ++ YBUG(); ++ return; ++ } ++ ++ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); ++ YBUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &obj->parent->variant.dir_variant.children) { ++ if (lh) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_verify_obj(list_obj); ++ if (obj == list_obj) ++ count++; ++ } ++ } ++ ++ if (count != 1) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory %d times", ++ count); ++ YBUG(); ++ } ++} ++ ++void yaffs_verify_dir(struct yaffs_obj *directory) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ ++ if (!directory) { ++ YBUG(); ++ return; ++ } ++ ++ if (yaffs_skip_full_verification(directory->my_dev)) ++ return; ++ ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Directory has wrong type: %d", ++ directory->variant_type); ++ YBUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &directory->variant.dir_variant.children) { ++ if (lh) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (list_obj->parent != directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory list has wrong parent %p", ++ list_obj->parent); ++ YBUG(); ++ } ++ yaffs_verify_obj_in_dir(list_obj); ++ } ++ } ++} ++ ++static int yaffs_free_verification_failures; ++ ++void yaffs_verify_free_chunks(struct yaffs_dev *dev) ++{ ++ int counted; ++ int difference; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ counted = yaffs_count_free_chunks(dev); ++ ++ difference = dev->n_free_chunks - counted; ++ ++ if (difference) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Freechunks verification failure %d %d %d", ++ dev->n_free_chunks, counted, difference); ++ yaffs_free_verification_failures++; ++ } ++} ++ ++int yaffs_verify_file_sane(struct yaffs_obj *in) ++{ ++ in = in; ++ return YAFFS_OK; ++} ++ +diff --git a/fs/yaffs2/yaffs_verify.h b/fs/yaffs2/yaffs_verify.h +new file mode 100644 +index 00000000..cc6f8899 +--- /dev/null ++++ b/fs/yaffs2/yaffs_verify.h +@@ -0,0 +1,43 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_VERIFY_H__ ++#define __YAFFS_VERIFY_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, ++ int n); ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n); ++void yaffs_verify_blocks(struct yaffs_dev *dev); ++ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check); ++void yaffs_verify_file(struct yaffs_obj *obj); ++void yaffs_verify_link(struct yaffs_obj *obj); ++void yaffs_verify_symlink(struct yaffs_obj *obj); ++void yaffs_verify_special(struct yaffs_obj *obj); ++void yaffs_verify_obj(struct yaffs_obj *obj); ++void yaffs_verify_objects(struct yaffs_dev *dev); ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); ++void yaffs_verify_dir(struct yaffs_obj *directory); ++void yaffs_verify_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_verify_file_sane(struct yaffs_obj *obj); ++ ++int yaffs_skip_verification(struct yaffs_dev *dev); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_vfs.c b/fs/yaffs2/yaffs_vfs.c +new file mode 100644 +index 00000000..d5b87531 +--- /dev/null ++++ b/fs/yaffs2/yaffs_vfs.c +@@ -0,0 +1,2792 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * Acknowledgements: ++ * Luc van OostenRyck for numerous patches. ++ * Nick Bane for numerous patches. ++ * Nick Bane for 2.5/2.6 integration. ++ * Andras Toth for mknod rdev issue. ++ * Michael Fischer for finding the problem with inode inconsistency. ++ * Some code bodily lifted from JFFS ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * ++ * This is the file system front-end to YAFFS that hooks it up to ++ * the VFS. ++ * ++ * Special notes: ++ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with ++ * this superblock ++ * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this ++ * superblock ++ * >> inode->u.generic_ip points to the associated struct yaffs_obj. ++ */ ++ ++/* ++ * NB There are two variants of Linux VFS glue code. This variant supports ++ * a single version and should not include any multi-version code. ++ */ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#define UnlockPage(p) unlock_page(p) ++#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) ++ ++#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) ++ ++#define YPROC_ROOT NULL ++ ++#define Y_INIT_TIMER(a) init_timer_on_stack(a) ++ ++#define WRITE_SIZE_STR "writesize" ++#define WRITE_SIZE(mtd) ((mtd)->writesize) ++ ++static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) ++{ ++ uint64_t result = partition_size; ++ do_div(result, block_size); ++ return (uint32_t) result; ++} ++ ++#include ++#include ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#include "yaffs_linux.h" ++ ++#include "yaffs_mtdif.h" ++#include "yaffs_mtdif1.h" ++#include "yaffs_mtdif2.h" ++ ++unsigned int yaffs_trace_mask = YAFFS_TRACE_BAD_BLOCKS | YAFFS_TRACE_ALWAYS; ++unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; ++unsigned int yaffs_auto_checkpoint = 1; ++unsigned int yaffs_gc_control = 1; ++unsigned int yaffs_bg_enable = 1; ++ ++/* Module Parameters */ ++module_param(yaffs_trace_mask, uint, 0644); ++module_param(yaffs_wr_attempts, uint, 0644); ++module_param(yaffs_auto_checkpoint, uint, 0644); ++module_param(yaffs_gc_control, uint, 0644); ++module_param(yaffs_bg_enable, uint, 0644); ++ ++ ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) ++#define yaffs_inode_to_obj(iptr) ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) ++#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) ++ ++#define update_dir_time(dir) do {\ ++ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ ++ } while(0) ++ ++ ++static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) ++{ ++ return yaffs_gc_control; ++} ++ ++static void yaffs_gross_lock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); ++ mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); ++} ++ ++static void yaffs_gross_unlock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); ++ mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++} ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj); ++ ++static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) ++{ ++ struct inode *inode; ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); ++ ++ inode = iget_locked(sb, ino); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ yaffs_gross_unlock(dev); ++ ++ unlock_new_inode(inode); ++ return inode; ++} ++ ++struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, ++ struct yaffs_obj *obj) ++{ ++ struct inode *inode; ++ ++ if (!sb) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL super_block!!"); ++ return NULL; ++ ++ } ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL object!!"); ++ return NULL; ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for object %d", ++ obj->obj_id); ++ ++ inode = yaffs_iget(sb, obj->obj_id); ++ if (IS_ERR(inode)) ++ return NULL; ++ ++ /* NB Side effect: iget calls back to yaffs_read_inode(). */ ++ /* iget also increments the inode's i_count */ ++ /* NB You can't be holding gross_lock or deadlock will happen! */ ++ ++ return inode; ++} ++ ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ dev_t rdev) ++{ ++ struct inode *inode; ++ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_dev *dev; ++ ++ struct yaffs_obj *parent = yaffs_inode_to_obj(dir); ++ ++ int error = -ENOSPC; ++ uid_t uid = current->cred->fsuid; ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? dir->i_gid : current->cred->fsgid; ++ ++ if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) ++ mode |= S_ISGID; ++ ++ if (parent) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: parent object %d type %d", ++ parent->obj_id, parent->variant_type); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: could not get parent object"); ++ return -EPERM; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: making oject for %s, mode %x dev %x", ++ dentry->d_name.name, mode, rdev); ++ ++ dev = parent->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ switch (mode & S_IFMT) { ++ default: ++ /* Special (socket, fifo, device...) */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, old_encode_dev(rdev)); ++ break; ++ case S_IFREG: /* file */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); ++ obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, ++ gid); ++ break; ++ case S_IFDIR: /* directory */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); ++ obj = yaffs_create_dir(parent, dentry->d_name.name, mode, ++ uid, gid); ++ break; ++ case S_IFLNK: /* symlink */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); ++ obj = NULL; /* Do we ever get here? */ ++ break; ++ } ++ ++ /* Can not call yaffs_get_inode() with gross lock held */ ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod created object %d count = %d", ++ obj->obj_id, atomic_read(&inode->i_count)); ++ error = 0; ++ yaffs_fill_inode_from_obj(dir, parent); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); ++ error = -ENOMEM; ++ } ++ ++ return error; ++} ++ ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++{ ++ return yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); ++} ++ ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *n) ++{ ++ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); ++} ++ ++static int yaffs_link(struct dentry *old_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ struct inode *inode = old_dentry->d_inode; ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *link = NULL; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ ++ link = ++ yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ obj); ++ ++ if (link) { ++ old_dentry->d_inode->i_nlink = yaffs_get_obj_link_count(obj); ++ d_instantiate(dentry, old_dentry->d_inode); ++ atomic_inc(&old_dentry->d_inode->i_count); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_link link count %d i_count %d", ++ old_dentry->d_inode->i_nlink, ++ atomic_read(&old_dentry->d_inode->i_count)); ++ } ++ ++ yaffs_gross_unlock(dev); ++ ++ if (link) { ++ update_dir_time(dir); ++ return 0; ++ } ++ ++ return -EPERM; ++} ++ ++static int yaffs_symlink(struct inode *dir, struct dentry *dentry, ++ const char *symname) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ uid_t uid = current->cred->fsuid; ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? dir->i_gid : current->cred->fsgid; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); ++ ++ dev = yaffs_inode_to_obj(dir)->my_dev; ++ yaffs_gross_lock(dev); ++ obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ S_IFLNK | S_IRWXUGO, uid, gid, symname); ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ struct inode *inode; ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); ++ return 0; ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); ++ } ++ ++ return -ENOMEM; ++} ++ ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *n) ++{ ++ struct yaffs_obj *obj; ++ struct inode *inode = NULL; ++ ++ struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_lookup for %d:%s", ++ yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); ++ ++ obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); ++ ++ obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ ++ ++ /* Can't hold gross lock when calling yaffs_get_inode() */ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_lookup found %d", obj->obj_id); ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ ++ if (inode) { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_loookup dentry"); ++ d_add(dentry, inode); ++ /* return dentry; */ ++ return NULL; ++ } ++ ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); ++ ++ } ++ ++ d_add(dentry, inode); ++ ++ return NULL; ++} ++ ++static int yaffs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int ret_val; ++ ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_unlink %d:%s", ++ (int)(dir->i_ino), dentry->d_name.name); ++ obj = yaffs_inode_to_obj(dir); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ ret_val = yaffs_unlinker(obj, dentry->d_name.name); ++ ++ if (ret_val == YAFFS_OK) { ++ dentry->d_inode->i_nlink--; ++ dir->i_version++; ++ yaffs_gross_unlock(dev); ++ mark_inode_dirty(dentry->d_inode); ++ update_dir_time(dir); ++ return 0; ++ } ++ yaffs_gross_unlock(dev); ++ return -ENOTEMPTY; ++} ++ ++static int yaffs_sync_object(struct file *file, int datasync) ++{ ++ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct dentry *dentry = file->f_path.dentry; ++ ++ obj = yaffs_dentry_to_obj(dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, "yaffs_sync_object"); ++ yaffs_gross_lock(dev); ++ yaffs_flush_file(obj, 1, datasync); ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++/* ++ * The VFS layer already does all the dentry stuff for rename. ++ * ++ * NB: POSIX says you can rename an object over an old object of the same name ++ */ ++static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ struct yaffs_dev *dev; ++ int ret_val = YAFFS_FAIL; ++ struct yaffs_obj *target; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); ++ dev = yaffs_inode_to_obj(old_dir)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ /* Check if the target is an existing directory that is not empty. */ ++ target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ ++ if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && ++ !list_empty(&target->variant.dir_variant.children)) { ++ ++ yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); ++ ++ ret_val = YAFFS_FAIL; ++ } else { ++ /* Now does unlinking internally using shadowing mechanism */ ++ yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); ++ ++ ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), ++ old_dentry->d_name.name, ++ yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ } ++ yaffs_gross_unlock(dev); ++ ++ if (ret_val == YAFFS_OK) { ++ if (target) { ++ new_dentry->d_inode->i_nlink--; ++ mark_inode_dirty(new_dentry->d_inode); ++ } ++ ++ update_dir_time(old_dir); ++ if (old_dir != new_dir) ++ update_dir_time(new_dir); ++ return 0; ++ } else { ++ return -ENOTEMPTY; ++ } ++} ++ ++static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_setattr of object %d", ++ yaffs_inode_to_obj(inode)->obj_id); ++ ++ /* Fail if a requested resize >= 2GB */ ++ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) ++ error = -EINVAL; ++ ++ if (error == 0) ++ error = inode_change_ok(inode, attr); ++ if (error == 0) { ++ int result; ++ if (!error) { ++ setattr_copy(inode, attr); ++ yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); ++ if (attr->ia_valid & ATTR_SIZE) { ++ truncate_setsize(inode, attr->ia_size); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ } ++ } ++ dev = yaffs_inode_to_obj(inode)->my_dev; ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_trace(YAFFS_TRACE_OS, "resize to %d(%x)", ++ (int)(attr->ia_size), ++ (int)(attr->ia_size)); ++ } ++ yaffs_gross_lock(dev); ++ result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); ++ if (result == YAFFS_OK) { ++ error = 0; ++ } else { ++ error = -EPERM; ++ } ++ yaffs_gross_unlock(dev); ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); ++ ++ return error; ++} ++ ++#ifdef CONFIG_YAFFS_XATTR ++static int yaffs_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_set_xattrib(obj, name, value, size, flags); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, void *buff, ++ size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_getxattr \"%s\" from object %d", ++ name, obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_get_xattrib(obj, name, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_removexattr(struct dentry *dentry, const char *name) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_remove_xattrib(obj, name); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_list_xattrib(obj, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr done returning %d", error); ++ ++ return error; ++} ++ ++#endif ++ ++static const struct inode_operations yaffs_dir_inode_operations = { ++ .create = yaffs_create, ++ .lookup = yaffs_lookup, ++ .link = yaffs_link, ++ .unlink = yaffs_unlink, ++ .symlink = yaffs_symlink, ++ .mkdir = yaffs_mkdir, ++ .rmdir = yaffs_unlink, ++ .mknod = yaffs_mknod, ++ .rename = yaffs_rename, ++ .setattr = yaffs_setattr, ++#ifdef CONFIG_YAFFS_XATTR ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++#endif ++}; ++/*-----------------------------------------------------------------*/ ++/* Directory search context allows us to unlock access to yaffs during ++ * filldir without causing problems with the directory being modified. ++ * This is similar to the tried and tested mechanism used in yaffs direct. ++ * ++ * A search context iterates along a doubly linked list of siblings in the ++ * directory. If the iterating object is deleted then this would corrupt ++ * the list iteration, likely causing a crash. The search context avoids ++ * this by using the remove_obj_fn to move the search context to the ++ * next object before the object is deleted. ++ * ++ * Many readdirs (and thus seach conexts) may be alive simulateously so ++ * each struct yaffs_dev has a list of these. ++ * ++ * A seach context lives for the duration of a readdir. ++ * ++ * All these functions must be called while yaffs is locked. ++ */ ++ ++struct yaffs_search_context { ++ struct yaffs_dev *dev; ++ struct yaffs_obj *dir_obj; ++ struct yaffs_obj *next_return; ++ struct list_head others; ++}; ++ ++/* ++ * yaffs_new_search() creates a new search context, initialises it and ++ * adds it to the device's search context list. ++ * ++ * Called at start of readdir. ++ */ ++static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) ++{ ++ struct yaffs_dev *dev = dir->my_dev; ++ struct yaffs_search_context *sc = ++ kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); ++ if (sc) { ++ sc->dir_obj = dir; ++ sc->dev = dev; ++ if (list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else ++ sc->next_return = ++ list_entry(dir->variant.dir_variant.children.next, ++ struct yaffs_obj, siblings); ++ INIT_LIST_HEAD(&sc->others); ++ list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); ++ } ++ return sc; ++} ++ ++/* ++ * yaffs_search_end() disposes of a search context and cleans up. ++ */ ++static void yaffs_search_end(struct yaffs_search_context *sc) ++{ ++ if (sc) { ++ list_del(&sc->others); ++ kfree(sc); ++ } ++} ++ ++/* ++ * yaffs_search_advance() moves a search context to the next object. ++ * Called when the search iterates or when an object removal causes ++ * the search context to be moved to the next object. ++ */ ++static void yaffs_search_advance(struct yaffs_search_context *sc) ++{ ++ if (!sc) ++ return; ++ ++ if (sc->next_return == NULL || ++ list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else { ++ struct list_head *next = sc->next_return->siblings.next; ++ ++ if (next == &sc->dir_obj->variant.dir_variant.children) ++ sc->next_return = NULL; /* end of list */ ++ else ++ sc->next_return = ++ list_entry(next, struct yaffs_obj, siblings); ++ } ++} ++ ++/* ++ * yaffs_remove_obj_callback() is called when an object is unlinked. ++ * We check open search contexts and advance any which are currently ++ * on the object being iterated. ++ */ ++static void yaffs_remove_obj_callback(struct yaffs_obj *obj) ++{ ++ ++ struct list_head *i; ++ struct yaffs_search_context *sc; ++ struct list_head *search_contexts = ++ &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); ++ ++ /* Iterate through the directory search contexts. ++ * If any are currently on the object being removed, then advance ++ * the search context to the next object to prevent a hanging pointer. ++ */ ++ list_for_each(i, search_contexts) { ++ if (i) { ++ sc = list_entry(i, struct yaffs_search_context, others); ++ if (sc->next_return == obj) ++ yaffs_search_advance(sc); ++ } ++ } ++ ++} ++ ++static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ struct inode *inode = f->f_dentry->d_inode; ++ unsigned long offset, curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ offset = f->f_pos; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: starting at %d", (int)offset); ++ ++ if (offset == 0) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry . ino %d", ++ (int)inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ if (offset == 1) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry .. ino %d", ++ (int)f->f_dentry->d_parent->d_inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, "..", 2, offset, ++ f->f_dentry->d_parent->d_inode->i_ino, ++ DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ ++ curoffs = 1; ++ ++ /* If the directory has changed since the open or last call to ++ readdir, rewind to after the 2 canned entries. */ ++ if (f->f_version != inode->i_version) { ++ offset = 2; ++ f->f_pos = offset; ++ f->f_version = inode->i_version; ++ } ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= offset) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (filldir(dirent, ++ name, ++ strlen(name), ++ offset, this_inode, this_type) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ offset++; ++ f->f_pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++ ++static const struct file_operations yaffs_dir_operations = { ++ .read = generic_read_dir, ++ .readdir = yaffs_readdir, ++ .fsync = yaffs_sync_object, ++ .llseek = generic_file_llseek, ++}; ++ ++ ++ ++static int yaffs_file_flush(struct file *file, fl_owner_t id) ++{ ++ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); ++ ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_flush object %d (%s)", ++ obj->obj_id, obj->dirty ? "dirty" : "clean"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_file(obj, 1, 0); ++ ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .splice_read = generic_file_splice_read, ++ .splice_write = generic_file_splice_write, ++ .llseek = generic_file_llseek, ++}; ++ ++ ++/* ExportFS support */ ++static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, ++ uint32_t generation) ++{ ++ return yaffs_iget(sb, ino); ++} ++ ++static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_parent(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++struct dentry *yaffs2_get_parent(struct dentry *dentry) ++{ ++ ++ struct super_block *sb = dentry->d_inode->i_sb; ++ struct dentry *parent = ERR_PTR(-ENOENT); ++ struct inode *inode; ++ unsigned long parent_ino; ++ struct yaffs_obj *d_obj; ++ struct yaffs_obj *parent_obj; ++ ++ d_obj = yaffs_inode_to_obj(dentry->d_inode); ++ ++ if (d_obj) { ++ parent_obj = d_obj->parent; ++ if (parent_obj) { ++ parent_ino = yaffs_get_obj_inode(parent_obj); ++ inode = yaffs_iget(sb, parent_ino); ++ ++ if (IS_ERR(inode)) { ++ parent = ERR_CAST(inode); ++ } else { ++ parent = d_obtain_alias(inode); ++ if (!IS_ERR(parent)) { ++ parent = ERR_PTR(-ENOMEM); ++ iput(inode); ++ } ++ } ++ } ++ } ++ ++ return parent; ++} ++ ++/* Just declare a zero structure as a NULL value implies ++ * using the default functions of exportfs. ++ */ ++ ++static struct export_operations yaffs_export_ops = { ++ .fh_to_dentry = yaffs2_fh_to_dentry, ++ .fh_to_parent = yaffs2_fh_to_parent, ++ .get_parent = yaffs2_get_parent, ++}; ++ ++ ++/*-----------------------------------------------------------------*/ ++ ++static int yaffs_readlink(struct dentry *dentry, char __user * buffer, ++ int buflen) ++{ ++ unsigned char *alias; ++ int ret; ++ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) ++ return -ENOMEM; ++ ++ ret = vfs_readlink(dentry, buffer, buflen, alias); ++ kfree(alias); ++ return ret; ++} ++ ++static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ unsigned char *alias; ++ void *ret; ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) { ++ ret = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ nd_set_link(nd, alias); ++ ret = (void *)alias; ++out: ++ return ret; ++} ++ ++void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) ++{ ++ kfree(alias); ++} ++ ++ ++static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) ++{ ++ /* Clear the association between the inode and ++ * the struct yaffs_obj. ++ */ ++ obj->my_inode = NULL; ++ yaffs_inode_to_obj_lv(inode) = NULL; ++ ++ /* If the object freeing was deferred, then the real ++ * free happens now. ++ * This should fix the inode inconsistency problem. ++ */ ++ yaffs_handle_defered_free(obj); ++} ++ ++/* yaffs_evict_inode combines into one operation what was previously done in ++ * yaffs_clear_inode() and yaffs_delete_inode() ++ * ++ */ ++static void yaffs_evict_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ int deleteme = 0; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_evict_inode: ino %d, count %d %s", ++ (int)inode->i_ino, ++ atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (!inode->i_nlink && !is_bad_inode(inode)) ++ deleteme = 1; ++ truncate_inode_pages(&inode->i_data, 0); ++ end_writeback(inode); ++ ++ if (deleteme && obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++ ++} ++ ++static void yaffs_touch_super(struct yaffs_dev *dev) ++{ ++ struct super_block *sb = yaffs_dev_to_lc(dev)->super; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_touch_super() sb = %p", sb); ++ if (sb) ++ sb->s_dirt = 1; ++} ++ ++static int yaffs_readpage_nolock(struct file *f, struct page *pg) ++{ ++ /* Lifted from jffs2 */ ++ ++ struct yaffs_obj *obj; ++ unsigned char *pg_buf; ++ int ret; ++ ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readpage_nolock at %08x, size %08x", ++ (unsigned)(pg->index << PAGE_CACHE_SHIFT), ++ (unsigned)PAGE_CACHE_SIZE); ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ BUG_ON(!PageLocked(pg)); ++ ++ pg_buf = kmap(pg); ++ /* FIXME: Can kmap fail? */ ++ ++ yaffs_gross_lock(dev); ++ ++ ret = yaffs_file_rd(obj, pg_buf, ++ pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (ret >= 0) ++ ret = 0; ++ ++ if (ret) { ++ ClearPageUptodate(pg); ++ SetPageError(pg); ++ } else { ++ SetPageUptodate(pg); ++ ClearPageError(pg); ++ } ++ ++ flush_dcache_page(pg); ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); ++ return ret; ++} ++ ++static int yaffs_readpage_unlock(struct file *f, struct page *pg) ++{ ++ int ret = yaffs_readpage_nolock(f, pg); ++ UnlockPage(pg); ++ return ret; ++} ++ ++static int yaffs_readpage(struct file *f, struct page *pg) ++{ ++ int ret; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); ++ ret = yaffs_readpage_unlock(f, pg); ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); ++ return ret; ++} ++ ++/* writepage inspired by/stolen from smbfs */ ++ ++static int yaffs_writepage(struct page *page, struct writeback_control *wbc) ++{ ++ struct yaffs_dev *dev; ++ struct address_space *mapping = page->mapping; ++ struct inode *inode; ++ unsigned long end_index; ++ char *buffer; ++ struct yaffs_obj *obj; ++ int n_written = 0; ++ unsigned n_bytes; ++ loff_t i_size; ++ ++ if (!mapping) ++ BUG(); ++ inode = mapping->host; ++ if (!inode) ++ BUG(); ++ i_size = i_size_read(inode); ++ ++ end_index = i_size >> PAGE_CACHE_SHIFT; ++ ++ if (page->index < end_index) ++ n_bytes = PAGE_CACHE_SIZE; ++ else { ++ n_bytes = i_size & (PAGE_CACHE_SIZE - 1); ++ ++ if (page->index > end_index || !n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %08x, inode size = %08x!!!", ++ (unsigned)(page->index << PAGE_CACHE_SHIFT), ++ (unsigned)inode->i_size); ++ yaffs_trace(YAFFS_TRACE_OS, ++ " -> don't care!!"); ++ ++ zero_user_segment(page, 0, PAGE_CACHE_SIZE); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ return 0; ++ } ++ } ++ ++ if (n_bytes != PAGE_CACHE_SIZE) ++ zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); ++ ++ get_page(page); ++ ++ buffer = kmap(page); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %08x, size %08x", ++ (unsigned)(page->index << PAGE_CACHE_SHIFT), n_bytes); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag0: obj = %05x, ino = %05x", ++ (int)obj->variant.file_variant.file_size, (int)inode->i_size); ++ ++ n_written = yaffs_wr_file(obj, buffer, ++ page->index << PAGE_CACHE_SHIFT, n_bytes, 0); ++ ++ yaffs_touch_super(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag1: obj = %05x, ino = %05x", ++ (int)obj->variant.file_variant.file_size, (int)inode->i_size); ++ ++ yaffs_gross_unlock(dev); ++ ++ kunmap(page); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ put_page(page); ++ ++ return (n_written == n_bytes) ? 0 : -ENOSPC; ++} ++ ++/* Space holding and freeing is done to ensure we have space available for ++ * write_begin/end. ++ * For now we just assume few parallel writes and check against a small ++ * number. ++ * Todo: need to do this with a counter to handle parallel reads better. ++ */ ++ ++static ssize_t yaffs_hold_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ int n_free_chunks; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ n_free_chunks = yaffs_get_n_free_chunks(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ return (n_free_chunks > 20) ? 1 : 0; ++} ++ ++static void yaffs_release_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_gross_unlock(dev); ++} ++ ++static int yaffs_write_begin(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ ++ struct page *pg = NULL; ++ pgoff_t index = pos >> PAGE_CACHE_SHIFT; ++ ++ int ret = 0; ++ int space_held = 0; ++ ++ /* Get a page */ ++ pg = grab_cache_page_write_begin(mapping, index, flags); ++ ++ *pagep = pg; ++ if (!pg) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "start yaffs_write_begin index %d(%x) uptodate %d", ++ (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); ++ ++ /* Get fs space */ ++ space_held = yaffs_hold_space(filp); ++ ++ if (!space_held) { ++ ret = -ENOSPC; ++ goto out; ++ } ++ ++ /* Update page if required */ ++ ++ if (!Page_Uptodate(pg)) ++ ret = yaffs_readpage_nolock(filp, pg); ++ ++ if (ret) ++ goto out; ++ ++ /* Happy path return */ ++ yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); ++ ++ return 0; ++ ++out: ++ yaffs_trace(YAFFS_TRACE_OS, ++ "end yaffs_write_begin fail returning %d", ret); ++ if (space_held) ++ yaffs_release_space(filp); ++ if (pg) { ++ unlock_page(pg); ++ page_cache_release(pg); ++ } ++ return ret; ++} ++ ++static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, ++ loff_t * pos) ++{ ++ struct yaffs_obj *obj; ++ int n_written, ipos; ++ struct inode *inode; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ inode = f->f_dentry->d_inode; ++ ++ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) ++ ipos = inode->i_size; ++ else ++ ipos = *pos; ++ ++ if (!obj) ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: hey obj is null!"); ++ else ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write about to write writing %u(%x) bytes to object %d at %d(%x)", ++ (unsigned)n, (unsigned)n, obj->obj_id, ipos, ipos); ++ ++ n_written = yaffs_wr_file(obj, buf, ipos, n, 0); ++ ++ yaffs_touch_super(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: %d(%x) bytes written", ++ (unsigned)n, (unsigned)n); ++ ++ if (n_written > 0) { ++ ipos += n_written; ++ *pos = ipos; ++ if (ipos > inode->i_size) { ++ inode->i_size = ipos; ++ inode->i_blocks = (ipos + 511) >> 9; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write size updated to %d bytes, %d blocks", ++ ipos, (int)(inode->i_blocks)); ++ } ++ ++ } ++ yaffs_gross_unlock(dev); ++ return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; ++} ++ ++static int yaffs_write_end(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *pg, void *fsdadata) ++{ ++ int ret = 0; ++ void *addr, *kva; ++ uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); ++ ++ kva = kmap(pg); ++ addr = kva + offset_into_page; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end addr %p pos %x n_bytes %d", ++ addr, (unsigned)pos, copied); ++ ++ ret = yaffs_file_write(filp, addr, copied, &pos); ++ ++ if (ret != copied) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end not same size ret %d copied %d", ++ ret, copied); ++ SetPageError(pg); ++ } ++ ++ kunmap(pg); ++ ++ yaffs_release_space(filp); ++ unlock_page(pg); ++ page_cache_release(pg); ++ return ret; ++} ++ ++static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ struct super_block *sb = dentry->d_sb; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); ++ ++ yaffs_gross_lock(dev); ++ ++ buf->f_type = YAFFS_MAGIC; ++ buf->f_bsize = sb->s_blocksize; ++ buf->f_namelen = 255; ++ ++ if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { ++ /* Do this if chunk size is not a power of 2 */ ++ ++ uint64_t bytes_in_dev; ++ uint64_t bytes_free; ++ ++ bytes_in_dev = ++ ((uint64_t) ++ ((dev->param.end_block - dev->param.start_block + ++ 1))) * ((uint64_t) (dev->param.chunks_per_block * ++ dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ ++ buf->f_blocks = bytes_in_dev; ++ ++ bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * ++ ((uint64_t) (dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_free, sb->s_blocksize); ++ ++ buf->f_bfree = bytes_free; ++ ++ } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { ++ ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ } else { ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ } ++ ++ buf->f_files = 0; ++ buf->f_ffree = 0; ++ buf->f_bavail = buf->f_bfree; ++ ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++static void yaffs_flush_inodes(struct super_block *sb) ++{ ++ struct inode *iptr; ++ struct yaffs_obj *obj; ++ ++ list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { ++ obj = yaffs_inode_to_obj(iptr); ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "flushing obj %d", obj->obj_id); ++ yaffs_flush_file(obj, 1, 0); ++ } ++ } ++} ++ ++static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ if (!dev) ++ return; ++ ++ yaffs_flush_inodes(sb); ++ yaffs_update_dirty_dirs(dev); ++ yaffs_flush_whole_cache(dev); ++ if (do_checkpoint) ++ yaffs_checkpoint_save(dev); ++} ++ ++static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) ++{ ++ unsigned erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned scattered = 0; /* Free chunks not in an erased block */ ++ ++ if (erased_chunks < dev->n_free_chunks) ++ scattered = (dev->n_free_chunks - erased_chunks); ++ ++ if (!context->bg_running) ++ return 0; ++ else if (scattered < (dev->param.chunks_per_block * 2)) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 2) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 4) ++ return 1; ++ else ++ return 2; ++} ++ ++static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) ++{ ++ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); ++ unsigned gc_urgent = yaffs_bg_gc_urgency(dev); ++ int do_checkpoint; ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_do_sync_fs: gc-urgency %d %s %s%s", ++ gc_urgent, ++ sb->s_dirt ? "dirty" : "clean", ++ request_checkpoint ? "checkpoint requested" : "no checkpoint", ++ oneshot_checkpoint ? " one-shot" : ""); ++ ++ yaffs_gross_lock(dev); ++ do_checkpoint = ((request_checkpoint && !gc_urgent) || ++ oneshot_checkpoint) && !dev->is_checkpointed; ++ ++ if (sb->s_dirt || do_checkpoint) { ++ yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); ++ sb->s_dirt = 0; ++ if (oneshot_checkpoint) ++ yaffs_auto_checkpoint &= ~4; ++ } ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++/* ++ * yaffs background thread functions . ++ * yaffs_bg_thread_fn() the thread function ++ * yaffs_bg_start() launches the background thread. ++ * yaffs_bg_stop() cleans up the background thread. ++ * ++ * NB: ++ * The thread should only run after the yaffs is initialised ++ * The thread should be stopped before yaffs is unmounted. ++ * The thread should not do any writing while the fs is in read only. ++ */ ++ ++void yaffs_background_waker(unsigned long data) ++{ ++ wake_up_process((struct task_struct *)data); ++} ++ ++static int yaffs_bg_thread_fn(void *data) ++{ ++ struct yaffs_dev *dev = (struct yaffs_dev *)data; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned long now = jiffies; ++ unsigned long next_dir_update = now; ++ unsigned long next_gc = now; ++ unsigned long expires; ++ unsigned int urgency; ++ ++ int gc_result; ++ struct timer_list timer; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "yaffs_background starting for dev %p", (void *)dev); ++ ++ set_freezable(); ++ while (context->bg_running) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); ++ ++ if (kthread_should_stop()) ++ break; ++ ++ if (try_to_freeze()) ++ continue; ++ ++ yaffs_gross_lock(dev); ++ ++ now = jiffies; ++ ++ if (time_after(now, next_dir_update) && yaffs_bg_enable) { ++ yaffs_update_dirty_dirs(dev); ++ next_dir_update = now + HZ; ++ } ++ ++ if (time_after(now, next_gc) && yaffs_bg_enable) { ++ if (!dev->is_checkpointed) { ++ urgency = yaffs_bg_gc_urgency(dev); ++ gc_result = yaffs_bg_gc(dev, urgency); ++ if (urgency > 1) ++ next_gc = now + HZ / 20 + 1; ++ else if (urgency > 0) ++ next_gc = now + HZ / 10 + 1; ++ else ++ next_gc = now + HZ * 2; ++ } else { ++ /* ++ * gc not running so set to next_dir_update ++ * to cut down on wake ups ++ */ ++ next_gc = next_dir_update; ++ } ++ } ++ yaffs_gross_unlock(dev); ++ expires = next_dir_update; ++ if (time_before(next_gc, expires)) ++ expires = next_gc; ++ if (time_before(expires, now)) ++ expires = now + HZ; ++ ++ Y_INIT_TIMER(&timer); ++ timer.expires = expires + 1; ++ timer.data = (unsigned long)current; ++ timer.function = yaffs_background_waker; ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ add_timer(&timer); ++ schedule(); ++ del_timer_sync(&timer); ++ } ++ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ int retval = 0; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ ++ if (dev->read_only) ++ return -1; ++ ++ context->bg_running = 1; ++ ++ context->bg_thread = kthread_run(yaffs_bg_thread_fn, ++ (void *)dev, "yaffs-bg-%d", ++ context->mount_id); ++ ++ if (IS_ERR(context->bg_thread)) { ++ retval = PTR_ERR(context->bg_thread); ++ context->bg_thread = NULL; ++ context->bg_running = 0; ++ } ++ return retval; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); ++ ++ ctxt->bg_running = 0; ++ ++ if (ctxt->bg_thread) { ++ kthread_stop(ctxt->bg_thread); ++ ctxt->bg_thread = NULL; ++ } ++} ++ ++static void yaffs_write_super(struct super_block *sb) ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_write_super%s", ++ request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++} ++ ++static int yaffs_sync_fs(struct super_block *sb, int wait) ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++ return 0; ++} ++ ++ ++static LIST_HEAD(yaffs_context_list); ++struct mutex yaffs_context_lock; ++ ++ ++ ++struct yaffs_options { ++ int inband_tags; ++ int skip_checkpoint_read; ++ int skip_checkpoint_write; ++ int no_cache; ++ int tags_ecc_on; ++ int tags_ecc_overridden; ++ int lazy_loading_enabled; ++ int lazy_loading_overridden; ++ int empty_lost_and_found; ++ int empty_lost_and_found_overridden; ++}; ++ ++#define MAX_OPT_LEN 30 ++static int yaffs_parse_options(struct yaffs_options *options, ++ const char *options_str) ++{ ++ char cur_opt[MAX_OPT_LEN + 1]; ++ int p; ++ int error = 0; ++ ++ /* Parse through the options which is a comma seperated list */ ++ ++ while (options_str && *options_str && !error) { ++ memset(cur_opt, 0, MAX_OPT_LEN + 1); ++ p = 0; ++ ++ while (*options_str == ',') ++ options_str++; ++ ++ while (*options_str && *options_str != ',') { ++ if (p < MAX_OPT_LEN) { ++ cur_opt[p] = *options_str; ++ p++; ++ } ++ options_str++; ++ } ++ ++ if (!strcmp(cur_opt, "inband-tags")) { ++ options->inband_tags = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-off")) { ++ options->tags_ecc_on = 0; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-on")) { ++ options->tags_ecc_on = 1; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-off")) { ++ options->lazy_loading_enabled = 0; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-on")) { ++ options->lazy_loading_enabled = 1; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { ++ options->empty_lost_and_found = 0; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { ++ options->empty_lost_and_found = 1; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "no-cache")) { ++ options->no_cache = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-read")) { ++ options->skip_checkpoint_read = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-write")) { ++ options->skip_checkpoint_write = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint")) { ++ options->skip_checkpoint_read = 1; ++ options->skip_checkpoint_write = 1; ++ } else { ++ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", ++ cur_opt); ++ error = 1; ++ } ++ } ++ ++ return error; ++} ++ ++static struct address_space_operations yaffs_file_address_operations = { ++ .readpage = yaffs_readpage, ++ .writepage = yaffs_writepage, ++ .write_begin = yaffs_write_begin, ++ .write_end = yaffs_write_end, ++}; ++ ++ ++ ++static const struct inode_operations yaffs_file_inode_operations = { ++ .setattr = yaffs_setattr, ++#ifdef CONFIG_YAFFS_XATTR ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++#endif ++}; ++ ++static const struct inode_operations yaffs_symlink_inode_operations = { ++ .readlink = yaffs_readlink, ++ .follow_link = yaffs_follow_link, ++ .put_link = yaffs_put_link, ++ .setattr = yaffs_setattr, ++#ifdef CONFIG_YAFFS_XATTR ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++#endif ++}; ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj) ++{ ++ if (inode && obj) { ++ ++ /* Check mode against the variant type and attempt to repair if broken. */ ++ u32 mode = obj->yst_mode; ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (!S_ISREG(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFREG; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ if (!S_ISLNK(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFLNK; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!S_ISDIR(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFDIR; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ default: ++ /* TODO? */ ++ break; ++ } ++ ++ inode->i_flags |= S_NOATIME; ++ ++ inode->i_ino = obj->obj_id; ++ inode->i_mode = obj->yst_mode; ++ inode->i_uid = obj->yst_uid; ++ inode->i_gid = obj->yst_gid; ++ ++ inode->i_rdev = old_decode_dev(obj->yst_rdev); ++ ++ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); ++ inode->i_atime.tv_nsec = 0; ++ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; ++ inode->i_mtime.tv_nsec = 0; ++ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; ++ inode->i_ctime.tv_nsec = 0; ++ inode->i_size = yaffs_get_obj_length(obj); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ ++ inode->i_nlink = yaffs_get_obj_link_count(obj); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode mode %x uid %d gid %d size %d count %d", ++ inode->i_mode, inode->i_uid, inode->i_gid, ++ (int)inode->i_size, atomic_read(&inode->i_count)); ++ ++ switch (obj->yst_mode & S_IFMT) { ++ default: /* fifo, device or socket */ ++ init_special_inode(inode, obj->yst_mode, ++ old_decode_dev(obj->yst_rdev)); ++ break; ++ case S_IFREG: /* file */ ++ inode->i_op = &yaffs_file_inode_operations; ++ inode->i_fop = &yaffs_file_operations; ++ inode->i_mapping->a_ops = ++ &yaffs_file_address_operations; ++ break; ++ case S_IFDIR: /* directory */ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ break; ++ case S_IFLNK: /* symlink */ ++ inode->i_op = &yaffs_symlink_inode_operations; ++ break; ++ } ++ ++ yaffs_inode_to_obj_lv(inode) = obj; ++ ++ obj->my_inode = inode; ++ ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode invalid parameters"); ++ } ++} ++ ++static void yaffs_put_super(struct super_block *sb) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_put_super"); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "Shutting down yaffs background thread"); ++ yaffs_bg_stop(dev); ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "yaffs background thread shut down"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_super(sb, 1); ++ ++ if (yaffs_dev_to_lc(dev)->put_super_fn) ++ yaffs_dev_to_lc(dev)->put_super_fn(sb); ++ ++ yaffs_deinitialise(dev); ++ ++ yaffs_gross_unlock(dev); ++ mutex_lock(&yaffs_context_lock); ++ list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); ++ mutex_unlock(&yaffs_context_lock); ++ ++ if (yaffs_dev_to_lc(dev)->spare_buffer) { ++ kfree(yaffs_dev_to_lc(dev)->spare_buffer); ++ yaffs_dev_to_lc(dev)->spare_buffer = NULL; ++ } ++ ++ kfree(dev); ++} ++ ++static void yaffs_mtd_put_super(struct super_block *sb) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(yaffs_super_to_dev(sb)); ++ ++ if (mtd->sync) ++ mtd->sync(mtd); ++ ++ put_mtd_device(mtd); ++} ++ ++static const struct super_operations yaffs_super_ops = { ++ .statfs = yaffs_statfs, ++ .put_super = yaffs_put_super, ++ .evict_inode = yaffs_evict_inode, ++ .sync_fs = yaffs_sync_fs, ++ .write_super = yaffs_write_super, ++}; ++ ++static struct super_block *yaffs_internal_read_super(int yaffs_version, ++ struct super_block *sb, ++ void *data, int silent) ++{ ++ int n_blocks; ++ struct inode *inode = NULL; ++ struct dentry *root; ++ struct yaffs_dev *dev = 0; ++ char devname_buf[BDEVNAME_SIZE + 1]; ++ struct mtd_info *mtd; ++ int err; ++ char *data_str = (char *)data; ++ struct yaffs_linux_context *context = NULL; ++ struct yaffs_param *param; ++ ++ int read_only = 0; ++ ++ struct yaffs_options options; ++ ++ unsigned mount_id; ++ int found; ++ struct yaffs_linux_context *context_iterator; ++ struct list_head *l; ++ ++ sb->s_magic = YAFFS_MAGIC; ++ sb->s_op = &yaffs_super_ops; ++ sb->s_flags |= MS_NOATIME; ++ ++ read_only = ((sb->s_flags & MS_RDONLY) != 0); ++ ++ sb->s_export_op = &yaffs_export_ops; ++ ++ if (!sb) ++ printk(KERN_INFO "yaffs: sb is NULL\n"); ++ else if (!sb->s_dev) ++ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); ++ else if (!yaffs_devname(sb, devname_buf)) ++ printk(KERN_INFO "yaffs: devname is NULL\n"); ++ else ++ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", ++ sb->s_dev, ++ yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); ++ ++ if (!data_str) ++ data_str = ""; ++ ++ printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); ++ ++ memset(&options, 0, sizeof(options)); ++ ++ if (yaffs_parse_options(&options, data_str)) { ++ /* Option parsing failed */ ++ return NULL; ++ } ++ ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: Using yaffs%d", yaffs_version); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Attempting MTD mount of %u.%u,\"%s\"", ++ MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ yaffs_devname(sb, devname_buf)); ++ ++ /* Check it's an mtd device..... */ ++ if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) ++ return NULL; /* This isn't an mtd device */ ++ ++ /* Get the device */ ++ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); ++ if (!mtd) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device #%u doesn't appear to exist", ++ MINOR(sb->s_dev)); ++ return NULL; ++ } ++ /* Check it's NAND */ ++ if (mtd->type != MTD_NANDFLASH) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device is not NAND it's type %d", ++ mtd->type); ++ return NULL; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, " erase %p", mtd->erase); ++ yaffs_trace(YAFFS_TRACE_OS, " read %p", mtd->read); ++ yaffs_trace(YAFFS_TRACE_OS, " write %p", mtd->write); ++ yaffs_trace(YAFFS_TRACE_OS, " readoob %p", mtd->read_oob); ++ yaffs_trace(YAFFS_TRACE_OS, " writeoob %p", mtd->write_oob); ++ yaffs_trace(YAFFS_TRACE_OS, " block_isbad %p", mtd->block_isbad); ++ yaffs_trace(YAFFS_TRACE_OS, " block_markbad %p", mtd->block_markbad); ++ yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); ++ yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); ++ yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); ++ yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); ++ ++#ifdef CONFIG_YAFFS_AUTO_YAFFS2 ++ ++ if (yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); ++ yaffs_version = 2; ++ } ++ ++ /* Added NCB 26/5/2006 for completeness */ ++ if (yaffs_version == 2 && !options.inband_tags ++ && WRITE_SIZE(mtd) == 512) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); ++ yaffs_version = 1; ++ } ++#endif ++ ++ if (yaffs_version == 2) { ++ /* Check for version 2 style functions */ ++ if (!mtd->erase || ++ !mtd->block_isbad || ++ !mtd->block_markbad || ++ !mtd->read || ++ !mtd->write || !mtd->read_oob || !mtd->write_oob) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not support required functions"); ++ return NULL; ++ } ++ ++ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || ++ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && ++ !options.inband_tags) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not have the right page sizes"); ++ return NULL; ++ } ++ } else { ++ /* Check for V1 style functions */ ++ if (!mtd->erase || ++ !mtd->read || ++ !mtd->write || !mtd->read_oob || !mtd->write_oob) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not support required functions"); ++ return NULL; ++ } ++ ++ if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || ++ mtd->oobsize != YAFFS_BYTES_PER_SPARE) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not support have the right page sizes"); ++ return NULL; ++ } ++ } ++ ++ /* OK, so if we got here, we have an MTD that's NAND and looks ++ * like it has the right capabilities ++ * Set the struct yaffs_dev up for mtd ++ */ ++ ++ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { ++ read_only = 1; ++ printk(KERN_INFO ++ "yaffs: mtd is read only, setting superblock read only"); ++ sb->s_flags |= MS_RDONLY; ++ } ++ ++ dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); ++ context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); ++ ++ if (!dev || !context) { ++ if (dev) ++ kfree(dev); ++ if (context) ++ kfree(context); ++ dev = NULL; ++ context = NULL; ++ } ++ ++ if (!dev) { ++ /* Deep shit could not allocate device structure */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super failed trying to allocate yaffs_dev"); ++ return NULL; ++ } ++ memset(dev, 0, sizeof(struct yaffs_dev)); ++ param = &(dev->param); ++ ++ memset(context, 0, sizeof(struct yaffs_linux_context)); ++ dev->os_context = context; ++ INIT_LIST_HEAD(&(context->context_list)); ++ context->dev = dev; ++ context->super = sb; ++ ++ dev->read_only = read_only; ++ ++ sb->s_fs_info = dev; ++ ++ dev->driver_context = mtd; ++ param->name = mtd->name; ++ ++ /* Set up the memory size parameters.... */ ++ ++ n_blocks = ++ YCALCBLOCKS(mtd->size, ++ (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK)); ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; ++ param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; ++ param->n_reserved_blocks = 5; ++ param->n_caches = (options.no_cache) ? 0 : 10; ++ param->inband_tags = options.inband_tags; ++ ++#ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD ++ param->disable_lazy_load = 1; ++#endif ++#ifdef CONFIG_YAFFS_XATTR ++ param->enable_xattr = 1; ++#endif ++ if (options.lazy_loading_overridden) ++ param->disable_lazy_load = !options.lazy_loading_enabled; ++ ++#ifdef CONFIG_YAFFS_DISABLE_TAGS_ECC ++ param->no_tags_ecc = 1; ++#endif ++ ++#ifdef CONFIG_YAFFS_DISABLE_BACKGROUND ++#else ++ param->defered_dir_update = 1; ++#endif ++ ++ if (options.tags_ecc_overridden) ++ param->no_tags_ecc = !options.tags_ecc_on; ++ ++#ifdef CONFIG_YAFFS_EMPTY_LOST_AND_FOUND ++ param->empty_lost_n_found = 1; ++#endif ++ ++#ifdef CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING ++ param->refresh_period = 0; ++#else ++ param->refresh_period = 500; ++#endif ++ ++#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED ++ param->always_check_erased = 1; ++#endif ++ ++ if (options.empty_lost_and_found_overridden) ++ param->empty_lost_n_found = options.empty_lost_and_found; ++ ++ /* ... and the functions. */ ++ if (yaffs_version == 2) { ++ param->write_chunk_tags_fn = nandmtd2_write_chunk_tags; ++ param->read_chunk_tags_fn = nandmtd2_read_chunk_tags; ++ param->bad_block_fn = nandmtd2_mark_block_bad; ++ param->query_block_fn = nandmtd2_query_block; ++ yaffs_dev_to_lc(dev)->spare_buffer = ++ kmalloc(mtd->oobsize, GFP_NOFS); ++ param->is_yaffs2 = 1; ++ param->total_bytes_per_chunk = mtd->writesize; ++ param->chunks_per_block = mtd->erasesize / mtd->writesize; ++ n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ } else { ++ /* use the MTD interface in yaffs_mtdif1.c */ ++ param->write_chunk_tags_fn = nandmtd1_write_chunk_tags; ++ param->read_chunk_tags_fn = nandmtd1_read_chunk_tags; ++ param->bad_block_fn = nandmtd1_mark_block_bad; ++ param->query_block_fn = nandmtd1_query_block; ++ param->is_yaffs2 = 0; ++ } ++ /* ... and common functions */ ++ param->erase_fn = nandmtd_erase_block; ++ param->initialise_flash_fn = nandmtd_initialise; ++ ++ yaffs_dev_to_lc(dev)->put_super_fn = yaffs_mtd_put_super; ++ ++ param->sb_dirty_fn = yaffs_touch_super; ++ param->gc_control = yaffs_gc_control_callback; ++ ++ yaffs_dev_to_lc(dev)->super = sb; ++ ++#ifndef CONFIG_YAFFS_DOES_ECC ++ param->use_nand_ecc = 1; ++#endif ++ ++ param->skip_checkpt_rd = options.skip_checkpoint_read; ++ param->skip_checkpt_wr = options.skip_checkpoint_write; ++ ++ mutex_lock(&yaffs_context_lock); ++ /* Get a mount id */ ++ found = 0; ++ for (mount_id = 0; !found; mount_id++) { ++ found = 1; ++ list_for_each(l, &yaffs_context_list) { ++ context_iterator = ++ list_entry(l, struct yaffs_linux_context, ++ context_list); ++ if (context_iterator->mount_id == mount_id) ++ found = 0; ++ } ++ } ++ context->mount_id = mount_id; ++ ++ list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), ++ &yaffs_context_list); ++ mutex_unlock(&yaffs_context_lock); ++ ++ /* Directory search handling... */ ++ INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); ++ param->remove_obj_fn = yaffs_remove_obj_callback; ++ ++ mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ ++ yaffs_gross_lock(dev); ++ ++ err = yaffs_guts_initialise(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: guts initialised %s", ++ (err == YAFFS_OK) ? "OK" : "FAILED"); ++ ++ if (err == YAFFS_OK) ++ yaffs_bg_start(dev); ++ ++ if (!context->bg_thread) ++ param->defered_dir_update = 0; ++ ++ /* Release lock before yaffs_get_inode() */ ++ yaffs_gross_unlock(dev); ++ ++ /* Create root inode */ ++ if (err == YAFFS_OK) ++ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); ++ ++ if (!inode) ++ return NULL; ++ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); ++ ++ root = d_alloc_root(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: d_alloc_root done"); ++ ++ if (!root) { ++ iput(inode); ++ return NULL; ++ } ++ sb->s_root = root; ++ sb->s_dirt = !dev->is_checkpointed; ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); ++ return sb; ++} ++ ++static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++static int yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data, struct vfsmount *mnt) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd, mnt); ++} ++ ++static struct file_system_type yaffs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs", ++ .get_sb = yaffs_read_super, ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++ ++#ifdef CONFIG_YAFFS_YAFFS2 ++ ++static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++static int yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, void *data, ++ struct vfsmount *mnt) ++{ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd, mnt); ++} ++ ++static struct file_system_type yaffs2_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs2", ++ .get_sb = yaffs2_read_super, ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#endif /* CONFIG_YAFFS_YAFFS2 */ ++ ++static struct proc_dir_entry *my_proc_entry; ++ ++static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) ++{ ++ struct yaffs_param *param = &dev->param; ++ buf += sprintf(buf, "start_block........... %d\n", param->start_block); ++ buf += sprintf(buf, "end_block............. %d\n", param->end_block); ++ buf += sprintf(buf, "total_bytes_per_chunk. %d\n", ++ param->total_bytes_per_chunk); ++ buf += sprintf(buf, "use_nand_ecc.......... %d\n", ++ param->use_nand_ecc); ++ buf += sprintf(buf, "no_tags_ecc........... %d\n", param->no_tags_ecc); ++ buf += sprintf(buf, "is_yaffs2............. %d\n", param->is_yaffs2); ++ buf += sprintf(buf, "inband_tags........... %d\n", param->inband_tags); ++ buf += sprintf(buf, "empty_lost_n_found.... %d\n", ++ param->empty_lost_n_found); ++ buf += sprintf(buf, "disable_lazy_load..... %d\n", ++ param->disable_lazy_load); ++ buf += sprintf(buf, "refresh_period........ %d\n", ++ param->refresh_period); ++ buf += sprintf(buf, "n_caches.............. %d\n", param->n_caches); ++ buf += sprintf(buf, "n_reserved_blocks..... %d\n", ++ param->n_reserved_blocks); ++ buf += sprintf(buf, "always_check_erased... %d\n", ++ param->always_check_erased); ++ ++ return buf; ++} ++ ++static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) ++{ ++ buf += ++ sprintf(buf, "data_bytes_per_chunk.. %d\n", ++ dev->data_bytes_per_chunk); ++ buf += sprintf(buf, "chunk_grp_bits........ %d\n", dev->chunk_grp_bits); ++ buf += sprintf(buf, "chunk_grp_size........ %d\n", dev->chunk_grp_size); ++ buf += ++ sprintf(buf, "n_erased_blocks....... %d\n", dev->n_erased_blocks); ++ buf += ++ sprintf(buf, "blocks_in_checkpt..... %d\n", dev->blocks_in_checkpt); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_tnodes.............. %d\n", dev->n_tnodes); ++ buf += sprintf(buf, "n_obj................. %d\n", dev->n_obj); ++ buf += sprintf(buf, "n_free_chunks......... %d\n", dev->n_free_chunks); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_page_writes......... %u\n", dev->n_page_writes); ++ buf += sprintf(buf, "n_page_reads.......... %u\n", dev->n_page_reads); ++ buf += sprintf(buf, "n_erasures............ %u\n", dev->n_erasures); ++ buf += sprintf(buf, "n_gc_copies........... %u\n", dev->n_gc_copies); ++ buf += sprintf(buf, "all_gcs............... %u\n", dev->all_gcs); ++ buf += ++ sprintf(buf, "passive_gc_count...... %u\n", dev->passive_gc_count); ++ buf += ++ sprintf(buf, "oldest_dirty_gc_count. %u\n", ++ dev->oldest_dirty_gc_count); ++ buf += sprintf(buf, "n_gc_blocks........... %u\n", dev->n_gc_blocks); ++ buf += sprintf(buf, "bg_gcs................ %u\n", dev->bg_gcs); ++ buf += ++ sprintf(buf, "n_retired_writes...... %u\n", dev->n_retired_writes); ++ buf += ++ sprintf(buf, "n_retired_blocks...... %u\n", dev->n_retired_blocks); ++ buf += sprintf(buf, "n_ecc_fixed........... %u\n", dev->n_ecc_fixed); ++ buf += sprintf(buf, "n_ecc_unfixed......... %u\n", dev->n_ecc_unfixed); ++ buf += ++ sprintf(buf, "n_tags_ecc_fixed...... %u\n", dev->n_tags_ecc_fixed); ++ buf += ++ sprintf(buf, "n_tags_ecc_unfixed.... %u\n", ++ dev->n_tags_ecc_unfixed); ++ buf += sprintf(buf, "cache_hits............ %u\n", dev->cache_hits); ++ buf += ++ sprintf(buf, "n_deleted_files....... %u\n", dev->n_deleted_files); ++ buf += ++ sprintf(buf, "n_unlinked_files...... %u\n", dev->n_unlinked_files); ++ buf += sprintf(buf, "refresh_count......... %u\n", dev->refresh_count); ++ buf += sprintf(buf, "n_bg_deletions........ %u\n", dev->n_bg_deletions); ++ ++ return buf; ++} ++ ++static int yaffs_proc_read(char *page, ++ char **start, ++ off_t offset, int count, int *eof, void *data) ++{ ++ struct list_head *item; ++ char *buf = page; ++ int step = offset; ++ int n = 0; ++ ++ /* Get proc_file_read() to step 'offset' by one on each sucessive call. ++ * We use 'offset' (*ppos) to indicate where we are in dev_list. ++ * This also assumes the user has posted a read buffer large ++ * enough to hold the complete output; but that's life in /proc. ++ */ ++ ++ *(int *)start = 1; ++ ++ /* Print header first */ ++ if (step == 0) ++ buf += sprintf(buf, "YAFFS built:" __DATE__ " " __TIME__ "\n"); ++ else if (step == 1) ++ buf += sprintf(buf, "\n"); ++ else { ++ step -= 2; ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (n < (step & ~1)) { ++ n += 2; ++ continue; ++ } ++ if ((step & 1) == 0) { ++ buf += ++ sprintf(buf, "\nDevice %d \"%s\"\n", n, ++ dev->param.name); ++ buf = yaffs_dump_dev_part0(buf, dev); ++ } else { ++ buf = yaffs_dump_dev_part1(buf, dev); ++ } ++ ++ break; ++ } ++ mutex_unlock(&yaffs_context_lock); ++ } ++ ++ return buf - page < count ? buf - page : count; ++} ++ ++ ++/** ++ * Set the verbosity of the warnings and error messages. ++ * ++ * Note that the names can only be a..z or _ with the current code. ++ */ ++ ++static struct { ++ char *mask_name; ++ unsigned mask_bitfield; ++} mask_flags[] = { ++ {"allocate", YAFFS_TRACE_ALLOCATE}, ++ {"always", YAFFS_TRACE_ALWAYS}, ++ {"background", YAFFS_TRACE_BACKGROUND}, ++ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, ++ {"buffers", YAFFS_TRACE_BUFFERS}, ++ {"bug", YAFFS_TRACE_BUG}, ++ {"checkpt", YAFFS_TRACE_CHECKPOINT}, ++ {"deletion", YAFFS_TRACE_DELETION}, ++ {"erase", YAFFS_TRACE_ERASE}, ++ {"error", YAFFS_TRACE_ERROR}, ++ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, ++ {"gc", YAFFS_TRACE_GC}, ++ {"lock", YAFFS_TRACE_LOCK}, ++ {"mtd", YAFFS_TRACE_MTD}, ++ {"nandaccess", YAFFS_TRACE_NANDACCESS}, ++ {"os", YAFFS_TRACE_OS}, ++ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, ++ {"scan", YAFFS_TRACE_SCAN}, ++ {"mount", YAFFS_TRACE_MOUNT}, ++ {"tracing", YAFFS_TRACE_TRACING}, ++ {"sync", YAFFS_TRACE_SYNC}, ++ {"write", YAFFS_TRACE_WRITE}, ++ {"verify", YAFFS_TRACE_VERIFY}, ++ {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, ++ {"verify_full", YAFFS_TRACE_VERIFY_FULL}, ++ {"verify_all", YAFFS_TRACE_VERIFY_ALL}, ++ {"all", 0xffffffff}, ++ {"none", 0}, ++ {NULL, 0}, ++}; ++ ++#define MAX_MASK_NAME_LENGTH 40 ++static int yaffs_proc_write_trace_options(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ unsigned rg = 0, mask_bitfield; ++ char *end; ++ char *mask_name; ++ const char *x; ++ char substring[MAX_MASK_NAME_LENGTH + 1]; ++ int i; ++ int done = 0; ++ int add, len = 0; ++ int pos = 0; ++ ++ rg = yaffs_trace_mask; ++ ++ while (!done && (pos < count)) { ++ done = 1; ++ while ((pos < count) && isspace(buf[pos])) ++ pos++; ++ ++ switch (buf[pos]) { ++ case '+': ++ case '-': ++ case '=': ++ add = buf[pos]; ++ pos++; ++ break; ++ ++ default: ++ add = ' '; ++ break; ++ } ++ mask_name = NULL; ++ ++ mask_bitfield = simple_strtoul(buf + pos, &end, 0); ++ ++ if (end > buf + pos) { ++ mask_name = "numeral"; ++ len = end - (buf + pos); ++ pos += len; ++ done = 0; ++ } else { ++ for (x = buf + pos, i = 0; ++ (*x == '_' || (*x >= 'a' && *x <= 'z')) && ++ i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) ++ substring[i] = *x; ++ substring[i] = '\0'; ++ ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ if (strcmp(substring, mask_flags[i].mask_name) ++ == 0) { ++ mask_name = mask_flags[i].mask_name; ++ mask_bitfield = ++ mask_flags[i].mask_bitfield; ++ done = 0; ++ break; ++ } ++ } ++ } ++ ++ if (mask_name != NULL) { ++ done = 0; ++ switch (add) { ++ case '-': ++ rg &= ~mask_bitfield; ++ break; ++ case '+': ++ rg |= mask_bitfield; ++ break; ++ case '=': ++ rg = mask_bitfield; ++ break; ++ default: ++ rg |= mask_bitfield; ++ break; ++ } ++ } ++ } ++ ++ yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; ++ ++ printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); ++ ++ if (rg & YAFFS_TRACE_ALWAYS) { ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ char flag; ++ flag = ((rg & mask_flags[i].mask_bitfield) == ++ mask_flags[i].mask_bitfield) ? '+' : '-'; ++ printk(KERN_DEBUG "%c%s\n", flag, ++ mask_flags[i].mask_name); ++ } ++ } ++ ++ return count; ++} ++ ++static int yaffs_proc_write(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ return yaffs_proc_write_trace_options(file, buf, count, data); ++} ++ ++/* Stuff to handle installation of file systems */ ++struct file_system_to_install { ++ struct file_system_type *fst; ++ int installed; ++}; ++ ++static struct file_system_to_install fs_to_install[] = { ++ {&yaffs_fs_type, 0}, ++ {&yaffs2_fs_type, 0}, ++ {NULL, 0} ++}; ++ ++static int __init init_yaffs_fs(void) ++{ ++ int error = 0; ++ struct file_system_to_install *fsinst; ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs built " __DATE__ " " __TIME__ " Installing."); ++ ++#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "\n\nYAFFS-WARNING CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED selected.\n\n\n"); ++#endif ++ ++ mutex_init(&yaffs_context_lock); ++ ++ /* Install the proc_fs entries */ ++ my_proc_entry = create_proc_entry("yaffs", ++ S_IRUGO | S_IFREG, YPROC_ROOT); ++ ++ if (my_proc_entry) { ++ my_proc_entry->write_proc = yaffs_proc_write; ++ my_proc_entry->read_proc = yaffs_proc_read; ++ my_proc_entry->data = NULL; ++ } else { ++ return -ENOMEM; ++ } ++ ++ ++ /* Now add the file system entries */ ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst && !error) { ++ error = register_filesystem(fsinst->fst); ++ if (!error) ++ fsinst->installed = 1; ++ fsinst++; ++ } ++ ++ /* Any errors? uninstall */ ++ if (error) { ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++ } ++ ++ return error; ++} ++ ++static void __exit exit_yaffs_fs(void) ++{ ++ ++ struct file_system_to_install *fsinst; ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs built " __DATE__ " " __TIME__ " removing."); ++ ++ remove_proc_entry("yaffs", YPROC_ROOT); ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++} ++ ++module_init(init_yaffs_fs) ++ module_exit(exit_yaffs_fs) ++ ++ MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); ++MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2010"); ++MODULE_LICENSE("GPL"); +diff --git a/fs/yaffs2/yaffs_yaffs1.c b/fs/yaffs2/yaffs_yaffs1.c +new file mode 100644 +index 00000000..9eb60308 +--- /dev/null ++++ b/fs/yaffs2/yaffs_yaffs1.c +@@ -0,0 +1,433 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_yaffs1.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++#include "yaffs_attribs.h" ++ ++int yaffs1_scan(struct yaffs_dev *dev) ++{ ++ struct yaffs_ext_tags tags; ++ int blk; ++ int result; ++ ++ int chunk; ++ int c; ++ int deleted; ++ enum yaffs_block_state state; ++ struct yaffs_obj *hard_list = NULL; ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ ++ int alloc_failed = 0; ++ ++ struct yaffs_shadow_fixer *shadow_fixers = NULL; ++ ++ u8 *chunk_data; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs1_scan starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ chunk_data = yaffs_get_temp_buffer(dev, __LINE__); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, state, seq_number); ++ ++ if (state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } ++ bi++; ++ } ++ ++ /* For each block.... */ ++ for (blk = dev->internal_start_block; ++ !alloc_failed && blk <= dev->internal_end_block; blk++) { ++ ++ cond_resched(); ++ ++ bi = yaffs_get_block_info(dev, blk); ++ state = bi->block_state; ++ ++ deleted = 0; ++ ++ /* For each chunk in each block that needs scanning.... */ ++ for (c = 0; !alloc_failed && c < dev->param.chunks_per_block && ++ state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++) { ++ /* Read the tags and decide what to do */ ++ chunk = blk * dev->param.chunks_per_block + c; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, ++ &tags); ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED ++ || tags.is_deleted) { ++ /* YAFFS1 only... ++ * A deleted chunk ++ */ ++ deleted++; ++ dev->n_free_chunks++; ++ /*T((" %d %d deleted\n",blk,c)); */ ++ } else if (!tags.chunk_used) { ++ /* An unassigned chunk in the block ++ * This means that either the block is empty or ++ * this is the one being allocated from ++ */ ++ ++ if (c == 0) { ++ /* We're looking at the first chunk in the block so the block is unused */ ++ state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ /* this is the block being allocated from */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, c); ++ state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = c; ++ dev->alloc_block_finder = blk; ++ /* Set block finder here to encourage the allocator to go forth from here. */ ++ ++ } ++ ++ dev->n_free_chunks += ++ (dev->param.chunks_per_block - c); ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ unsigned int endpos; ++ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ /* PutChunkIntoFile checks for a clash (two data chunks with ++ * the same chunk_id). ++ */ ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in) { ++ if (!yaffs_put_chunk_in_file ++ (in, tags.chunk_id, chunk, 1)) ++ alloc_failed = 1; ++ } ++ ++ endpos = ++ (tags.chunk_id - ++ 1) * dev->data_bytes_per_chunk + ++ tags.n_bytes; ++ if (in ++ && in->variant_type == ++ YAFFS_OBJECT_TYPE_FILE ++ && in->variant.file_variant.scanned_size < ++ endpos) { ++ in->variant.file_variant.scanned_size = ++ endpos; ++ if (!dev->param.use_header_file_size) { ++ in->variant. ++ file_variant.file_size = ++ in->variant. ++ file_variant.scanned_size; ++ } ++ ++ } ++ /* T((" %d %d data %d %d\n",blk,c,tags.obj_id,tags.chunk_id)); */ ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Thus, we read in the object header and make the object ++ */ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ in = yaffs_find_by_number(dev, tags.obj_id); ++ if (in && in->variant_type != oh->type) { ++ /* This should not happen, but somehow ++ * Wev'e ended up with an obj_id that has been reused but not yet ++ * deleted, and worse still it has changed type. Delete the old object. ++ */ ++ ++ yaffs_del_obj(in); ++ ++ in = 0; ++ } ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ oh->type); ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in && oh->shadows_obj > 0) { ++ ++ struct yaffs_shadow_fixer *fixer; ++ fixer = ++ kmalloc(sizeof ++ (struct yaffs_shadow_fixer), ++ GFP_NOFS); ++ if (fixer) { ++ fixer->next = shadow_fixers; ++ shadow_fixers = fixer; ++ fixer->obj_id = tags.obj_id; ++ fixer->shadowed_id = ++ oh->shadows_obj; ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Shadow fixer: %d shadows %d", ++ fixer->obj_id, ++ fixer->shadowed_id); ++ ++ } ++ ++ } ++ ++ if (in && in->valid) { ++ /* We have already filled this one. We have a duplicate and need to resolve it. */ ++ ++ unsigned existing_serial = in->serial; ++ unsigned new_serial = ++ tags.serial_number; ++ ++ if (((existing_serial + 1) & 3) == ++ new_serial) { ++ /* Use new one - destroy the exisiting one */ ++ yaffs_chunk_del(dev, ++ in->hdr_chunk, ++ 1, __LINE__); ++ in->valid = 0; ++ } else { ++ /* Use existing - destroy this one. */ ++ yaffs_chunk_del(dev, chunk, 1, ++ __LINE__); ++ } ++ } ++ ++ if (in && !in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == ++ YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle with directory structure */ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ } else if (in && !in->valid) { ++ /* we need to load this info */ ++ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ in->dirty = 0; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ parent = ++ yaffs_find_or_create_by_number ++ (dev, oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ if (!parent) ++ alloc_failed = 1; ++ if (parent && parent->variant_type == ++ YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant.children); ++ } else if (!parent ++ || parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, another problem.... ++ * We're trying to use a non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ if (0 && (parent == dev->del_dir || ++ parent == ++ dev->unlinked_dir)) { ++ in->deleted = 1; /* If it is unlinked at start up then it wants deleting */ ++ dev->n_deleted_files++; ++ } ++ /* Note re hardlinks. ++ * Since we might scan a hardlink before its equivalent object is scanned ++ * we put them all in a list. ++ * After scanning is complete, we should have all the objects, so we run through this ++ * list and fix up all the chains. ++ */ ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (dev->param. ++ use_header_file_size) ++ ++ in->variant. ++ file_variant.file_size ++ = oh->file_size; ++ ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant. ++ hardlink_variant.equiv_id = ++ oh->equiv_id; ++ in->hard_links.next = ++ (struct list_head *) ++ hard_list; ++ hard_list = in; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant. ++ alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant. ++ symlink_variant.alias) ++ alloc_failed = 1; ++ break; ++ } ++ ++ } ++ } ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { ++ /* If we got this far while scanning, then the block is fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ /* If the block was partially allocated then treat it as fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ bi->block_state = state; ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ yaffs_block_became_dirty(dev, blk); ++ } ++ ++ } ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We should now have scanned all the objects, now it's time to add these ++ * hardlinks. ++ */ ++ ++ yaffs_link_fixup(dev, hard_list); ++ ++ /* Fix up any shadowed objects */ ++ { ++ struct yaffs_shadow_fixer *fixer; ++ struct yaffs_obj *obj; ++ ++ while (shadow_fixers) { ++ fixer = shadow_fixers; ++ shadow_fixers = fixer->next; ++ /* Complete the rename transaction by deleting the shadowed object ++ * then setting the object header to unshadowed. ++ */ ++ obj = yaffs_find_by_number(dev, fixer->shadowed_id); ++ if (obj) ++ yaffs_del_obj(obj); ++ ++ obj = yaffs_find_by_number(dev, fixer->obj_id); ++ ++ if (obj) ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ kfree(fixer); ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, chunk_data, __LINE__); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); ++ ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_yaffs1.h b/fs/yaffs2/yaffs_yaffs1.h +new file mode 100644 +index 00000000..db23e049 +--- /dev/null ++++ b/fs/yaffs2/yaffs_yaffs1.h +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS1_H__ ++#define __YAFFS_YAFFS1_H__ ++ ++#include "yaffs_guts.h" ++int yaffs1_scan(struct yaffs_dev *dev); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_yaffs2.c b/fs/yaffs2/yaffs_yaffs2.c +new file mode 100644 +index 00000000..33397af7 +--- /dev/null ++++ b/fs/yaffs2/yaffs_yaffs2.c +@@ -0,0 +1,1598 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_checkptrw.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_verify.h" ++#include "yaffs_attribs.h" ++ ++/* ++ * Checkpoints are really no benefit on very small partitions. ++ * ++ * To save space on small partitions don't bother with checkpoints unless ++ * the partition is at least this big. ++ */ ++#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 ++ ++#define YAFFS_SMALL_HOLE_THRESHOLD 4 ++ ++/* ++ * Oldest Dirty Sequence Number handling. ++ */ ++ ++/* yaffs_calc_oldest_dirty_seq() ++ * yaffs2_find_oldest_dirty_seq() ++ * Calculate the oldest dirty sequence number if we don't know it. ++ */ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ int i; ++ unsigned seq; ++ unsigned block_no = 0; ++ struct yaffs_block_info *b; ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ /* Find the oldest dirty sequence number. */ ++ seq = dev->seq_number + 1; ++ b = dev->block_info; ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ if (b->block_state == YAFFS_BLOCK_STATE_FULL && ++ (b->pages_in_use - b->soft_del_pages) < ++ dev->param.chunks_per_block && b->seq_number < seq) { ++ seq = b->seq_number; ++ block_no = i; ++ } ++ b++; ++ } ++ ++ if (block_no) { ++ dev->oldest_dirty_seq = seq; ++ dev->oldest_dirty_block = block_no; ++ } ++ ++} ++ ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->oldest_dirty_seq) ++ yaffs_calc_oldest_dirty_seq(dev); ++} ++ ++/* ++ * yaffs_clear_oldest_dirty_seq() ++ * Called when a block is erased or marked bad. (ie. when its seq_number ++ * becomes invalid). If the value matches the oldest then we clear ++ * dev->oldest_dirty_seq to force its recomputation. ++ */ ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!bi || bi->seq_number == dev->oldest_dirty_seq) { ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ } ++} ++ ++/* ++ * yaffs2_update_oldest_dirty_seq() ++ * Update the oldest dirty sequence number whenever we dirty a block. ++ * Only do this if the oldest_dirty_seq is actually being tracked. ++ */ ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (dev->oldest_dirty_seq) { ++ if (dev->oldest_dirty_seq > bi->seq_number) { ++ dev->oldest_dirty_seq = bi->seq_number; ++ dev->oldest_dirty_block = block_no; ++ } ++ } ++} ++ ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return 1; /* disqualification only applies to yaffs2. */ ++ ++ if (!bi->has_shrink_hdr) ++ return 1; /* can gc */ ++ ++ yaffs2_find_oldest_dirty_seq(dev); ++ ++ /* Can't do gc of this block if there are any blocks older than this one that have ++ * discarded pages. ++ */ ++ return (bi->seq_number <= dev->oldest_dirty_seq); ++} ++ ++/* ++ * yaffs2_find_refresh_block() ++ * periodically finds the oldest full block by sequence number for refreshing. ++ * Only for yaffs2. ++ */ ++u32 yaffs2_find_refresh_block(struct yaffs_dev * dev) ++{ ++ u32 b; ++ ++ u32 oldest = 0; ++ u32 oldest_seq = 0; ++ ++ struct yaffs_block_info *bi; ++ ++ if (!dev->param.is_yaffs2) ++ return oldest; ++ ++ /* ++ * If refresh period < 10 then refreshing is disabled. ++ */ ++ if (dev->param.refresh_period < 10) ++ return oldest; ++ ++ /* ++ * Fix broken values. ++ */ ++ if (dev->refresh_skip > dev->param.refresh_period) ++ dev->refresh_skip = dev->param.refresh_period; ++ ++ if (dev->refresh_skip > 0) ++ return oldest; ++ ++ /* ++ * Refresh skip is now zero. ++ * We'll do a refresh this time around.... ++ * Update the refresh skip and find the oldest block. ++ */ ++ dev->refresh_skip = dev->param.refresh_period; ++ dev->refresh_count++; ++ bi = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ ++ if (oldest < 1 || bi->seq_number < oldest_seq) { ++ oldest = b; ++ oldest_seq = bi->seq_number; ++ } ++ } ++ bi++; ++ } ++ ++ if (oldest > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC refresh count %d selected block %d with seq_number %d", ++ dev->refresh_count, oldest, oldest_seq); ++ } ++ ++ return oldest; ++} ++ ++int yaffs2_checkpt_required(struct yaffs_dev *dev) ++{ ++ int nblocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ nblocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ return !dev->param.skip_checkpt_wr && ++ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); ++} ++ ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) ++{ ++ int retval; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { ++ /* Not a valid value so recalculate */ ++ int n_bytes = 0; ++ int n_blocks; ++ int dev_blocks = ++ (dev->param.end_block - dev->param.start_block + 1); ++ ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(struct yaffs_checkpt_dev); ++ n_bytes += dev_blocks * sizeof(struct yaffs_block_info); ++ n_bytes += dev_blocks * dev->chunk_bit_stride; ++ n_bytes += ++ (sizeof(struct yaffs_checkpt_obj) + ++ sizeof(u32)) * (dev->n_obj); ++ n_bytes += (dev->tnode_size + sizeof(u32)) * (dev->n_tnodes); ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(u32); /* checksum */ ++ ++ /* Round up and add 2 blocks to allow for some bad blocks, so add 3 */ ++ ++ n_blocks = ++ (n_bytes / ++ (dev->data_bytes_per_chunk * ++ dev->param.chunks_per_block)) + 3; ++ ++ dev->checkpoint_blocks_required = n_blocks; ++ } ++ ++ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; ++ if (retval < 0) ++ retval = 0; ++ return retval; ++} ++ ++/*--------------------- Checkpointing --------------------*/ ++ ++static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ ++ memset(&cp, 0, sizeof(cp)); ++ ++ cp.struct_type = sizeof(cp); ++ cp.magic = YAFFS_MAGIC; ++ cp.version = YAFFS_CHECKPOINT_VERSION; ++ cp.head = (head) ? 1 : 0; ++ ++ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ if (ok) ++ ok = (cp.struct_type == sizeof(cp)) && ++ (cp.magic == YAFFS_MAGIC) && ++ (cp.version == YAFFS_CHECKPOINT_VERSION) && ++ (cp.head == ((head) ? 1 : 0)); ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, ++ struct yaffs_dev *dev) ++{ ++ cp->n_erased_blocks = dev->n_erased_blocks; ++ cp->alloc_block = dev->alloc_block; ++ cp->alloc_page = dev->alloc_page; ++ cp->n_free_chunks = dev->n_free_chunks; ++ ++ cp->n_deleted_files = dev->n_deleted_files; ++ cp->n_unlinked_files = dev->n_unlinked_files; ++ cp->n_bg_deletions = dev->n_bg_deletions; ++ cp->seq_number = dev->seq_number; ++ ++} ++ ++static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, ++ struct yaffs_checkpt_dev *cp) ++{ ++ dev->n_erased_blocks = cp->n_erased_blocks; ++ dev->alloc_block = cp->alloc_block; ++ dev->alloc_page = cp->alloc_page; ++ dev->n_free_chunks = cp->n_free_chunks; ++ ++ dev->n_deleted_files = cp->n_deleted_files; ++ dev->n_unlinked_files = cp->n_unlinked_files; ++ dev->n_bg_deletions = cp->n_bg_deletions; ++ dev->seq_number = cp->seq_number; ++} ++ ++static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = ++ (dev->internal_end_block - dev->internal_start_block + 1); ++ ++ int ok; ++ ++ /* Write device runtime values */ ++ yaffs2_dev_to_checkpt_dev(&cp, dev); ++ cp.struct_type = sizeof(cp); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ /* Write block info */ ++ if (ok) { ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == ++ n_bytes); ++ } ++ ++ /* Write chunk bits */ ++ if (ok) { ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == ++ n_bytes); ++ } ++ return ok ? 1 : 0; ++ ++} ++ ++static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = ++ (dev->internal_end_block - dev->internal_start_block + 1); ++ ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ if (cp.struct_type != sizeof(cp)) ++ return 0; ++ ++ yaffs_checkpt_dev_to_dev(dev, &cp); ++ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); ++ ++ if (!ok) ++ return 0; ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, ++ struct yaffs_obj *obj) ++{ ++ ++ cp->obj_id = obj->obj_id; ++ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; ++ cp->hdr_chunk = obj->hdr_chunk; ++ cp->variant_type = obj->variant_type; ++ cp->deleted = obj->deleted; ++ cp->soft_del = obj->soft_del; ++ cp->unlinked = obj->unlinked; ++ cp->fake = obj->fake; ++ cp->rename_allowed = obj->rename_allowed; ++ cp->unlink_allowed = obj->unlink_allowed; ++ cp->serial = obj->serial; ++ cp->n_data_chunks = obj->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ cp->size_or_equiv_obj = obj->variant.file_variant.file_size; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; ++} ++ ++static int taffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, ++ struct yaffs_checkpt_obj *cp) ++{ ++ ++ struct yaffs_obj *parent; ++ ++ if (obj->variant_type != cp->variant_type) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Checkpoint read object %d type %d chunk %d does not match existing object type %d", ++ cp->obj_id, cp->variant_type, cp->hdr_chunk, ++ obj->variant_type); ++ return 0; ++ } ++ ++ obj->obj_id = cp->obj_id; ++ ++ if (cp->parent_id) ++ parent = yaffs_find_or_create_by_number(obj->my_dev, ++ cp->parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ else ++ parent = NULL; ++ ++ if (parent) { ++ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", ++ cp->obj_id, cp->parent_id, ++ cp->variant_type, cp->hdr_chunk, ++ parent->variant_type); ++ return 0; ++ } ++ yaffs_add_obj_to_dir(parent, obj); ++ } ++ ++ obj->hdr_chunk = cp->hdr_chunk; ++ obj->variant_type = cp->variant_type; ++ obj->deleted = cp->deleted; ++ obj->soft_del = cp->soft_del; ++ obj->unlinked = cp->unlinked; ++ obj->fake = cp->fake; ++ obj->rename_allowed = cp->rename_allowed; ++ obj->unlink_allowed = cp->unlink_allowed; ++ obj->serial = cp->serial; ++ obj->n_data_chunks = cp->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ obj->variant.file_variant.file_size = cp->size_or_equiv_obj; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; ++ ++ if (obj->hdr_chunk > 0) ++ obj->lazy_loaded = 1; ++ return 1; ++} ++ ++static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, ++ struct yaffs_tnode *tn, u32 level, ++ int chunk_offset) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ int ok = 1; ++ ++ if (tn) { ++ if (level > 0) { ++ ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { ++ if (tn->internal[i]) { ++ ok = yaffs2_checkpt_tnode_worker(in, ++ tn-> ++ internal ++ [i], ++ level - ++ 1, ++ (chunk_offset ++ << ++ YAFFS_TNODES_INTERNAL_BITS) ++ + i); ++ } ++ } ++ } else if (level == 0) { ++ u32 base_offset = ++ chunk_offset << YAFFS_TNODES_LEVEL0_BITS; ++ ok = (yaffs2_checkpt_wr ++ (dev, &base_offset, ++ sizeof(base_offset)) == sizeof(base_offset)); ++ if (ok) ++ ok = (yaffs2_checkpt_wr ++ (dev, tn, ++ dev->tnode_size) == dev->tnode_size); ++ } ++ } ++ ++ return ok; ++ ++} ++ ++static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 end_marker = ~0; ++ int ok = 1; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) { ++ ok = yaffs2_checkpt_tnode_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant.file_variant. ++ top_level, 0); ++ if (ok) ++ ok = (yaffs2_checkpt_wr ++ (obj->my_dev, &end_marker, ++ sizeof(end_marker)) == sizeof(end_marker)); ++ } ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 base_chunk; ++ int ok = 1; ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; ++ struct yaffs_tnode *tn; ++ int nread = 0; ++ ++ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == ++ sizeof(base_chunk)); ++ ++ while (ok && (~base_chunk)) { ++ nread++; ++ /* Read level 0 tnode */ ++ ++ tn = yaffs_get_tnode(dev); ++ if (tn) { ++ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ } else { ++ ok = 0; ++ } ++ ++ if (tn && ok) ++ ok = yaffs_add_find_tnode_0(dev, ++ file_stuct_ptr, ++ base_chunk, tn) ? 1 : 0; ++ ++ if (ok) ++ ok = (yaffs2_checkpt_rd ++ (dev, &base_chunk, ++ sizeof(base_chunk)) == sizeof(base_chunk)); ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read tnodes %d records, last %d. ok %d", ++ nread, base_chunk, ok); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int i; ++ int ok = 1; ++ struct list_head *lh; ++ ++ /* Iterate through the objects in each hash entry, ++ * dumping them to the checkpointing stream. ++ */ ++ ++ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ if (lh) { ++ obj = ++ list_entry(lh, struct yaffs_obj, hash_link); ++ if (!obj->defered_free) { ++ yaffs2_obj_checkpt_obj(&cp, obj); ++ cp.struct_type = sizeof(cp); ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", ++ cp.obj_id, cp.parent_id, ++ cp.variant_type, cp.hdr_chunk, obj); ++ ++ ok = (yaffs2_checkpt_wr ++ (dev, &cp, ++ sizeof(cp)) == sizeof(cp)); ++ ++ if (ok ++ && obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) ++ ok = yaffs2_wr_checkpt_tnodes ++ (obj); ++ } ++ } ++ } ++ } ++ ++ /* Dump end of list */ ++ memset(&cp, 0xFF, sizeof(struct yaffs_checkpt_obj)); ++ cp.struct_type = sizeof(cp); ++ ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int ok = 1; ++ int done = 0; ++ struct yaffs_obj *hard_list = NULL; ++ ++ while (ok && !done) { ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (cp.struct_type != sizeof(cp)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "struct size %d instead of %d ok %d", ++ cp.struct_type, (int)sizeof(cp), ok); ++ ok = 0; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read object %d parent %d type %d chunk %d ", ++ cp.obj_id, cp.parent_id, cp.variant_type, ++ cp.hdr_chunk); ++ ++ if (ok && cp.obj_id == ~0) { ++ done = 1; ++ } else if (ok) { ++ obj = ++ yaffs_find_or_create_by_number(dev, cp.obj_id, ++ cp.variant_type); ++ if (obj) { ++ ok = taffs2_checkpt_obj_to_obj(obj, &cp); ++ if (!ok) ++ break; ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) { ++ ok = yaffs2_rd_checkpt_tnodes(obj); ++ } else if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_HARDLINK) { ++ obj->hard_links.next = ++ (struct list_head *)hard_list; ++ hard_list = obj; ++ } ++ } else { ++ ok = 0; ++ } ++ } ++ } ++ ++ if (ok) ++ yaffs_link_fixup(dev, hard_list); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum); ++ ++ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == ++ sizeof(checkpt_sum)); ++ ++ if (!ok) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum0; ++ u32 checkpt_sum1; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum0); ++ ++ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == ++ sizeof(checkpt_sum1)); ++ ++ if (!ok) ++ return 0; ++ ++ if (checkpt_sum0 != checkpt_sum1) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!yaffs2_checkpt_required(dev)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint write"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 1); ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint device"); ++ ok = yaffs2_wr_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint objects"); ++ ok = yaffs2_wr_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) ++ ok = yaffs2_wr_checkpt_sum(dev); ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return dev->is_checkpointed; ++} ++ ++static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!dev->param.is_yaffs2) ++ ok = 0; ++ ++ if (ok && dev->param.skip_checkpt_rd) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint read"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 0); /* open for read */ ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint device"); ++ ok = yaffs2_rd_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint objects"); ++ ok = yaffs2_rd_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) { ++ ok = yaffs2_rd_checkpt_sum(dev); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint checksum %d", ok); ++ } ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return ok ? 1 : 0; ++ ++} ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) ++{ ++ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { ++ dev->is_checkpointed = 0; ++ yaffs2_checkpt_invalidate_stream(dev); ++ } ++ if (dev->param.sb_dirty_fn) ++ dev->param.sb_dirty_fn(dev); ++} ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev) ++{ ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "save entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ ++ if (!dev->is_checkpointed) { ++ yaffs2_checkpt_invalidate(dev); ++ yaffs2_wr_checkpt_data(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, ++ "save exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return dev->is_checkpointed; ++} ++ ++int yaffs2_checkpt_restore(struct yaffs_dev *dev) ++{ ++ int retval; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ retval = yaffs2_rd_checkpt_data(dev); ++ ++ if (dev->is_checkpointed) { ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return retval; ++} ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) ++{ ++ /* if new_size > old_file_size. ++ * We're going to be writing a hole. ++ * If the hole is small then write zeros otherwise write a start of hole marker. ++ */ ++ ++ loff_t old_file_size; ++ int increase; ++ int small_hole; ++ int result = YAFFS_OK; ++ struct yaffs_dev *dev = NULL; ++ ++ u8 *local_buffer = NULL; ++ ++ int small_increase_ok = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ dev = obj->my_dev; ++ ++ /* Bail out if not yaffs2 mode */ ++ if (!dev->param.is_yaffs2) ++ return YAFFS_OK; ++ ++ old_file_size = obj->variant.file_variant.file_size; ++ ++ if (new_size <= old_file_size) ++ return YAFFS_OK; ++ ++ increase = new_size - old_file_size; ++ ++ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && ++ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) ++ small_hole = 1; ++ else ++ small_hole = 0; ++ ++ if (small_hole) ++ local_buffer = yaffs_get_temp_buffer(dev, __LINE__); ++ ++ if (local_buffer) { ++ /* fill hole with zero bytes */ ++ int pos = old_file_size; ++ int this_write; ++ int written; ++ memset(local_buffer, 0, dev->data_bytes_per_chunk); ++ small_increase_ok = 1; ++ ++ while (increase > 0 && small_increase_ok) { ++ this_write = increase; ++ if (this_write > dev->data_bytes_per_chunk) ++ this_write = dev->data_bytes_per_chunk; ++ written = ++ yaffs_do_file_wr(obj, local_buffer, pos, this_write, ++ 0); ++ if (written == this_write) { ++ pos += this_write; ++ increase -= this_write; ++ } else { ++ small_increase_ok = 0; ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, local_buffer, __LINE__); ++ ++ /* If we were out of space then reverse any chunks we've added */ ++ if (!small_increase_ok) ++ yaffs_resize_file_down(obj, old_file_size); ++ } ++ ++ if (!small_increase_ok && ++ obj->parent && ++ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { ++ /* Write a hole start header with the old file size */ ++ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); ++ } ++ ++ return result; ++ ++} ++ ++struct yaffs_block_index { ++ int seq; ++ int block; ++}; ++ ++static int yaffs2_ybicmp(const void *a, const void *b) ++{ ++ int aseq = ((struct yaffs_block_index *)a)->seq; ++ int bseq = ((struct yaffs_block_index *)b)->seq; ++ int ablock = ((struct yaffs_block_index *)a)->block; ++ int bblock = ((struct yaffs_block_index *)b)->block; ++ if (aseq == bseq) ++ return ablock - bblock; ++ else ++ return aseq - bseq; ++} ++ ++int yaffs2_scan_backwards(struct yaffs_dev *dev) ++{ ++ struct yaffs_ext_tags tags; ++ int blk; ++ int block_iter; ++ int start_iter; ++ int end_iter; ++ int n_to_scan = 0; ++ ++ int chunk; ++ int result; ++ int c; ++ int deleted; ++ enum yaffs_block_state state; ++ struct yaffs_obj *hard_list = NULL; ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ int is_unlinked; ++ u8 *chunk_data; ++ ++ int file_size; ++ int is_shrink; ++ int found_chunks; ++ int equiv_id; ++ int alloc_failed = 0; ++ ++ struct yaffs_block_index *block_index = NULL; ++ int alt_block_index = 0; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ block_index = kmalloc(n_blocks * sizeof(struct yaffs_block_index), ++ GFP_NOFS); ++ ++ if (!block_index) { ++ block_index = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_index)); ++ alt_block_index = 1; ++ } ++ ++ if (!block_index) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards() could not allocate block index!" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ chunk_data = yaffs_get_temp_buffer(dev, __LINE__); ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ bi->block_state = state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, state, seq_number); ++ ++ if (state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ dev->blocks_in_checkpt++; ++ ++ } else if (state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { ++ ++ /* Determine the highest sequence number */ ++ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && ++ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { ++ ++ block_index[n_to_scan].seq = seq_number; ++ block_index[n_to_scan].block = blk; ++ ++ n_to_scan++; ++ ++ if (seq_number >= dev->seq_number) ++ dev->seq_number = seq_number; ++ } else { ++ /* TODO: Nasty sequence number! */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Block scanning block %d has bad sequence number %d", ++ blk, seq_number); ++ ++ } ++ } ++ bi++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "%d blocks to be sorted...", n_to_scan); ++ ++ cond_resched(); ++ ++ /* Sort the blocks by sequence number */ ++ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), ++ yaffs2_ybicmp, NULL); ++ ++ cond_resched(); ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "...done"); ++ ++ /* Now scan the blocks looking at the data. */ ++ start_iter = 0; ++ end_iter = n_to_scan - 1; ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); ++ ++ /* For each block.... backwards */ ++ for (block_iter = end_iter; !alloc_failed && block_iter >= start_iter; ++ block_iter--) { ++ /* Cooperative multitasking! This loop can run for so ++ long that watchdog timers expire. */ ++ cond_resched(); ++ ++ /* get the block to scan in the correct order */ ++ blk = block_index[block_iter].block; ++ ++ bi = yaffs_get_block_info(dev, blk); ++ ++ state = bi->block_state; ++ ++ deleted = 0; ++ ++ /* For each chunk in each block that needs scanning.... */ ++ found_chunks = 0; ++ for (c = dev->param.chunks_per_block - 1; ++ !alloc_failed && c >= 0 && ++ (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || ++ state == YAFFS_BLOCK_STATE_ALLOCATING); c--) { ++ /* Scan backwards... ++ * Read the tags and decide what to do ++ */ ++ ++ chunk = blk * dev->param.chunks_per_block + c; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, ++ &tags); ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (!tags.chunk_used) { ++ /* An unassigned chunk in the block. ++ * If there are used chunks after this one, then ++ * it is a chunk that was skipped due to failing the erased ++ * check. Just skip it so that it can be deleted. ++ * But, more typically, We get here when this is an unallocated ++ * chunk and his means that either the block is empty or ++ * this is the one being allocated from ++ */ ++ ++ if (found_chunks) { ++ /* This is a chunk that was skipped due to failing the erased check */ ++ } else if (c == 0) { ++ /* We're looking at the first chunk in the block so the block is unused */ ++ state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ if (state == ++ YAFFS_BLOCK_STATE_NEEDS_SCANNING ++ || state == ++ YAFFS_BLOCK_STATE_ALLOCATING) { ++ if (dev->seq_number == ++ bi->seq_number) { ++ /* this is the block being allocated from */ ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, c); ++ ++ state = ++ YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = c; ++ dev-> ++ alloc_block_finder = ++ blk; ++ } else { ++ /* This is a partially written block that is not ++ * the current allocation block. ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Partially written block %d detected", ++ blk); ++ } ++ } ++ } ++ ++ dev->n_free_chunks++; ++ ++ } else if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Unfixed ECC in chunk(%d:%d), chunk ignored", ++ blk, c); ++ ++ dev->n_free_chunks++; ++ ++ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || ++ tags.chunk_id > YAFFS_MAX_CHUNK_ID || ++ (tags.chunk_id > 0 ++ && tags.n_bytes > dev->data_bytes_per_chunk) ++ || tags.seq_number != bi->seq_number) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", ++ blk, c, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ ++ dev->n_free_chunks++; ++ ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ unsigned int endpos; ++ u32 chunk_base = ++ (tags.chunk_id - ++ 1) * dev->data_bytes_per_chunk; ++ ++ found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ if (!in) { ++ /* Out of memory */ ++ alloc_failed = 1; ++ } ++ ++ if (in && ++ in->variant_type == YAFFS_OBJECT_TYPE_FILE ++ && chunk_base < ++ in->variant.file_variant.shrink_size) { ++ /* This has not been invalidated by a resize */ ++ if (!yaffs_put_chunk_in_file ++ (in, tags.chunk_id, chunk, -1)) { ++ alloc_failed = 1; ++ } ++ ++ /* File size is calculated by looking at the data chunks if we have not ++ * seen an object header yet. Stop this practice once we find an object header. ++ */ ++ endpos = chunk_base + tags.n_bytes; ++ ++ if (!in->valid && /* have not got an object header yet */ ++ in->variant.file_variant. ++ scanned_size < endpos) { ++ in->variant.file_variant. ++ scanned_size = endpos; ++ in->variant.file_variant. ++ file_size = endpos; ++ } ++ ++ } else if (in) { ++ /* This chunk has been invalidated by a resize, or a past file deletion ++ * so delete the chunk*/ ++ yaffs_chunk_del(dev, chunk, 1, ++ __LINE__); ++ ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Thus, we read in the object header and make the object ++ */ ++ found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ oh = NULL; ++ in = NULL; ++ ++ if (tags.extra_available) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags. ++ obj_id, ++ tags. ++ extra_obj_type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ ++ if (!in || ++ (!in->valid && dev->param.disable_lazy_load) ++ || tags.extra_shadows || (!in->valid ++ && (tags.obj_id == ++ YAFFS_OBJECTID_ROOT ++ || tags. ++ obj_id == ++ YAFFS_OBJECTID_LOSTNFOUND))) ++ { ++ ++ /* If we don't have valid info then we need to read the chunk ++ * TODO In future we can probably defer reading the chunk and ++ * living with invalid data until needed. ++ */ ++ ++ result = yaffs_rd_chunk_tags_nand(dev, ++ chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ if (dev->param.inband_tags) { ++ /* Fix up the header if they got corrupted by inband tags */ ++ oh->shadows_obj = ++ oh->inband_shadowed_obj_id; ++ oh->is_shrink = ++ oh->inband_is_shrink; ++ } ++ ++ if (!in) { ++ in = yaffs_find_or_create_by_number(dev, tags.obj_id, oh->type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ ++ } ++ ++ if (!in) { ++ /* TODO Hoosterman we have a problem! */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Could not make object for object %d at chunk %d during scan", ++ tags.obj_id, chunk); ++ continue; ++ } ++ ++ if (in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate that will be discarded, but ++ * we first have to suck out resize info if it is a file. ++ */ ++ ++ if ((in->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) && ((oh ++ && ++ oh-> ++ type ++ == ++ YAFFS_OBJECT_TYPE_FILE) ++ || ++ (tags. ++ extra_available ++ && ++ tags. ++ extra_obj_type ++ == ++ YAFFS_OBJECT_TYPE_FILE))) ++ { ++ u32 this_size = ++ (oh) ? oh-> ++ file_size : ++ tags.extra_length; ++ u32 parent_obj_id = ++ (oh) ? oh->parent_obj_id : ++ tags.extra_parent_id; ++ ++ is_shrink = ++ (oh) ? oh-> ++ is_shrink : ++ tags.extra_is_shrink; ++ ++ /* If it is deleted (unlinked at start also means deleted) ++ * we treat the file size as being zeroed at this point. ++ */ ++ if (parent_obj_id == ++ YAFFS_OBJECTID_DELETED ++ || parent_obj_id == ++ YAFFS_OBJECTID_UNLINKED) { ++ this_size = 0; ++ is_shrink = 1; ++ } ++ ++ if (is_shrink ++ && in->variant.file_variant. ++ shrink_size > this_size) ++ in->variant. ++ file_variant. ++ shrink_size = ++ this_size; ++ ++ if (is_shrink) ++ bi->has_shrink_hdr = 1; ++ ++ } ++ /* Use existing - destroy this one. */ ++ yaffs_chunk_del(dev, chunk, 1, ++ __LINE__); ++ ++ } ++ ++ if (!in->valid && in->variant_type != ++ (oh ? oh->type : tags.extra_obj_type)) ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Bad object type, %d != %d, for object %d at chunk %d during scan", ++ oh ? ++ oh->type : tags.extra_obj_type, ++ in->variant_type, tags.obj_id, ++ chunk); ++ ++ if (!in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == ++ YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle with directory structure */ ++ in->valid = 1; ++ ++ if (oh) { ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->lazy_loaded = 0; ++ } else { ++ in->lazy_loaded = 1; ++ } ++ in->hdr_chunk = chunk; ++ ++ } else if (!in->valid) { ++ /* we need to load this info */ ++ ++ in->valid = 1; ++ in->hdr_chunk = chunk; ++ ++ if (oh) { ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ ++ if (oh->shadows_obj > 0) ++ yaffs_handle_shadowed_obj ++ (dev, ++ oh->shadows_obj, ++ 1); ++ ++ yaffs_set_obj_name_from_oh(in, ++ oh); ++ parent = ++ yaffs_find_or_create_by_number ++ (dev, oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ ++ file_size = oh->file_size; ++ is_shrink = oh->is_shrink; ++ equiv_id = oh->equiv_id; ++ ++ } else { ++ in->variant_type = ++ tags.extra_obj_type; ++ parent = ++ yaffs_find_or_create_by_number ++ (dev, tags.extra_parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = tags.extra_length; ++ is_shrink = ++ tags.extra_is_shrink; ++ equiv_id = tags.extra_equiv_id; ++ in->lazy_loaded = 1; ++ ++ } ++ in->dirty = 0; ++ ++ if (!parent) ++ alloc_failed = 1; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ if (parent && parent->variant_type == ++ YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant.children); ++ } else if (!parent ++ || parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, another problem.... ++ * We're trying to use a non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ is_unlinked = (parent == dev->del_dir) ++ || (parent == dev->unlinked_dir); ++ ++ if (is_shrink) { ++ /* Mark the block as having a shrink header */ ++ bi->has_shrink_hdr = 1; ++ } ++ ++ /* Note re hardlinks. ++ * Since we might scan a hardlink before its equivalent object is scanned ++ * we put them all in a list. ++ * After scanning is complete, we should have all the objects, so we run ++ * through this list and fix up all the chains. ++ */ ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ ++ if (in->variant. ++ file_variant.scanned_size < ++ file_size) { ++ /* This covers the case where the file size is greater ++ * than where the data is ++ * This will happen if the file is resized to be larger ++ * than its current data extents. ++ */ ++ in->variant. ++ file_variant. ++ file_size = ++ file_size; ++ in->variant. ++ file_variant. ++ scanned_size = ++ file_size; ++ } ++ ++ if (in->variant.file_variant. ++ shrink_size > file_size) ++ in->variant. ++ file_variant. ++ shrink_size = ++ file_size; ++ ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ if (!is_unlinked) { ++ in->variant. ++ hardlink_variant. ++ equiv_id = equiv_id; ++ in->hard_links.next = ++ (struct list_head *) ++ hard_list; ++ hard_list = in; ++ } ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ if (oh) { ++ in->variant. ++ symlink_variant. ++ alias = ++ yaffs_clone_str(oh-> ++ alias); ++ if (!in->variant. ++ symlink_variant. ++ alias) ++ alloc_failed = ++ 1; ++ } ++ break; ++ } ++ ++ } ++ ++ } ++ ++ } /* End of scanning for each chunk */ ++ ++ if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { ++ /* If we got this far while scanning, then the block is fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ bi->block_state = state; ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ yaffs_block_became_dirty(dev, blk); ++ } ++ ++ } ++ ++ yaffs_skip_rest_of_block(dev); ++ ++ if (alt_block_index) ++ vfree(block_index); ++ else ++ kfree(block_index); ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We should now have scanned all the objects, now it's time to add these ++ * hardlinks. ++ */ ++ yaffs_link_fixup(dev, hard_list); ++ ++ yaffs_release_temp_buffer(dev, chunk_data, __LINE__); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); ++ ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_yaffs2.h b/fs/yaffs2/yaffs_yaffs2.h +new file mode 100644 +index 00000000..e1a9287f +--- /dev/null ++++ b/fs/yaffs2/yaffs_yaffs2.h +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS2_H__ ++#define __YAFFS_YAFFS2_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi); ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); ++int yaffs2_checkpt_required(struct yaffs_dev *dev); ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); ++int yaffs2_checkpt_save(struct yaffs_dev *dev); ++int yaffs2_checkpt_restore(struct yaffs_dev *dev); ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); ++int yaffs2_scan_backwards(struct yaffs_dev *dev); ++ ++#endif +diff --git a/fs/yaffs2/yportenv.h b/fs/yaffs2/yportenv.h +new file mode 100644 +index 00000000..81834254 +--- /dev/null ++++ b/fs/yaffs2/yportenv.h +@@ -0,0 +1,70 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2010 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YPORTENV_LINUX_H__ ++#define __YPORTENV_LINUX_H__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define YCHAR char ++#define YUCHAR unsigned char ++#define _Y(x) x ++ ++#define YAFFS_LOSTNFOUND_NAME "lost+found" ++#define YAFFS_LOSTNFOUND_PREFIX "obj" ++ ++ ++#define YAFFS_ROOT_MODE 0755 ++#define YAFFS_LOSTNFOUND_MODE 0700 ++ ++#define Y_CURRENT_TIME CURRENT_TIME.tv_sec ++#define Y_TIME_CONVERT(x) (x).tv_sec ++ ++#define compile_time_assertion(assertion) \ ++ ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) ++ ++ ++#ifndef Y_DUMP_STACK ++#define Y_DUMP_STACK() dump_stack() ++#endif ++ ++#define yaffs_trace(msk, fmt, ...) do { \ ++ if(yaffs_trace_mask & (msk)) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ ++} while(0) ++ ++#ifndef YBUG ++#define YBUG() do {\ ++ yaffs_trace(YAFFS_TRACE_BUG,\ ++ "bug " __FILE__ " %d",\ ++ __LINE__);\ ++ Y_DUMP_STACK();\ ++} while (0) ++#endif ++ ++#endif +diff --git a/include/linux/Kbuild b/include/linux/Kbuild +index f2f73f9b..4bf41009 100644 +--- a/include/linux/Kbuild ++++ b/include/linux/Kbuild +@@ -183,6 +183,8 @@ header-y += if_plip.h + header-y += if_ppp.h + header-y += if_pppol2tp.h + header-y += if_pppox.h ++header-y += if_pppolac.h ++header-y += if_pppopns.h + header-y += if_slip.h + header-y += if_strip.h + header-y += if_team.h +@@ -375,6 +377,7 @@ header-y += tty.h + header-y += types.h + header-y += udf_fs_i.h + header-y += udp.h ++header-y += uhid.h + header-y += uinput.h + header-y += uio.h + header-y += ultrasound.h +diff --git a/include/linux/akm8975.h b/include/linux/akm8975.h +new file mode 100644 +index 00000000..6a7c4326 +--- /dev/null ++++ b/include/linux/akm8975.h +@@ -0,0 +1,87 @@ ++/* ++ * Definitions for akm8975 compass chip. ++ */ ++#ifndef AKM8975_H ++#define AKM8975_H ++ ++#include ++ ++/*! \name AK8975 operation mode ++ \anchor AK8975_Mode ++ Defines an operation mode of the AK8975.*/ ++/*! @{*/ ++#define AK8975_MODE_SNG_MEASURE 0x01 ++#define AK8975_MODE_SELF_TEST 0x08 ++#define AK8975_MODE_FUSE_ACCESS 0x0F ++#define AK8975_MODE_POWER_DOWN 0x00 ++/*! @}*/ ++ ++#define RBUFF_SIZE 8 /* Rx buffer size */ ++ ++/*! \name AK8975 register address ++\anchor AK8975_REG ++Defines a register address of the AK8975.*/ ++/*! @{*/ ++#define AK8975_REG_WIA 0x00 ++#define AK8975_REG_INFO 0x01 ++#define AK8975_REG_ST1 0x02 ++#define AK8975_REG_HXL 0x03 ++#define AK8975_REG_HXH 0x04 ++#define AK8975_REG_HYL 0x05 ++#define AK8975_REG_HYH 0x06 ++#define AK8975_REG_HZL 0x07 ++#define AK8975_REG_HZH 0x08 ++#define AK8975_REG_ST2 0x09 ++#define AK8975_REG_CNTL 0x0A ++#define AK8975_REG_RSV 0x0B ++#define AK8975_REG_ASTC 0x0C ++#define AK8975_REG_TS1 0x0D ++#define AK8975_REG_TS2 0x0E ++#define AK8975_REG_I2CDIS 0x0F ++/*! @}*/ ++ ++/*! \name AK8975 fuse-rom address ++\anchor AK8975_FUSE ++Defines a read-only address of the fuse ROM of the AK8975.*/ ++/*! @{*/ ++#define AK8975_FUSE_ASAX 0x10 ++#define AK8975_FUSE_ASAY 0x11 ++#define AK8975_FUSE_ASAZ 0x12 ++/*! @}*/ ++ ++#define AKMIO 0xA1 ++ ++/* IOCTLs for AKM library */ ++#define ECS_IOCTL_WRITE _IOW(AKMIO, 0x02, char[5]) ++#define ECS_IOCTL_READ _IOWR(AKMIO, 0x03, char[5]) ++#define ECS_IOCTL_GETDATA _IOR(AKMIO, 0x08, char[RBUFF_SIZE]) ++#define ECS_IOCTL_SET_YPR _IOW(AKMIO, 0x0C, short[12]) ++#define ECS_IOCTL_GET_OPEN_STATUS _IOR(AKMIO, 0x0D, int) ++#define ECS_IOCTL_GET_CLOSE_STATUS _IOR(AKMIO, 0x0E, int) ++#define ECS_IOCTL_GET_DELAY _IOR(AKMIO, 0x30, short) ++ ++/* IOCTLs for APPs */ ++#define ECS_IOCTL_APP_SET_MFLAG _IOW(AKMIO, 0x11, short) ++#define ECS_IOCTL_APP_GET_MFLAG _IOW(AKMIO, 0x12, short) ++#define ECS_IOCTL_APP_SET_AFLAG _IOW(AKMIO, 0x13, short) ++#define ECS_IOCTL_APP_GET_AFLAG _IOR(AKMIO, 0x14, short) ++#define ECS_IOCTL_APP_SET_DELAY _IOW(AKMIO, 0x18, short) ++#define ECS_IOCTL_APP_GET_DELAY ECS_IOCTL_GET_DELAY ++/* Set raw magnetic vector flag */ ++#define ECS_IOCTL_APP_SET_MVFLAG _IOW(AKMIO, 0x19, short) ++/* Get raw magnetic vector flag */ ++#define ECS_IOCTL_APP_GET_MVFLAG _IOR(AKMIO, 0x1A, short) ++#define ECS_IOCTL_APP_SET_TFLAG _IOR(AKMIO, 0x15, short) ++ ++ ++struct akm8975_platform_data { ++ int intr; ++ ++ int (*init)(void); ++ void (*exit)(void); ++ int (*power_on)(void); ++ int (*power_off)(void); ++}; ++ ++#endif ++ +diff --git a/include/linux/akuio_driver.h b/include/linux/akuio_driver.h +new file mode 100644 +index 00000000..19ee327e +--- /dev/null ++++ b/include/linux/akuio_driver.h +@@ -0,0 +1,37 @@ ++/** ++ * @filename include/linux/akuio_driver.h ++ * @brief This file provide the ioctl definitions of akuio driver for hw codec. ++ * Copyright (C) 2011 Anyka(Guangzhou) Microelectronics Technology CO.,LTD. ++ * @author Jacky Lau ++ * @date 2011-07-05 ++ * @version 0.1 ++ * @ref Please refer to uio_ak98_vcodec.c ++ */ ++ ++#ifndef _AKUIO_DRIVER_H ++#define _AKUIO_DRIVER_H ++ ++/* struct used by AKUIO_SYSREG_WRITE */ ++struct akuio_sysreg_write_t ++{ ++ unsigned int paddr; ++ unsigned int val; ++ unsigned int mask; ++}; ++ ++#define DBG(fmt...) //printk(fmt) ++ ++/* write system register */ ++#define AKUIO_SYSREG_WRITE _IOW('U', 100, struct akuio_sysreg_write_t) ++ ++/* wait for a interrupt occur */ ++#define AKUIO_WAIT_IRQ _IOR('U', 101, int) ++ ++/* invalidate the l2 cache in ak98 */ ++#define AKUIO_INVALIDATE_L2CACHE _IOR('U', 102, int) ++ ++/* incalidate the l1 cache in ak98 */ ++#define AKUIO_INVALIDATE_L1CACHE _IOR('U', 103, int) ++ ++ ++#endif +diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h +index 975009e1..96c5c249 100644 +--- a/include/linux/alarmtimer.h ++++ b/include/linux/alarmtimer.h +@@ -76,4 +76,7 @@ static inline int alarmtimer_callback_running(struct alarm *timer) + } + + ++/* Provide way to access the rtc device being used by alarmtimers */ ++struct rtc_device *alarmtimer_get_rtcdev(void); ++ + #endif +diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h +index 32a89cf5..9d1d9caf 100644 +--- a/include/linux/amba/mmci.h ++++ b/include/linux/amba/mmci.h +@@ -5,6 +5,15 @@ + #define AMBA_MMCI_H + + #include ++#include ++#include ++ ++struct embedded_sdio_data { ++ struct sdio_cis cis; ++ struct sdio_cccr cccr; ++ struct sdio_embedded_func *funcs; ++ int num_funcs; ++}; + + + /* +@@ -73,6 +82,9 @@ struct mmci_platform_data { + bool (*dma_filter)(struct dma_chan *chan, void *filter_param); + void *dma_rx_param; + void *dma_tx_param; ++ unsigned int status_irq; ++ struct embedded_sdio_data *embedded_sdio; ++ int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); + }; + + #endif +diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h +new file mode 100644 +index 00000000..06264b8b +--- /dev/null ++++ b/include/linux/android_aid.h +@@ -0,0 +1,28 @@ ++/* include/linux/android_aid.h ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_ANDROID_AID_H ++#define _LINUX_ANDROID_AID_H ++ ++/* AIDs that the kernel treats differently */ ++#define AID_OBSOLETE_000 3001 /* was NET_BT_ADMIN */ ++#define AID_OBSOLETE_001 3002 /* was NET_BT */ ++#define AID_INET 3003 ++#define AID_NET_RAW 3004 ++#define AID_NET_ADMIN 3005 ++#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ ++#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ ++ ++#endif +diff --git a/include/linux/capability.h b/include/linux/capability.h +index 12d52ded..c398cff3 100644 +--- a/include/linux/capability.h ++++ b/include/linux/capability.h +@@ -360,8 +360,11 @@ struct cpu_vfs_cap_data { + + #define CAP_WAKE_ALARM 35 + ++/* Allow preventing system suspends while epoll events are pending */ + +-#define CAP_LAST_CAP CAP_WAKE_ALARM ++#define CAP_EPOLLWAKEUP 36 ++ ++#define CAP_LAST_CAP CAP_EPOLLWAKEUP + + #define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP) + +diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h +index 5a85b341..b2a37357 100644 +--- a/include/linux/cgroup.h ++++ b/include/linux/cgroup.h +@@ -84,12 +84,6 @@ enum { + CSS_REMOVED, /* This CSS is dead */ + }; + +-/* Caller must verify that the css is not for root cgroup */ +-static inline void __css_get(struct cgroup_subsys_state *css, int count) +-{ +- atomic_add(count, &css->refcnt); +-} +- + /* + * Call css_get() to hold a reference on the css; it can be used + * for a reference obtained via: +@@ -97,6 +91,7 @@ static inline void __css_get(struct cgroup_subsys_state *css, int count) + * - task->cgroups for a locked task + */ + ++extern void __css_get(struct cgroup_subsys_state *css, int count); + static inline void css_get(struct cgroup_subsys_state *css) + { + /* We don't need to reference count the root state */ +@@ -143,10 +138,7 @@ static inline void css_put(struct cgroup_subsys_state *css) + enum { + /* Control Group is dead */ + CGRP_REMOVED, +- /* +- * Control Group has previously had a child cgroup or a task, +- * but no longer (only if CGRP_NOTIFY_ON_RELEASE is set) +- */ ++ /* Control Group has ever had a child cgroup or a task */ + CGRP_RELEASABLE, + /* Control Group requires release notifications to userspace */ + CGRP_NOTIFY_ON_RELEASE, +@@ -255,6 +247,7 @@ struct css_set { + + /* For RCU-protected deletion */ + struct rcu_head rcu_head; ++ struct work_struct work; + }; + + /* +@@ -455,6 +448,7 @@ struct cgroup_subsys { + struct cgroup_subsys_state *(*create)(struct cgroup *cgrp); + int (*pre_destroy)(struct cgroup *cgrp); + void (*destroy)(struct cgroup *cgrp); ++ int (*allow_attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); + int (*can_attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); + void (*cancel_attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); + void (*attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); +diff --git a/include/linux/cpu.h b/include/linux/cpu.h +index 78ed62f3..caec85be 100644 +--- a/include/linux/cpu.h ++++ b/include/linux/cpu.h +@@ -213,4 +213,11 @@ static inline int disable_nonboot_cpus(void) { return 0; } + static inline void enable_nonboot_cpus(void) {} + #endif /* !CONFIG_PM_SLEEP_SMP */ + ++#define IDLE_START 1 ++#define IDLE_END 2 ++ ++void idle_notifier_register(struct notifier_block *n); ++void idle_notifier_unregister(struct notifier_block *n); ++void idle_notifier_call_chain(unsigned long val); ++ + #endif /* _LINUX_CPU_H_ */ +diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h +index b60f6ba0..d1c3bb0e 100644 +--- a/include/linux/cpufreq.h ++++ b/include/linux/cpufreq.h +@@ -364,6 +364,9 @@ extern struct cpufreq_governor cpufreq_gov_ondemand; + #elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE) + extern struct cpufreq_governor cpufreq_gov_conservative; + #define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_conservative) ++#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE) ++extern struct cpufreq_governor cpufreq_gov_interactive; ++#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_interactive) + #endif + + +diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h +index 6c26a3da..5ab71833 100644 +--- a/include/linux/cpuidle.h ++++ b/include/linux/cpuidle.h +@@ -57,6 +57,7 @@ struct cpuidle_state { + + /* Idle State Flags */ + #define CPUIDLE_FLAG_TIME_VALID (0x01) /* is residency time measurable? */ ++#define CPUIDLE_FLAG_COUPLED (0x02) /* state applies to multiple cpus */ + + #define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000) + +@@ -100,6 +101,12 @@ struct cpuidle_device { + struct list_head device_list; + struct kobject kobj; + struct completion kobj_unregister; ++ ++#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED ++ int safe_state_index; ++ cpumask_t coupled_cpus; ++ struct cpuidle_coupled *coupled; ++#endif + }; + + DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices); +@@ -176,6 +183,10 @@ static inline int cpuidle_play_dead(void) {return -ENODEV; } + + #endif + ++#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED ++void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a); ++#endif ++ + /****************************** + * CPUIDLE GOVERNOR INTERFACE * + ******************************/ +diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h +index 3efbfc21..1f78d159 100644 +--- a/include/linux/dma-buf.h ++++ b/include/linux/dma-buf.h +@@ -61,6 +61,10 @@ struct dma_buf_attachment; + * This Callback must not sleep. + * @kmap: maps a page from the buffer into kernel address space. + * @kunmap: [optional] unmaps a page from the buffer. ++ * @mmap: used to expose the backing storage to userspace. Note that the ++ * mapping needs to be coherent - if the exporter doesn't directly ++ * support this, it needs to fake coherency by shooting down any ptes ++ * when transitioning away from the cpu domain. + */ + struct dma_buf_ops { + int (*attach)(struct dma_buf *, struct device *, +@@ -92,6 +96,8 @@ struct dma_buf_ops { + void (*kunmap_atomic)(struct dma_buf *, unsigned long, void *); + void *(*kmap)(struct dma_buf *, unsigned long); + void (*kunmap)(struct dma_buf *, unsigned long, void *); ++ ++ int (*mmap)(struct dma_buf *, struct vm_area_struct *vma); + }; + + /** +@@ -167,6 +173,9 @@ void *dma_buf_kmap_atomic(struct dma_buf *, unsigned long); + void dma_buf_kunmap_atomic(struct dma_buf *, unsigned long, void *); + void *dma_buf_kmap(struct dma_buf *, unsigned long); + void dma_buf_kunmap(struct dma_buf *, unsigned long, void *); ++ ++int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *, ++ unsigned long); + #else + + static inline struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf, +@@ -248,6 +257,13 @@ static inline void dma_buf_kunmap(struct dma_buf *dmabuf, + unsigned long pnum, void *vaddr) + { + } ++ ++static inline int dma_buf_mmap(struct dma_buf *dmabuf, ++ struct vm_area_struct *vma, ++ unsigned long pgoff) ++{ ++ return -ENODEV; ++} + #endif /* CONFIG_DMA_SHARED_BUFFER */ + + #endif /* __DMA_BUF_H__ */ +diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h +index 657ab55b..6f8be328 100644 +--- a/include/linux/eventpoll.h ++++ b/include/linux/eventpoll.h +@@ -26,6 +26,18 @@ + #define EPOLL_CTL_DEL 2 + #define EPOLL_CTL_MOD 3 + ++/* ++ * Request the handling of system wakeup events so as to prevent system suspends ++ * from happening while those events are being processed. ++ * ++ * Assuming neither EPOLLET nor EPOLLONESHOT is set, system suspends will not be ++ * re-allowed until epoll_wait is called again after consuming the wakeup ++ * event(s). ++ * ++ * Requires CAP_EPOLLWAKEUP ++ */ ++#define EPOLLWAKEUP (1 << 29) ++ + /* Set the One Shot behaviour for the target file descriptor */ + #define EPOLLONESHOT (1 << 30) + +diff --git a/include/linux/freezer.h b/include/linux/freezer.h +index ee899329..b79e4d87 100644 +--- a/include/linux/freezer.h ++++ b/include/linux/freezer.h +@@ -41,6 +41,17 @@ extern int freeze_kernel_threads(void); + extern void thaw_processes(void); + extern void thaw_kernel_threads(void); + ++/* ++ * HACK: prevent sleeping while atomic warnings due to ARM signal handling ++ * disabling irqs ++ */ ++static inline bool try_to_freeze_nowarn(void) ++{ ++ if (likely(!freezing(current))) ++ return false; ++ return __refrigerator(false); ++} ++ + static inline bool try_to_freeze(void) + { + might_sleep(); +diff --git a/include/linux/gpio_event.h b/include/linux/gpio_event.h +new file mode 100644 +index 00000000..2613fc5e +--- /dev/null ++++ b/include/linux/gpio_event.h +@@ -0,0 +1,170 @@ ++/* include/linux/gpio_event.h ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_GPIO_EVENT_H ++#define _LINUX_GPIO_EVENT_H ++ ++#include ++ ++struct gpio_event_input_devs { ++ int count; ++ struct input_dev *dev[]; ++}; ++enum { ++ GPIO_EVENT_FUNC_UNINIT = 0x0, ++ GPIO_EVENT_FUNC_INIT = 0x1, ++ GPIO_EVENT_FUNC_SUSPEND = 0x2, ++ GPIO_EVENT_FUNC_RESUME = 0x3, ++}; ++struct gpio_event_info { ++ int (*func)(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, ++ void **data, int func); ++ int (*event)(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, ++ void **data, unsigned int dev, unsigned int type, ++ unsigned int code, int value); /* out events */ ++ bool no_suspend; ++}; ++ ++struct gpio_event_platform_data { ++ const char *name; ++ struct gpio_event_info **info; ++ size_t info_count; ++ int (*power)(const struct gpio_event_platform_data *pdata, bool on); ++ const char *names[]; /* If name is NULL, names contain a NULL */ ++ /* terminated list of input devices to create */ ++}; ++ ++#define GPIO_EVENT_DEV_NAME "gpio-event" ++ ++/* Key matrix */ ++ ++enum gpio_event_matrix_flags { ++ /* unset: drive active output low, set: drive active output high */ ++ GPIOKPF_ACTIVE_HIGH = 1U << 0, ++ GPIOKPF_DEBOUNCE = 1U << 1, ++ GPIOKPF_REMOVE_SOME_PHANTOM_KEYS = 1U << 2, ++ GPIOKPF_REMOVE_PHANTOM_KEYS = GPIOKPF_REMOVE_SOME_PHANTOM_KEYS | ++ GPIOKPF_DEBOUNCE, ++ GPIOKPF_DRIVE_INACTIVE = 1U << 3, ++ GPIOKPF_LEVEL_TRIGGERED_IRQ = 1U << 4, ++ GPIOKPF_PRINT_UNMAPPED_KEYS = 1U << 16, ++ GPIOKPF_PRINT_MAPPED_KEYS = 1U << 17, ++ GPIOKPF_PRINT_PHANTOM_KEYS = 1U << 18, ++}; ++ ++#define MATRIX_CODE_BITS (10) ++#define MATRIX_KEY_MASK ((1U << MATRIX_CODE_BITS) - 1) ++#define MATRIX_KEY(dev, code) \ ++ (((dev) << MATRIX_CODE_BITS) | (code & MATRIX_KEY_MASK)) ++ ++extern int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func); ++struct gpio_event_matrix_info { ++ /* initialize to gpio_event_matrix_func */ ++ struct gpio_event_info info; ++ /* size must be ninputs * noutputs */ ++ const unsigned short *keymap; ++ unsigned int *input_gpios; ++ unsigned int *output_gpios; ++ unsigned int ninputs; ++ unsigned int noutputs; ++ /* time to wait before reading inputs after driving each output */ ++ ktime_t settle_time; ++ /* time to wait before scanning the keypad a second time */ ++ ktime_t debounce_delay; ++ ktime_t poll_time; ++ unsigned flags; ++}; ++ ++/* Directly connected inputs and outputs */ ++ ++enum gpio_event_direct_flags { ++ GPIOEDF_ACTIVE_HIGH = 1U << 0, ++/* GPIOEDF_USE_DOWN_IRQ = 1U << 1, */ ++/* GPIOEDF_USE_IRQ = (1U << 2) | GPIOIDF_USE_DOWN_IRQ, */ ++ GPIOEDF_PRINT_KEYS = 1U << 8, ++ GPIOEDF_PRINT_KEY_DEBOUNCE = 1U << 9, ++ GPIOEDF_PRINT_KEY_UNSTABLE = 1U << 10, ++}; ++ ++struct gpio_event_direct_entry { ++ uint32_t gpio:16; ++ uint32_t code:10; ++ uint32_t dev:6; ++}; ++ ++/* inputs */ ++extern int gpio_event_input_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func); ++struct gpio_event_input_info { ++ /* initialize to gpio_event_input_func */ ++ struct gpio_event_info info; ++ ktime_t debounce_time; ++ ktime_t poll_time; ++ uint16_t flags; ++ uint16_t type; ++ const struct gpio_event_direct_entry *keymap; ++ size_t keymap_size; ++}; ++ ++/* outputs */ ++extern int gpio_event_output_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func); ++extern int gpio_event_output_event(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, ++ unsigned int dev, unsigned int type, ++ unsigned int code, int value); ++struct gpio_event_output_info { ++ /* initialize to gpio_event_output_func and gpio_event_output_event */ ++ struct gpio_event_info info; ++ uint16_t flags; ++ uint16_t type; ++ const struct gpio_event_direct_entry *keymap; ++ size_t keymap_size; ++}; ++ ++ ++/* axes */ ++ ++enum gpio_event_axis_flags { ++ GPIOEAF_PRINT_UNKNOWN_DIRECTION = 1U << 16, ++ GPIOEAF_PRINT_RAW = 1U << 17, ++ GPIOEAF_PRINT_EVENT = 1U << 18, ++}; ++ ++extern int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func); ++struct gpio_event_axis_info { ++ /* initialize to gpio_event_axis_func */ ++ struct gpio_event_info info; ++ uint8_t count; /* number of gpios for this axis */ ++ uint8_t dev; /* device index when using multiple input devices */ ++ uint8_t type; /* EV_REL or EV_ABS */ ++ uint16_t code; ++ uint16_t decoded_size; ++ uint16_t (*map)(struct gpio_event_axis_info *info, uint16_t in); ++ uint32_t *gpio; ++ uint32_t flags; ++}; ++#define gpio_axis_2bit_gray_map gpio_axis_4bit_gray_map ++#define gpio_axis_3bit_gray_map gpio_axis_4bit_gray_map ++uint16_t gpio_axis_4bit_gray_map( ++ struct gpio_event_axis_info *info, uint16_t in); ++uint16_t gpio_axis_5bit_singletrack_map( ++ struct gpio_event_axis_info *info, uint16_t in); ++ ++#endif +diff --git a/include/linux/hid.h b/include/linux/hid.h +index 3a95da60..773b4acf 100644 +--- a/include/linux/hid.h ++++ b/include/linux/hid.h +@@ -618,6 +618,8 @@ struct hid_usage_id { + * @input_mapping: invoked on input registering before mapping an usage + * @input_mapped: invoked on input registering after mapping an usage + * @feature_mapping: invoked on feature registering ++ * @input_register: called just before input device is registered after reports ++ * are parsed. + * @suspend: invoked on suspend (NULL means nop) + * @resume: invoked on resume if device was not reset (NULL means nop) + * @reset_resume: invoked on resume if device was reset (NULL means nop) +@@ -664,6 +666,8 @@ struct hid_driver { + void (*feature_mapping)(struct hid_device *hdev, + struct hid_field *field, + struct hid_usage *usage); ++ int (*input_register)(struct hid_device *hdev, struct hid_input ++ *hidinput); + #ifdef CONFIG_PM + int (*suspend)(struct hid_device *hdev, pm_message_t message); + int (*resume)(struct hid_device *hdev); +diff --git a/include/linux/if_pppolac.h b/include/linux/if_pppolac.h +new file mode 100644 +index 00000000..c06bd6c8 +--- /dev/null ++++ b/include/linux/if_pppolac.h +@@ -0,0 +1,33 @@ ++/* include/linux/if_pppolac.h ++ * ++ * Header for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661) ++ * ++ * Copyright (C) 2009 Google, Inc. ++ * Author: Chia-chi Yeh ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __LINUX_IF_PPPOLAC_H ++#define __LINUX_IF_PPPOLAC_H ++ ++#include ++#include ++ ++struct sockaddr_pppolac { ++ sa_family_t sa_family; /* AF_PPPOX */ ++ unsigned int sa_protocol; /* PX_PROTO_OLAC */ ++ int udp_socket; ++ struct __attribute__((packed)) { ++ __u16 tunnel, session; ++ } local, remote; ++} __attribute__((packed)); ++ ++#endif /* __LINUX_IF_PPPOLAC_H */ +diff --git a/include/linux/if_pppopns.h b/include/linux/if_pppopns.h +new file mode 100644 +index 00000000..0cf34b4d +--- /dev/null ++++ b/include/linux/if_pppopns.h +@@ -0,0 +1,32 @@ ++/* include/linux/if_pppopns.h ++ * ++ * Header for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) ++ * ++ * Copyright (C) 2009 Google, Inc. ++ * Author: Chia-chi Yeh ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __LINUX_IF_PPPOPNS_H ++#define __LINUX_IF_PPPOPNS_H ++ ++#include ++#include ++ ++struct sockaddr_pppopns { ++ sa_family_t sa_family; /* AF_PPPOX */ ++ unsigned int sa_protocol; /* PX_PROTO_OPNS */ ++ int tcp_socket; ++ __u16 local; ++ __u16 remote; ++} __attribute__((packed)); ++ ++#endif /* __LINUX_IF_PPPOPNS_H */ +diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h +index b5f927f5..1597b8bc 100644 +--- a/include/linux/if_pppox.h ++++ b/include/linux/if_pppox.h +@@ -28,6 +28,8 @@ + #include + #endif /* __KERNEL__ */ + #include ++#include ++#include + + /* For user-space programs to pick up these definitions + * which they wouldn't get otherwise without defining __KERNEL__ +@@ -61,7 +63,9 @@ struct pptp_addr { + #define PX_PROTO_OE 0 /* Currently just PPPoE */ + #define PX_PROTO_OL2TP 1 /* Now L2TP also */ + #define PX_PROTO_PPTP 2 +-#define PX_MAX_PROTO 3 ++#define PX_PROTO_OLAC 3 ++#define PX_PROTO_OPNS 4 ++#define PX_MAX_PROTO 5 + + struct sockaddr_pppox { + __kernel_sa_family_t sa_family; /* address family, AF_PPPOX */ +@@ -168,6 +172,25 @@ struct pptp_opt { + u32 seq_sent, seq_recv; + int ppp_flags; + }; ++ ++struct pppolac_opt { ++ __u32 local; ++ __u32 remote; ++ __u32 recv_sequence; ++ __u32 xmit_sequence; ++ atomic_t sequencing; ++ int (*backlog_rcv)(struct sock *sk_udp, struct sk_buff *skb); ++}; ++ ++struct pppopns_opt { ++ __u16 local; ++ __u16 remote; ++ __u32 recv_sequence; ++ __u32 xmit_sequence; ++ void (*data_ready)(struct sock *sk_raw, int length); ++ int (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb); ++}; ++ + #include + + struct pppox_sock { +@@ -178,6 +201,8 @@ struct pppox_sock { + union { + struct pppoe_opt pppoe; + struct pptp_opt pptp; ++ struct pppolac_opt lac; ++ struct pppopns_opt pns; + } proto; + __be16 num; + }; +diff --git a/include/linux/input.h b/include/linux/input.h +index a8167145..49fb20e3 100644 +--- a/include/linux/input.h ++++ b/include/linux/input.h +@@ -154,6 +154,9 @@ struct input_keymap_entry { + + #define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ + ++#define EVIOCGSUSPENDBLOCK _IOR('E', 0x91, int) /* get suspend block enable */ ++#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int) /* set suspend block enable */ ++ + #define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */ + + /* +diff --git a/include/linux/input/akmatrix_keypad.h b/include/linux/input/akmatrix_keypad.h +new file mode 100755 +index 00000000..49dae8d3 +--- /dev/null ++++ b/include/linux/input/akmatrix_keypad.h +@@ -0,0 +1,105 @@ ++#ifndef _MATRIX_KEYPAD_H ++#define _MATRIX_KEYPAD_H ++ ++#include ++#include ++#include ++ ++#define MATRIX_MAX_ROWS 16 ++#define MATRIX_MAX_COLS 16 ++ ++#define KEY(row, col, val) ((((row) & (MATRIX_MAX_ROWS - 1)) << 24) |\ ++ (((col) & (MATRIX_MAX_COLS - 1)) << 16) |\ ++ (val & 0xffff)) ++ ++#define KEY_ROW(k) (((k) >> 24) & 0xff) ++#define KEY_COL(k) (((k) >> 16) & 0xff) ++#define KEY_VAL(k) ((k) & 0xffff) ++ ++#define MATRIX_SCAN_CODE(row, col, row_shift) (((row) << (row_shift)) + (col)) ++ ++/** ++ * struct matrix_keymap_data - keymap for matrix keyboards ++ * @keymap: pointer to array of uint32 values encoded with KEY() macro ++ * representing keymap ++ * @keymap_size: number of entries (initialized) in this keymap ++ * ++ * This structure is supposed to be used by platform code to supply ++ * keymaps to drivers that implement matrix-like keypads/keyboards. ++ */ ++struct matrix_keymap_data { ++ const uint32_t *keymap; ++ unsigned int keymap_size; ++}; ++ ++/** ++ * struct matrix_keypad_platform_data - platform-dependent keypad data ++ * @keymap_data: pointer to &matrix_keymap_data ++ * @row_gpios: pointer to array of gpio numbers representing rows ++ * @col_gpios: pointer to array of gpio numbers reporesenting colums ++ * @num_row_gpios: actual number of row gpios used by device ++ * @num_col_gpios: actual number of col gpios used by device ++ * @col_scan_delay_us: delay, measured in microseconds, that is ++ * needed before we can keypad after activating column gpio ++ * @debounce_ms: debounce interval in milliseconds ++ * ++ * This structure represents platform-specific data that use used by ++ * matrix_keypad driver to perform proper initialization. ++ */ ++struct matrix_keypad_platform_data { ++ const struct matrix_keymap_data *keymap_data; ++ ++ const unsigned int *row_gpios; ++ const unsigned int *col_gpios; ++ ++ unsigned int num_row_gpios; ++ unsigned int num_col_gpios; ++ ++ unsigned int col_scan_delay_us; ++ ++ /* key debounce interval in milli-second */ ++ unsigned int debounce_ms; ++ ++ bool active_low; ++ bool wakeup; ++ ++ /* add for anyka */ ++ struct gpio_info row_gpios_cfginfo; ++ struct gpio_info col_gpios_cfginfo; ++ /* true: one column selecting line is grounded */ ++ bool grounding; ++}; ++ ++/** ++ * matrix_keypad_build_keymap - convert platform keymap into matrix keymap ++ * @keymap_data: keymap supplied by the platform code ++ * @row_shift: number of bits to shift row value by to advance to the next ++ * line in the keymap ++ * @keymap: expanded version of keymap that is suitable for use by ++ * matrix keyboad driver ++ * @keybit: pointer to bitmap of keys supported by input device ++ * ++ * This function converts platform keymap (encoded with KEY() macro) into ++ * an array of keycodes that is suitable for using in a standard matrix ++ * keyboard driver that uses row and col as indices. ++ */ ++static inline void ++matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, ++ unsigned int row_shift, ++ unsigned short *keymap, unsigned long *keybit) ++{ ++ int i; ++ ++ for (i = 0; i < keymap_data->keymap_size; i++) { ++ unsigned int key = keymap_data->keymap[i]; ++ unsigned int row = KEY_ROW(key); ++ unsigned int col = KEY_COL(key); ++ unsigned short code = KEY_VAL(key); ++ ++ keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code; ++ __set_bit(code, keybit); ++ } ++ __clear_bit(KEY_RESERVED, keybit); ++} ++ ++#endif /* _MATRIX_KEYPAD_H */ +diff --git a/include/linux/ion.h b/include/linux/ion.h +new file mode 100644 +index 00000000..8414a6d9 +--- /dev/null ++++ b/include/linux/ion.h +@@ -0,0 +1,373 @@ ++/* ++ * include/linux/ion.h ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_ION_H ++#define _LINUX_ION_H ++ ++#include ++ ++struct ion_handle; ++/** ++ * enum ion_heap_types - list of all possible types of heaps ++ * @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc ++ * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc ++ * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved ++ * carveout heap, allocations are physically ++ * contiguous ++ * @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask ++ * is used to identify the heaps, so only 32 ++ * total heap types are supported ++ */ ++enum ion_heap_type { ++ ION_HEAP_TYPE_SYSTEM, ++ ION_HEAP_TYPE_SYSTEM_CONTIG, ++ ION_HEAP_TYPE_CARVEOUT, ++ ION_HEAP_TYPE_CHUNK, ++ ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always ++ are at the end of this enum */ ++ ION_NUM_HEAPS = 16, ++}; ++ ++#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM) ++#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG) ++#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT) ++ ++#define ION_NUM_HEAP_IDS sizeof(unsigned int) * 8 ++ ++/** ++ * allocation flags - the lower 16 bits are used by core ion, the upper 16 ++ * bits are reserved for use by the heaps themselves. ++ */ ++#define ION_FLAG_CACHED 1 /* mappings of this buffer should be ++ cached, ion will do cache ++ maintenance when the buffer is ++ mapped for dma */ ++#define ION_FLAG_CACHED_NEEDS_SYNC 2 /* mappings of this buffer will created ++ at mmap time, if this is set ++ caches must be managed manually */ ++ ++#ifdef __KERNEL__ ++struct ion_device; ++struct ion_heap; ++struct ion_mapper; ++struct ion_client; ++struct ion_buffer; ++ ++/* This should be removed some day when phys_addr_t's are fully ++ plumbed in the kernel, and all instances of ion_phys_addr_t should ++ be converted to phys_addr_t. For the time being many kernel interfaces ++ do not accept phys_addr_t's that would have to */ ++#define ion_phys_addr_t unsigned long ++ ++/** ++ * struct ion_platform_heap - defines a heap in the given platform ++ * @type: type of the heap from ion_heap_type enum ++ * @id: unique identifier for heap. When allocating higher numbers ++ * will be allocated from first. At allocation these are passed ++ * as a bit mask and therefore can not exceed ION_NUM_HEAP_IDS. ++ * @name: used for debug purposes ++ * @base: base address of heap in physical memory if applicable ++ * @size: size of the heap in bytes if applicable ++ * @align: required alignment in physical memory if applicable ++ * @priv: private info passed from the board file ++ * ++ * Provided by the board file. ++ */ ++struct ion_platform_heap { ++ enum ion_heap_type type; ++ unsigned int id; ++ const char *name; ++ ion_phys_addr_t base; ++ size_t size; ++ ion_phys_addr_t align; ++ void *priv; ++}; ++ ++/** ++ * struct ion_platform_data - array of platform heaps passed from board file ++ * @nr: number of structures in the array ++ * @heaps: array of platform_heap structions ++ * ++ * Provided by the board file in the form of platform data to a platform device. ++ */ ++struct ion_platform_data { ++ int nr; ++ struct ion_platform_heap heaps[]; ++}; ++ ++/** ++ * ion_reserve() - reserve memory for ion heaps if applicable ++ * @data: platform data specifying starting physical address and ++ * size ++ * ++ * Calls memblock reserve to set aside memory for heaps that are ++ * located at specific memory addresses or of specfic sizes not ++ * managed by the kernel ++ */ ++void ion_reserve(struct ion_platform_data *data); ++ ++/** ++ * ion_client_create() - allocate a client and returns it ++ * @dev: the global ion device ++ * @heap_type_mask: mask of heaps this client can allocate from ++ * @name: used for debugging ++ */ ++struct ion_client *ion_client_create(struct ion_device *dev, ++ const char *name); ++ ++/** ++ * ion_client_destroy() - free's a client and all it's handles ++ * @client: the client ++ * ++ * Free the provided client and all it's resources including ++ * any handles it is holding. ++ */ ++void ion_client_destroy(struct ion_client *client); ++ ++/** ++ * ion_alloc - allocate ion memory ++ * @client: the client ++ * @len: size of the allocation ++ * @align: requested allocation alignment, lots of hardware blocks ++ * have alignment requirements of some kind ++ * @heap_id_mask: mask of heaps to allocate from, if multiple bits are set ++ * heaps will be tried in order from highest to lowest ++ * id ++ * @flags: heap flags, the low 16 bits are consumed by ion, the ++ * high 16 bits are passed on to the respective heap and ++ * can be heap custom ++ * ++ * Allocate memory in one of the heaps provided in heap mask and return ++ * an opaque handle to it. ++ */ ++struct ion_handle *ion_alloc(struct ion_client *client, size_t len, ++ size_t align, unsigned int heap_id_mask, ++ unsigned int flags); ++ ++/** ++ * ion_free - free a handle ++ * @client: the client ++ * @handle: the handle to free ++ * ++ * Free the provided handle. ++ */ ++void ion_free(struct ion_client *client, struct ion_handle *handle); ++ ++/** ++ * ion_phys - returns the physical address and len of a handle ++ * @client: the client ++ * @handle: the handle ++ * @addr: a pointer to put the address in ++ * @len: a pointer to put the length in ++ * ++ * This function queries the heap for a particular handle to get the ++ * handle's physical address. It't output is only correct if ++ * a heap returns physically contiguous memory -- in other cases ++ * this api should not be implemented -- ion_sg_table should be used ++ * instead. Returns -EINVAL if the handle is invalid. This has ++ * no implications on the reference counting of the handle -- ++ * the returned value may not be valid if the caller is not ++ * holding a reference. ++ */ ++int ion_phys(struct ion_client *client, struct ion_handle *handle, ++ ion_phys_addr_t *addr, size_t *len); ++ ++/** ++ * ion_map_dma - return an sg_table describing a handle ++ * @client: the client ++ * @handle: the handle ++ * ++ * This function returns the sg_table describing ++ * a particular ion handle. ++ */ ++struct sg_table *ion_sg_table(struct ion_client *client, ++ struct ion_handle *handle); ++ ++/** ++ * ion_map_kernel - create mapping for the given handle ++ * @client: the client ++ * @handle: handle to map ++ * ++ * Map the given handle into the kernel and return a kernel address that ++ * can be used to access this address. ++ */ ++void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle); ++ ++/** ++ * ion_unmap_kernel() - destroy a kernel mapping for a handle ++ * @client: the client ++ * @handle: handle to unmap ++ */ ++void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle); ++ ++/** ++ * ion_share_dma_buf() - share buffer as dma-buf ++ * @client: the client ++ * @handle: the handle ++ */ ++struct dma_buf *ion_share_dma_buf(struct ion_client *client, ++ struct ion_handle *handle); ++ ++/** ++ * ion_share_dma_buf_fd() - given an ion client, create a dma-buf fd ++ * @client: the client ++ * @handle: the handle ++ */ ++int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle); ++ ++/** ++ * ion_import_dma_buf() - given an dma-buf fd from the ion exporter get handle ++ * @client: the client ++ * @fd: the dma-buf fd ++ * ++ * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf, ++ * import that fd and return a handle representing it. If a dma-buf from ++ * another exporter is passed in this function will return ERR_PTR(-EINVAL) ++ */ ++struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd); ++ ++#endif /* __KERNEL__ */ ++ ++/** ++ * DOC: Ion Userspace API ++ * ++ * create a client by opening /dev/ion ++ * most operations handled via following ioctls ++ * ++ */ ++ ++/** ++ * struct ion_allocation_data - metadata passed from userspace for allocations ++ * @len: size of the allocation ++ * @align: required alignment of the allocation ++ * @heap_id_mask: mask of heap ids to allocate from ++ * @flags: flags passed to heap ++ * @handle: pointer that will be populated with a cookie to use to ++ * refer to this allocation ++ * ++ * Provided by userspace as an argument to the ioctl ++ */ ++struct ion_allocation_data { ++ size_t len; ++ size_t align; ++ unsigned int heap_id_mask; ++ unsigned int flags; ++ struct ion_handle *handle; ++}; ++ ++/** ++ * struct ion_fd_data - metadata passed to/from userspace for a handle/fd pair ++ * @handle: a handle ++ * @fd: a file descriptor representing that handle ++ * ++ * For ION_IOC_SHARE or ION_IOC_MAP userspace populates the handle field with ++ * the handle returned from ion alloc, and the kernel returns the file ++ * descriptor to share or map in the fd field. For ION_IOC_IMPORT, userspace ++ * provides the file descriptor and the kernel returns the handle. ++ */ ++struct ion_fd_data { ++ struct ion_handle *handle; ++ int fd; ++}; ++ ++/** ++ * struct ion_handle_data - a handle passed to/from the kernel ++ * @handle: a handle ++ */ ++struct ion_handle_data { ++ struct ion_handle *handle; ++}; ++ ++/** ++ * struct ion_custom_data - metadata passed to/from userspace for a custom ioctl ++ * @cmd: the custom ioctl function to call ++ * @arg: additional data to pass to the custom ioctl, typically a user ++ * pointer to a predefined structure ++ * ++ * This works just like the regular cmd and arg fields of an ioctl. ++ */ ++struct ion_custom_data { ++ unsigned int cmd; ++ unsigned long arg; ++}; ++ ++#define ION_IOC_MAGIC 'I' ++ ++/** ++ * DOC: ION_IOC_ALLOC - allocate memory ++ * ++ * Takes an ion_allocation_data struct and returns it with the handle field ++ * populated with the opaque handle for the allocation. ++ */ ++#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ ++ struct ion_allocation_data) ++ ++/** ++ * DOC: ION_IOC_FREE - free memory ++ * ++ * Takes an ion_handle_data struct and frees the handle. ++ */ ++#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) ++ ++/** ++ * DOC: ION_IOC_MAP - get a file descriptor to mmap ++ * ++ * Takes an ion_fd_data struct with the handle field populated with a valid ++ * opaque handle. Returns the struct with the fd field set to a file ++ * descriptor open in the current address space. This file descriptor ++ * can then be used as an argument to mmap. ++ */ ++#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) ++ ++/** ++ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation ++ * ++ * Takes an ion_fd_data struct with the handle field populated with a valid ++ * opaque handle. Returns the struct with the fd field set to a file ++ * descriptor open in the current address space. This file descriptor ++ * can then be passed to another process. The corresponding opaque handle can ++ * be retrieved via ION_IOC_IMPORT. ++ */ ++#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) ++ ++/** ++ * DOC: ION_IOC_IMPORT - imports a shared file descriptor ++ * ++ * Takes an ion_fd_data struct with the fd field populated with a valid file ++ * descriptor obtained from ION_IOC_SHARE and returns the struct with the handle ++ * filed set to the corresponding opaque handle. ++ */ ++#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) ++ ++/** ++ * DOC: ION_IOC_SYNC - syncs a shared file descriptors to memory ++ * ++ * Deprecated in favor of using the dma_buf api's correctly (syncing ++ * will happend automatically when the buffer is mapped to a device). ++ * If necessary should be used after touching a cached buffer from the cpu, ++ * this will make the buffer in memory coherent. ++ */ ++#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data) ++ ++/** ++ * DOC: ION_IOC_CUSTOM - call architecture specific ion ioctl ++ * ++ * Takes the argument of the architecture specific ioctl to call and ++ * passes appropriate userdata for that ioctl ++ */ ++#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) ++ ++#endif /* _LINUX_ION_H */ +diff --git a/include/linux/kernel.h b/include/linux/kernel.h +index 645231c3..747404ab 100644 +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -705,6 +705,9 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { } + + extern int do_sysinfo(struct sysinfo *info); + ++/* To identify board information in panic logs, set this */ ++extern char *mach_panic_string; ++ + #endif /* __KERNEL__ */ + + #endif +diff --git a/include/linux/keychord.h b/include/linux/keychord.h +new file mode 100644 +index 00000000..856a5850 +--- /dev/null ++++ b/include/linux/keychord.h +@@ -0,0 +1,52 @@ ++/* ++ * Key chord input driver ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++*/ ++ ++#ifndef __LINUX_KEYCHORD_H_ ++#define __LINUX_KEYCHORD_H_ ++ ++#include ++ ++#define KEYCHORD_VERSION 1 ++ ++/* ++ * One or more input_keychord structs are written to /dev/keychord ++ * at once to specify the list of keychords to monitor. ++ * Reading /dev/keychord returns the id of a keychord when the ++ * keychord combination is pressed. A keychord is signalled when ++ * all of the keys in the keycode list are in the pressed state. ++ * The order in which the keys are pressed does not matter. ++ * The keychord will not be signalled if keys not in the keycode ++ * list are pressed. ++ * Keychords will not be signalled on key release events. ++ */ ++struct input_keychord { ++ /* should be KEYCHORD_VERSION */ ++ __u16 version; ++ /* ++ * client specified ID, returned from read() ++ * when this keychord is pressed. ++ */ ++ __u16 id; ++ ++ /* number of keycodes in this keychord */ ++ __u16 count; ++ ++ /* variable length array of keycodes */ ++ __u16 keycodes[]; ++}; ++ ++#endif /* __LINUX_KEYCHORD_H_ */ +diff --git a/include/linux/keyreset.h b/include/linux/keyreset.h +new file mode 100644 +index 00000000..a2ac49e5 +--- /dev/null ++++ b/include/linux/keyreset.h +@@ -0,0 +1,28 @@ ++/* ++ * include/linux/keyreset.h - platform data structure for resetkeys driver ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_KEYRESET_H ++#define _LINUX_KEYRESET_H ++ ++#define KEYRESET_NAME "keyreset" ++ ++struct keyreset_platform_data { ++ int (*reset_fn)(void); ++ int *keys_up; ++ int keys_down[]; /* 0 terminated */ ++}; ++ ++#endif /* _LINUX_KEYRESET_H */ +diff --git a/include/linux/mm.h b/include/linux/mm.h +index 441a5641..01ebcc59 100644 +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -873,6 +873,7 @@ extern bool skip_free_areas_node(unsigned int flags, int nid); + + int shmem_lock(struct file *file, int lock, struct user_struct *user); + struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags); ++void shmem_set_file(struct vm_area_struct *vma, struct file *file); + int shmem_zero_setup(struct vm_area_struct *); + + extern int can_do_mlock(void); +diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h +index 1b431c72..884c08bb 100644 +--- a/include/linux/mmc/core.h ++++ b/include/linux/mmc/core.h +@@ -103,6 +103,7 @@ struct mmc_data { + unsigned int timeout_clks; /* data timeout (in clocks) */ + unsigned int blksz; /* data block size */ + unsigned int blocks; /* number of blocks */ ++ unsigned int retries; /* max number of retries */ + unsigned int error; /* data error */ + unsigned int flags; + +diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h +index 0707d228..5674504e 100644 +--- a/include/linux/mmc/host.h ++++ b/include/linux/mmc/host.h +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -289,12 +290,17 @@ struct mmc_host { + int claim_cnt; /* "claim" nesting count */ + + struct delayed_work detect; ++ struct wake_lock detect_wake_lock; + int detect_change; /* card detect flag */ + struct mmc_hotplug hotplug; + + const struct mmc_bus_ops *bus_ops; /* current bus driver */ + unsigned int bus_refs; /* reference counter */ + ++ unsigned int bus_resume_flags; ++#define MMC_BUSRESUME_MANUAL_RESUME (1 << 0) ++#define MMC_BUSRESUME_NEEDS_RESUME (1 << 1) ++ + unsigned int sdio_irqs; + struct task_struct *sdio_irq_thread; + bool sdio_irq_pending; +@@ -320,6 +326,15 @@ struct mmc_host { + + unsigned int actual_clock; /* Actual HC clock rate */ + ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ struct { ++ struct sdio_cis *cis; ++ struct sdio_cccr *cccr; ++ struct sdio_embedded_func *funcs; ++ int num_funcs; ++ } embedded_sdio_data; ++#endif ++ + unsigned long private[0] ____cacheline_aligned; + }; + +@@ -328,6 +343,14 @@ extern int mmc_add_host(struct mmc_host *); + extern void mmc_remove_host(struct mmc_host *); + extern void mmc_free_host(struct mmc_host *); + ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++extern void mmc_set_embedded_sdio_data(struct mmc_host *host, ++ struct sdio_cis *cis, ++ struct sdio_cccr *cccr, ++ struct sdio_embedded_func *funcs, ++ int num_funcs); ++#endif ++ + static inline void *mmc_priv(struct mmc_host *host) + { + return (void *)host->private; +@@ -338,6 +361,18 @@ static inline void *mmc_priv(struct mmc_host *host) + #define mmc_dev(x) ((x)->parent) + #define mmc_classdev(x) (&(x)->class_dev) + #define mmc_hostname(x) (dev_name(&(x)->class_dev)) ++#define mmc_bus_needs_resume(host) ((host)->bus_resume_flags & MMC_BUSRESUME_NEEDS_RESUME) ++#define mmc_bus_manual_resume(host) ((host)->bus_resume_flags & MMC_BUSRESUME_MANUAL_RESUME) ++ ++static inline void mmc_set_bus_resume_policy(struct mmc_host *host, int manual) ++{ ++ if (manual) ++ host->bus_resume_flags |= MMC_BUSRESUME_MANUAL_RESUME; ++ else ++ host->bus_resume_flags &= ~MMC_BUSRESUME_MANUAL_RESUME; ++} ++ ++extern int mmc_resume_bus(struct mmc_host *host); + + extern int mmc_suspend_host(struct mmc_host *); + extern int mmc_resume_host(struct mmc_host *); +diff --git a/include/linux/mmc/pm.h b/include/linux/mmc/pm.h +index 4a139204..6e2d6a13 100644 +--- a/include/linux/mmc/pm.h ++++ b/include/linux/mmc/pm.h +@@ -26,5 +26,6 @@ typedef unsigned int mmc_pm_flag_t; + + #define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */ + #define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */ ++#define MMC_PM_IGNORE_PM_NOTIFY (1 << 2) /* ignore mmc pm notify */ + + #endif /* LINUX_MMC_PM_H */ +diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h +old mode 100644 +new mode 100755 +index 50f0bc95..dc680c4b +--- a/include/linux/mmc/sdio_func.h ++++ b/include/linux/mmc/sdio_func.h +@@ -22,6 +22,14 @@ struct sdio_func; + + typedef void (sdio_irq_handler_t)(struct sdio_func *); + ++/* ++ * Structure used to hold embedded SDIO device data from platform layer ++ */ ++struct sdio_embedded_func { ++ uint8_t f_class; ++ uint32_t f_maxblksize; ++}; ++ + /* + * SDIO function CIS tuple (unknown to the core) + */ +@@ -130,6 +138,8 @@ extern int sdio_release_irq(struct sdio_func *func); + extern unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz); + + extern u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret); ++extern u8 sdio_readb_ext(struct sdio_func *func, unsigned int addr, int *err_ret, ++ unsigned in); + extern u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret); + extern u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret); + +diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h +index 34066e65..f38d4f0a 100644 +--- a/include/linux/msdos_fs.h ++++ b/include/linux/msdos_fs.h +@@ -101,6 +101,7 @@ struct __fat_dirent { + /* has used 0x72 ('r') in collision, so skip a few */ + #define FAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, __u32) + #define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, __u32) ++#define VFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32) + + struct fat_boot_sector { + __u8 ignored[3]; /* Boot strap short or near jump */ +@@ -138,6 +139,17 @@ struct fat_boot_fsinfo { + __le32 reserved2[4]; + }; + ++struct fat_boot_bsx { ++ __u8 drive; /* drive number */ ++ __u8 reserved1; ++ __u8 signature; /* extended boot signature */ ++ __u8 vol_id[4]; /* volume ID */ ++ __u8 vol_label[11]; /* volume label */ ++ __u8 type[8]; /* file system type */ ++}; ++#define FAT16_BSX_OFFSET 36 /* offset of fat_boot_bsx in FAT12 and FAT16 */ ++#define FAT32_BSX_OFFSET 64 /* offset of fat_boot_bsx in FAT32 */ ++ + struct msdos_dir_entry { + __u8 name[MSDOS_NAME];/* name and extension */ + __u8 attr; /* attribute bits */ +diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h +index cf5ea8cd..76e77810 100644 +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -224,6 +224,9 @@ struct mtd_info { + int (*_get_device) (struct mtd_info *mtd); + void (*_put_device) (struct mtd_info *mtd); + ++ /*Get device chip ID.*/ ++ int (*get_device_id) (struct mtd_info *mtd); ++ + /* Backing device capabilities for this device + * - provides mmap capabilities + */ +diff --git a/include/linux/netfilter/xt_IDLETIMER.h b/include/linux/netfilter/xt_IDLETIMER.h +index 208ae938..faaa28b3 100644 +--- a/include/linux/netfilter/xt_IDLETIMER.h ++++ b/include/linux/netfilter/xt_IDLETIMER.h +@@ -4,6 +4,7 @@ + * Header file for Xtables timer target module. + * + * Copyright (C) 2004, 2010 Nokia Corporation ++ * + * Written by Timo Teras + * + * Converted to x_tables and forward-ported to 2.6.34 +@@ -32,12 +33,19 @@ + #include + + #define MAX_IDLETIMER_LABEL_SIZE 28 ++#define NLMSG_MAX_SIZE 64 ++ ++#define NL_EVENT_TYPE_INACTIVE 0 ++#define NL_EVENT_TYPE_ACTIVE 1 + + struct idletimer_tg_info { + __u32 timeout; + + char label[MAX_IDLETIMER_LABEL_SIZE]; + ++ /* Use netlink messages for notification in addition to sysfs */ ++ __u8 send_nl_msg; ++ + /* for kernel module internal use only */ + struct idletimer_tg *timer __attribute__((aligned(8))); + }; +diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h +new file mode 100644 +index 00000000..ca60fbde +--- /dev/null ++++ b/include/linux/netfilter/xt_qtaguid.h +@@ -0,0 +1,13 @@ ++#ifndef _XT_QTAGUID_MATCH_H ++#define _XT_QTAGUID_MATCH_H ++ ++/* For now we just replace the xt_owner. ++ * FIXME: make iptables aware of qtaguid. */ ++#include ++ ++#define XT_QTAGUID_UID XT_OWNER_UID ++#define XT_QTAGUID_GID XT_OWNER_GID ++#define XT_QTAGUID_SOCKET XT_OWNER_SOCKET ++#define xt_qtaguid_match_info xt_owner_match_info ++ ++#endif /* _XT_QTAGUID_MATCH_H */ +diff --git a/include/linux/netfilter/xt_quota2.h b/include/linux/netfilter/xt_quota2.h +new file mode 100644 +index 00000000..eadc6903 +--- /dev/null ++++ b/include/linux/netfilter/xt_quota2.h +@@ -0,0 +1,25 @@ ++#ifndef _XT_QUOTA_H ++#define _XT_QUOTA_H ++ ++enum xt_quota_flags { ++ XT_QUOTA_INVERT = 1 << 0, ++ XT_QUOTA_GROW = 1 << 1, ++ XT_QUOTA_PACKET = 1 << 2, ++ XT_QUOTA_NO_CHANGE = 1 << 3, ++ XT_QUOTA_MASK = 0x0F, ++}; ++ ++struct xt_quota_counter; ++ ++struct xt_quota_mtinfo2 { ++ char name[15]; ++ u_int8_t flags; ++ ++ /* Comparison-invariant */ ++ aligned_u64 quota; ++ ++ /* Used internally by the kernel */ ++ struct xt_quota_counter *master __attribute__((aligned(8))); ++}; ++ ++#endif /* _XT_QUOTA_H */ +diff --git a/include/linux/netfilter/xt_socket.h b/include/linux/netfilter/xt_socket.h +index 26d7217b..63594564 100644 +--- a/include/linux/netfilter/xt_socket.h ++++ b/include/linux/netfilter/xt_socket.h +@@ -11,4 +11,10 @@ struct xt_socket_mtinfo1 { + __u8 flags; + }; + ++void xt_socket_put_sk(struct sock *sk); ++struct sock *xt_socket_get4_sk(const struct sk_buff *skb, ++ struct xt_action_param *par); ++struct sock *xt_socket_get6_sk(const struct sk_buff *skb, ++ struct xt_action_param *par); ++ + #endif /* _XT_SOCKET_H */ +diff --git a/include/linux/nmi.h b/include/linux/nmi.h +index db50840e..c8f8aa03 100644 +--- a/include/linux/nmi.h ++++ b/include/linux/nmi.h +@@ -14,8 +14,11 @@ + * may be used to reset the timeout - for code which intentionally + * disables interrupts for a long time. This call is stateless. + */ +-#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR) ++#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR_NMI) + #include ++#endif ++ ++#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR) + extern void touch_nmi_watchdog(void); + #else + static inline void touch_nmi_watchdog(void) +diff --git a/drivers/staging/android/persistent_ram.h b/include/linux/persistent_ram.h +similarity index 96% +rename from drivers/staging/android/persistent_ram.h +rename to include/linux/persistent_ram.h +index f41e2086..22422171 100644 +--- a/drivers/staging/android/persistent_ram.h ++++ b/include/linux/persistent_ram.h +@@ -31,6 +31,11 @@ struct persistent_ram { + phys_addr_t start; + phys_addr_t size; + ++ int ecc_block_size; ++ int ecc_size; ++ int ecc_symsize; ++ int ecc_poly; ++ + int num_descs; + struct persistent_ram_descriptor *descs; + +diff --git a/include/linux/platform_data/android_battery.h b/include/linux/platform_data/android_battery.h +new file mode 100644 +index 00000000..f6c8298f +--- /dev/null ++++ b/include/linux/platform_data/android_battery.h +@@ -0,0 +1,47 @@ ++/* ++ * android_battery.h ++ * ++ * Copyright (C) 2012 Samsung Electronics ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef _LINUX_ANDROID_BATTERY_H ++#define _LINUX_ANDROID_BATTERY_H ++ ++enum { ++ CHARGE_SOURCE_NONE = 0, ++ CHARGE_SOURCE_AC, ++ CHARGE_SOURCE_USB, ++}; ++ ++struct android_bat_callbacks { ++ void (*charge_source_changed) ++ (struct android_bat_callbacks *, int); ++ void (*battery_set_full)(struct android_bat_callbacks *); ++}; ++ ++struct android_bat_platform_data { ++ void (*register_callbacks)(struct android_bat_callbacks *); ++ void (*unregister_callbacks)(void); ++ void (*set_charging_current) (int); ++ void (*set_charging_enable) (int); ++ int (*poll_charge_source) (void); ++ int (*get_capacity) (void); ++ int (*get_temperature) (int *); ++ int (*get_voltage_now)(void); ++ int (*get_current_now)(int *); ++ ++ int temp_high_threshold; ++ int temp_high_recovery; ++ int temp_low_recovery; ++ int temp_low_threshold; ++ ++ unsigned long full_charging_time; ++ unsigned long recharging_time; ++ unsigned int recharging_voltage; ++}; ++ ++#endif +diff --git a/include/linux/platform_data/ds2482.h b/include/linux/platform_data/ds2482.h +new file mode 100644 +index 00000000..5a6879e2 +--- /dev/null ++++ b/include/linux/platform_data/ds2482.h +@@ -0,0 +1,21 @@ ++/* ++ * Copyright (C) 2012 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __PLATFORM_DATA_DS2482__ ++#define __PLATFORM_DATA_DS2482__ ++ ++struct ds2482_platform_data { ++ int slpz_gpio; ++}; ++ ++#endif /* __PLATFORM_DATA_DS2482__ */ +diff --git a/include/linux/pm.h b/include/linux/pm.h +index 715305e0..f067e60a 100644 +--- a/include/linux/pm.h ++++ b/include/linux/pm.h +@@ -544,8 +544,6 @@ struct dev_pm_info { + unsigned long active_jiffies; + unsigned long suspended_jiffies; + unsigned long accounting_timestamp; +- ktime_t suspend_time; +- s64 max_time_suspended_ns; + struct dev_pm_qos_request *pq_req; + #endif + struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ +diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h +index 91f82861..30f794eb 100644 +--- a/include/linux/pm_domain.h ++++ b/include/linux/pm_domain.h +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + enum gpd_status { + GPD_STATE_ACTIVE = 0, /* PM domain is active */ +@@ -70,9 +71,9 @@ struct generic_pm_domain { + int (*power_on)(struct generic_pm_domain *domain); + s64 power_on_latency_ns; + struct gpd_dev_ops dev_ops; +- s64 break_even_ns; /* Power break even for the entire domain. */ + s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ +- ktime_t power_off_time; ++ bool max_off_time_changed; ++ bool cached_power_down_ok; + struct device_node *of_node; /* Node in device tree */ + }; + +@@ -93,13 +94,17 @@ struct gpd_timing_data { + s64 start_latency_ns; + s64 save_state_latency_ns; + s64 restore_state_latency_ns; +- s64 break_even_ns; ++ s64 effective_constraint_ns; ++ bool constraint_changed; ++ bool cached_stop_ok; + }; + + struct generic_pm_domain_data { + struct pm_domain_data base; + struct gpd_dev_ops ops; + struct gpd_timing_data td; ++ struct notifier_block nb; ++ struct mutex lock; + bool need_restore; + bool always_on; + }; +@@ -141,6 +146,7 @@ static inline int pm_genpd_of_add_device(struct device_node *genpd_node, + extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, + struct device *dev); + extern void pm_genpd_dev_always_on(struct device *dev, bool val); ++extern void pm_genpd_dev_need_restore(struct device *dev, bool val); + extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, + struct generic_pm_domain *new_subdomain); + extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, +@@ -184,6 +190,7 @@ static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd, + return -ENOSYS; + } + static inline void pm_genpd_dev_always_on(struct device *dev, bool val) {} ++static inline void pm_genpd_dev_need_restore(struct device *dev, bool val) {} + static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, + struct generic_pm_domain *new_sd) + { +diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h +index 609daae7..f271860c 100644 +--- a/include/linux/pm_runtime.h ++++ b/include/linux/pm_runtime.h +@@ -150,9 +150,6 @@ static inline void pm_runtime_set_autosuspend_delay(struct device *dev, + static inline unsigned long pm_runtime_autosuspend_expiration( + struct device *dev) { return 0; } + +-static inline void pm_runtime_update_max_time_suspended(struct device *dev, +- s64 delta_ns) {} +- + #endif /* !CONFIG_PM_RUNTIME */ + + static inline int pm_runtime_idle(struct device *dev) +diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h +index d9f05113..569781fa 100644 +--- a/include/linux/pm_wakeup.h ++++ b/include/linux/pm_wakeup.h +@@ -33,12 +33,15 @@ + * + * @total_time: Total time this wakeup source has been active. + * @max_time: Maximum time this wakeup source has been continuously active. +- * @last_time: Monotonic clock when the wakeup source's was activated last time. ++ * @last_time: Monotonic clock when the wakeup source's was touched last time. ++ * @prevent_sleep_time: Total time this source has been preventing autosleep. + * @event_count: Number of signaled wakeup events. + * @active_count: Number of times the wakeup sorce was activated. + * @relax_count: Number of times the wakeup sorce was deactivated. +- * @hit_count: Number of times the wakeup sorce might abort system suspend. ++ * @expire_count: Number of times the wakeup source's timeout has expired. ++ * @wakeup_count: Number of times the wakeup source might abort suspend. + * @active: Status of the wakeup source. ++ * @has_timeout: The wakeup source has been activated with a timeout. + */ + struct wakeup_source { + const char *name; +@@ -49,11 +52,15 @@ struct wakeup_source { + ktime_t total_time; + ktime_t max_time; + ktime_t last_time; ++ ktime_t start_prevent_time; ++ ktime_t prevent_sleep_time; + unsigned long event_count; + unsigned long active_count; + unsigned long relax_count; +- unsigned long hit_count; +- unsigned int active:1; ++ unsigned long expire_count; ++ unsigned long wakeup_count; ++ bool active:1; ++ bool autosleep_enabled:1; + }; + + #ifdef CONFIG_PM_SLEEP +diff --git a/include/linux/power/smb347-charger.h b/include/linux/power/smb347-charger.h +index b3cb20da..e9aab944 100644 +--- a/include/linux/power/smb347-charger.h ++++ b/include/linux/power/smb347-charger.h +@@ -110,8 +110,14 @@ struct smb347_charger_platform_data { + bool use_mains; + bool use_usb; + bool use_usb_otg; ++ bool disable_automatic_recharge; + int irq_gpio; ++ bool disable_stat_interrupts; + enum smb347_chg_enable enable_control; ++ bool usb_mode_pin_ctrl; ++ char **supplied_to; ++ size_t num_supplicants; ++ int en_gpio; + }; + + #endif /* SMB347_CHARGER_H */ +diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h +index c38c13db..e1f54479 100644 +--- a/include/linux/power_supply.h ++++ b/include/linux/power_supply.h +@@ -124,6 +124,10 @@ enum power_supply_property { + POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ + POWER_SUPPLY_PROP_SCOPE, ++ /* Local extensions */ ++ POWER_SUPPLY_PROP_USB_HC, ++ POWER_SUPPLY_PROP_USB_OTG, ++ POWER_SUPPLY_PROP_CHARGE_ENABLED, + /* Properties of type `const char *' */ + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +@@ -172,6 +176,8 @@ struct power_supply { + /* private */ + struct device *dev; + struct work_struct changed_work; ++ spinlock_t changed_lock; ++ bool changed; + + #ifdef CONFIG_LEDS_TRIGGERS + struct led_trigger *charging_full_trig; +diff --git a/include/linux/sched.h b/include/linux/sched.h +index 3dd0efbb..937ab61f 100644 +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -1804,6 +1804,9 @@ static inline void put_task_struct(struct task_struct *t) + extern void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st); + extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st); + ++extern int task_free_register(struct notifier_block *n); ++extern int task_free_unregister(struct notifier_block *n); ++ + /* + * Per process flags + */ +diff --git a/include/linux/security.h b/include/linux/security.h +index 673afbb8..b62f3969 100644 +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -1381,6 +1381,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) + struct security_operations { + char name[SECURITY_NAME_MAX + 1]; + ++ int (*binder_set_context_mgr) (struct task_struct *mgr); ++ int (*binder_transaction) (struct task_struct *from, struct task_struct *to); ++ int (*binder_transfer_binder) (struct task_struct *from, struct task_struct *to); ++ int (*binder_transfer_file) (struct task_struct *from, struct task_struct *to, struct file *file); ++ + int (*ptrace_access_check) (struct task_struct *child, unsigned int mode); + int (*ptrace_traceme) (struct task_struct *parent); + int (*capget) (struct task_struct *target, +@@ -1664,6 +1669,10 @@ extern void __init security_fixup_ops(struct security_operations *ops); + + + /* Security operations */ ++int security_binder_set_context_mgr(struct task_struct *mgr); ++int security_binder_transaction(struct task_struct *from, struct task_struct *to); ++int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to); ++int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file); + int security_ptrace_access_check(struct task_struct *child, unsigned int mode); + int security_ptrace_traceme(struct task_struct *parent); + int security_capget(struct task_struct *target, +@@ -1842,6 +1851,26 @@ static inline int security_init(void) + return 0; + } + ++static inline int security_binder_set_context_mgr(struct task_struct *mgr) ++{ ++ return 0; ++} ++ ++static inline int security_binder_transaction(struct task_struct *from, struct task_struct *to) ++{ ++ return 0; ++} ++ ++static inline int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to) ++{ ++ return 0; ++} ++ ++static inline int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) ++{ ++ return 0; ++} ++ + static inline int security_ptrace_access_check(struct task_struct *child, + unsigned int mode) + { +diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h +index 2db407a4..c1df3fe2 100644 +--- a/include/linux/serial_core.h ++++ b/include/linux/serial_core.h +@@ -213,6 +213,12 @@ + /* Energy Micro efm32 SoC */ + #define PORT_EFMUART 100 + ++/* Anyka AK39xx UART */ ++#define PORT_AK39 101 ++ ++/* Anyka GPIO UART */ ++#define PORT_GPIO 102 ++ + #ifdef __KERNEL__ + + #include +@@ -252,6 +258,7 @@ struct uart_ops { + void (*pm)(struct uart_port *, unsigned int state, + unsigned int oldstate); + int (*set_wake)(struct uart_port *, unsigned int state); ++ void (*wake_peer)(struct uart_port *); + + /* + * Return a string describing the type of the port +diff --git a/include/linux/sockios.h b/include/linux/sockios.h +index 7997a506..f7ffe36d 100644 +--- a/include/linux/sockios.h ++++ b/include/linux/sockios.h +@@ -65,6 +65,7 @@ + #define SIOCDIFADDR 0x8936 /* delete PA address */ + #define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */ + #define SIOCGIFCOUNT 0x8938 /* get number of devices */ ++#define SIOCKILLADDR 0x8939 /* kill sockets with this local addr */ + + #define SIOCGIFBR 0x8940 /* Bridging support */ + #define SIOCSIFBR 0x8941 /* Set bridging options */ +diff --git a/include/linux/spi/flash.h b/include/linux/spi/flash.h +index 3f22932e..7a76e33d 100644 +--- a/include/linux/spi/flash.h ++++ b/include/linux/spi/flash.h +@@ -3,6 +3,14 @@ + + struct mtd_partition; + ++ ++ ++#define FLASH_BUS_WIDTH_1WIRE (1<<0) ++#define FLASH_BUS_WIDTH_2WIRE (1<<1) ++#define FLASH_BUS_WIDTH_4WIRE (1<<2) ++ ++ ++ + /** + * struct flash_platform_data: board-specific flash data + * @name: optional flash device name (eg, as used with mtdparts=) +@@ -25,6 +33,7 @@ struct flash_platform_data { + + char *type; + ++ u8 bus_width; + /* we'll likely add more ... use JEDEC IDs, etc */ + }; + +diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h +index fa702aeb..10fedb05 100644 +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -72,7 +72,7 @@ struct spi_device { + struct spi_master *master; + u32 max_speed_hz; + u8 chip_select; +- u8 mode; ++ u16 mode; + #define SPI_CPHA 0x01 /* clock phase */ + #define SPI_CPOL 0x02 /* clock polarity */ + #define SPI_MODE_0 (0|0) /* (original MicroWire) */ +@@ -85,6 +85,7 @@ struct spi_device { + #define SPI_LOOP 0x20 /* loopback mode */ + #define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */ + #define SPI_READY 0x80 /* slave pulls low to pause */ ++ + u8 bits_per_word; + int irq; + void *controller_state; +@@ -507,9 +508,15 @@ struct spi_transfer { + u16 delay_usecs; + u32 speed_hz; + ++ u8 xfer_mode; ++#define XFER_1DATAWIRE 0x00 ++#define XFER_2DATAWIRE 0x01 ++#define XFER_4DATAWIRE 0x02 ++ + struct list_head transfer_list; + }; + ++ + /** + * struct spi_message - one multi-segment SPI transaction + * @transfers: list of transfer segments in this transaction +@@ -806,7 +813,7 @@ struct spi_board_info { + /* mode becomes spi_device.mode, and is essential for chips + * where the default of SPI_CS_HIGH = 0 is wrong. + */ +- u8 mode; ++ u16 mode; + + /* ... may need additional spi_device chip config data here. + * avoid stuff protocol drivers can set; but include stuff +diff --git a/include/linux/suspend.h b/include/linux/suspend.h +index ac1c114c..cd83059f 100644 +--- a/include/linux/suspend.h ++++ b/include/linux/suspend.h +@@ -356,8 +356,9 @@ extern int unregister_pm_notifier(struct notifier_block *nb); + extern bool events_check_enabled; + + extern bool pm_wakeup_pending(void); +-extern bool pm_get_wakeup_count(unsigned int *count); ++extern bool pm_get_wakeup_count(unsigned int *count, bool block); + extern bool pm_save_wakeup_count(unsigned int count); ++extern void pm_wakep_autosleep_enabled(bool set); + + static inline void lock_system_sleep(void) + { +@@ -407,6 +408,17 @@ static inline void unlock_system_sleep(void) {} + + #endif /* !CONFIG_PM_SLEEP */ + ++#ifdef CONFIG_PM_AUTOSLEEP ++ ++/* kernel/power/autosleep.c */ ++void queue_up_suspend_work(void); ++ ++#else /* !CONFIG_PM_AUTOSLEEP */ ++ ++static inline void queue_up_suspend_work(void) {} ++ ++#endif /* !CONFIG_PM_AUTOSLEEP */ ++ + #ifdef CONFIG_ARCH_SAVE_PAGE_KEYS + /* + * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture +diff --git a/include/linux/sw_sync.h b/include/linux/sw_sync.h +new file mode 100644 +index 00000000..bd6f2089 +--- /dev/null ++++ b/include/linux/sw_sync.h +@@ -0,0 +1,58 @@ ++/* ++ * include/linux/sw_sync.h ++ * ++ * Copyright (C) 2012 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_SW_SYNC_H ++#define _LINUX_SW_SYNC_H ++ ++#include ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++struct sw_sync_timeline { ++ struct sync_timeline obj; ++ ++ u32 value; ++}; ++ ++struct sw_sync_pt { ++ struct sync_pt pt; ++ ++ u32 value; ++}; ++ ++struct sw_sync_timeline *sw_sync_timeline_create(const char *name); ++void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc); ++ ++struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value); ++ ++#endif /* __KERNEL __ */ ++ ++struct sw_sync_create_fence_data { ++ __u32 value; ++ char name[32]; ++ __s32 fence; /* fd of new fence */ ++}; ++ ++#define SW_SYNC_IOC_MAGIC 'W' ++ ++#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\ ++ struct sw_sync_create_fence_data) ++#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32) ++ ++ ++#endif /* _LINUX_SW_SYNC_H */ +diff --git a/include/linux/switch.h b/include/linux/switch.h +new file mode 100644 +index 00000000..3e4c748e +--- /dev/null ++++ b/include/linux/switch.h +@@ -0,0 +1,53 @@ ++/* ++ * Switch class driver ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++*/ ++ ++#ifndef __LINUX_SWITCH_H__ ++#define __LINUX_SWITCH_H__ ++ ++struct switch_dev { ++ const char *name; ++ struct device *dev; ++ int index; ++ int state; ++ ++ ssize_t (*print_name)(struct switch_dev *sdev, char *buf); ++ ssize_t (*print_state)(struct switch_dev *sdev, char *buf); ++}; ++ ++struct gpio_switch_platform_data { ++ const char *name; ++ unsigned gpio; ++ ++ /* if NULL, switch_dev.name will be printed */ ++ const char *name_on; ++ const char *name_off; ++ /* if NULL, "0" or "1" will be printed */ ++ const char *state_on; ++ const char *state_off; ++}; ++ ++extern int switch_dev_register(struct switch_dev *sdev); ++extern void switch_dev_unregister(struct switch_dev *sdev); ++ ++static inline int switch_get_state(struct switch_dev *sdev) ++{ ++ return sdev->state; ++} ++ ++extern void switch_set_state(struct switch_dev *sdev, int state); ++ ++#endif /* __LINUX_SWITCH_H__ */ +diff --git a/include/linux/synaptics_i2c_rmi.h b/include/linux/synaptics_i2c_rmi.h +new file mode 100644 +index 00000000..5539cc52 +--- /dev/null ++++ b/include/linux/synaptics_i2c_rmi.h +@@ -0,0 +1,55 @@ ++/* ++ * include/linux/synaptics_i2c_rmi.h - platform data structure for f75375s sensor ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_SYNAPTICS_I2C_RMI_H ++#define _LINUX_SYNAPTICS_I2C_RMI_H ++ ++#define SYNAPTICS_I2C_RMI_NAME "synaptics-rmi-ts" ++ ++enum { ++ SYNAPTICS_FLIP_X = 1UL << 0, ++ SYNAPTICS_FLIP_Y = 1UL << 1, ++ SYNAPTICS_SWAP_XY = 1UL << 2, ++ SYNAPTICS_SNAP_TO_INACTIVE_EDGE = 1UL << 3, ++}; ++ ++struct synaptics_i2c_rmi_platform_data { ++ uint32_t version; /* Use this entry for panels with */ ++ /* (major << 8 | minor) version or above. */ ++ /* If non-zero another array entry follows */ ++ int (*power)(int on); /* Only valid in first array entry */ ++ uint32_t flags; ++ unsigned long irqflags; ++ uint32_t inactive_left; /* 0x10000 = screen width */ ++ uint32_t inactive_right; /* 0x10000 = screen width */ ++ uint32_t inactive_top; /* 0x10000 = screen height */ ++ uint32_t inactive_bottom; /* 0x10000 = screen height */ ++ uint32_t snap_left_on; /* 0x10000 = screen width */ ++ uint32_t snap_left_off; /* 0x10000 = screen width */ ++ uint32_t snap_right_on; /* 0x10000 = screen width */ ++ uint32_t snap_right_off; /* 0x10000 = screen width */ ++ uint32_t snap_top_on; /* 0x10000 = screen height */ ++ uint32_t snap_top_off; /* 0x10000 = screen height */ ++ uint32_t snap_bottom_on; /* 0x10000 = screen height */ ++ uint32_t snap_bottom_off; /* 0x10000 = screen height */ ++ uint32_t fuzz_x; /* 0x10000 = screen width */ ++ uint32_t fuzz_y; /* 0x10000 = screen height */ ++ int fuzz_p; ++ int fuzz_w; ++ int8_t sensitivity_adjust; ++}; ++ ++#endif /* _LINUX_SYNAPTICS_I2C_RMI_H */ +diff --git a/include/linux/sync.h b/include/linux/sync.h +new file mode 100644 +index 00000000..5f493638 +--- /dev/null ++++ b/include/linux/sync.h +@@ -0,0 +1,427 @@ ++/* ++ * include/linux/sync.h ++ * ++ * Copyright (C) 2012 Google, Inc. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_SYNC_H ++#define _LINUX_SYNC_H ++ ++#include ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct sync_timeline; ++struct sync_pt; ++struct sync_fence; ++ ++/** ++ * struct sync_timeline_ops - sync object implementation ops ++ * @driver_name: name of the implentation ++ * @dup: duplicate a sync_pt ++ * @has_signaled: returns: ++ * 1 if pt has signaled ++ * 0 if pt has not signaled ++ * <0 on error ++ * @compare: returns: ++ * 1 if b will signal before a ++ * 0 if a and b will signal at the same time ++ * -1 if a will signabl before b ++ * @free_pt: called before sync_pt is freed ++ * @release_obj: called before sync_timeline is freed ++ * @print_obj: deprecated ++ * @print_pt: deprecated ++ * @fill_driver_data: write implmentation specific driver data to data. ++ * should return an error if there is not enough room ++ * as specified by size. This information is returned ++ * to userspace by SYNC_IOC_FENCE_INFO. ++ * @timeline_value_str: fill str with the value of the sync_timeline's counter ++ * @pt_value_str: fill str with the value of the sync_pt ++ */ ++struct sync_timeline_ops { ++ const char *driver_name; ++ ++ /* required */ ++ struct sync_pt *(*dup)(struct sync_pt *pt); ++ ++ /* required */ ++ int (*has_signaled)(struct sync_pt *pt); ++ ++ /* required */ ++ int (*compare)(struct sync_pt *a, struct sync_pt *b); ++ ++ /* optional */ ++ void (*free_pt)(struct sync_pt *sync_pt); ++ ++ /* optional */ ++ void (*release_obj)(struct sync_timeline *sync_timeline); ++ ++ /* deprecated */ ++ void (*print_obj)(struct seq_file *s, ++ struct sync_timeline *sync_timeline); ++ ++ /* deprecated */ ++ void (*print_pt)(struct seq_file *s, struct sync_pt *sync_pt); ++ ++ /* optional */ ++ int (*fill_driver_data)(struct sync_pt *syncpt, void *data, int size); ++ ++ /* optional */ ++ void (*timeline_value_str)(struct sync_timeline *timeline, char *str, ++ int size); ++ ++ /* optional */ ++ void (*pt_value_str)(struct sync_pt *pt, char *str, int size); ++}; ++ ++/** ++ * struct sync_timeline - sync object ++ * @kref: reference count on fence. ++ * @ops: ops that define the implementaiton of the sync_timeline ++ * @name: name of the sync_timeline. Useful for debugging ++ * @destoryed: set when sync_timeline is destroyed ++ * @child_list_head: list of children sync_pts for this sync_timeline ++ * @child_list_lock: lock protecting @child_list_head, destroyed, and ++ * sync_pt.status ++ * @active_list_head: list of active (unsignaled/errored) sync_pts ++ * @sync_timeline_list: membership in global sync_timeline_list ++ */ ++struct sync_timeline { ++ struct kref kref; ++ const struct sync_timeline_ops *ops; ++ char name[32]; ++ ++ /* protected by child_list_lock */ ++ bool destroyed; ++ ++ struct list_head child_list_head; ++ spinlock_t child_list_lock; ++ ++ struct list_head active_list_head; ++ spinlock_t active_list_lock; ++ ++ struct list_head sync_timeline_list; ++}; ++ ++/** ++ * struct sync_pt - sync point ++ * @parent: sync_timeline to which this sync_pt belongs ++ * @child_list: membership in sync_timeline.child_list_head ++ * @active_list: membership in sync_timeline.active_list_head ++ * @signaled_list: membership in temorary signaled_list on stack ++ * @fence: sync_fence to which the sync_pt belongs ++ * @pt_list: membership in sync_fence.pt_list_head ++ * @status: 1: signaled, 0:active, <0: error ++ * @timestamp: time which sync_pt status transitioned from active to ++ * singaled or error. ++ */ ++struct sync_pt { ++ struct sync_timeline *parent; ++ struct list_head child_list; ++ ++ struct list_head active_list; ++ struct list_head signaled_list; ++ ++ struct sync_fence *fence; ++ struct list_head pt_list; ++ ++ /* protected by parent->active_list_lock */ ++ int status; ++ ++ ktime_t timestamp; ++}; ++ ++/** ++ * struct sync_fence - sync fence ++ * @file: file representing this fence ++ * @kref: referenace count on fence. ++ * @name: name of sync_fence. Useful for debugging ++ * @pt_list_head: list of sync_pts in ths fence. immutable once fence ++ * is created ++ * @waiter_list_head: list of asynchronous waiters on this fence ++ * @waiter_list_lock: lock protecting @waiter_list_head and @status ++ * @status: 1: signaled, 0:active, <0: error ++ * ++ * @wq: wait queue for fence signaling ++ * @sync_fence_list: membership in global fence list ++ */ ++struct sync_fence { ++ struct file *file; ++ struct kref kref; ++ char name[32]; ++ ++ /* this list is immutable once the fence is created */ ++ struct list_head pt_list_head; ++ ++ struct list_head waiter_list_head; ++ spinlock_t waiter_list_lock; /* also protects status */ ++ int status; ++ ++ wait_queue_head_t wq; ++ ++ struct list_head sync_fence_list; ++}; ++ ++struct sync_fence_waiter; ++typedef void (*sync_callback_t)(struct sync_fence *fence, ++ struct sync_fence_waiter *waiter); ++ ++/** ++ * struct sync_fence_waiter - metadata for asynchronous waiter on a fence ++ * @waiter_list: membership in sync_fence.waiter_list_head ++ * @callback: function pointer to call when fence signals ++ * @callback_data: pointer to pass to @callback ++ */ ++struct sync_fence_waiter { ++ struct list_head waiter_list; ++ ++ sync_callback_t callback; ++}; ++ ++static inline void sync_fence_waiter_init(struct sync_fence_waiter *waiter, ++ sync_callback_t callback) ++{ ++ waiter->callback = callback; ++} ++ ++/* ++ * API for sync_timeline implementers ++ */ ++ ++/** ++ * sync_timeline_create() - creates a sync object ++ * @ops: specifies the implemention ops for the object ++ * @size: size to allocate for this obj ++ * @name: sync_timeline name ++ * ++ * Creates a new sync_timeline which will use the implemetation specified by ++ * @ops. @size bytes will be allocated allowing for implemntation specific ++ * data to be kept after the generic sync_timeline stuct. ++ */ ++struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, ++ int size, const char *name); ++ ++/** ++ * sync_timeline_destory() - destorys a sync object ++ * @obj: sync_timeline to destroy ++ * ++ * A sync implemntation should call this when the @obj is going away ++ * (i.e. module unload.) @obj won't actually be freed until all its childern ++ * sync_pts are freed. ++ */ ++void sync_timeline_destroy(struct sync_timeline *obj); ++ ++/** ++ * sync_timeline_signal() - signal a status change on a sync_timeline ++ * @obj: sync_timeline to signal ++ * ++ * A sync implemntation should call this any time one of it's sync_pts ++ * has signaled or has an error condition. ++ */ ++void sync_timeline_signal(struct sync_timeline *obj); ++ ++/** ++ * sync_pt_create() - creates a sync pt ++ * @parent: sync_pt's parent sync_timeline ++ * @size: size to allocate for this pt ++ * ++ * Creates a new sync_pt as a chiled of @parent. @size bytes will be ++ * allocated allowing for implemntation specific data to be kept after ++ * the generic sync_timeline struct. ++ */ ++struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size); ++ ++/** ++ * sync_pt_free() - frees a sync pt ++ * @pt: sync_pt to free ++ * ++ * This should only be called on sync_pts which have been created but ++ * not added to a fence. ++ */ ++void sync_pt_free(struct sync_pt *pt); ++ ++/** ++ * sync_fence_create() - creates a sync fence ++ * @name: name of fence to create ++ * @pt: sync_pt to add to the fence ++ * ++ * Creates a fence containg @pt. Once this is called, the fence takes ++ * ownership of @pt. ++ */ ++struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt); ++ ++/* ++ * API for sync_fence consumers ++ */ ++ ++/** ++ * sync_fence_merge() - merge two fences ++ * @name: name of new fence ++ * @a: fence a ++ * @b: fence b ++ * ++ * Creates a new fence which contains copies of all the sync_pts in both ++ * @a and @b. @a and @b remain valid, independent fences. ++ */ ++struct sync_fence *sync_fence_merge(const char *name, ++ struct sync_fence *a, struct sync_fence *b); ++ ++/** ++ * sync_fence_fdget() - get a fence from an fd ++ * @fd: fd referencing a fence ++ * ++ * Ensures @fd references a valid fence, increments the refcount of the backing ++ * file, and returns the fence. ++ */ ++struct sync_fence *sync_fence_fdget(int fd); ++ ++/** ++ * sync_fence_put() - puts a refernnce of a sync fence ++ * @fence: fence to put ++ * ++ * Puts a reference on @fence. If this is the last reference, the fence and ++ * all it's sync_pts will be freed ++ */ ++void sync_fence_put(struct sync_fence *fence); ++ ++/** ++ * sync_fence_install() - installs a fence into a file descriptor ++ * @fence: fence to instal ++ * @fd: file descriptor in which to install the fence ++ * ++ * Installs @fence into @fd. @fd's should be acquired through get_unused_fd(). ++ */ ++void sync_fence_install(struct sync_fence *fence, int fd); ++ ++/** ++ * sync_fence_wait_async() - registers and async wait on the fence ++ * @fence: fence to wait on ++ * @waiter: waiter callback struck ++ * ++ * Returns 1 if @fence has already signaled. ++ * ++ * Registers a callback to be called when @fence signals or has an error. ++ * @waiter should be initialized with sync_fence_waiter_init(). ++ */ ++int sync_fence_wait_async(struct sync_fence *fence, ++ struct sync_fence_waiter *waiter); ++ ++/** ++ * sync_fence_cancel_async() - cancels an async wait ++ * @fence: fence to wait on ++ * @waiter: waiter callback struck ++ * ++ * returns 0 if waiter was removed from fence's async waiter list. ++ * returns -ENOENT if waiter was not found on fence's async waiter list. ++ * ++ * Cancels a previously registered async wait. Will fail gracefully if ++ * @waiter was never registered or if @fence has already signaled @waiter. ++ */ ++int sync_fence_cancel_async(struct sync_fence *fence, ++ struct sync_fence_waiter *waiter); ++ ++/** ++ * sync_fence_wait() - wait on fence ++ * @fence: fence to wait on ++ * @tiemout: timeout in ms ++ * ++ * Wait for @fence to be signaled or have an error. Waits indefinitely ++ * if @timeout < 0 ++ */ ++int sync_fence_wait(struct sync_fence *fence, long timeout); ++ ++#endif /* __KERNEL__ */ ++ ++/** ++ * struct sync_merge_data - data passed to merge ioctl ++ * @fd2: file descriptor of second fence ++ * @name: name of new fence ++ * @fence: returns the fd of the new fence to userspace ++ */ ++struct sync_merge_data { ++ __s32 fd2; /* fd of second fence */ ++ char name[32]; /* name of new fence */ ++ __s32 fence; /* fd on newly created fence */ ++}; ++ ++/** ++ * struct sync_pt_info - detailed sync_pt information ++ * @len: length of sync_pt_info including any driver_data ++ * @obj_name: name of parent sync_timeline ++ * @driver_name: name of driver implmenting the parent ++ * @status: status of the sync_pt 0:active 1:signaled <0:error ++ * @timestamp_ns: timestamp of status change in nanoseconds ++ * @driver_data: any driver dependant data ++ */ ++struct sync_pt_info { ++ __u32 len; ++ char obj_name[32]; ++ char driver_name[32]; ++ __s32 status; ++ __u64 timestamp_ns; ++ ++ __u8 driver_data[0]; ++}; ++ ++/** ++ * struct sync_fence_info_data - data returned from fence info ioctl ++ * @len: ioctl caller writes the size of the buffer its passing in. ++ * ioctl returns length of sync_fence_data reutnred to userspace ++ * including pt_info. ++ * @name: name of fence ++ * @status: status of fence. 1: signaled 0:active <0:error ++ * @pt_info: a sync_pt_info struct for every sync_pt in the fence ++ */ ++struct sync_fence_info_data { ++ __u32 len; ++ char name[32]; ++ __s32 status; ++ ++ __u8 pt_info[0]; ++}; ++ ++#define SYNC_IOC_MAGIC '>' ++ ++/** ++ * DOC: SYNC_IOC_WAIT - wait for a fence to signal ++ * ++ * pass timeout in milliseconds. Waits indefinitely timeout < 0. ++ */ ++#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32) ++ ++/** ++ * DOC: SYNC_IOC_MERGE - merge two fences ++ * ++ * Takes a struct sync_merge_data. Creates a new fence containing copies of ++ * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the ++ * new fence's fd in sync_merge_data.fence ++ */ ++#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data) ++ ++/** ++ * DOC: SYNC_IOC_FENCE_INFO - get detailed information on a fence ++ * ++ * Takes a struct sync_fence_info_data with extra space allocated for pt_info. ++ * Caller should write the size of the buffer into len. On return, len is ++ * updated to reflect the total size of the sync_fence_info_data including ++ * pt_info. ++ * ++ * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence. ++ * To itterate over the sync_pt_infos, use the sync_pt_info.len field. ++ */ ++#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\ ++ struct sync_fence_info_data) ++ ++#endif /* _LINUX_SYNC_H */ +diff --git a/include/linux/uhid.h b/include/linux/uhid.h +new file mode 100644 +index 00000000..9c6974f1 +--- /dev/null ++++ b/include/linux/uhid.h +@@ -0,0 +1,104 @@ ++#ifndef __UHID_H_ ++#define __UHID_H_ ++ ++/* ++ * User-space I/O driver support for HID subsystem ++ * Copyright (c) 2012 David Herrmann ++ */ ++ ++/* ++ * 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. ++ */ ++ ++/* ++ * Public header for user-space communication. We try to keep every structure ++ * aligned but to be safe we also use __attribute__((__packed__)). Therefore, ++ * the communication should be ABI compatible even between architectures. ++ */ ++ ++#include ++#include ++ ++enum uhid_event_type { ++ UHID_CREATE, ++ UHID_DESTROY, ++ UHID_START, ++ UHID_STOP, ++ UHID_OPEN, ++ UHID_CLOSE, ++ UHID_OUTPUT, ++ UHID_OUTPUT_EV, ++ UHID_INPUT, ++ UHID_FEATURE, ++ UHID_FEATURE_ANSWER, ++}; ++ ++struct uhid_create_req { ++ __u8 name[128]; ++ __u8 phys[64]; ++ __u8 uniq[64]; ++ __u8 __user *rd_data; ++ __u16 rd_size; ++ ++ __u16 bus; ++ __u32 vendor; ++ __u32 product; ++ __u32 version; ++ __u32 country; ++} __attribute__((__packed__)); ++ ++#define UHID_DATA_MAX 4096 ++ ++enum uhid_report_type { ++ UHID_FEATURE_REPORT, ++ UHID_OUTPUT_REPORT, ++ UHID_INPUT_REPORT, ++}; ++ ++struct uhid_input_req { ++ __u8 data[UHID_DATA_MAX]; ++ __u16 size; ++} __attribute__((__packed__)); ++ ++struct uhid_output_req { ++ __u8 data[UHID_DATA_MAX]; ++ __u16 size; ++ __u8 rtype; ++} __attribute__((__packed__)); ++ ++struct uhid_output_ev_req { ++ __u16 type; ++ __u16 code; ++ __s32 value; ++} __attribute__((__packed__)); ++ ++struct uhid_feature_req { ++ __u32 id; ++ __u8 rnum; ++ __u8 rtype; ++} __attribute__((__packed__)); ++ ++struct uhid_feature_answer_req { ++ __u32 id; ++ __u16 err; ++ __u16 size; ++ __u8 data[UHID_DATA_MAX]; ++}; ++ ++struct uhid_event { ++ __u32 type; ++ ++ union { ++ struct uhid_create_req create; ++ struct uhid_input_req input; ++ struct uhid_output_req output; ++ struct uhid_output_ev_req output_ev; ++ struct uhid_feature_req feature; ++ struct uhid_feature_answer_req feature_answer; ++ } u; ++} __attribute__((__packed__)); ++ ++#endif /* __UHID_H_ */ +diff --git a/include/linux/uid_stat.h b/include/linux/uid_stat.h +new file mode 100644 +index 00000000..6bd6c4e5 +--- /dev/null ++++ b/include/linux/uid_stat.h +@@ -0,0 +1,29 @@ ++/* include/linux/uid_stat.h ++ * ++ * Copyright (C) 2008-2009 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __uid_stat_h ++#define __uid_stat_h ++ ++/* Contains definitions for resource tracking per uid. */ ++ ++#ifdef CONFIG_UID_STAT ++int uid_stat_tcp_snd(uid_t uid, int size); ++int uid_stat_tcp_rcv(uid_t uid, int size); ++#else ++#define uid_stat_tcp_snd(uid, size) do {} while (0); ++#define uid_stat_tcp_rcv(uid, size) do {} while (0); ++#endif ++ ++#endif /* _LINUX_UID_STAT_H */ +diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h +index 1ad47244..f72d1ad5 100644 +--- a/include/linux/uio_driver.h ++++ b/include/linux/uio_driver.h +@@ -14,6 +14,7 @@ + #ifndef _UIO_DRIVER_H_ + #define _UIO_DRIVER_H_ + ++#include + #include + #include + +@@ -41,7 +42,7 @@ struct uio_mem { + struct uio_map *map; + }; + +-#define MAX_UIO_MAPS 5 ++#define MAX_UIO_MAPS 16 + + struct uio_portio; + +@@ -95,6 +96,7 @@ struct uio_info { + int (*open)(struct uio_info *info, struct inode *inode); + int (*release)(struct uio_info *info, struct inode *inode); + int (*irqcontrol)(struct uio_info *info, s32 irq_on); ++ int (*ioctl)(struct uio_info *info, unsigned int cmd, unsigned long arg); + }; + + extern int __must_check +diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h +index a316fba7..6938a860 100644 +--- a/include/linux/usb/composite.h ++++ b/include/linux/usb/composite.h +@@ -242,6 +242,9 @@ int usb_add_config(struct usb_composite_dev *, + struct usb_configuration *, + int (*)(struct usb_configuration *)); + ++int usb_remove_config(struct usb_composite_dev *, ++ struct usb_configuration *); ++ + /** + * struct usb_composite_driver - groups configurations into a gadget + * @name: For diagnostics, identifies the driver. +diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h +new file mode 100644 +index 00000000..61ebe0aa +--- /dev/null ++++ b/include/linux/usb/f_accessory.h +@@ -0,0 +1,146 @@ ++/* ++ * Gadget Function Driver for Android USB accessories ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * Author: Mike Lockwood ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __LINUX_USB_F_ACCESSORY_H ++#define __LINUX_USB_F_ACCESSORY_H ++ ++/* Use Google Vendor ID when in accessory mode */ ++#define USB_ACCESSORY_VENDOR_ID 0x18D1 ++ ++ ++/* Product ID to use when in accessory mode */ ++#define USB_ACCESSORY_PRODUCT_ID 0x2D00 ++ ++/* Product ID to use when in accessory mode and adb is enabled */ ++#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 ++ ++/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */ ++#define ACCESSORY_STRING_MANUFACTURER 0 ++#define ACCESSORY_STRING_MODEL 1 ++#define ACCESSORY_STRING_DESCRIPTION 2 ++#define ACCESSORY_STRING_VERSION 3 ++#define ACCESSORY_STRING_URI 4 ++#define ACCESSORY_STRING_SERIAL 5 ++ ++/* Control request for retrieving device's protocol version ++ * ++ * requestType: USB_DIR_IN | USB_TYPE_VENDOR ++ * request: ACCESSORY_GET_PROTOCOL ++ * value: 0 ++ * index: 0 ++ * data version number (16 bits little endian) ++ * 1 for original accessory support ++ * 2 adds HID and device to host audio support ++ */ ++#define ACCESSORY_GET_PROTOCOL 51 ++ ++/* Control request for host to send a string to the device ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_SEND_STRING ++ * value: 0 ++ * index: string ID ++ * data zero terminated UTF8 string ++ * ++ * The device can later retrieve these strings via the ++ * ACCESSORY_GET_STRING_* ioctls ++ */ ++#define ACCESSORY_SEND_STRING 52 ++ ++/* Control request for starting device in accessory mode. ++ * The host sends this after setting all its strings to the device. ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_START ++ * value: 0 ++ * index: 0 ++ * data none ++ */ ++#define ACCESSORY_START 53 ++ ++/* Control request for registering a HID device. ++ * Upon registering, a unique ID is sent by the accessory in the ++ * value parameter. This ID will be used for future commands for ++ * the device ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_REGISTER_HID_DEVICE ++ * value: Accessory assigned ID for the HID device ++ * index: total length of the HID report descriptor ++ * data none ++ */ ++#define ACCESSORY_REGISTER_HID 54 ++ ++/* Control request for unregistering a HID device. ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_REGISTER_HID ++ * value: Accessory assigned ID for the HID device ++ * index: 0 ++ * data none ++ */ ++#define ACCESSORY_UNREGISTER_HID 55 ++ ++/* Control request for sending the HID report descriptor. ++ * If the HID descriptor is longer than the endpoint zero max packet size, ++ * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC ++ * commands. The data for the descriptor must be sent sequentially ++ * if multiple packets are needed. ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_SET_HID_REPORT_DESC ++ * value: Accessory assigned ID for the HID device ++ * index: offset of data in descriptor ++ * (needed when HID descriptor is too big for one packet) ++ * data the HID report descriptor ++ */ ++#define ACCESSORY_SET_HID_REPORT_DESC 56 ++ ++/* Control request for sending HID events. ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_SEND_HID_EVENT ++ * value: Accessory assigned ID for the HID device ++ * index: 0 ++ * data the HID report for the event ++ */ ++#define ACCESSORY_SEND_HID_EVENT 57 ++ ++/* Control request for setting the audio mode. ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_SET_AUDIO_MODE ++ * value: 0 - no audio ++ * 1 - device to host, 44100 16-bit stereo PCM ++ * index: 0 ++ * data none ++ */ ++#define ACCESSORY_SET_AUDIO_MODE 58 ++ ++/* ioctls for retrieving strings set by the host */ ++#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) ++#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) ++#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) ++#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) ++#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) ++#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) ++/* returns 1 if there is a start request pending */ ++#define ACCESSORY_IS_START_REQUESTED _IO('M', 7) ++/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ ++#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) ++ ++#endif /* __LINUX_USB_F_ACCESSORY_H */ +diff --git a/include/linux/usb/f_mtp.h b/include/linux/usb/f_mtp.h +new file mode 100644 +index 00000000..72a432e2 +--- /dev/null ++++ b/include/linux/usb/f_mtp.h +@@ -0,0 +1,75 @@ ++/* ++ * Gadget Function Driver for MTP ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * Author: Mike Lockwood ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __LINUX_USB_F_MTP_H ++#define __LINUX_USB_F_MTP_H ++ ++#include ++ ++#ifdef __KERNEL__ ++ ++struct mtp_data_header { ++ /* length of packet, including this header */ ++ uint32_t length; ++ /* container type (2 for data packet) */ ++ uint16_t type; ++ /* MTP command code */ ++ uint16_t command; ++ /* MTP transaction ID */ ++ uint32_t transaction_id; ++}; ++ ++#endif /* __KERNEL__ */ ++ ++struct mtp_file_range { ++ /* file descriptor for file to transfer */ ++ int fd; ++ /* offset in file for start of transfer */ ++ loff_t offset; ++ /* number of bytes to transfer */ ++ int64_t length; ++ /* MTP command ID for data header, ++ * used only for MTP_SEND_FILE_WITH_HEADER ++ */ ++ uint16_t command; ++ /* MTP transaction ID for data header, ++ * used only for MTP_SEND_FILE_WITH_HEADER ++ */ ++ uint32_t transaction_id; ++}; ++ ++struct mtp_event { ++ /* size of the event */ ++ size_t length; ++ /* event data to send */ ++ void *data; ++}; ++ ++/* Sends the specified file range to the host */ ++#define MTP_SEND_FILE _IOW('M', 0, struct mtp_file_range) ++/* Receives data from the host and writes it to a file. ++ * The file is created if it does not exist. ++ */ ++#define MTP_RECEIVE_FILE _IOW('M', 1, struct mtp_file_range) ++/* Sends an event to the host via the interrupt endpoint */ ++#define MTP_SEND_EVENT _IOW('M', 3, struct mtp_event) ++/* Sends the specified file range to the host, ++ * with a 12 byte MTP data packet header at the beginning. ++ */ ++#define MTP_SEND_FILE_WITH_HEADER _IOW('M', 4, struct mtp_file_range) ++ ++#endif /* __LINUX_USB_F_MTP_H */ +diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h +index c9c9a468..38ea5643 100644 +--- a/include/linux/videodev2.h ++++ b/include/linux/videodev2.h +@@ -1191,6 +1191,13 @@ struct v4l2_querymenu { + #define V4L2_CID_MAX_CTRLS 1024 + #define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900) + #define V4L2_CID_USER_BASE V4L2_CID_BASE ++#define V4L2_CID_SCENE (V4L2_CID_USER_BASE + 0) ++#define V4L2_CID_FOCUS (V4L2_CID_USER_BASE + 1) ++#define V4L2_CID_FLASH (V4L2_CID_USER_BASE + 2) ++#define V4L2_CID_PREVIEW (V4L2_CID_USER_BASE + 3) ++#define V4L2_CID_PICTURE (V4L2_CID_USER_BASE + 4) ++#define V4L2_CID_FRAME (V4L2_CID_USER_BASE + 5) //use to report how many frames are abandoned before ecoding ++#define V4L2_CID_NIGHTMODE (V4L2_CID_USER_BASE + 6) + /* IDs reserved for driver specific controls */ + #define V4L2_CID_PRIVATE_BASE 0x08000000 + +diff --git a/include/linux/wakelock.h b/include/linux/wakelock.h +new file mode 100644 +index 00000000..f4a698a2 +--- /dev/null ++++ b/include/linux/wakelock.h +@@ -0,0 +1,67 @@ ++/* include/linux/wakelock.h ++ * ++ * Copyright (C) 2007-2012 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_WAKELOCK_H ++#define _LINUX_WAKELOCK_H ++ ++#include ++#include ++ ++/* A wake_lock prevents the system from entering suspend or other low power ++ * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock ++ * prevents a full system suspend. ++ */ ++ ++enum { ++ WAKE_LOCK_SUSPEND, /* Prevent suspend */ ++ WAKE_LOCK_TYPE_COUNT ++}; ++ ++struct wake_lock { ++ struct wakeup_source ws; ++}; ++ ++static inline void wake_lock_init(struct wake_lock *lock, int type, ++ const char *name) ++{ ++ wakeup_source_init(&lock->ws, name); ++} ++ ++static inline void wake_lock_destroy(struct wake_lock *lock) ++{ ++ wakeup_source_trash(&lock->ws); ++} ++ ++static inline void wake_lock(struct wake_lock *lock) ++{ ++ __pm_stay_awake(&lock->ws); ++} ++ ++static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) ++{ ++ __pm_wakeup_event(&lock->ws, jiffies_to_msecs(timeout)); ++} ++ ++static inline void wake_unlock(struct wake_lock *lock) ++{ ++ __pm_relax(&lock->ws); ++} ++ ++static inline int wake_lock_active(struct wake_lock *lock) ++{ ++ return lock->ws.active; ++} ++ ++#endif +diff --git a/include/linux/wifi_tiwlan.h b/include/linux/wifi_tiwlan.h +new file mode 100644 +index 00000000..f07e0679 +--- /dev/null ++++ b/include/linux/wifi_tiwlan.h +@@ -0,0 +1,27 @@ ++/* include/linux/wifi_tiwlan.h ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++#ifndef _LINUX_WIFI_TIWLAN_H_ ++#define _LINUX_WIFI_TIWLAN_H_ ++ ++#include ++ ++#define WMPA_NUMBER_OF_SECTIONS 3 ++#define WMPA_NUMBER_OF_BUFFERS 160 ++#define WMPA_SECTION_HEADER 24 ++#define WMPA_SECTION_SIZE_0 (WMPA_NUMBER_OF_BUFFERS * 64) ++#define WMPA_SECTION_SIZE_1 (WMPA_NUMBER_OF_BUFFERS * 256) ++#define WMPA_SECTION_SIZE_2 (WMPA_NUMBER_OF_BUFFERS * 2048) ++ ++#endif +diff --git a/include/linux/wl127x-rfkill.h b/include/linux/wl127x-rfkill.h +new file mode 100644 +index 00000000..9057ec63 +--- /dev/null ++++ b/include/linux/wl127x-rfkill.h +@@ -0,0 +1,35 @@ ++/* ++ * Bluetooth TI wl127x rfkill power control via GPIO ++ * ++ * Copyright (C) 2009 Motorola, Inc. ++ * Copyright (C) 2008 Texas Instruments ++ * Initial code: Pavan Savoy (wl127x_power.c) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#ifndef _LINUX_WL127X_RFKILL_H ++#define _LINUX_WL127X_RFKILL_H ++ ++#include ++ ++struct wl127x_rfkill_platform_data { ++ int nshutdown_gpio; ++ ++ struct rfkill *rfkill; /* for driver only */ ++}; ++ ++#endif +diff --git a/include/linux/wlan_plat.h b/include/linux/wlan_plat.h +new file mode 100644 +index 00000000..40ec3482 +--- /dev/null ++++ b/include/linux/wlan_plat.h +@@ -0,0 +1,27 @@ ++/* include/linux/wlan_plat.h ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++#ifndef _LINUX_WLAN_PLAT_H_ ++#define _LINUX_WLAN_PLAT_H_ ++ ++struct wifi_platform_data { ++ int (*set_power)(int val); ++ int (*set_reset)(int val); ++ int (*set_carddetect)(int val); ++ void *(*mem_prealloc)(int section, unsigned long size); ++ int (*get_mac_addr)(unsigned char *buf); ++ void *(*get_country_code)(char *ccode); ++}; ++ ++#endif +diff --git a/include/mach-anyka/Kbuild b/include/mach-anyka/Kbuild +new file mode 100644 +index 00000000..486cf616 +--- /dev/null ++++ b/include/mach-anyka/Kbuild +@@ -0,0 +1,4 @@ ++header-y += fha.h ++header-y += anyka_types.h ++header-y += nand_list.h ++ +diff --git a/include/mach-anyka/aec_interface.h b/include/mach-anyka/aec_interface.h +new file mode 100644 +index 00000000..e5aed96d +--- /dev/null ++++ b/include/mach-anyka/aec_interface.h +@@ -0,0 +1,193 @@ ++ ++/** ++* @file aec_interface.h ++* @brief Anyka AEC Module interfaces header file. ++* ++* This file declare Anyka AEC Module interfaces. ++* Copyright (C) 2011 Anyka (Guangzhou) Microelectronics Technology Co., Ltd. ++* @author Tang Xuechai ++* @date 2013-07-15 ++* @version V1.0.0 ++* @ref ++*/ ++ ++#ifndef _AEC_INTERFACE_H_ ++#define _AEC_INTERFACE_H_ ++ ++#include "anyka_types.h" ++ ++#define AECS_VERSION_STRING (T_U8 *)"EchoS Version V1.0.04_svn95" ++ ++ ++typedef T_pVOID (*AEC_CALLBACK_FUN_MALLOC)(T_U32 size); ++typedef T_VOID (*AEC_CALLBACK_FUN_FREE)(T_pVOID mem); ++typedef T_VOID (*AEC_CALLBACK_FUN_PRINTF)(T_pCSTR format, ...); ++typedef T_VOID (*AEC_CALLBACK_FUN_RTC_DELAY) (T_U32 ulTicks); ++typedef T_VOID (*AEC_CALLBACK_FUN_NOTIFY) (T_U32 event); ++ ++typedef enum ++{ ++ AEC_TYPE_UNKNOWN, ++ AEC_TYPE_1, ++ AEC_TYPE_2 ++}T_AEC_TYPE; ++ ++typedef struct ++{ ++ AEC_CALLBACK_FUN_MALLOC Malloc; ++ AEC_CALLBACK_FUN_FREE Free; ++ AEC_CALLBACK_FUN_PRINTF printf; ++ AEC_CALLBACK_FUN_RTC_DELAY delay; ++ AEC_CALLBACK_FUN_NOTIFY notify; ++}T_AEC_CB_FUNS; ++ ++typedef struct ++{ ++ T_U32 m_Type; //չãAEC㷨T_AEC_TYPEö֮һ ++ T_U32 m_SampleRate; //sample rate, sample per second ++ T_U16 m_Channels; //channel number ++ T_U16 m_BitsPerSample; //bits per sample ++ ++ union ++ { ++ struct ++ { ++ T_U32 m_aecBypass; ++ T_U32 m_framelen; //NN ++ T_U32 m_tail; //TAIL ++ ++ /* ADCڲ */ ++ /* adcֵ Ĭֵ 512micȹϵȽϴ*/ ++ T_U32 AdcMinSpeechPow; ++ /* adcĹʱ䣬(ms)ΪλĬֵ 920ԼǷɱϡ*/ ++ T_U32 AdcSpeechHoldTime; ++ /* adcڻϵĬ (T_U32)(1.8*(1<<14)) ++ ԽֵҪԽСȻܻҪʵAdcMinSpeechPow */ ++ T_U32 AdcSpeechMultiple; ++ /* adcıʱʱ䣬(ms)ΪλĬֵ 5000 */ ++ T_S32 AdcConvergTime; ++ /* adcʼһʱݶöʱ䳤ȣ(ms)ΪλĬֵ 100 */ ++ T_U32 AdcCutTime; ++ ++ /* DACڲ */ ++ /* Էֻ˴ֵ Ĭֵ 512 */ ++ T_U32 DacMinSpeechPow; ++ /* Էֻ˴Ĺʱ䣬(ms)ΪλĬֵ 920 */ ++ T_U32 DacSpeechHoldTime; ++ /* ԷֻڻϵĬ(T_U32)(1.8*(1<<14)) ++ ԽֵҪԽСȻܻҪʵDacMinSpeechPow */ ++ T_U32 DacSpeechMultiple; ++ //Էֻıʱʱ䣬(ms)ΪλĬֵ5000 ++ T_U32 DacConvergTime; ++ ++ //dac޻ֵĬ0 ++ T_U16 DacFadeOutThreshold; ++ //DAC á1024ʱŴ=1>1024ʱŴ>1; <1024ʱŴ<1 ++ T_U16 DacVolume; ++ ++ // for agc ++ T_U8 m_PreprocessEna; ++ T_U16 AGClevel; ++ T_U16 maxGain; // Ŵ ++ T_U16 minGain; // СŴ ++ }m_aec; ++ }m_Private; ++}T_AEC_IN_INFO; ++ ++typedef struct ++{ ++ T_AEC_CB_FUNS cb_fun; ++ T_AEC_IN_INFO m_info; ++}T_AEC_INPUT; ++ ++typedef struct ++{ ++ T_VOID *buf_near; //near, mic ++ T_U32 len_near; ++ T_VOID *buf_far; //far, speaker ++ T_U32 len_far; ++ T_VOID *buf_out; // output of aec ++ T_U32 len_out; ++}T_AEC_BUF; ++ ++ ++/** ++* @brief open the AEC's device. ++* @author Tang Xuechai ++* @date 2013-07-15 ++* @param [in] info: input information. Please refer "T_AEC_INPUT" for detail ++* @return T_VOID * ++* @retval the pointer to AEC's memory ++*/ ++T_VOID *AECLib_Open(T_AEC_INPUT *info); ++ ++/** ++* @brief close the AEC's device. ++* @author Tang Xuechai ++* @date 2013-07-15 ++* @param [in] p_aec: the pointer to AEC's memory, get from AECLib_Open ++* @return T_S32 ++* @retval AK_TRUE : close success ++* @retval AK_FLASE : close fail ++*/ ++T_S32 AECLib_Close(T_VOID *p_aec); ++ ++/** ++* @brief process ADC's data. Should be called in ADC's interrupt ++* @author Tang Xuechai ++* @date 2013-07-15 ++* @param [in] p_aec: the pointer to AEC's memory, get from AECLib_Open ++* @param [in] in: ADC's pcm data ++* @param [in] len: ADC's pcm data length ++* @return T_VOID ++*/ ++T_VOID AECLib_DacInt(T_VOID *p_aec, T_U8 *in, T_S32 len); ++ ++/** ++* @brief process DAC's data. Should be called in DAC's interrupt ++* @author Tang Xuechai ++* @date 2013-07-15 ++* @param [in] p_aec: the pointer to AEC's memory, get from AECLib_Open ++* @param [in] in: DAC's pcm data ++* @param [in] len: DAC's pcm data length ++* @return T_VOID ++*/ ++T_VOID AECLib_AdcInt(T_VOID *p_aec, T_U8 *in, T_S32 len); ++ ++/** ++* @brief main process of AEC. Should be called before transmit data to far end ++* @author Tang Xuechai ++* @date 2013-07-15 ++* @param [in] p_aec: the pointer to AEC's memory, get from AECLib_Open ++* @param [in] p_aec_buf: buffer structure for saving output pcm data. ++* @return T_S32 ++* @retval <0: error ++* @retval >=0: output pcm data length (bytes count) ++*/ ++T_S32 AECLib_Control(T_VOID *p_aec, T_AEC_BUF *p_aec_buf); ++ ++/** ++* @brief pre process DAC/ADC data. Should be called while received fardata's packet ++* @author Tang Xuechai ++* @date 2013-08-13 ++* @param [in] p_aec: the pointer to AEC's memory, get from AECLib_Open ++* @param [in] p_aec_buf: buffer structure for saving output pcm data. ++* @return T_S32 ++* @retval <0: error ++* @retval >=0: output pcm data length (bytes count) ++*/ ++T_S32 AECLib_FarPreprocess(T_VOID *p_aec, T_AEC_BUF *p_aec_buf); ++ ++/** ++* @brief set DAC's volume ++* @author Tang Xuechai ++* @date 2013-07-15 ++* @param [in] p_aec: the pointer to AEC's memory, get from AECLib_Open ++* @param [in] volume: the object volume. ++* @return T_S32 ++* @retval AK_TRUE : success ++* @retval AK_FLASE : fail ++*/ ++T_S32 AECLib_SetDaVolume(T_VOID *p_aec, T_U16 volume); ++ ++#endif +\ No newline at end of file +diff --git a/include/mach-anyka/anyka_types.h b/include/mach-anyka/anyka_types.h +new file mode 100644 +index 00000000..e2d3d1fb +--- /dev/null ++++ b/include/mach-anyka/anyka_types.h +@@ -0,0 +1,70 @@ ++/** @file ++ * @brief Define the register operator for system ++ * ++ * Copyright (C) 2006 Anyka (GuangZhou) Software Technology Co., Ltd. ++ * @author ++ * @date 2006-01-16 ++ * @version 1.0 ++ */ ++ ++#ifndef _ANYKA_TYPES_H_ ++#define _ANYKA_TYPES_H_ ++ ++/** @defgroup ANYKA_CPU ++ * @ingroup M3PLATFORM ++ */ ++/*@{*/ ++ ++/* preliminary type definition for global area */ ++typedef unsigned char T_U8; /* unsigned 8 bit integer */ ++typedef unsigned short T_U16; /* unsigned 16 bit integer */ ++typedef unsigned long T_U32; /* unsigned 32 bit integer */ ++typedef signed char T_S8; /* signed 8 bit integer */ ++typedef signed short T_S16; /* signed 16 bit integer */ ++typedef signed long T_S32; /* signed 32 bit integer */ ++typedef void T_VOID; /* void */ ++ ++#define T_U8_MAX ((T_U8)0xff) // maximum T_U8 value ++#define T_U16_MAX ((T_U16)0xffff) // maximum T_U16 value ++#define T_U32_MAX ((T_U32)0xffffffff) // maximum T_U32 value ++#define T_S8_MIN ((T_S8)(-127-1)) // minimum T_S8 value ++#define T_S8_MAX ((T_S8)127) // maximum T_S8 value ++#define T_S16_MIN ((T_S16)(-32767L-1L)) // minimum T_S16 value ++#define T_S16_MAX ((T_S16)(32767L)) // maximum T_S16 value ++#define T_S32_MIN ((T_S32)(-2147483647L-1L)) // minimum T_S32 value ++#define T_S32_MAX ((T_S32)(2147483647L)) // maximum T_S32 value ++ ++/* basal type definition for global area */ ++typedef T_S8 T_CHR; /* char */ ++typedef T_U8 T_BOOL; /* BOOL type */ ++ ++typedef T_VOID * T_pVOID; /* pointer of void data */ ++typedef const T_VOID * T_pCVOID; /* const pointer of void data */ ++ ++typedef T_S8 * T_pSTR; /* pointer of string */ ++typedef const T_S8 * T_pCSTR; /* const pointer of string */ ++ ++ ++typedef T_U16 T_WCHR; /**< unicode char */ ++typedef T_U16 * T_pWSTR; /* pointer of unicode string */ ++typedef const T_U16 * T_pCWSTR; /* const pointer of unicode string */ ++ ++ ++typedef T_U8 * T_pDATA; /* pointer of data */ ++typedef const T_U8 * T_pCDATA; /* const pointer of data */ ++ ++typedef T_U32 T_COLOR; /* color value */ ++ ++typedef T_U32 T_HANDLE; /* a handle */ ++ ++#define AK_FALSE 0 ++#define AK_TRUE 1 ++#undef AK_NULL ++#define AK_NULL ((T_pVOID)(0)) ++ ++#define AK_EMPTY ++/*@}*/ ++ ++ ++#endif // _ANYKA_TYPES_H_ ++ +diff --git a/include/mach-anyka/fha.h b/include/mach-anyka/fha.h +new file mode 100644 +index 00000000..a7667333 +--- /dev/null ++++ b/include/mach-anyka/fha.h +@@ -0,0 +1,387 @@ ++#ifndef _FHA_H_ ++#define _FHA_H_ ++ ++#include "anyka_types.h" ++#include "nand_list.h" ++ ++#define FHA_SUCCESS 1 ++#define FHA_FAIL 0 ++ ++#define VER_NAME_FHA "FHA" ++#define VER_NAME_FS "FS" ++#define VER_NAME_MTD "MTD" ++#define VER_NAME_DRV "DRV" ++#define VER_NAME_MOUNT "MOUNT" ++#define VER_NAME_FSA "FSA" ++ ++typedef enum ++{ ++ FHA_CHIP_880X, //aspen3 ++ FHA_CHIP_10XX, //snowbirds ++ FHA_CHIP_980X, //aspen3s ++ FHA_CHIP_37XX, //Sundance3 ++ FHA_CHIP_11XX, //snowbird2 ++ FHA_CHIP_39XX, //tmp!!!!!! ++}E_FHA_CHIP_TYPE; ++ ++#if defined (CONFIG_ARCH_AK98) ++#define FHA_CHIP_SET_TYPE FHA_CHIP_980X ++#elif defined (CONFIG_ARCH_AK37) ++#define FHA_CHIP_SET_TYPE FHA_CHIP_37XX ++#elif defined (CONFIG_ARCH_AK39) ++#define FHA_CHIP_SET_TYPE FHA_CHIP_39XX ++#endif ++ ++typedef enum ++{ ++ PLAT_SPOT, //spot system ++ PLAT_SPR, //spring system ++ PLAT_SWORD, //sword system ++ PLAT_LINUX //linux system ++}E_FHA_PLATFORM_TYPE; ++ ++typedef enum ++{ ++ MEDIUM_NAND, //nand ++ MEDIUM_SPIFLASH, //spiflash ++ MEDIUM_EMMC, //sd, emmc, inand ++ MEDIUM_SPI_EMMC, //data is stored in spiflash and sd ++ MEDIUM_EMMC_SPIBOOT, //data is stored in sd and boot from spiflash ++}E_FHA_MEDIUM_TYPE; ++ ++typedef enum ++{ ++ MODE_NEWBURN = 1, //new burn ++ MODE_UPDATE, //update mode ++ MODE_UPDATE_SELF, //update mode self ++}E_BURN_MODE; ++ ++typedef enum ++{ ++ FHA_DATA_BOOT, ++ FHA_DATA_ASA, ++ FHA_DATA_BIN, ++ FHA_DATA_FS, ++ FHA_GET_NAND_PARAM ++}E_FHA_DATA_TYPE; ++ ++typedef struct ++{ ++ T_U8 lib_name[10]; ++ T_U8 lib_version[40]; ++}T_LIB_VER_INFO; ++ ++ ++/************************************************************************ ++ * NAME: FHA_Erase ++ * FUNCTION callback function, medium erase ++ * PARAM: [in] nChip--meidum chip ++ * [in] nPage--medium page ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++typedef T_U32 (*FHA_Erase)(T_U32 nChip, T_U32 nPage); ++ ++/************************************************************************ ++ * NAME: FHA_Write ++ * FUNCTION callback function, medium write ++ * PARAM: [in] nChip-----meidum chip ++ * [in] nPage-----medium page ++ * [in] pData-----need to write data pointer addr ++ * [in] nDataLen--need to write data length ++ * nand(unit byte) ++ * SD(unit sector count(1sec = 512byte)) ++ * SPI(unit page count, page size in platform define, generally is 256bytes) ++ * [in] pOob------Spare areaOut Of Band, only nand use ++ * [in] nOobLen---Spare area length ++ * [in] eDataType-burn medium data type ++ * nand -- E_FHA_DATA_TYPE ++ * SD----- MEDIUM_EMMC ++ * SPI---- MEDIUM_SPIFLASH ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++typedef T_U32 (*FHA_Write)(T_U32 nChip, T_U32 nPage, const T_U8 *pData, T_U32 nDataLen, T_U8 *pOob, T_U32 nOobLen, T_U32 eDataType); ++ ++/************************************************************************ ++ * NAME: FHA_Read ++ * FUNCTION callback function, medium read ++ * PARAM: [in] nChip-----meidum chip ++ * [in] nPage-----medium page ++ * [out]pData-----need to read data pointer addr ++ * [in] nDataLen--need to ren data length ++ * nand(unit byte) ++ * SD(unit sector count(1sec = 512byte)) ++ * SPI(unit page count, page size in platform define, generally is 256bytes) ++ * [out]pOob------Spare areaOut Of Band, only nand use ++ * [in] nOobLen---Spare area length ++ * [in] eDataType-burn medium data type ++ * nand -- E_FHA_DATA_TYPE ++ * SD----- MEDIUM_EMMC ++ * SPI---- MEDIUM_SPIFLASH ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++typedef T_U32 (*FHA_Read)(T_U32 nChip, T_U32 nPage, T_U8 *pData, T_U32 nDataLen, T_U8 *pOob, T_U32 nOobLen , T_U32 eDataType); ++ ++/************************************************************************ ++ * NAME: FHA_ReadNandBytes ++ * FUNCTION callback function, nand read no ECC ++ * PARAM: [in] nChip-------meidum chip ++ * [in] rowAddr-----nand physical row addr ++ * [in] columnAddr--nand physical cloumn addr ++ * [out] pData------need to read data pointer addr ++ * [in] nDataLen----need to ren data length ++ * nand(unit byte) ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++typedef T_U32 (*FHA_ReadNandBytes)(T_U32 nChip, T_U32 rowAddr, T_U32 columnAddr, T_U8 *pData, T_U32 nDataLen); ++ ++typedef T_pVOID (*FHA_RamAlloc)(T_U32 size); ++typedef T_pVOID (*FHA_RamFree)(T_pVOID var); ++typedef T_pVOID (*FHA_MemSet)(T_pVOID pBuf, T_S32 value, T_U32 count); ++typedef T_pVOID (*FHA_MemCpy)(T_pVOID dst, T_pCVOID src, T_U32 count); ++typedef T_S32 (*FHA_MemCmp)(T_pCVOID pbuf1, T_pCVOID pbuf2, T_U32 count); ++typedef T_pVOID (*FHA_MemMov)(T_pVOID dst, const T_pCVOID src, T_U32 count); ++typedef T_S32 (*FHA_Printf)(T_pCSTR s, ...); ++ ++typedef struct tag_FHA_LibCallback ++{ ++ FHA_Erase Erase; ++ FHA_Write Write; ++ FHA_Read Read; ++ FHA_ReadNandBytes ReadNandBytes; ++ FHA_RamAlloc RamAlloc; ++ FHA_RamFree RamFree; ++ FHA_MemSet MemSet; ++ FHA_MemCpy MemCpy; ++ FHA_MemCmp MemCmp; ++ FHA_MemMov MemMov; ++ FHA_Printf Printf; ++}T_FHA_LIB_CALLBACK, *T_PFHA_LIB_CALLBACK; ++ ++typedef struct tag_FHA_Init_Info ++{ ++ T_U32 nChipCnt; //Ƭѡ ++ T_U32 nBlockStep; //nand block stepֵ ++ E_FHA_CHIP_TYPE eAKChip; //AKоƬ ++ E_FHA_PLATFORM_TYPE ePlatform; //ϵͳ ++ E_FHA_MEDIUM_TYPE eMedium; //洢 ++ E_BURN_MODE eMode; //¼ģʽ ++}T_FHA_INIT_INFO, *T_PFHA_INIT_INFO; ++ ++typedef struct tag_FHABinParam ++{ ++ T_U32 data_length; //ݳ ++ T_U32 ld_addr; //еַ ++ T_U8 file_name[16]; //ļ ++ T_BOOL bBackup; //Ƿ񱸷 ++ T_BOOL bCheck; //ǷУ ++ T_BOOL bUpdateSelf; //spotlightãÿBINԤͬĿռ ++}T_FHA_BIN_PARAM, *T_PFHA_BIN_PARAM; ++ ++typedef struct ++{ ++ T_U32 BinPageStart; /*bin data start addr*/ ++ T_U32 PageSize; /*spi page size*/ ++ T_U32 PagesPerBlock;/*page per block*/ ++}T_SPI_INIT_INFO, *T_PSPI_INIT_INFO; ++ ++/************************************************************************ ++ * NAME: FHA_burn_init ++ * FUNCTION Initial FHA callback function, init fha init info para ++ * PARAM: [in] pInit----burn platform init struct pointer ++ * [in] pCB------callback function struct pointer ++ * [in] pPhyInfo-input medium struct pointer ++ * NAND-------T_NAND_PHY_INFO* ++ * SPIFLASH---T_PSPI_INIT_INFO ++ * EMMC-------AK_NULL ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_burn_init(T_PFHA_INIT_INFO pInit, T_PFHA_LIB_CALLBACK pCB, T_pVOID pPhyInfo); ++ ++/************************************************************************ ++ * NAME: FHA_set_resv_zone_info ++ * FUNCTION set reserve area ++ * PARAM: [in] nSize--set reserve area size(unit Mbyte) ++ * [in] bErase-if == 1 ,erase reserve area, or else not erase ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_set_resv_zone_info(T_U32 nSize, T_BOOL bErase); ++ ++/************************************************************************ ++ * NAME: FHA_set_bin_resv_size ++ * FUNCTION set write bin reserve size(unit byte) ++ * PARAM: [in] bin_size--reserve bin size ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_set_bin_resv_size(T_U32 bin_size); ++ ++/************************************************************************ ++ * NAME: FHA_write_bin_begin ++ * FUNCTION set write bin start init para ++ * PARAM: [in] bin_param--Bin file info struct ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_write_bin_begin(T_PFHA_BIN_PARAM bin_param); ++ ++/************************************************************************ ++ * NAME: FHA_write_bin ++ * FUNCTION write bin to medium ++ * PARAM: [in] pData-----need to write bin data pointer addr ++ * [in] data_len--need to write bin data length ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_write_bin(const T_U8 * pData, T_U32 data_len); ++ ++/************************************************************************ ++ * NAME: FHA_write_boot_begin ++ * FUNCTION set write boot start init para ++ * PARAM: [in] bin_len--boot length ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_write_boot_begin(T_U32 bin_len); ++ ++/************************************************************************ ++ * NAME: FHA_write_boot ++ * FUNCTION write boot to medium ++ * PARAM: [in] pData-----need to write boot data pointer addr ++ * [in] data_len--need to write boot data length ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_write_boot(const T_U8 *pData, T_U32 data_len); ++ ++/************************************************************************ ++ * NAME: FHA_get_last_pos ++ * FUNCTION get fs start position ++ * PARAM: NULL ++ * RETURN: success return fs start block, fail retuen 0 ++**************************************************************************/ ++T_U32 FHA_get_last_pos(void); ++ ++/************************************************************************ ++ * NAME: FHA_set_fs_part ++ * FUNCTION set fs partition info to medium ++ * PARAM: [in] pInfoBuf-----need to write fs info data pointer addr ++ * [in] data_len--need to write fs info data length ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_set_fs_part(const T_U8 *pInfoBuf, T_U32 buf_len); ++ ++/************************************************************************ ++ * NAME: FHA_close ++ * FUNCTION flush all need to save data to medium, and free all fha malloc ram ++ * PARAM: NULL ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_close(T_VOID); ++ ++/************************************************************************ ++ * NAME: FHA_mount ++ * FUNCTION Initial FHA mount callback function, init fha mount init info para ++ * PARAM: [in] pInit----burn platform init struct pointer ++ * [in] pCB------callback function struct pointer ++ * [in] pPhyInfo-input medium struct pointer ++ * NAND-------T_NAND_PHY_INFO*, linux platform == AK_NULL ++ * SPIFLASH---T_PSPI_INIT_INFO ++ * EMMC-------AK_NULL ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_mount(T_PFHA_INIT_INFO pInit, T_PFHA_LIB_CALLBACK pCB, T_pVOID pPhyInfo); ++ ++/************************************************************************ ++ * NAME: FHA_read_bin_begin ++ * FUNCTION set read bin start init para ++ * PARAM: [in] bin_param--Bin file info struct ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_read_bin_begin(T_PFHA_BIN_PARAM bin_param); ++ ++/************************************************************************ ++ * NAME: FHA_read_bin ++ * FUNCTION read bin to buf from medium ++ * PARAM: [out]pData-----need to read bin data buf pointer addr ++ * [in] data_len--need to read bin data length ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_read_bin(T_U8 *pData, T_U32 data_len); ++ ++/************************************************************************ ++ * NAME: FHA_get_maplist ++ * FUNCTION get block address map of bin. (only nand) ++ * PARAM: [in] file_name---need to get bin's file name ++ * [out]map_data----need to get bin's block map buf pointer addr ++ * [out]file_len----need to get bin's file length ++ * [in] bBackup-----if AK_TRUE == bBackup, get backup block map, or else get origin block map ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_get_maplist(T_U8 file_name[], T_U16 *map_data, T_U32 *file_len, T_BOOL bBackup); ++ ++/************************************************************************ ++ * NAME: FHA_get_nand_para ++ * FUNCTION get nand para ++ * PARAM: [out] pNandPhyInfo--nand info struct pointer ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_get_nand_para(T_NAND_PHY_INFO *pNandPhyInfo); ++ ++/************************************************************************ ++ * NAME: FHA_get_fs_part ++ * FUNCTION get fs partition info ++ * PARAM: [out]pInfoBuf---need to get fs info data pointer addr ++ * [in] data_len---need to get fs info data length ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_get_fs_part(T_U8 *pInfoBuf , T_U32 buf_len); ++ ++/************************************************************************ ++ * NAME: FHA_get_resv_zone_info ++ * FUNCTION get reserve area ++ * PARAM: [out] start_block--get reserve area start block ++ * [out] block_cnt----get reserve area block count ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_get_resv_zone_info(T_U16 *start_block, T_U16 *block_cnt); ++ ++/************************************************************************ ++ * NAME: FHA_get_bin_num ++ * FUNCTION get bin file number ++ * [out] cnt----bin file count in medium ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_get_bin_num(T_U32 *cnt); ++ ++/************************************************************************ ++ * NAME: FHA_set_lib_version ++ * FUNCTION set burn all lib version ++ * PARAM: [in] lib_info--all lib version struct pointer addr ++ * [in] lib_cnt---lib count ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_set_lib_version(T_LIB_VER_INFO *lib_info, T_U32 lib_cnt); ++ ++/************************************************************************ ++ * NAME: FHA_get_lib_verison ++ * FUNCTION get burn lib version by input lib_info->lib_name ++ * PARAM: [in-out] lib_info--input lib_info->lib_name, output lib_info->lib_version ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_get_lib_verison(T_LIB_VER_INFO *lib_info); ++ ++/************************************************************************ ++ * NAME: FHA_get_version ++ * FUNCTION get fha version ++ * PARAM: NULL ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U8 *FHA_get_version(void); ++ ++/************************************************************************ ++ * NAME: FHA_check_lib_version ++ * FUNCTION check burn lib version ++ * PARAM: [in] lib_info lib name and verison ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_check_lib_version(T_LIB_VER_INFO *lib_info); ++ ++ ++#endif //_FHA_BINBURN_H_ ++ ++ +diff --git a/include/mach-anyka/fha_asa.h b/include/mach-anyka/fha_asa.h +new file mode 100644 +index 00000000..5a30deaa +--- /dev/null ++++ b/include/mach-anyka/fha_asa.h +@@ -0,0 +1,81 @@ ++#ifndef _FHA_ASA_H_ ++#define _FHA_ASA_H_ ++ ++#define ASA_FILE_FAIL 0 ++#define ASA_FILE_SUCCESS 1 ++#define ASA_FILE_EXIST 2 ++#define ASA_MODE_OPEN 0 ++#define ASA_MODE_CREATE 1 ++ ++#define ASA_FORMAT_NORMAL 0 ++#define ASA_FORMAT_EWR 1 ++#define ASA_FORMAT_RESTORE 2 ++ ++/************************************************************************ ++ * NAME: FHA_asa_scan ++ * FUNCTION scan nand flash security area ++ * PARAM: [in] bMount -- if buffer is enough, set AK_TURE, scan speedup ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_asa_scan(T_BOOL bMount); ++ ++/************************************************************************ ++ * NAME: FHA_asa_format ++ * FUNCTION nand flash format security area ++ * PARAM: [in] type -- ASA_FORMAT_NORMAL: only scan initial bad blocks ++ * ASA_FORMAT_EWR: Erase-wirte-read test ++ * ASA_FORMAT_RESTORE: do nothing ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_asa_format(T_U32 type); ++ ++/************************************************************************ ++ * NAME: FHA_set_bad_block ++ * FUNCTION set nand flash bad block ++ * PARAM: [in] block -- the bad block item ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_set_bad_block(T_U32 block); ++ ++/************************************************************************ ++ * NAME: FHA_check_bad_block ++ * FUNCTION check nand flash bad block ++ * PARAM: [in] block -- need to check block item ++ * RETURN: is bad block return FHA_SUCCESS, or else retuen FHA_ FAIL ++**************************************************************************/ ++T_BOOL FHA_check_bad_block(T_U32 block); ++ ++/************************************************************************ ++ * NAME: FHA_get_bad_block ++ * FUNCTION get bad block information of one or more blocks ++ * PARAM: [in] start_block -- start block ++ * [out]pData -------- buffer used to store bad blocks information data ++ * [in] blk_cnt ------ how many blocks you want to know ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_get_bad_block(T_U32 start_block, T_U8 *pData, T_U32 blk_cnt); ++ ++/************************************************************************ ++ * NAME: FHA_asa_write_file ++ * FUNCTION write important infomation to security area, data_len can't too large ++ * PARAM: [in] file_name -- asa file name ++ * [in] pData ------ buffer used to store information data ++ * [in] data_len --- need to store data length ++ * [in] mode-------- operation mode ++ * ASA_MODE_OPEN------open ++ * ASA_MODE_CREATE----create ++ * RETURN: success return ASA_FILE_SUCCESS, fail retuen ASA_FILE_FAIL ++**************************************************************************/ ++T_U32 FHA_asa_write_file(T_U8 file_name[], const T_U8 *pData, T_U32 data_len, T_U8 mode); ++ ++/************************************************************************ ++ * NAME: FHA_asa_read_file ++ * FUNCTION get infomation from security area by input file name ++ * PARAM: [in] file_name -- asa file name ++ * [out]pData ------ buffer used to store information data ++ * [in] data_len --- need to get data length ++ * RETURN: success return ASA_FILE_SUCCESS, fail retuen ASA_FILE_FAIL ++**************************************************************************/ ++T_U32 FHA_asa_read_file(T_U8 file_name[], T_U8 *pData, T_U32 data_len); ++#endif // ++ +diff --git a/include/mach-anyka/mac.h b/include/mach-anyka/mac.h +new file mode 100755 +index 00000000..e5c585c7 +--- /dev/null ++++ b/include/mach-anyka/mac.h +@@ -0,0 +1,20 @@ ++#ifndef __AK_MAC_H ++#define __AK_MAC_H ++ ++/* platfrom data for platfrom device structure's platfrom_data field */ ++ ++#define MAC_ADDR_LEN 6 ++#define MAC_ADDR_STRING_LEN (MAC_ADDR_LEN * 3 - 1) ++ ++ ++struct ak_mac_data { ++ unsigned int flags; ++ unsigned char dev_addr[MAC_ADDR_LEN]; ++ void (* gpio_init) (const struct gpio_info *); ++ struct gpio_info pwr_gpio; // power up ++ struct gpio_info phy_rst_gpio; // PHY RESET gpio ++}; ++ ++ ++#endif /* __AK_MAC_H */ ++ +diff --git a/include/mach-anyka/nand_list.h b/include/mach-anyka/nand_list.h +new file mode 100644 +index 00000000..6f2f609d +--- /dev/null ++++ b/include/mach-anyka/nand_list.h +@@ -0,0 +1,85 @@ ++/** ++ * @filename nand_list.h ++ * @brief: AK3223M interrupt ++ * ++ * This file describe what is in the table of nand list ++ * Copyright (C) 2006 Anyka (GuangZhou) Software Technology Co., Ltd. ++ * @author zhaojiahuan ++ * @modify chenyanyan ++ * @date 2007-1-10 ++ * @version 1.0 ++ * @ref ++ */ ++ ++#ifndef __CHIP_NFC_3224__ ++#define __CHIP_NFC_3224__ ++ ++#include "anyka_types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** @defgroup NandFlash Architecture NandFlash Interface ++ * @ingroup Architecture ++ */ ++/*@{*/ ++ ++/** ++* @BRIEF Nandflash info define ++* @AUTHOR zhaojiahuan ++* @DATE 2006-7-17 ++*/ ++ ++typedef struct ++Nand_phy_info{ ++ T_U32 chip_id;//chip id ++ T_U16 page_size; //page size ++ T_U16 page_per_blk; //page of one blok ++ T_U16 blk_num;//total block number ++ T_U16 group_blk_num;//the same concept as die, according to nand's struture ++ T_U16 plane_blk_num; ++ T_U8 spare_size;//spareСĵλ255 Byte ++ T_U8 col_cycle;//column address cycle ++ T_U8 lst_col_mask;//last column addrress cycle mask bit ++ T_U8 row_cycle;//row address cycle ++ T_U8 delay_cnt;//Rb delay, unit is 1024 asic clock, default value corresponds to 84MHz ++ T_U8 custom_nd;//nand type flag, used to detect the original invilid block ++ //currently there are 7 types, more types might be added when new nand come out ++ //˵ǰһpage,һpageеλ, ЩλòΪ0xFFblockdz ++ //NAND_TYPE_SAMSUNG: 0x1 СҳSLC([0,1],[517]), ҳSLC([0,1],[2048]), MLC([127], [2048/4096]) ++ //NAND_TYPE_HYNIX: 0x2 СҳSLC([0,1],[517]), ҳSLC([0,1],[2048]), MLC([125,127], [2048/4096]) ++ //NAND_TYPE_TOSHIBA: 0x3 СҳSLC([0,1],[0,512]), ҳSLC([0,1],[0,2048]), MLC([127], [0,2048/4096]) ++ //NAND_TYPE_TOSHIBA_EXT: 0x4 СҳSLC(), ҳSLC(), MLC([0,127/255], [0,2048/4096/8192]) ++ //NAND_TYPE_MICRON: 0x5 СҳSLC([0,1],[512]), ҳSLC([0,1],[2048]), MLC([0,1], [2048/4096]) ++ //NAND_TYPE_ST: 0x6 СҳSLC([0,1],[517]), ҳSLC([0],[2048,2053]), MLC([127], [0]) ++ //NAND_TYPE_MICRON_4K 0x7 СҳSLC(), ҳSLC(), MLC([0], [4096 ~ 4096+218]) ++ T_U32 flag;//character bits, 4λʾplaneԣλʾǷҪblock˳дpage ++ //bit31ʾǷcopyback1ʾcopyback ++ //bit30ʾǷֻһplane1ʾֻһplane ++ //bit29ʾǷǰplane1ʾǰplane ++ //bit28ʾǷżplane1ʾżplane ++ ++ //bitΪ˽pageblockַӵĿbit: ++ //bit11ʾblock number per dieǷҪϹToshiba TH58NVG6D2ETA202048 block/die(ʵ2084 block/die) ++ //Ϊ˶һdieblockҪΪ4096 block/dieײ ++ //bit10ʾpage numberǷҪϹTLC192page/blockΪ˶һblockҪΪ256page/block ++ ++ //bit8~9ʾspareСĸλλ256 Bytesspare_sizeΪT_U8Աʾnand400ֽڵspareС ++ //bit4-7ʾECCͣ0Ϊ4 bit/512B1Ϊ8 bit/512B2Ϊ12 bit/512B3Ϊ16 bit/512B4Ϊ24 bit/1024B5Ϊ32 bit/1024B ++ //bit0ʾͬһblockǷҪ˳дpage1ʾҪ˳дnandΪMLC ++ //ע: (bit29bit28)Ϊ'11'ʾchip4planeżҲǰplane ++ ++ T_U32 cmd_len;//nandflash command length ++ T_U32 data_len;//nandflash data length ++ T_U8 des_str[32];//descriptor string ++}T_NAND_PHY_INFO; ++ ++#define ERROR_CHIP_ID 0xFFFFFFFF ++ ++/*@}*/ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/include/net/activity_stats.h b/include/net/activity_stats.h +new file mode 100644 +index 00000000..10e4c150 +--- /dev/null ++++ b/include/net/activity_stats.h +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Author: Mike Chan (mike@android.com) ++ */ ++ ++#ifndef __activity_stats_h ++#define __activity_stats_h ++ ++#ifdef CONFIG_NET_ACTIVITY_STATS ++void activity_stats_update(void); ++#else ++#define activity_stats_update(void) {} ++#endif ++ ++#endif /* _NET_ACTIVITY_STATS_H */ +diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h +index d47e523c..78132a8a 100644 +--- a/include/net/bluetooth/hci.h ++++ b/include/net/bluetooth/hci.h +@@ -174,8 +174,10 @@ enum { + #define ESCO_2EV5 0x0100 + #define ESCO_3EV5 0x0200 + +-#define SCO_ESCO_MASK (ESCO_HV1 | ESCO_HV2 | ESCO_HV3) +-#define EDR_ESCO_MASK (ESCO_2EV3 | ESCO_3EV3 | ESCO_2EV5 | ESCO_3EV5) ++#define SCO_ESCO_MASK (ESCO_HV1 | ESCO_HV2 | ESCO_HV3) ++#define EDR_ESCO_MASK (ESCO_2EV3 | ESCO_3EV3 | ESCO_2EV5 | ESCO_3EV5) ++#define ALL_ESCO_MASK (SCO_ESCO_MASK | ESCO_EV3 | ESCO_EV4 | ESCO_EV5 | \ ++ EDR_ESCO_MASK) + + /* ACL flags */ + #define ACL_START_NO_FLUSH 0x00 +@@ -1392,6 +1394,9 @@ struct hci_conn_info { + __u8 out; + __u16 state; + __u32 link_mode; ++ __u32 mtu; ++ __u32 cnt; ++ __u32 pkts; + }; + + struct hci_dev_req { +diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h +index db1c5df4..392b2cab 100644 +--- a/include/net/bluetooth/hci_core.h ++++ b/include/net/bluetooth/hci_core.h +@@ -561,7 +561,8 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle); + void hci_setup_sync(struct hci_conn *conn, __u16 handle); + void hci_sco_setup(struct hci_conn *conn, __u8 status); + +-struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst); ++struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, ++ __u16 pkt_type, bdaddr_t *dst); + int hci_conn_del(struct hci_conn *conn); + void hci_conn_hash_flush(struct hci_dev *hdev); + void hci_conn_check_pending(struct hci_dev *hdev); +@@ -570,8 +571,9 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn); + int hci_chan_del(struct hci_chan *chan); + void hci_chan_list_flush(struct hci_conn *conn); + +-struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, +- __u8 sec_level, __u8 auth_type); ++struct hci_conn *hci_connect(struct hci_dev *hdev, int type, ++ __u16 pkt_type, bdaddr_t *dst, ++ __u8 sec_level, __u8 auth_type); + int hci_conn_check_link_mode(struct hci_conn *conn); + int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level); + int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type); +@@ -598,7 +600,7 @@ static inline void hci_conn_put(struct hci_conn *conn) + if (conn->state == BT_CONNECTED) { + timeo = msecs_to_jiffies(conn->disc_timeout); + if (!conn->out) +- timeo *= 2; ++ timeo *= 20; + } else { + timeo = msecs_to_jiffies(10); + } +diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h +index 1e35c436..6d1857ab 100644 +--- a/include/net/bluetooth/sco.h ++++ b/include/net/bluetooth/sco.h +@@ -37,6 +37,7 @@ + struct sockaddr_sco { + sa_family_t sco_family; + bdaddr_t sco_bdaddr; ++ __u16 sco_pkt_type; + }; + + /* SCO socket options */ +@@ -72,7 +73,8 @@ struct sco_conn { + + struct sco_pinfo { + struct bt_sock bt; +- __u32 flags; ++ __u16 pkt_type; ++ + struct sco_conn *conn; + }; + +diff --git a/include/net/tcp.h b/include/net/tcp.h +index 2757a115..ed5b0c70 100644 +--- a/include/net/tcp.h ++++ b/include/net/tcp.h +@@ -1456,6 +1456,8 @@ extern struct sk_buff **tcp4_gro_receive(struct sk_buff **head, + extern int tcp_gro_complete(struct sk_buff *skb); + extern int tcp4_gro_complete(struct sk_buff *skb); + ++extern int tcp_nuke_addr(struct net *net, struct sockaddr *addr); ++ + #ifdef CONFIG_PROC_FS + extern int tcp4_proc_init(void); + extern void tcp4_proc_exit(void); +diff --git a/include/plat-anyka/adkey.h b/include/plat-anyka/adkey.h +new file mode 100755 +index 00000000..0e98c1a9 +--- /dev/null ++++ b/include/plat-anyka/adkey.h +@@ -0,0 +1,78 @@ ++/* ++ * include/plat-anyka/adkey.h ++ */ ++ ++#ifndef __ADKEY_H ++#define __ADKEY_H ++ ++#include ++#include "notify.h" ++ ++typedef enum { ++ PLUGIN_NODEV = 0x0, ++ PLUGIN_DEV1 = 0x1, //dev1 ++ PLUGIN_DEV2 = 0x2, //dev2 ++ PLUGIN_DEV3 = 0x4, //dev3 ++ PLUGIN_DEV4 = 0x8, //dev4 ++ PLUGIN_DEV12 = PLUGIN_DEV1|PLUGIN_DEV2, //dev12 ++ PLUGIN_DEV13 = PLUGIN_DEV1|PLUGIN_DEV3, //dev13 ++ PLUGIN_DEV23 = PLUGIN_DEV2|PLUGIN_DEV3, //dev23 ++ PLUGIN_DEV123 = PLUGIN_DEV1|PLUGIN_DEV2|PLUGIN_DEV3, //dev123 ++ ++ PLUGIN_INVALID, ++} T_ad_check; ++ ++ ++#define PLUGIN_MMC PLUGIN_DEV1 //sd card ++#define PLUGIN_SDIO PLUGIN_DEV2 //sdio dev ++#define PLUGIN_AC PLUGIN_DEV3 //ac ++#define PLUGIN_MMC_SDIO PLUGIN_DEV12 //mmc+ac ++#define PLUGIN_MMC_AC PLUGIN_DEV13 //hp+mmc ++#define PLUGIN_SDIO_AC PLUGIN_DEV23 //sdio+ac ++#define PLUGIN_MMC_SDIO_AC PLUGIN_DEV123 //mmc+sdio+ac ++ ++#define ADDETECT_MMC_PLUGIN ADDEDECT_DEV1_PLUGIN ++#define ADDETECT_MMC_PLUGOUT ADDEDECT_DEV1_PLUGOUT ++#define ADDETECT_SDIO_PLUGIN ADDEDECT_DEV2_PLUGIN ++#define ADDETECT_SDIO_PLUGOUT ADDEDECT_DEV2_PLUGOUT ++#define ADDETECT_AC_PLUGIN ADDEDECT_DEV3_PLUGIN ++#define ADDETECT_AC_PLUGOUT ADDEDECT_DEV3_PLUGOUT ++ ++struct adgpio_key { ++ int code; /* Key code */ ++ int min; /* min voltage */ ++ int max; /* max voltage */ ++}; ++ ++struct multi_addetect { ++ int unpress_min; ++ int unpress_max; ++ struct adgpio_key *fixkeys; ++ T_ad_check plugdev; ++}; ++ ++struct analog_gpio_key { ++ char *desc; /* key description */ ++ int interval; /* the value is poll adkey */ ++ int debounce_interval; /* press key delay */ ++ ++ struct multi_addetect *addet; ++ int naddet; ++ int nkey; /* key number */ ++ ++ int chanel; /* AD channel */ ++ int wakeup; /* enable ad-key wakeup from standby */ ++}; ++ ++#if defined(CONFIG_ARCH_AK39) ++static inline void adkey_wakeup_enable(int en) ++{ ++} ++#else ++static inline void adkey_wakeup_enable(int en) ++{ ++} ++#endif ++ ++#endif /* end __ADKEY_H */ ++ +diff --git a/include/plat-anyka/ak_camera.h b/include/plat-anyka/ak_camera.h +new file mode 100755 +index 00000000..cca9f0ce +--- /dev/null ++++ b/include/plat-anyka/ak_camera.h +@@ -0,0 +1,119 @@ ++/* ++ * ak_camera.h - AK camera driver header file ++ * ++ * Copyright (c) 2008, Paulius Zaleckas ++ * Copyright (C) 2009, Darius Augulis ++ * ++ * Based on PXA camera.h file: ++ * Copyright (C) 2003, Intel Corporation ++ * Copyright (C) 2008, Guennadi Liakhovetski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef __ASM_ARCH_CAMERA_H_ ++#define __ASM_ARCH_CAMERA_H_ ++ ++#include ++ ++#define AK_CAM_DRV_NAME "ak_camera" ++#define MAX_VIDEO_MEM 16 ++ ++#if 0 ++#define CSI_BUS_FLAGS (SOCAM_MASTER | SOCAM_HSYNC_ACTIVE_HIGH | \ ++ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | \ ++ SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \ ++ SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW | \ ++ SOCAM_DATAWIDTH_8) ++#endif ++ ++#define CSI_BUS_FLAGS (V4L2_MBUS_MASTER | V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ ++ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | \ ++ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \ ++ V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_LOW) ++ ++#define AK_CAMERA_MASTER 1 ++#define AK_CAMERA_DATAWIDTH_4 2 ++#define AK_CAMERA_DATAWIDTH_5 4 ++#define AK_CAMERA_DATAWIDTH_8 8 ++#define AK_CAMERA_DATAWIDTH_9 0x10 ++#define AK_CAMERA_DATAWIDTH_10 0x20 ++ ++#define AK_CAMERA_VSYNC_HIGH 0x100 ++#define AK_CAMERA_PCLK_RISING 0x200 ++#define AK_CAMERA_DATA_HIGH 0x400 ++ ++/* Image Sensor Command Register */ ++#define CICR_DATA_FMT (0x3 << 0) ++#define CICR_HSCAL_EN (1 << 2) ++#define CICR_VSCAL_EN (1 << 3) ++#define CICR_MODE (1 << 4) ++#define CICR_FULL_RANGE_YUV (1 << 5) ++#define CICR_DATA_FMT_VAL(x) (((x) << 0) & CICR_DATA_FMT) ++ ++ ++/* Personal */ ++ ++ ++/** @{@name IMAGE sensor module register and bit map define ++ */ ++/* image capturing command */ ++#define IMG_CMD 0x0000 ++/* Source/Destination image horizontal length */ ++#define IMG_HINFO1 0x0004 ++/* Horizontal scalling information */ ++#define IMG_HINFO2 0x0008 ++/* Source/Destination image vertical length */ ++#define IMG_VINFO1 0x000C ++/* Horizontal scalling information */ ++#define IMG_VINFO2 0x0010 ++ ++/* DMA starting address of external RAM for Y component of odd frame */ ++#define IMG_YODD 0x0018 ++#define IMG_UODD 0x001c ++#define IMG_VODD 0x0020 ++#define IMG_RGBODD 0x0024 ++#define IMG_YEVE 0x0028 ++#define IMG_UEVE 0x002c ++#define IMG_VEVE 0x0030 ++#define IMG_RGBEVE 0x0034 ++/* Image sensor configuration */ ++#define IMG_CONFIG 0x0040 ++/* Status of the current frame */ ++#define IMG_STATUS 0x0060 ++/* The line number of a frame when JPEG-compressed format */ ++#define IMG_NUM 0x0080 ++/** @} */ ++ ++ ++/** @{@name IMAGE sensor module register and bit map define ++ */ ++/* Multiple function control register */ ++#define MUL_FUN_CTL_REG (AK_VA_SYSCTRL + 0x0058) ++/* Multiple function control register2 */ ++#define MUL_FUN_CTL_REG2 (AK_VA_SYSCTRL + 0x0014) ++/** @} */ ++#define MUL_RESET_CTRL_REG (AK_VA_SYSCTRL + 0x0010) ++ ++struct captureSync{ ++ unsigned long long adcCapture_bytes; ++ struct timeval tv; ++ unsigned int rate; ++ unsigned int frame_bits; ++}; ++ ++ ++/** ++ * struct ak_camera_pdata - AK camera platform data ++ * @mclk: master clock frequency in MHz units ++ * @flags: AK camera platform flags ++ */ ++struct ak_camera_pdata { ++ unsigned long mclk; ++ unsigned long flags; ++}; ++ ++#endif /* __ASM_ARCH_CAMERA_H_ */ ++ +diff --git a/include/plat-anyka/ak_motor.h b/include/plat-anyka/ak_motor.h +new file mode 100644 +index 00000000..b8df1643 +--- /dev/null ++++ b/include/plat-anyka/ak_motor.h +@@ -0,0 +1,55 @@ ++#ifndef __AK_MOTOR_H__ ++#define __AK_MOTOR_H__ ++ ++#ifdef __KERNEL__ ++enum ak_motor_phase { ++ AK_MOTOR_PHASE_A = 0, ++ AK_MOTOR_PHASE_B, ++ AK_MOTOR_PHASE_C, ++ AK_MOTOR_PHASE_D, ++ AK_MOTOR_PHASE_NUM, ++}; ++ ++enum ak_motor_hit { ++ AK_MOTOR_HIT_LEFT = 0, ++ AK_MOTOR_HIT_RIGHT, ++ AK_MOTOR_HIT_NUM, ++}; ++ ++ ++struct ak_motor_plat_data ++{ ++ struct gpio_info gpio_phase[AK_MOTOR_PHASE_NUM]; ++ struct gpio_info gpio_hit[AK_MOTOR_HIT_NUM]; ++ unsigned int irq_hit_type[AK_MOTOR_HIT_NUM]; ++ void (* gpio_init) (const struct gpio_info *); ++ ++ unsigned int angular_speed; ++}; ++#endif ++ ++#define AK_MOTOR_IOC_MAGIC 'm' ++#define AK_MOTOR_SET_ANG_SPEED _IOW(AK_MOTOR_IOC_MAGIC, 11, int) ++#define AK_MOTOR_GET_ANG_SPEED _IOR(AK_MOTOR_IOC_MAGIC, 12, int) ++#define AK_MOTOR_TURN_CLKWISE _IOW(AK_MOTOR_IOC_MAGIC, 13, int) ++#define AK_MOTOR_TURN_ANTICLKWISE _IOW(AK_MOTOR_IOC_MAGIC, 14, int) ++#define AK_MOTOR_GET_HIT_STATUS _IOW(AK_MOTOR_IOC_MAGIC, 15, int) ++#define AK_MOTOR_TURN_STOP _IOW(AK_MOTOR_IOC_MAGIC, 16, int) ++ ++#define AK_MOTOR_EVENT_HIT (1) ++#define AK_MOTOR_EVENT_UNHIT (2) ++#define AK_MOTOR_EVENT_STOP (3) ++ ++#define AK_MOTOR_HITTING_LEFT (1<<0) ++#define AK_MOTOR_HITTING_RIGHT (1<<1) ++ ++#define AK_MOTOR_MIN_SPEED (1) ++#define AK_MOTOR_MAX_SPEED (16) ++struct notify_data ++{ ++ int hit_num; ++ int event; ++ int remain_angle; ++}; ++ ++#endif +diff --git a/include/plat-anyka/akgpios.h b/include/plat-anyka/akgpios.h +new file mode 100755 +index 00000000..ab08cd83 +--- /dev/null ++++ b/include/plat-anyka/akgpios.h +@@ -0,0 +1,40 @@ ++#ifndef __AKAPPLY_GPIO_H ++#define __AKAPPLY_GPIO_H ++ ++ ++#define APPLY_IOC_MAGIC 'f' ++ ++/* ++ Command definitions which are supported by this dirver ++*/ ++#define GET_GPIO_NUM _IOR(APPLY_IOC_MAGIC, 40, __u8) ++#define GET_AVAILABLE_GPIO _IOR(APPLY_IOC_MAGIC, 41, __u8) ++#define SET_GPIO_FUNC _IOW(APPLY_IOC_MAGIC, 42, __u8) ++#define GET_GPIO_VALUE _IOWR(APPLY_IOC_MAGIC, 43, __u8) ++#define SET_GPIO_IRQ _IOW(APPLY_IOC_MAGIC, 44, __u8) ++#define LISTEN_GPIO_IRQ _IOW(APPLY_IOC_MAGIC, 45, __u8) ++#define DELETE_GPIO_IRQ _IOW(APPLY_IOC_MAGIC, 46, __u8) ++#define SET_GPIO_LEVEL _IOW(APPLY_IOC_MAGIC, 47, __u8) ++#define SET_GROUP_GPIO_LEVEL _IOW(APPLY_IOC_MAGIC, 48, __u8) ++ ++ ++#ifdef __KERNEL__ ++ ++struct custom_gpio_data { ++ unsigned int *gpiopin; ++ unsigned int ngpiopin; ++}; ++ ++struct gpio_ginfo { ++ int pin; ++ int value; ++}; ++ ++struct gpio_group_info { ++ struct gpio_ginfo *gpio; ++ int gpio_num; ++}; ++ ++#endif /* end __KERNEL__ */ ++ ++#endif /* end AKAPPLY_GPIO_H */ +diff --git a/include/plat-anyka/akmci.h b/include/plat-anyka/akmci.h +new file mode 100644 +index 00000000..a35ebea5 +--- /dev/null ++++ b/include/plat-anyka/akmci.h +@@ -0,0 +1,225 @@ ++/* ++ * include/plat-anyka/akmci.h - Anyka MMC/SD driver ++ * ++ * Copyright (C) 2010 Anyka, Ltd, All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef __AK_MCI_H__ ++#define __AK_MCI_H__ ++ ++#define MCI_CLK_REG 0x004 ++#define MMC_CLK_DIVL(x) ((x) & 0xff) ++#define MMC_CLK_DIVH(x) (((x) & 0xff) << 8) ++#define MCI_CLK_ENABLE (1 << 16) ++#define MCI_CLK_PWRSAVE (1 << 17) ++#define MCI_FAIL_TRIGGER (1 << 19) ++#define MCI_ENABLE (1 << 20) ++ ++#define MCI_ARGUMENT_REG 0x008 ++#define MCI_COMMAND_REG 0x00c ++#define MCI_CPSM_ENABLE (1 << 0) ++#define MCI_CPSM_CMD(x) (((x) & 0x3f) << 1) ++#define MCI_CPSM_RESPONSE (1 << 7) ++#define MCI_CPSM_LONGRSP (1 << 8) ++#define MCI_CPSM_PENDING (1 << 9) ++#define MCI_CPSM_RSPCRC_NOCHK (1 << 10) ++#define MCI_CPSM_WITHDATA (1 << 11) ++ ++#define MCI_RESPCMD_REG 0x010 ++#define MCI_RESPONSE0_REG 0x014 ++#define MCI_RESPONSE1_REG 0x018 ++#define MCI_RESPONSE2_REG 0x01c ++#define MCI_RESPONSE3_REG 0x020 ++#define MCI_DATATIMER_REG 0x024 ++#define MCI_DATALENGTH_REG 0x028 ++#define MCI_DATACTRL_REG 0x02c ++#define MCI_DPSM_ENABLE (1 << 0) ++#define MCI_DPSM_DIRECTION (1 << 1) ++#define MCI_DPSM_STREAM (1 << 2) ++#define MCI_DPSM_BUSMODE(x) (((x) & 0x3) << 3) ++#define MCI_DPSM_BLOCKSIZE(x) (((x) & 0xfff) << 16) ++ ++#define MCI_DATACNT_REG 0x030 ++#define MCI_STATUS_REG 0x034 ++#define MCI_RESPCRCFAIL (1 << 0) ++#define MCI_DATACRCFAIL (1 << 1) ++#define MCI_RESPTIMEOUT (1 << 2) ++#define MCI_DATATIMEOUT (1 << 3) ++#define MCI_RESPEND (1 << 4) ++#define MCI_CMDSENT (1 << 5) ++#define MCI_DATAEND (1 << 6) ++#define MCI_DATABLOCKEND (1 << 7) ++#define MCI_STARTBIT_ERR (1 << 8) ++#define MCI_CMDACTIVE (1 << 9) ++#define MCI_TXACTIVE (1 << 10) ++#define MCI_RXACTIVE (1 << 11) ++#define MCI_FIFOFULL (1 << 12) ++#define MCI_FIFOEMPTY (1 << 13) ++#define MCI_FIFOHALFFULL (1 << 14) ++#define MCI_FIFOHALFEMPTY (1 << 15) ++#define MCI_DATATRANS_FINISH (1 << 16) ++#define MCI_SDIOINT (1 << 17) ++ ++#define MCI_MASK_REG 0x038 ++#define MCI_RESPCRCFAILMASK (1 << 0) ++#define MCI_DATACRCFAILMASK (1 << 1) ++#define MCI_RESPTIMEOUTMASK (1 << 2) ++#define MCI_DATATIMEOUTMASK (1 << 3) ++#define MCI_RESPENDMASK (1 << 4) ++#define MCI_CMDSENTMASK (1 << 5) ++#define MCI_DATAENDMASK (1 << 6) ++#define MCI_DATABLOCKENDMASK (1 << 7) ++#define MCI_STARTBIT_ERRMASK (1 << 8) ++#define MCI_CMDACTIVEMASK (1 << 9) ++#define MCI_TXACTIVEMASK (1 << 10) ++#define MCI_RXACTIVEMASK (1 << 11) ++#define MCI_FIFOFULLMASK (1 << 12) ++#define MCI_FIFOEMPTYMASK (1 << 13) ++#define MCI_FIFOHALFFULLMASK (1 << 14) ++#define MCI_FIFOHALFEMPTYMASK (1 << 15) ++#define MCI_DATATRANS_FINISHMASK (1 << 16) ++#define MCI_SDIOINTMASK (1 << 17) ++ ++#define MCI_DMACTRL_REG 0x03c ++#define MCI_DMA_BUFEN (1 << 0) ++#define MCI_DMA_ADDR(x) (((x) & 0x7fff) << 1) ++#define MCI_DMA_EN (1 << 16) ++#define MCI_DMA_SIZE(x) (((x) & 0x7fff) << 17) ++ ++#define MCI_FIFO_REG 0x040 ++ ++#define SDIO_INTRCTR_REG 0x000 ++#define SDIO_INTR_CTR_ENABLE (1 << 8) ++#define SDIO_INTR_ENABLE (1 << 17) ++ ++ ++#define MCI_CMDIRQMASKS \ ++ (MCI_CMDSENTMASK|MCI_RESPENDMASK| \ ++ MCI_RESPCRCFAILMASK|MCI_RESPTIMEOUTMASK) ++ ++#define MCI_DATAIRQMASKS \ ++ (MCI_DATAEND|MCI_DATABLOCKENDMASK| \ ++ MCI_DATACRCFAILMASK|MCI_DATATIMEOUTMASK) ++ ++/*| \ ++ MCI_STARTBIT_ERRMASK) ++*/ ++ ++/* ++ * The size of the FIFO in bytes. ++ */ ++#define MCI_FIFOSIZE 4 ++#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2) ++ ++#define NR_SG 16 ++ ++#define L2BASE 0x2002c000 ++#define L2FIFO_DMACONF 0x80 ++#define L2FIFO_CONF1 0x88 ++#define L2FIFO_ASSIGN1 0x90 ++#define L2FIFO_INTEN 0x9c ++ ++#define L2FIFOBASE 0x48000000 ++#define L2ADDR(n) (L2FIFOBASE + 512 * (n)) ++#define MCI_L2FIFO_NUM 2 /* #6 l2fifo */ ++#define MCI_L2FIFO_SIZE 512 ++ ++#define L2DMA_MAX_SIZE (64*255) ++ ++#define L2_DMA_ALIGN (512) ++ ++enum akmci_detect_mode { ++ AKMCI_PLUGIN_ALWAY, ++ AKMCI_DETECT_MODE_GPIO, ++ AKMCI_DETECT_MODE_AD, ++}; ++ ++enum akmci_xfer_mode { ++ AKMCI_XFER_UNKNOWN, ++ AKMCI_XFER_L2DMA, ++ AKMCI_XFER_L2PIO, ++ AKMCI_XFER_INNERPIO, ++ ++}; ++ ++struct ak_mci_platform_data { ++ int irq_cd_type; ++ int cap_highspeed; ++ int detect_mode; ++ int xfer_mode; ++ int mci_mode; ++ u32 max_speed_hz; ++ void (* gpio_init) (const struct gpio_info *); ++ struct gpio_info gpio_cd; /* card detect pin */ ++ struct gpio_info gpio_wp; /* write protect pin */ ++}; ++ ++struct clk; ++ ++#define MAX_STATUS_COUNT (1<<10) ++#define MAX_STATUS_MASK (MAX_STATUS_COUNT - 1) ++ ++ ++#define MCI_MODE_MMC_SD 0 ++#define MCI_MODE_SDIO 1 ++ ++ ++/* the 0x800000 value is reference RTOS platform, ++ * timeout is 419ms */ ++#define TRANS_DATA_TIMEOUT 0x800000 ++ ++#define MAX_MCI_REQ_SIZE (65536) ++/* as l2 fifo limit to 512 bytes */ ++#define MAX_MCI_BLOCK_SIZE (512) ++ ++struct akmci_host { ++ struct platform_device *pdev; ++ struct ak_mci_platform_data *plat; ++ struct mmc_request *mrq; ++ struct mmc_host *mmc; ++ struct mmc_data *data; ++ struct mmc_command *cmd; ++ void __iomem *base; ++ struct resource *mem; ++ struct clk *clk; ++ unsigned long asic_clk; ++ unsigned char bus_mode; ++ unsigned char bus_width; ++ unsigned long bus_clock; ++ unsigned long clkreg; ++ ++ int mci_mode; ++ int irq_mci; ++ int irq_cd; ++ int irq_cd_type; ++ int gpio_cd; ++ int gpio_wp; ++ int detect_mode; ++ int xfer_mode; ++ int data_err_flag; ++ u8 l2buf_id; ++ spinlock_t lock; ++ ++ unsigned int data_xfered; ++ unsigned int sg_len; ++ struct scatterlist *sg_ptr; ++ unsigned int sg_off; ++ unsigned int size; ++ ++ struct timer_list detect_timer; ++ struct notifier_block detect_nb; ++ int plugin_flag; ++ ++#ifdef CONFIG_CPU_FREQ ++ struct notifier_block freq_transition; ++ struct semaphore freq_lock; ++#endif ++}; ++ ++ ++ ++#endif /* end __AK_MCI_H__ */ ++ +diff --git a/include/plat-anyka/aksensor.h b/include/plat-anyka/aksensor.h +new file mode 100755 +index 00000000..c8be7c08 +--- /dev/null ++++ b/include/plat-anyka/aksensor.h +@@ -0,0 +1,92 @@ ++/* gc0308 Camera ++ * ++ * Copyright (C) 2008 Renesas Solutions Corp. ++ * Kuninori Morimoto ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef __AKSENSOR_H__ ++#define __AKSENSOR_H__ ++ ++#include ++#include ++ ++s32 aksensor_i2c_write_byte_data(u8 daddr, u8 raddr, u8 *data, u32 size); ++s32 aksensor_i2c_read_byte_data(u8 daddr, u8 raddr); ++ ++s32 aksensor_i2c_write_byte_short(u8 daddr, u16 raddr, u8 *data, u32 size); ++s32 aksensor_i2c_read_byte_short(u8 daddr, u16 raddr); ++ ++s32 aksensor_i2c_write_word_data(u8 daddr, u16 raddr, u16 *data, u32 size); ++s32 aksensor_i2c_read_word_data(u8 daddr, u16 raddr); ++ ++void aksensor_set_param(u32 cmd, u32 data); ++ ++ ++/* ++ * for Edge ctrl ++ * ++ * strength also control Auto or Manual Edge Control Mode ++ */ ++struct aksensor_edge_ctrl { ++ unsigned char strength; ++ unsigned char threshold; ++ unsigned char upper; ++ unsigned char lower; ++}; ++ ++/* ++ * aksensor camera info ++ */ ++struct aksensor_camera_info { ++ unsigned long buswidth; ++ unsigned long flags; ++ unsigned long pin_avdd; ++ unsigned long pin_power; ++ unsigned long pin_reset; ++ struct soc_camera_link link; ++ struct aksensor_edge_ctrl edgectrl; ++}; ++ ++struct aksensor_color_format { ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_colorspace colorspace; ++}; ++ ++ ++struct aksensor_win_size { ++ char *name; ++ __u32 width; ++ __u32 height; ++}; ++ ++struct sensor_info ++{ ++ const char *sensor_name; ++ int sensor_id; ++ const struct v4l2_ctrl_config *ctrls; ++ int nr_ctrls; ++ const struct aksensor_color_format *formats; ++ int num_formats; ++ const struct aksensor_win_size *resolution; ++ int num_resolution; ++ T_CAMERA_FUNCTION_HANDLER *handler; ++}; ++ ++#define SENSOR_MAX_SUPPORT 20 ++ ++/** ++ * @brief register a camera device, which will be probed at camera open ++ * @author dengzhou/wudaochao ++ * @date 2012-11-06 ++ * @param[in] struct sensor_info ++ * @param[in] handler camera interface ++ * @retval 0 register successfully ++ * @retval -1 register unsuccessfully ++ */ ++int register_sensor(struct sensor_info *si); ++#endif /* END __AKSENSOR_H__ */ ++ +diff --git a/include/plat-anyka/anyka_types.h b/include/plat-anyka/anyka_types.h +new file mode 100644 +index 00000000..e2d3d1fb +--- /dev/null ++++ b/include/plat-anyka/anyka_types.h +@@ -0,0 +1,70 @@ ++/** @file ++ * @brief Define the register operator for system ++ * ++ * Copyright (C) 2006 Anyka (GuangZhou) Software Technology Co., Ltd. ++ * @author ++ * @date 2006-01-16 ++ * @version 1.0 ++ */ ++ ++#ifndef _ANYKA_TYPES_H_ ++#define _ANYKA_TYPES_H_ ++ ++/** @defgroup ANYKA_CPU ++ * @ingroup M3PLATFORM ++ */ ++/*@{*/ ++ ++/* preliminary type definition for global area */ ++typedef unsigned char T_U8; /* unsigned 8 bit integer */ ++typedef unsigned short T_U16; /* unsigned 16 bit integer */ ++typedef unsigned long T_U32; /* unsigned 32 bit integer */ ++typedef signed char T_S8; /* signed 8 bit integer */ ++typedef signed short T_S16; /* signed 16 bit integer */ ++typedef signed long T_S32; /* signed 32 bit integer */ ++typedef void T_VOID; /* void */ ++ ++#define T_U8_MAX ((T_U8)0xff) // maximum T_U8 value ++#define T_U16_MAX ((T_U16)0xffff) // maximum T_U16 value ++#define T_U32_MAX ((T_U32)0xffffffff) // maximum T_U32 value ++#define T_S8_MIN ((T_S8)(-127-1)) // minimum T_S8 value ++#define T_S8_MAX ((T_S8)127) // maximum T_S8 value ++#define T_S16_MIN ((T_S16)(-32767L-1L)) // minimum T_S16 value ++#define T_S16_MAX ((T_S16)(32767L)) // maximum T_S16 value ++#define T_S32_MIN ((T_S32)(-2147483647L-1L)) // minimum T_S32 value ++#define T_S32_MAX ((T_S32)(2147483647L)) // maximum T_S32 value ++ ++/* basal type definition for global area */ ++typedef T_S8 T_CHR; /* char */ ++typedef T_U8 T_BOOL; /* BOOL type */ ++ ++typedef T_VOID * T_pVOID; /* pointer of void data */ ++typedef const T_VOID * T_pCVOID; /* const pointer of void data */ ++ ++typedef T_S8 * T_pSTR; /* pointer of string */ ++typedef const T_S8 * T_pCSTR; /* const pointer of string */ ++ ++ ++typedef T_U16 T_WCHR; /**< unicode char */ ++typedef T_U16 * T_pWSTR; /* pointer of unicode string */ ++typedef const T_U16 * T_pCWSTR; /* const pointer of unicode string */ ++ ++ ++typedef T_U8 * T_pDATA; /* pointer of data */ ++typedef const T_U8 * T_pCDATA; /* const pointer of data */ ++ ++typedef T_U32 T_COLOR; /* color value */ ++ ++typedef T_U32 T_HANDLE; /* a handle */ ++ ++#define AK_FALSE 0 ++#define AK_TRUE 1 ++#undef AK_NULL ++#define AK_NULL ((T_pVOID)(0)) ++ ++#define AK_EMPTY ++/*@}*/ ++ ++ ++#endif // _ANYKA_TYPES_H_ ++ +diff --git a/include/plat-anyka/bat.h b/include/plat-anyka/bat.h +new file mode 100644 +index 00000000..3fbdb6de +--- /dev/null ++++ b/include/plat-anyka/bat.h +@@ -0,0 +1,85 @@ ++#ifndef __AK_BAT_H_ ++#define __AK_BAT_H_ __FILE__ ++ ++#include ++#include ++#include ++#include ++ ++#define poweroff_enable 1 ++#define poweroff_disable 0 ++ ++#define BAT_CHARGE_ADC_DETECT 0 ++#define BAT_CHARGE_GPIO_DETECT 1 ++ ++struct bat_gpio{ ++ int is_detect_mode; /* ac detect mode: ADC or GPIO */ ++ int active; /* active value */ ++ int irq; /* use for ac in or out irq */ ++ int delay; /* delay irq handler */ ++ struct gpio_info pindata; /* battery gpios info */ ++ ++}; ++ ++struct bat_info{ ++ int charge_full_pin; /* use for juge if battery charge full */ ++ int power_on_voltage; /* if (voltage<=power_on_voltage), disable power on */ ++ int power_on_correct; /* correct power on voltage */ ++ int charge_min_voltage; /* charge minute voltage */ ++ int max_voltage; /* max battery voltage */ ++ int min_voltage; /* min battery voltage */ ++ int power_off; /* enable or disable machine power off in battery driver */ ++ int full_capacity; ++ int voltage_sample; ++ int poweroff_cap; ++ int low_cap; ++ int recover_cap; ++ int cpower_on_voltage; /* if (voltage<=cpower_on_voltage in charge), disable power on */ ++ unsigned int full_delay;/* time to up to full capacity, unit: minute */ ++ int full_voltage; /* if voltage > full_voltage in full capacity, when in discharge,display full */ ++}; ++ ++struct read_voltage_sample{ ++ int index; // voltage[6] index ++ int max; // sample max voltage ++ int min; // sample min voltage ++ int sum; // sum of read voltage ++ int sample; /* read how many voltge sample */ ++ int design_max; ++}; ++ ++ ++struct bat_ad4{ ++ int up_resistance; /* read ad4 voltage resistance */ ++ int dw_resistance; ++ int voltage_correct; /* correct voltage from adc1-4*/ ++ int adc_avdd; /* adc avdd */ ++}; ++ ++struct ak_bat_mach_info { ++ ++ void (* gpio_init) (const struct gpio_info *); ++ struct bat_gpio usb_gpio; ++ struct bat_gpio ac_gpio; ++ struct bat_gpio full_gpio; ++ struct bat_info bat_mach_info; ++ struct bat_ad4 bat_adc; ++}; ++ ++ ++int ak_bat_read_voltage(struct ak_bat_mach_info *info); ++ ++#ifdef CONFIG_STARTUP_CHECK_VOLTAGE ++void ak_mach_check_bat_vol(struct ak_bat_mach_info *batinfo, ++ struct ak_gpio_keys_button *pwr_gpio, int len); ++#else ++static inline void ak_mach_check_bat_vol(struct ak_bat_mach_info *batinfo, ++ struct ak_gpio_keys_button *pwr_gpio, int len) ++{ ++} ++#endif ++ ++ ++#endif /* __AK_BAT_H_ */ ++ ++ +diff --git a/include/plat-anyka/cam_com_sensor.h b/include/plat-anyka/cam_com_sensor.h +new file mode 100755 +index 00000000..2e2c8d69 +--- /dev/null ++++ b/include/plat-anyka/cam_com_sensor.h +@@ -0,0 +1,259 @@ ++#ifndef __CAM_COM_SENSOR__ ++#define __CAM_COM_SENSOR__ ++ ++#ifdef CONFIG_LINUX_AKSENSOR ++#include ++#endif ++ ++#define M_DRVSYS "DRVLIB" /* module name */ ++ ++#define C1 1 /*Fatal error message*/ ++#define C2 2 /*Error message*/ ++#define C3 3 /*Common message*/ ++ ++typedef enum { ++ CAMERA_WMODE_PREV = 0, //Ԥģʽ ++ CAMERA_WMODE_CAP, // ģʽ ++ CAMERA_WMODE_REC // ¼ģʽ ++} T_CAMERA_WORKMODE; ++ ++typedef enum { ++ PIN_AVDD = 0, //ȡAVDD GPIO ֵ ++ PIN_POWER, //ȡPOWER GPIO ֵ ++ PIN_RESET //ȡRESET GPIO ֵ ++} T_CAMERA_PINTYPE; ++ ++/** @brief Camera type definition ++ * ++ * This structure define the type of camera ++ */ ++typedef enum ++{ ++ CAMERA_P3M = 0x00000001, ++ CAMERA_1P3M = 0x00000002, ++ CAMERA_2M = 0x00000004, ++ CAMERA_3M = 0x00000008, ++ CAMERA_4M = 0x00000010, ++ CAMERA_5M = 0x00000020, ++ CAMERA_ZOOM = 0x00000040 ++}T_CAMERA_TYPE; ++ ++/** @brief Camera Parameter Night Mode definition ++ * ++ * This structure define the value of parameter Night Mode ++ */ ++typedef enum ++{ ++ CAMERA_DAY_MODE, ++ CAMERA_NIGHT_MODE, ++ CAMERA_NIGHT_NUM ++}T_NIGHT_MODE; ++ ++typedef enum { ++ CAMERA_MODE_QXGA = 0, // 2048 X 1536 ++ CAMERA_MODE_UXGA, // 1600 X 1200 ++ CAMERA_MODE_SXGA, // 1280 X 1024 ++ CAMERA_MODE_XGA, // 1024 X 768 ++ CAMERA_MODE_SVGA, // 800 X 600 ++ CAMERA_MODE_VGA, // 640 X 480 ++ CAMERA_MODE_QSVGA, // 400 X 300 ++ CAMERA_MODE_CIF, // 352 X 288 ++ CAMERA_MODE_QVGA, // 320 X 240 ++ CAMERA_MODE_QCIF, // 176 X 144 ++ CAMERA_MODE_QQVGA, // 160 X 120 ++ CAMERA_MODE_PREV, // 640 X 480 ++ CAMERA_MODE_REC, // 352 X 288 ++ CAMERA_MODE_720P, // 1280 X 720 ++ CAMERA_MODE_800P, // 1280 X 800 ++ CAMERA_MODE_960P, // 1280 X 960 ++ CAMERA_MODE_D1, // 720 X 480 ++ CAMERA_MODE_QSXGA, //2592X1944 ++ CAMERA_MODE_QXGA_JPEG, //2048X1536 jpeg ++ CAMERA_MODE_QSXGA_JPEG, //2592X1944 jpeg ++ CAMERA_MODE_VGA_JPEG, // 640 X 480 jpeg ++ CAMERA_MODE_NUM ++} T_CAMERA_MODE; ++ ++/** @brief Camera Parameter Exposure definition ++ * ++ * This structure define the value of parameter Exposure ++ */ ++typedef enum ++{ ++ EXPOSURE_WHOLE = 0, ++ EXPOSURE_CENTER, ++ EXPOSURE_MIDDLE, ++ CAMERA_EXPOSURE_NUM ++}T_CAMERA_EXPOSURE; ++ ++/** @brief Camera Parameter Brightness definition ++ * ++ * This structure define the value of parameter Brightness ++ */ ++typedef enum ++{ ++ CAMERA_BRIGHTNESS_0 = 0, ++ CAMERA_BRIGHTNESS_1, ++ CAMERA_BRIGHTNESS_2, ++ CAMERA_BRIGHTNESS_3, ++ CAMERA_BRIGHTNESS_4, ++ CAMERA_BRIGHTNESS_5, ++ CAMERA_BRIGHTNESS_6, ++ CAMERA_BRIGHTNESS_NUM ++}T_CAMERA_BRIGHTNESS; ++ ++/** @brief Camera Parameter Contrast definition ++ * ++ * This structure define the value of parameter Contrast ++ */ ++typedef enum ++{ ++ CAMERA_CONTRAST_1 = 0, ++ CAMERA_CONTRAST_2, ++ CAMERA_CONTRAST_3, ++ CAMERA_CONTRAST_4, ++ CAMERA_CONTRAST_5, ++ CAMERA_CONTRAST_6, ++ CAMERA_CONTRAST_7, ++ CAMERA_CONTRAST_NUM ++}T_CAMERA_CONTRAST; ++ ++/** @brief Camera Parameter Saturation definition ++ * ++ * This structure define the value of parameter Saturation ++ */ ++typedef enum ++{ ++ CAMERA_SATURATION_0 = 0, ++ CAMERA_SATURATION_1, ++ CAMERA_SATURATION_2, ++ CAMERA_SATURATION_3, ++ CAMERA_SATURATION_4, ++ CAMERA_SATURATION_5, ++ CAMERA_SATURATION_6, ++ CAMERA_SATURATION_NUM ++}T_CAMERA_SATURATION; ++ ++/** @brief Camera Parameter Sharpness definition ++ * ++ * This structure define the value of parameter Sharpness ++ */ ++typedef enum ++{ ++ CAMERA_SHARPNESS_0 = 0, ++ CAMERA_SHARPNESS_1, ++ CAMERA_SHARPNESS_2, ++ CAMERA_SHARPNESS_3, ++ CAMERA_SHARPNESS_4, ++ CAMERA_SHARPNESS_5, ++ CAMERA_SHARPNESS_6, ++ CAMERA_SHARPNESS_NUM ++}T_CAMERA_SHARPNESS; ++ ++/** @brief Camera Parameter AWB definition ++ * ++ * This structure define the value of parameter AWB ++ */ ++typedef enum ++{ ++ AWB_AUTO = 0, ++ AWB_SUNNY, ++ AWB_CLOUDY, ++ AWB_OFFICE, ++ AWB_HOME, ++ AWB_NIGHT, ++ AWB_NUM ++}T_CAMERA_AWB; ++ ++/** @brief Camera Parameter Mirror definition ++ * ++ * This structure define the value of parameter Mirror ++ */ ++typedef enum ++{ ++ CAMERA_MIRROR_V = 0, ++ CAMERA_MIRROR_H, ++ CAMERA_MIRROR_NORMAL, ++ CAMERA_MIRROR_FLIP, ++ CAMERA_MIRROR_NUM ++}T_CAMERA_MIRROR; ++ ++/** @brief Camera Parameter Effect definition ++ * ++ * This structure define the value of parameter Effect ++ */ ++typedef enum ++{ ++ CAMERA_EFFECT_NORMAL = 0, ++ CAMERA_EFFECT_SEPIA, ++ CAMERA_EFFECT_ANTIQUE, ++ CAMERA_EFFECT_BLUE, ++ CAMERA_EFFECT_GREEN, ++ CAMERA_EFFECT_RED, ++ CAMERA_EFFECT_NEGATIVE, ++ CAMERA_EFFECT_BW, ++ CAMERA_EFFECT_BWN, ++ CAMERA_EFFECT_AQUA, // PO1200 additional mode add by Liub 20060918 ++ CAMERA_EFFECT_COOL, ++ CAMERA_EFFECT_WARM, ++ CAMERA_EFFECT_NUM ++}T_CAMERA_EFFECT; ++ ++ ++/** @brief Camera Parameter CCIR601/656 protocol ++ * ++ * This structure define the CMOS sensor compatible with CCIR601 or CCIR656 protocol ++ */ ++typedef enum ++{ ++ CAMERA_CCIR_601, ++ CAMERA_CCIR_656, ++ CAMERA_CCIR_NUM ++}T_CAMERA_INTERFACE; ++ ++/** @brief Camera feature definition ++ * ++ * This structure define the feature list of camera ++ */ ++typedef enum { ++ CAM_FEATURE_NIGHT_MODE = 0, ++ CAM_FEATURE_EXPOSURE, ++ CAM_FEATURE_AWB, ++ CAM_FEATURE_BRIGHTNESS, ++ CAM_FEATURE_CONTRAST, ++ CAM_FEATURE_SATURATION, ++ CAM_FEATURE_SHARPNESS, ++ CAM_FEATURE_MIRROR, ++ CAM_FEATURE_EFFECT, ++ CAM_FEATURE_NUM ++}T_CAMERA_FEATURE; ++ ++ ++typedef struct ++{ ++ T_U32 cam_mclk; ++ T_VOID (*cam_open_func)(T_VOID); ++ T_BOOL (*cam_close_func)(T_VOID); ++ T_U32 (*cam_read_id_func)(T_VOID); ++ T_BOOL (*cam_init_func)(T_VOID); ++ T_VOID (*cam_set_mode_func)(T_CAMERA_MODE mode); ++ T_VOID (*cam_set_exposure_func)(T_CAMERA_EXPOSURE exposure); ++ T_VOID (*cam_set_brightness_func)(T_CAMERA_BRIGHTNESS brightness); ++ T_VOID (*cam_set_contrast_func)(T_CAMERA_CONTRAST contrast); ++ T_VOID (*cam_set_saturation_func)(T_CAMERA_SATURATION saturation); ++ T_VOID (*cam_set_sharpness_func)(T_CAMERA_SHARPNESS sharpness); ++ T_VOID (*cam_set_AWB_func)(T_CAMERA_AWB awb); ++ T_VOID (*cam_set_mirror_func)(T_CAMERA_MIRROR mirror); ++ T_VOID (*cam_set_effect_func)(T_CAMERA_EFFECT effect); ++ T_S32 (*cam_set_window_func)(T_U32 srcWidth, T_U32 srcHeight); ++ T_VOID (*cam_set_night_mode_func)(T_NIGHT_MODE mode); ++ T_U32 (*cam_set_framerate_func)(float framerate); ++ T_VOID (*cam_set_anti_flicker_func)(T_U32 value); ++ T_BOOL (*cam_set_to_cap_func)(T_U32 srcWidth, T_U32 srcHeight); ++ T_BOOL (*cam_set_to_prev_func)(T_U32 srcWidth, T_U32 srcHeight); ++ T_BOOL (*cam_set_to_record_func)(T_U32 srcWidth, T_U32 srcHeight); ++ T_CAMERA_TYPE (*cam_get_type)(T_VOID); ++ T_VOID (*cam_set_sensor_param_func)(T_U32 cmd, T_U32 data); ++}T_CAMERA_FUNCTION_HANDLER; ++#endif ++ +diff --git a/include/plat-anyka/drv_module_lock.h b/include/plat-anyka/drv_module_lock.h +new file mode 100755 +index 00000000..9f83886c +--- /dev/null ++++ b/include/plat-anyka/drv_module_lock.h +@@ -0,0 +1,143 @@ ++#ifndef __DRV_MODULE_LOCK__ ++#define __DRV_MODULE_LOCK__ ++ ++#include ++ ++/** ++ * @brief driver module define. ++ * define all the moudle ++ */ ++typedef enum ++{ ++ DRV_MODULE_AD = 0, ++ DRV_MODULE_DA, ++ DRV_MODULE_I2S, ++ DRV_MODULE_UART, ++ DRV_MODULE_CAMERA, ++ DRV_MODULE_DETECT, ++ DRV_MODULE_RTC, ++ DRV_MODULE_KEYPAD, ++ DRV_MODULE_VTIMER, ++ DRV_MODULE_TOUCH_SCREEN, ++ ++ DRV_MODULE_UVC, ++ DRV_MODULE_USB_DISK, ++ DRV_MODULE_USB_CDC, ++ DRV_MODULE_USB_CAMERA, ++ DRV_MODULE_USB_ANYKA, ++ DRV_MODULE_USB_CMMB, ++ ++ DRV_MODULE_UDISK_HOST, ++ DRV_MODULE_UVC_HOST, ++ ++ DRV_MODULE_USB_BUS, ++ ++ DRV_MODULE_FREQ, ++ DRV_MODULE_HTIMER, ++ DRV_MODULE_LCD_RGB, ++ DRV_MODULE_LCD_MPU, ++ DRV_MODULE_NAND, ++ DRV_MODULE_SDMMC, ++ DRV_MODULE_SPI, ++ DRV_MODULE_SDIO, ++ DRV_MODULE_I2C, ++ ++ DRV_MODULE_GPIO_EDGE, ++ ++ DRV_MODULE_JPEG_CODEC ++} E_DRV_MODULE; ++ ++typedef enum { ++ AK_MODULE_LOCK_1, ++ AK_MODULE_LOCK_2, ++ AK_MODULE_LOCK_3, ++ AK_MODULE_LOCK_4, ++ AK_MODULE_LOCK_5, ++ AK_MODULE_LOCK_6, ++ AK_MODULE_LOCK_7, ++ AK_MODULE_LOCK_8, ++ AK_MODULE_LOCK_9, ++ AK_MODULE_LOCK_10, ++ AK_MODULE_COUNT, ++} E_LOCK_NAME; ++ ++typedef enum { ++ TYPE_LOCK_SEMAPHORE, ++ TYPE_LOCK_MUTEX, ++ TYPE_LOCK_SPINLOCK, ++ TYPE_LOCK_SPINLOCK_IRQ ++} E_LOCK_TYPE; ++ ++struct ak_drv_module_lock { ++ E_DRV_MODULE drv_mod_name; ++ E_LOCK_NAME lock_name; ++ E_LOCK_TYPE lock_type; ++ unsigned long flags; ++ T_GPIO_SHAREPIN_CFG sharepin_cfg; ++}; ++ ++struct ak_drv_sharepin_lock { ++ E_DRV_MODULE drv_mod_name; ++ E_LOCK_NAME lock_name; ++ E_LOCK_TYPE lock_type; ++ unsigned long flags; ++ void (*config_share_pin)(void); ++}; ++ ++ ++/** ++ * function: acquire lock. protect driver module to do normally. ++ * as GPIOs are part of drivers module. ++ * param: [in]driver module name ++ * ret: void ++ */ ++int ak_drv_module_lock(E_DRV_MODULE drv_mod_name); ++ ++/** ++ * function: release lock. protect driver module to do normally. ++ * as GPIOs are part of drivers module. ++ * param: [in]driver module name ++ * ret: void ++ */ ++void ak_drv_module_unlock(E_DRV_MODULE drv_mod_name); ++ ++/** ++ * function: acquire lock. protect driver module to do normally. ++ * as the GPIO is defined only at board(name is product also). ++ * param: [in]driver module name ++ * ret: ++ * 0: if acquire lock successed; ++ * <0: indicate the driver is not need lock ++ */ ++int ak_drv_sharepin_lock(E_DRV_MODULE drv_mod_name); ++ ++/** ++ * function: release lock. protect driver module to do normally. ++ * as the GPIO is defined only at board(name is product also). ++ * param: [in]driver module name ++ * ret: ++ * 0: if acquire lock successed; ++ * <0: indicate the driver is not need lock ++ */ ++void ak_drv_sharepin_unlock(E_DRV_MODULE drv_mod_name); ++ ++/** ++ * function: acquire lock. protect driver module to do normally. ++ * param: [in]driver module name ++ * ret: void ++ */ ++void ak_drv_module_protect(E_DRV_MODULE drv_mod_name); ++ ++/** ++ * function: release lock. protect driver module to do normally. ++ * param: [in]driver module name ++ * ret: void ++ */ ++void ak_drv_module_unprotect(E_DRV_MODULE drv_mod_name); ++ ++void ak_set_sharepin_lock_table( ++ struct ak_drv_sharepin_lock *shpin_lock_table, int count, void **lock_array); ++ ++ ++#endif /* __DRV_MODULE_LOCK__ */ ++ +diff --git a/include/plat-anyka/fha_asa.h b/include/plat-anyka/fha_asa.h +new file mode 100644 +index 00000000..5a30deaa +--- /dev/null ++++ b/include/plat-anyka/fha_asa.h +@@ -0,0 +1,81 @@ ++#ifndef _FHA_ASA_H_ ++#define _FHA_ASA_H_ ++ ++#define ASA_FILE_FAIL 0 ++#define ASA_FILE_SUCCESS 1 ++#define ASA_FILE_EXIST 2 ++#define ASA_MODE_OPEN 0 ++#define ASA_MODE_CREATE 1 ++ ++#define ASA_FORMAT_NORMAL 0 ++#define ASA_FORMAT_EWR 1 ++#define ASA_FORMAT_RESTORE 2 ++ ++/************************************************************************ ++ * NAME: FHA_asa_scan ++ * FUNCTION scan nand flash security area ++ * PARAM: [in] bMount -- if buffer is enough, set AK_TURE, scan speedup ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_asa_scan(T_BOOL bMount); ++ ++/************************************************************************ ++ * NAME: FHA_asa_format ++ * FUNCTION nand flash format security area ++ * PARAM: [in] type -- ASA_FORMAT_NORMAL: only scan initial bad blocks ++ * ASA_FORMAT_EWR: Erase-wirte-read test ++ * ASA_FORMAT_RESTORE: do nothing ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_asa_format(T_U32 type); ++ ++/************************************************************************ ++ * NAME: FHA_set_bad_block ++ * FUNCTION set nand flash bad block ++ * PARAM: [in] block -- the bad block item ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_set_bad_block(T_U32 block); ++ ++/************************************************************************ ++ * NAME: FHA_check_bad_block ++ * FUNCTION check nand flash bad block ++ * PARAM: [in] block -- need to check block item ++ * RETURN: is bad block return FHA_SUCCESS, or else retuen FHA_ FAIL ++**************************************************************************/ ++T_BOOL FHA_check_bad_block(T_U32 block); ++ ++/************************************************************************ ++ * NAME: FHA_get_bad_block ++ * FUNCTION get bad block information of one or more blocks ++ * PARAM: [in] start_block -- start block ++ * [out]pData -------- buffer used to store bad blocks information data ++ * [in] blk_cnt ------ how many blocks you want to know ++ * RETURN: success return FHA_SUCCESS, fail retuen FHA_ FAIL ++**************************************************************************/ ++T_U32 FHA_get_bad_block(T_U32 start_block, T_U8 *pData, T_U32 blk_cnt); ++ ++/************************************************************************ ++ * NAME: FHA_asa_write_file ++ * FUNCTION write important infomation to security area, data_len can't too large ++ * PARAM: [in] file_name -- asa file name ++ * [in] pData ------ buffer used to store information data ++ * [in] data_len --- need to store data length ++ * [in] mode-------- operation mode ++ * ASA_MODE_OPEN------open ++ * ASA_MODE_CREATE----create ++ * RETURN: success return ASA_FILE_SUCCESS, fail retuen ASA_FILE_FAIL ++**************************************************************************/ ++T_U32 FHA_asa_write_file(T_U8 file_name[], const T_U8 *pData, T_U32 data_len, T_U8 mode); ++ ++/************************************************************************ ++ * NAME: FHA_asa_read_file ++ * FUNCTION get infomation from security area by input file name ++ * PARAM: [in] file_name -- asa file name ++ * [out]pData ------ buffer used to store information data ++ * [in] data_len --- need to get data length ++ * RETURN: success return ASA_FILE_SUCCESS, fail retuen ASA_FILE_FAIL ++**************************************************************************/ ++T_U32 FHA_asa_read_file(T_U8 file_name[], T_U8 *pData, T_U32 data_len); ++#endif // ++ +diff --git a/include/plat-anyka/gpio_keys.h b/include/plat-anyka/gpio_keys.h +new file mode 100755 +index 00000000..0da5c6e2 +--- /dev/null ++++ b/include/plat-anyka/gpio_keys.h +@@ -0,0 +1,36 @@ ++#ifndef _GPIO_KEYS_H ++#define _GPIO_KEYS_H ++ ++struct device; ++ ++struct gpio_keys_button { ++ /* Configuration parameters */ ++ unsigned int code; /* input event code (KEY_*, SW_*) */ ++ int gpio; /* -1 if this key does not support gpio */ ++ int active_low; ++ const char *desc; ++ unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */ ++ int wakeup; /* configure the button as a wake-up source */ ++ int debounce_interval; /* debounce ticks interval in msecs */ ++ bool can_disable; ++ int value; /* axis value for EV_ABS */ ++ unsigned int irq; /* Irq number in case of interrupt keys */ ++ ++ char pulldown; //pulldown function flag ++ char pullup; //pullup function flag ++ char dir; //direction input/output ++ char int_pol; //interrupt polarity ++}; ++ ++struct akgpio_keys_platform_data { ++ struct gpio_keys_button *buttons; ++ int nbuttons; ++ unsigned int poll_interval; /* polling interval in msecs - ++ for polling driver only */ ++ unsigned int rep:1; /* enable input subsystem auto repeat */ ++ int (*enable)(struct device *dev); ++ void (*disable)(struct device *dev); ++ const char *name; /* input device name */ ++}; ++ ++#endif +diff --git a/include/plat-anyka/isp_interface.h b/include/plat-anyka/isp_interface.h +new file mode 100755 +index 00000000..52fadbb1 +--- /dev/null ++++ b/include/plat-anyka/isp_interface.h +@@ -0,0 +1,246 @@ ++#ifndef _ISP_INTERFACE_H ++#define _ISP_INTERFACE_H ++ ++#define ISP_PARM_MODE 0x01 ++#define ISP_PARM_CHANNEL2 0x02 ++#define ISP_PARM_OSD 0x04 ++#define ISP_PARM_OCCLUSION 0x08 ++#define ISP_PARM_OCCLUSION_COLOR 0x10 ++#define ISP_PARM_ZOOM 0x20 ++ ++enum isp_working_mode { ++ ISP_JPEG_MODE, // JPEG compression frame mode ++ ISP_JPEG_VIDEO, // JPEG video frame mode ++ ISP_YUV_OUT, // YUV single frame mode. is not support minor channel ++ ISP_YUV_BYPASS, // YUV single frame mode and bypass ++ ISP_YUV_MERGER_OUT, ++ ISP_YUV_BIG, // YUV single frame and big image mode ++ ISP_YUV_VIDEO_OUT, // YUV video frame mode ++ ISP_YUV_VIDEO_BYPASS, // YUV video frame mode and bypass ++ ISP_YUV_VIDEO_MERGER_OUT, ++ ISP_RGB_OUT, // RGB single frame mode ++ ISP_RGB_VIDEO_OUT, // RGB video frame mode ++ ISP_RGB_BIG, // RGB single frame and big image mode ++}; ++ ++enum isp_mode_class { ++ ISP_RGB_CLASS, ++ ISP_YUV_CLASS, ++ ISP_JPEG_CLASS, ++}; ++ ++struct isp_channel2_info { ++ int type; ++ int width; ++ int height; ++ int enable; ++}; ++ ++struct isp_mode_info { ++ int type; ++ enum isp_working_mode mode; ++}; ++ ++struct isp_osd_info { ++ int type; ++ int channel; ++ int color_depth; ++ int color_transparency; ++ int start_xpos; ++ int end_xpos; ++ int start_ypos; ++ int end_ypos; ++ int enable; ++ unsigned long phys_addr; ++ unsigned long virt_addr; ++}; ++ ++struct isp_occlusion_info { ++ int type; ++ int channel; ++ int number; ++ int start_xpos; ++ int end_xpos; ++ int start_ypos; ++ int end_ypos; ++ int enable; ++}; ++ ++struct isp_occlusion_color { ++ int type; ++ int color_type; ++ int transparency; ++ int y_component; ++ int u_component; ++ int v_component; ++}; ++ ++struct isp_zoom_info { ++ int type; ++ int channel; // value: 1 indicate isp master channel, 2 indicate the second channel ++ int cut_xpos; ++ int cut_ypos; ++ int cut_width; ++ int cut_height; ++ int out_width; ++ int out_height; ++}; ++ ++ ++/* response pc tool control command */ ++#define ISP_CID_BLACK_BALANCE 0x60 //black balance ++#define ISP_CID_LENS 0x61 //lens correct ++#define ISP_CID_DEMOSAIC 0x62 //demosaic ++#define ISP_CID_RGB_FILTER 0x63 //RGB filter, noise reduce ++#define ISP_CID_UV_FILTER 0x64 //uv filter ++#define ISP_CID_DEFECT_PIXEL 0x65 //bad color correct ++#define ISP_CID_WHITE_BALANCE 0x66 //white balance ++#define ISP_CID_AUTO_WHITE_BALANCE 0x67 //auto white balance ++#define ISP_CID_COLOR 0x68 //color correct ++#define ISP_CID_GAMMA 0x69 //gamma calculate ++#define ISP_CID_BRIGHTNESS_ENHANCE 0x70 //brightness edge enhancement ++#define ISP_CID_SATURATION 0x72 //saturation ++#define ISP_CID_HISTOGRAM 0x73 //histogram ++#define ISP_CID_SPECIAL_EFFECT 0x74 //special effect ++#define ISP_CID_SET_SENSOR_PARAM 0x75 //set sensor parameter ++ ++/* response pc tool control command structure define */ ++struct isp_black_balance { ++ int type; ++ int enable; ++ unsigned int r_offset; //register table 0x20: offset register 13 [31:22] ++ unsigned int g_offset; //register table 0x20: offset register 14 [31:22] ++ unsigned int b_offset; //register table 0x20: offset register 15 [31:22] ++}; ++ ++struct isp_lens_correct { ++ int type; ++ int enable; //register table 0x20: offset register 24 [31] ++ int lens_coefa[10]; //register table 0x20: start offset register 13 [21:0] ++ int lens_coefb[10]; ++ int lens_coefc[10]; ++ unsigned int lens_range[10]; ++ unsigned int lens_xref; //register table 0x20: offset register 23 [31:22] ++ unsigned int lens_yref; //register table 0x20: offset register 23 [31:22] ++}; ++ ++//demosaic ++struct isp_demosaic { ++ int type; ++ int enable; //register table 0x20: offset register 2 [12] ++ unsigned int threshold; //register table 0x20: offset register 2 [11:0] ++}; ++ ++// noise reduce ++struct isp_rgb_filter { ++ int type; ++ int enable; //register table 0x24: offset register 1 [31] ++ unsigned int threshold; //register table 0x24: offset register 1 [21:12] ++}; ++ ++//uv iso filter ++struct isp_uv_filter { ++ int type; ++ int enable; //register table 0x24: offset register 13 [30] ++}; ++ ++// defect pixel ++struct isp_defect_pixel { ++ int type; ++ int enable; //register table 0x24: offset register 1 [30] ++ unsigned int threshold; //register table 0x24: offset register 1 [29:28] ++}; ++ ++struct isp_white_balance { ++ int type; ++ int enable; ++ unsigned int co_r; //register table 0x24: offset register 0 [11:0] ++ unsigned int co_g; //register table 0x24: offset register 0 [23:12] ++ unsigned int co_b; //register table 0x24: offset register 1 [11:0] ++}; ++ ++struct isp_auto_white_balance { ++ int type; ++ int enable; //register table 0x20: offset register 25 [31] ++ unsigned int gr_low; // 0.8125 ++ unsigned int gr_high; // 1.555 ++ unsigned int gb_low; // 0.863 ++ unsigned int gb_high; // 1.820 ++ unsigned int grb_low; // 0.461 ++ unsigned int grb_high; // 1.559 ++ //range of pixel ++ unsigned int r_low; ++ unsigned int r_high; ++ unsigned int g_low; ++ unsigned int g_high; ++ unsigned int b_low; ++ unsigned int b_high; ++ ++ unsigned int co_r; //register table 0x24: offset register 0 [11:0] ++ unsigned int co_g; //register table 0x24: offset register 0 [23:12] ++ unsigned int co_b; //register table 0x24: offset register 1 [11:0] ++}; ++ ++struct isp_color_correct { ++ int type; ++ int enable; //register table 0x24: offset register 11 [31] ++ unsigned int cc_thrs_low; ++ unsigned int cc_thrs_high; ++ int ccMtrx[3][3]; ++}; ++ ++struct isp_gamma_calculate { ++ int type; ++ int enable; //register table 0x24: offset register 11 [30] ++ int is_sync; //count: 0: 1 ++ unsigned int gamma[32]; //register table 0x28: 64 register ++}; ++ ++//brightness edge enhancement ++struct isp_brightness_enhance { ++ int type; ++ int enable; //register table 0x24: offset register 11 [29] ++ int ygain; //register table 0x24: offset register 12 [31:24] ++ unsigned int y_thrs; //register table 0x24: offset register 12 [22:12] ++ unsigned int y_edgek; //register table 0x24: offset register 12 [10:0] ++}; ++ ++struct isp_saturation { ++ int type; ++ int enable; //register table 0x24: offset register 13 [31] ++ int Khigh; //register table 0x24: offset register 13 [29:20], (0.1~3) *256 _8_8 ++ int Klow; //register table 0x24: offset register 13 [19:10], register value (0.1~3) ++ int Kslope; //register table 0x24: offset register 13 [9:0], (ih-il)*256/(ch-cl) ++ unsigned int Chigh; //register table 0x24: offset register 14 [31:24],0~255 ++ unsigned int Clow; //register table 0x24: offset register 14 [23:16], 0~255 ++}; ++ ++struct isp_histogram { ++ int type; ++ int enable; // ++ int sync; //sync: 0: 1 ++ unsigned int hist[32]; // ++}; ++ ++struct isp_special_effect { ++ int type; ++ int enable; //register table 0x24: offset register 11 [28] ++ int solar_enable; //register table 0x24: offset register 11 [27] ++ unsigned int solar_thrs; //register table 0x24: offset register 0 [31:24] ++ int y_eff_coefa; //register table 0x24: offset register 14 [15:8] ++ unsigned int y_eff_coefb; //register table 0x24: offset register 14 [7:0] ++ int u_eff_coefa; //register table 0x24: offset register 15 [31:24] ++ unsigned int u_eff_coefb; //register table 0x24: offset register 15 [23:16] ++ int v_eff_coefa; //register table 0x24: offset register 15 [15:8] ++ unsigned int v_eff_coefb; //register table 0x24: offset register 15 [7:0] ++}; ++ ++struct isp_config_sensor_reg { ++ int type; ++ int enable; ++ unsigned int cmd; ++ unsigned int data; ++}; ++ ++#endif ++ +diff --git a/include/plat-anyka/notify.h b/include/plat-anyka/notify.h +new file mode 100755 +index 00000000..b230fd4e +--- /dev/null ++++ b/include/plat-anyka/notify.h +@@ -0,0 +1,36 @@ ++/* ++ * include/plat-anyka/notify.h ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef __NOTIFY_H_ ++#define __NOTIFY_H_ ++ ++#define POWER_EVENT_AC_PLUGIN 0x01 ++#define POWER_EVENT_AC_PLUGOUT 0x02 ++#define POWER_EVENT_USB_PLUGIN 0x03 ++#define POWER_EVENT_USB_PLUGOUT 0x04 ++ ++#define ADDEDECT_DEV1_PLUGIN 0x10 ++#define ADDEDECT_DEV1_PLUGOUT 0x11 ++#define ADDEDECT_DEV2_PLUGIN 0x12 ++#define ADDEDECT_DEV2_PLUGOUT 0x13 ++#define ADDEDECT_DEV3_PLUGIN 0x14 ++#define ADDEDECT_DEV3_PLUGOUT 0x15 ++#define ADDEDECT_DEV4_PLUGIN 0x16 ++#define ADDEDECT_DEV4_PLUGOUT 0x17 ++ ++int power_register_client(struct notifier_block *nb); ++int power_unregister_client(struct notifier_block *nb); ++int power_notifier_call_chain(unsigned long val, void *v); ++ ++int addetect_register_client(struct notifier_block *nb); ++int addetect_unregister_client(struct notifier_block *nb); ++int addetect_notifier_call_chain(unsigned long val, void *v); ++ ++#endif /* __NOTIFY_H_ */ ++ +diff --git a/include/plat-anyka/otg-hshcd.h b/include/plat-anyka/otg-hshcd.h +new file mode 100755 +index 00000000..495927ba +--- /dev/null ++++ b/include/plat-anyka/otg-hshcd.h +@@ -0,0 +1,29 @@ ++/* ++ * AKOTG HS register declarations and HCD data structures ++ */ ++ ++#ifndef __ANYKA_OTG_HS_H_ ++#define __ANYKA_OTG_HS_H_ ++ ++ ++#include ++#include ++ ++#define AKOTG_HC_HCD ++ ++#define USB_OP_MOD_REG (AK_VA_SYSCTRL + 0x58) ++#define USB_MODULE_RESET_REG (AK_VA_SYSCTRL + 0x20) ++ ++ ++#define USB_HC_BASE_ADDR (AK_VA_USB) ++#define H_MAXPACKET 512 /* bytes in fifo */ ++ ++struct akotghc_usb_platform_data { ++ void (* gpio_init)(const struct gpio_info *info); ++ struct gpio_info gpio_pwr_on; ++ struct gpio_info gpio_pwr_off; ++ struct gpio_info switch_onboard; ++ struct gpio_info switch_extport; ++}; ++ ++#endif /* __ANYKA_OTG_HS_H_ */ +diff --git a/include/plat-anyka/spi-anyka-slave.h b/include/plat-anyka/spi-anyka-slave.h +new file mode 100755 +index 00000000..02f6aee7 +--- /dev/null ++++ b/include/plat-anyka/spi-anyka-slave.h +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (C) anyka 2012 ++ * Wangsheng Gao ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ */ ++#ifndef SPI_ANYKA_SLAVE_H_ ++#define SPI_ANYKA_SLAVE_H_ ++ ++ ++/* ++ * Spi command definitions ++ * Support 4294967295 commands ++ */ ++ ++ #define OPEN_WIFI 0x00000001 ++ #define CLOSE_WIFI 0x00000002 ++ ++ #define MAX_COMMAND 0xffffffff ++ ++#endif /*SPI_ANYKA_SLAVE_H_*/ +diff --git a/include/plat-anyka/tscp2007.h b/include/plat-anyka/tscp2007.h +new file mode 100755 +index 00000000..9c59df7d +--- /dev/null ++++ b/include/plat-anyka/tscp2007.h +@@ -0,0 +1,46 @@ ++#ifndef __LINUX_I2C_TSC2007_H ++#define __LINUX_I2C_TSC2007_H ++ ++enum { ++ ORIGIN_TOPLEFT = 1, ++ ORIGIN_BOTTOMLEFT, ++ ORIGIN_TOPRIGHT, ++ ORIGIN_BOTTOMRIGHT, ++}; ++ ++struct cp2007_intpin_info { ++ unsigned int pin; ++ char pulldown; //pulldown function flag ++ char pullup; //pullup function flag ++ char dir; //direction input/output ++ char int_pol; //interrupt polarity ++ unsigned int rst_pin; ++ int rst_time; ++ char rst_dir; ++ char rst_pol; ++ char rst_not; ++}; ++ ++ ++struct tscp2007_platform_data { ++ u16 model; /* 2007. */ ++ u16 x_plate_ohms; /* must be non-zero value */ ++ u16 max_rt; /* max. resistance above which samples are ignored */ ++ unsigned long poll_delay; /* delay (in ms) after pen-down event ++ before polling starts */ ++ unsigned long poll_period; /* time (in ms) between samples */ ++ int fuzzx; /* fuzz factor for X, Y and pressure axes */ ++ int fuzzy; ++ int fuzzz; ++ ++ struct cp2007_intpin_info intpin_info; ++ ++ int (*get_pendown_state)(unsigned int pin); ++ void (*clear_penirq)(void); /* If needed, clear 2nd level interrupt source */ ++ int (*init_platform_hw)(const struct cp2007_intpin_info *intpin_info); ++ void (*exit_platform_hw)(void); ++ ++ void (*reset_platform_hw)(const struct cp2007_intpin_info *intpin_info); ++}; ++ ++#endif /* __LINUX_I2C_TSC2007_H */ +\ No newline at end of file +diff --git a/include/plat-anyka/udc.h b/include/plat-anyka/udc.h +new file mode 100755 +index 00000000..aeef52af +--- /dev/null ++++ b/include/plat-anyka/udc.h +@@ -0,0 +1,240 @@ ++#ifndef __UDC_H__ ++#define __UDC_H__ ++ ++#include ++ ++#define USB_OP_MOD_REG (AK_VA_SYSCTRL + 0x58) ++#define USB_MODULE_RESET_REG (AK_VA_SYSCTRL + 0x20) ++ ++/* Anyka usb register */ ++#define USB_FUNCTION_ADDR 0x0 ++#define USB_POWER_CTRL 0x1 ++#define USB_INTERRUPT_1 0x2 ++#define USB_INTERRUPT_2 0x4 ++#define USB_INTERRUPT_TX 0x6 ++#define USB_INTERRUPT_RX 0x8 ++#define USB_INTERRUPT_COMM 0xA ++#define USB_INTERRUPT_USB 0xB ++#define USB_FRAME_NUM 0xC ++#define USB_EP_INDEX 0xE ++#define USB_TEST_MODE 0xF ++#define USB_TX_MAX 0x10 ++#define USB_CTRL_1 0x12 ++#define USB_CTRL_1_2 0x13 ++#define USB_RX_MAX 0x14 ++#define USB_CTRL_2 0x16 ++#define USB_CTRL_2_2 0x17 ++#define USB_EP_COUNT 0x18 ++#define USB_CFG_INFO 0x1F ++#define USB_EP0_FIFO 0x20 ++#define USB_EP1_FIFO 0x24 ++#define USB_EP2_FIFO 0x28 ++#define USB_EP3_FIFO 0x2C ++#define USB_EP4_FIFO 0x30 ++#define USB_EP5_FIFO 0x34 ++#define USB_DEVICE_CTRL 0x60 ++#define USB_DMA_INTR 0x200 ++#define USB_DMA_CTRL1 0x204 ++#define USB_DMA_CTRL2 0x214 ++#define USB_DMA_CTRL3 0x224 ++#define USB_DMA_CTRL4 0x234 ++#define USB_DMA_ADDR1 0x208 ++#define USB_DMA_ADDR2 0x218 ++#define USB_DMA_ADDR3 0x228 ++#define USB_DMA_ADDR4 0x238 ++#define USB_DMA_COUNT1 0x20C ++#define USB_DMA_COUNT2 0x21C ++#define USB_DMA_COUNT3 0x22C ++#define USB_DMA_COUNT4 0x23C ++#define USB_EP0_NUM 0x330 ++#define USB_EP2_NUM 0x334 ++#define USB_MODE_STATUS 0x344 ++ ++#define USB_LINESTATE_WP (3 << 15) ++#define USB_DP_PU_EN (1 << 14) ++#define USB_ID_CFG (3 << 12) ++#define USB_PHY_CFG (0x3f << 6) ++#define USB_SUSPEND_EN (1 << 2) ++#define USB_ID_CLIENT (3) ++#define USB_ID_HOST (1) ++#define USB_PHY_CLIENT (23) ++#define USB_PHY_HOST (31) ++#define USB_ISO_UPDATE (1 << 7) ++#define USB_HSPEED_EN (1 << 5) ++#define USB_HSPEED_MODE (1 << 4) ++#define USB_RESUME_EN (1 << 2) ++#define USB_SUSPEND_MODE (1 << 1) ++#define USB_SUSPENDM_EN (1 << 0) ++ ++#define USB_ENABLE_DMA (1) ++#define USB_DIRECTION_RX (0<<1) ++#define USB_DIRECTION_TX (1<<1) ++#define USB_DMA_MODE1 (1<<2) ++#define USB_DMA_MODE0 (0<<2) ++#define USB_DMA_INT_ENABLE (1<<3) ++#define USB_DMA_INT_DISABLE (0<<3) ++#define USB_DMA_BUS_ERROR (1<<8) ++#define USB_DMA_BUS_MODE0 (0<<9) ++#define USB_DMA_BUS_MODE1 (1<<9) ++#define USB_DMA_BUS_MODE2 (2<<9) ++#define USB_DMA_BUS_MODE3 (3<<9) ++#define DMA_CHANNEL1_INT (1) ++#define DMA_CHANNEL2_INT (2) ++#define DMA_CHANNEL3_INT (4) ++#define DMA_CHANNEL4_INT (8) ++#define USB_EP0_INDEX (0) ++#define USB_EP1_INDEX (1 << 0) ++#define USB_EP2_INDEX (1 << 1) ++#define USB_EP3_INDEX ((1 << 1)|(1 << 0)) ++#define USB_EP4_INDEX (1 << 2) ++#define USB_EP5_INDEX ((1 << 2)|(1 << 0)) ++ ++// #define USB_11 [> usb1.1 <] ++ ++#define EP0_FIFO_SIZE 64 /* control, not 64byte? */ ++#define EP1_FIFO_SIZE 64 /* interrupt */ ++#define EP2_FIFO_SIZE 512 /* ep2 in bulk */ ++#define EP3_FIFO_SIZE 512 /* ep3 out bulk */ ++#define EP4_FIFO_SIZE 512 /* ep4 in bulk */ ++#define EP5_FIFO_SIZE 512 /* ep5 out bulk */ ++ ++#define USB_TXCSR_AUTOSET (0x80) ++#define USB_TXCSR_ISO (0x40) ++#define USB_TXCSR_MODE1 (0x20) ++#define USB_TXCSR_DMAREQENABLE (0x10) ++#define USB_TXCSR_FRCDATATOG (0x8) ++#define USB_TXCSR_DMAREQMODE1 (0x4) ++#define USB_TXCSR_DMAREQMODE0 (0x0) ++ ++struct akudc_request; ++ ++/* the struct of endpoint for Anyka udc */ ++struct akudc_ep { ++ struct usb_ep ep; /* the basic endpoint */ ++ struct usb_gadget *gadget; /* the gadget for udc */ ++ struct usb_endpoint_descriptor *desc; /* the descriptor for this endpoint */ ++ ++ struct list_head queue; /* the queue head for request of this endpoint */ ++ ++ struct work_struct work; ++ struct akudc_request *req; /* req be about to handle */ ++ ++ struct akudc_udc *udc; /* the udc struct */ ++ int maxpacket; ++ volatile int done; /* whether the current request is complete */ ++ unsigned int bufaddr; ++ dma_addr_t bufphys; ++ ++ unsigned irq_enable; /* whether the endpoint cpu irq is enable */ ++ volatile unsigned stopped; /*whether the endpoint is stopped */ ++ // unsigned stopped:1; ++ unsigned is_in:1; /* whether the endpoint is in */ ++ unsigned is_iso:1; ++ // unsigned fifo_bank:1; ++ u8 l2_buf_id; /* the l2 buffer id for this endpoint */ ++}; ++ ++struct akudc_request { ++ struct list_head queue; /* ep's requests */ ++ struct usb_request req; ++ int status; ++}; ++ ++/* the status flag for ep0 Control SETUP Transaction */ ++enum ep0_status { ++ EP0_IDLE, ++ EP0_IN_DATA_PHASE, ++ EP0_OUT_DATA_PHASE, ++ EP0_END_XFER, ++ EP0_STALL, ++}; ++#if 0 ++static const char *ep0states[]= { ++ "EP0_IDLE", ++ "EP0_IN_DATA_PHASE", ++ "EP0_OUT_DATA_PHASE", ++ "EP0_END_XFER", ++ "EP0_STALL", ++}; ++#endif ++struct usb_l2 { ++ void *buf; ++ dma_addr_t phys; ++}; ++ ++static const char * const ep_name[] = { ++ "ep0", ++ "ep1-int", ++ "ep2in-bulk", ++ "ep3out-bulk", ++ "ep4in-bulk", ++ "ep5out-bulk", ++}; ++ ++#define ENDPOINTS_NUM ARRAY_SIZE(ep_name) ++ ++struct akudc_udc { ++ struct usb_gadget gadget; /* the core struct for anyka udc */ ++ struct usb_gadget_driver *driver; /* the core struct for function drivers */ ++ ++ struct akudc_ep ep[ENDPOINTS_NUM]; /* the endpoints for udc */ ++ enum ep0_status ep0_status; ++ ++ void __iomem *baseaddr; ++ ++ unsigned int mcu_irq; /* the irq for cpu irq */ ++ unsigned int dma_irq; /* the irq for l2 dma irq */ ++ struct clk *clk; /* the clk for udc */ ++ char addr; /* assigned device address */ ++ u16 devstatus; ++ int ep0state; /* the status flag for ep0 Control SETUP Transaction */ ++ unsigned req_std : 1; ++ unsigned req_config : 1; ++ unsigned req_pending : 1; ++ unsigned high_speed: 1; ++ struct proc_dir_entry *pde; ++}; ++ ++ ++/* usb dosen't have vbus and id pin,so these bit must be set */ ++static inline void set_usb_as_slave(void) ++{ ++ unsigned long con; ++ ++ con = __raw_readl(USB_OP_MOD_REG); ++ con &= ~(0xff << 6); ++ con |= ((0x3 << 12)|(0x17 << 6)); ++ __raw_writel(con, USB_OP_MOD_REG); ++} ++ ++static inline void usb_vbus_wakeup(int enable) ++{ ++#if defined (CONFIG_ARCH_AK37) ++ /* open usb detect */ ++ if(enable) { ++ REG32(AK_VA_SYSCTRL + 0xd8) &= ~(1 << 0); //falling-trig ++ ++ REG32(AK_VA_SYSCTRL + 0xd8) |= (1 << 2); //clear usb det ++ REG32(AK_VA_SYSCTRL + 0xd8) &= ~(1 << 2); ++ ++ REG32(AK_VA_SYSCTRL + 0xd8) |= (1 << 4); //enable usb detect ++ } else { ++ REG32(AK_VA_SYSCTRL + 0xd8) |= (1 << 2); //clear usb det ++ REG32(AK_VA_SYSCTRL + 0xd8) &= ~(1 << 2); ++ ++ REG32(AK_VA_SYSCTRL + 0xd8) &= ~(1 << 4); //disable usb detect ++ } ++#endif ++} ++ ++static void udc_reg_writel(unsigned long __iomem *__reg, ++ unsigned long value, int bits, int index); ++//static unsigned long udc_reg_readl(unsigned long __iomem *__reg, ++// int bits, int index); ++ ++static void udc_reinit(struct akudc_udc *udc); ++static void akudc_enable(struct akudc_udc *udc); ++static void akudc_disable(struct akudc_udc *udc); ++static void done(struct akudc_ep *ep, struct akudc_request *req, int status); ++ ++#endif /* __UDC_H__ */ +diff --git a/include/plat-anyka/usb-hc.h b/include/plat-anyka/usb-hc.h +new file mode 100755 +index 00000000..5294651c +--- /dev/null ++++ b/include/plat-anyka/usb-hc.h +@@ -0,0 +1,900 @@ ++#ifndef _ANYKA_USB_HC_H_ ++#define _ANYKA_USB_HC_H_ ++ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "usb-reg-define.h" ++ ++#include "otg-hshcd.h" ++ ++#define DYNAMIC_EPFIFO ++#define MAX_EP_NUM (5) ++#define USBDMA_CHANNEL_NUM (4) ++ ++#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ ++#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) ++ ++ /* usb 1.1 says max 90% of a frame is available for periodic transfers. ++ * this driver doesn't promise that much since it's got to handle an ++ * IRQ per packet; irq handling latencies also use up that time. ++ * ++ * NOTE: the periodic schedule is a sparse tree, with the load for ++ * each branch minimized. see fig 3.5 in the OHCI spec for example. ++ */ ++#define MAX_PERIODIC_LOAD 500 /* out of 1000 usec */ ++ ++ /*-------------------------------------------------------------------------*/ ++ ++ /*Common Registers Access - Just use base address plus offset */ ++ ++ static DEFINE_SPINLOCK(USB_HC_REG_LOCK); ++ ++#define hc_readb(reg) __raw_readb(USB_HC_BASE_ADDR + (reg)) ++#define hc_writeb(val, reg) __raw_writeb(val, USB_HC_BASE_ADDR + (reg)) ++ ++#define hc_readw(reg) __raw_readw(USB_HC_BASE_ADDR + (reg)) ++#define hc_writew(val, reg) __raw_writew(val, USB_HC_BASE_ADDR + (reg)) ++ ++#define hc_readl(reg) __raw_readl(USB_HC_BASE_ADDR + (reg)) ++#define hc_writel(val, reg) __raw_writel(val, USB_HC_BASE_ADDR + (reg)) ++ ++ struct akotghc_ep { ++ struct usb_host_endpoint *hep; ++ struct usb_device *udev; ++ ++ u16 maxpacket; ++ u8 epnum; ++ u8 epfifo; ++ u8 nextpid; ++ ++ u16 error_count; ++ u16 nak_count; ++ u16 length; /* of current packet */ ++ ++ bool bdma; ++ ++ /* periodic schedule */ ++ u16 period; ++ u16 branch; ++ u16 load; ++ struct akotghc_ep *next; ++ ++ /* async schedule */ ++ struct list_head schedule; ++ }; ++ ++/** ++ struct for usb dma channel ++ */ ++ struct akotg_dma ++ { ++ u8 channel; //channel num ++ u8 status; //channel status ++ u8 dir; //transfer direction, ++ u8 epfifo; //dma is bind to which usb ep fifo ++ u8 l2buffer; //l2 buffer which is bind to ep fifo ++ u32 addr; //inner addr for dma, start with 0x70000000 ++ u32 count; //dma count ++ u32 phyaddr; //phycical address for dma ++ }; ++ ++ struct akotg_usbhc { ++ spinlock_t lock; ++ ++ struct clk *clk; ++ ++ /* sw model */ ++ struct timer_list timer; ++ struct akotghc_ep *next_periodic; ++ struct akotghc_ep *next_async_ep0; ++ struct akotghc_ep *next_async_epx[MAX_EP_NUM]; ++ ++ struct akotghc_ep *active_ep0; ++ struct akotghc_ep *active_epx[MAX_EP_NUM]; ++ unsigned long jiffies_ep0; ++ unsigned long jiffies_epx[MAX_EP_NUM]; ++ ++ int mcu_irq; ++ ++ //dma ++ int dma_irq; //irq alloced for dma ++ struct akotg_dma dma_channel[USBDMA_CHANNEL_NUM]; ++ ++ u32 port_status; ++ u16 frame; ++ ++ /* async schedule: control, bulk */ ++ struct list_head async_ep0; ++ struct list_head async_epx[MAX_EP_NUM]; ++ ++ /* periodic schedule: interrupt, iso */ ++ u16 load[PERIODIC_SIZE]; ++ struct akotghc_ep *periodic[PERIODIC_SIZE]; ++ unsigned periodic_count; ++ }; ++ ++ ++ struct epfifo_mapping { ++ int epfifo; /* AKOTG HC EP FIFO number: 1 ~ 6 */ ++ int used; /* 0 - Unused, 1 - used */ ++ int epnum; /* USB Device endpoint number: 1 ~ 16 */ ++ int direction; /* 0 - In, 1 - Out */ ++}; ++ ++struct akotghc_epfifo_mapping { ++ spinlock_t lock; ++ struct epfifo_mapping mapping[MAX_EP_NUM]; ++}; ++ ++/** ++ enum for DMA channel status ++ */ ++enum ++{ ++ USB_DMA_CHANNEL_IDLE, //idle ++ USB_DMA_CHANNEL_ALLOC, //alloced for a specific ep fifo but not under tranfering ++ USB_DMA_CHANNEL_TRANS //there is a dma tranfering in this channel ++}; ++ ++irqreturn_t akotg_usbhc_irq(struct usb_hcd *hcd); ++irqreturn_t akotg_dma_irq(int irqnum, void *__hcd); ++ ++int akotg_usbhc_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); ++int akotg_usbhc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); ++void akotg_usbhc_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *hep); ++void akotg_usbhc_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep); ++int akotg_usbhc_get_frame(struct usb_hcd *hcd); ++int akotg_usbhc_hub_status_data(struct usb_hcd *hcd, char *buf); ++void akotg_usbhc_hub_descriptor (struct akotg_usbhc *akotghc, struct usb_hub_descriptor *desc); ++void akotg_usbhc_timer(unsigned long _akotghs); ++int akotg_usbhc_hub_control( ++ struct usb_hcd *hcd, ++ u16 typeReq, ++ u16 wValue, ++ u16 wIndex, ++ char *buf, ++ u16 wLength ++); ++ ++int akotg_usbhc_bus_suspend(struct usb_hcd *hcd); ++int akotg_usbhc_bus_resume(struct usb_hcd *hcd); ++void akotg_usbhc_stop(struct usb_hcd *hcd); ++int akotg_usbhc_start(struct usb_hcd *hcd); ++ ++void port_power(struct akotg_usbhc *akotghc, int is_on); ++ ++static inline struct akotg_usbhc *hcd_to_akotg_usbhc(struct usb_hcd *hcd) ++{ ++ return (struct akotg_usbhc *) (hcd->hcd_priv); ++} ++ ++static inline struct usb_hcd *akotg_usbhc_to_hcd(struct akotg_usbhc *akotghc) ++{ ++ return container_of((void *) akotghc, struct usb_hcd, hcd_priv); ++} ++ ++ ++/* ++ * Part II: Index Registers Access - Spinlock+IRQ protection ++ */ ++//static DEFINE_SPINLOCK(fsh_reg_lock); ++ ++static inline unsigned char hc_index_readb(int epindex, int reg) ++{ ++ unsigned long flags; ++ unsigned char val; ++ ++ spin_lock_irqsave(&USB_HC_REG_LOCK, flags); ++ ++ hc_writeb(epindex, USB_REG_INDEX); ++ val = hc_readb(reg); ++ ++ spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); ++ ++ return val; ++} ++ ++static inline void hc_index_writeb(int epindex, unsigned char val, int reg) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&USB_HC_REG_LOCK, flags); ++ ++ hc_writeb(epindex, USB_REG_INDEX); ++ hc_writeb(val, reg); ++ ++ spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); ++} ++ ++static inline unsigned short hc_index_readw(int epindex, int reg) ++{ ++ unsigned long flags; ++ unsigned short val; ++ ++ spin_lock_irqsave(&USB_HC_REG_LOCK, flags); ++ ++ hc_writeb(epindex, USB_REG_INDEX); ++ val = hc_readw(reg); ++ ++ spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); ++ ++ return val; ++ ++} ++ ++static inline void hc_index_writew(int epindex, unsigned short val, int reg) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&USB_HC_REG_LOCK, flags); ++ ++ hc_writeb(epindex, USB_REG_INDEX); ++ hc_writew(val, reg); ++ ++ spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); ++} ++ ++static inline unsigned long hc_index_readl(int epindex, int reg) ++{ ++ unsigned long flags; ++ unsigned long val; ++ ++ spin_lock_irqsave(&USB_HC_REG_LOCK, flags); ++ ++ hc_writeb(epindex, USB_REG_INDEX); ++ val = hc_readl(reg); ++ ++ spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); ++ ++ return val; ++} ++ ++static inline void hc_index_writel(int epindex, unsigned long val, int reg) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&USB_HC_REG_LOCK, flags); ++ ++ hc_writeb(epindex, USB_REG_INDEX); ++ hc_writel(val, reg); ++ ++ spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); ++ ++} ++static inline void flush_ep0_fifo(void) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&USB_HC_REG_LOCK, flags); ++ ++ hc_writeb(0, USB_REG_INDEX); ++ if (hc_readb(USB_REG_TXCSR1) & (USB_CSR01_RXPKTRDY | USB_CSR01_TXPKTRDY)) ++ hc_writeb(USB_CSR02_FLUSHFIFO, USB_REG_TXCSR2); ++ ++ spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); ++} ++ ++static inline void flush_epx_tx_fifo(int i) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&USB_HC_REG_LOCK, flags); ++ ++ hc_writeb(i, USB_REG_INDEX); ++ if (hc_readb(USB_REG_TXCSR1) & (USB_CSR01_RXPKTRDY)) ++ hc_writeb(USB_TXCSR1_FLUSHFIFO, USB_REG_TXCSR1); ++ ++ spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); ++ ++} ++ ++static inline void flush_epx_rx_fifo(int i) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&USB_HC_REG_LOCK, flags); ++ ++ hc_writeb(i, USB_REG_INDEX); ++ if (hc_readb(USB_REG_RXCSR1) & (USB_RXCSR1_RXPKTRDY)) ++ hc_writeb(USB_RXCSR1_FLUSHFIFO, USB_REG_RXCSR1); ++ ++ spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); ++} ++ ++static inline void flush_epx_fifo(int i) ++{ ++ flush_epx_tx_fifo(i); ++ flush_epx_rx_fifo(i); ++} ++ ++static inline void flush_ep_fifo(int i) ++{ ++ if (i == 0) ++ flush_ep0_fifo(); ++ else ++ flush_epx_fifo(i); ++} ++ ++static inline void set_epx_rx_mode(int i) ++{ ++ u8 regval; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&USB_HC_REG_LOCK, flags); ++ ++ hc_writeb(i, USB_REG_INDEX); ++ regval = hc_readb(USB_REG_TXCSR2); ++ regval &= ~USB_TXCSR2_MODE; ++ hc_writeb(regval, USB_REG_TXCSR2); ++ ++ spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); ++} ++ ++static inline void set_epx_tx_mode(int i) ++{ ++ u8 regval; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&USB_HC_REG_LOCK, flags); ++ ++ hc_writeb(i, USB_REG_INDEX); ++ regval = hc_readb(USB_REG_TXCSR2); ++ regval |= USB_TXCSR2_MODE; ++ hc_writeb(regval, USB_REG_TXCSR2); ++ ++ spin_unlock_irqrestore(&USB_HC_REG_LOCK, flags); ++} ++ ++static inline void clear_epx_tx_data_toggle(int i) ++{ ++ hc_index_writeb(i, USB_TXCSR1_CLRDATATOG, USB_REG_TXCSR1); ++ //hc_index_writeb(i, USB_RXCSR2_DMAREQENAB, USB_REG_TXCSR2); //??? ++} ++ ++static inline void clear_epx_rx_data_toggle(int i) ++{ ++ hc_index_writeb(i, USB_RXCSR1_CLRDATATOG, USB_REG_RXCSR1); ++} ++ ++/* ++ * Valid types: ++ * 1 - Isochronous ++ * 2 - Bulk ++ * 3 - Interrupt ++ * Invalid type: ++ * 0 - Illegal ++ */ ++static inline void set_epx_tx_type(int i, int epnum, int type) ++{ ++ BUG_ON(i < 0 || i > MAX_EP_NUM); ++ BUG_ON(type < 0 || type > 3); ++ ++ hc_index_writeb(i, type << 4 | epnum, USB_REG_TXTYPE); ++} ++ ++static inline void set_epx_rx_type(int i, int epnum, int type) ++{ ++ BUG_ON(i < 0 || i > MAX_EP_NUM); ++ BUG_ON(type < 0 || type > 3); ++ ++ hc_index_writeb(i, type << 4 | epnum, USB_REG_RXTYPE); ++} ++ ++static inline void enable_ep0_interrupt(void) ++{ ++ u8 regval; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ regval = hc_readb(USB_REG_INTRTXE); ++ regval |= USB_INTR_EP0; ++ hc_writeb(regval, USB_REG_INTRTXE); ++ ++ local_irq_restore(flags); ++} ++ ++static inline void enable_epx_tx_interrupt(int i) ++{ ++ u8 regval; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ regval = hc_readb(USB_REG_INTRTXE); ++ regval |= (1 << i); ++ hc_writeb(regval, USB_REG_INTRTXE); ++ ++ local_irq_restore(flags); ++} ++ ++static inline void enable_epx_rx_interrupt(int i) ++{ ++ u8 regval; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ regval = hc_readb(USB_REG_INTRRXE); ++ regval |= (1 << i); ++ hc_writeb(regval, USB_REG_INTRRXE); ++ ++ local_irq_restore(flags); ++} ++ ++static inline void disable_ep0_interrupt(void) ++{ ++ u8 regval; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ regval = hc_readb(USB_REG_INTRTXE); ++ regval &= ~USB_INTR_EP0; ++ hc_writeb(regval, USB_REG_INTRTXE); ++ ++ local_irq_restore(flags); ++} ++ ++static inline void disable_epx_tx_interrupt(int i) ++{ ++ u8 regval; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ regval = hc_readb(USB_REG_INTRTXE); ++ regval &= ~(1 << i); ++ hc_writeb(regval, USB_REG_INTRTXE); ++ ++ local_irq_restore(flags); ++} ++ ++static inline void disable_epx_rx_interrupt(int i) ++{ ++ u8 regval; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ regval = hc_readb(USB_REG_INTRRXE); ++ regval &= ~(1 << i); ++ hc_writeb(regval, USB_REG_INTRRXE); ++ ++ local_irq_restore(flags); ++} ++ ++static inline void disable_epx_interrupt(int i) ++{ ++ disable_epx_tx_interrupt(i); ++ disable_epx_rx_interrupt(i); ++} ++ ++static inline void disable_ep_interrupt(int i) ++{ ++ BUG_ON(i < 0 || i > MAX_EP_NUM); ++ ++ if (i == 0) { ++ disable_ep0_interrupt(); ++ } else { ++ disable_epx_interrupt(i); ++ } ++} ++ ++static inline void clear_all_interrupts(void) ++{ ++ hc_writeb(0x0, USB_REG_INTRUSBE); ++ hc_writew(0x0, USB_REG_INTRTXE); ++ hc_writew(0x0, USB_REG_INTRRXE); ++ ++ hc_readb(USB_REG_INTRUSB); ++ hc_readw(USB_REG_INTRTX); ++ hc_readw(USB_REG_INTRRX); ++} ++ ++static inline void reset_endpoint(int i) ++{ ++ BUG_ON(i < 0 || i > MAX_EP_NUM); ++ ++ disable_ep_interrupt(i); ++ if (i == 0) { ++ flush_ep0_fifo(); ++ } else { ++ flush_epx_fifo(i); ++ set_epx_rx_type(i, 0, 0); ++ set_epx_tx_type(i, 0, 0); ++ } ++} ++ ++static inline void reset_endpoints(void) ++{ ++ int i; ++ ++ for (i = 0; i < MAX_EP_NUM + 1; i++) { ++ reset_endpoint(i); ++ } ++} ++ ++ ++static inline u8 akotg_alloc_l2_buffer(int epfifo) ++{ ++ l2_device_t l2addr[] = {ADDR_USB_EP2, ADDR_USB_EP3, ADDR_USB_EP4, ADDR_USB_EP5}; ++ ++ //check param ++ if((epfifo < 2) || (epfifo > MAX_EP_NUM)) ++ return BUF_NULL; ++ ++ //alloc l2 buffer ++ return l2_alloc_nowait(l2addr[epfifo-2]); ++} ++ ++static inline void akotg_free_l2_buffer(int epfifo) ++{ ++ l2_device_t l2addr[] = {ADDR_USB_EP2, ADDR_USB_EP3, ADDR_USB_EP4, ADDR_USB_EP5}; ++ ++ //check param ++ if((epfifo < 2) || (epfifo > MAX_EP_NUM)) ++ return; ++ ++ //alloc l2 buffer ++ l2_free(l2addr[epfifo-2]); ++} ++ ++ ++ ++static inline void akotg_dma_init(struct akotg_usbhc *otg) ++{ ++ int i; ++ ++ memset(otg->dma_channel, 0, sizeof(otg->dma_channel)); ++ ++ for(i = 0; i < USBDMA_CHANNEL_NUM; i++) ++ { ++ otg->dma_channel[i].channel = i; ++ otg->dma_channel[i].status = USB_DMA_CHANNEL_IDLE; ++ otg->dma_channel[i].l2buffer = BUF_NULL; ++ } ++} ++ ++ ++/** ++ config dma register ++*/ ++static inline bool akotg_dma_config(struct akotg_dma *dma) ++{ ++ u8 channel = dma->channel; ++ u8 dir = dma->dir; ++ u8 epfifo = dma->epfifo; ++ ++ u32 dmactrl; ++ ++ ++ //config dma address and count ++ hc_writel(dma->addr, USB_DMA_ADDR(channel)); ++ hc_writel(dma->count, USB_DMA_COUNT(channel)); ++ ++ //config dma control ++ dmactrl = (USB_ENABLE_DMA|dir|USB_DMA_MODE1|USB_DMA_INT_ENABLE|(epfifo <<4)|USB_DMA_BUS_MODE3); ++ hc_writel(dmactrl, USB_DMA_CTRL(channel)); ++ ++ dma->status = USB_DMA_CHANNEL_TRANS; ++ ++ return true; ++} ++ ++ ++/** ++ config dma struct ++*/ ++static inline bool akotg_dma_set_struct(struct akotg_dma *dma, u8 dir, u8 epfifo, u8 l2buffer, u32 count, dma_addr_t phyaddr) ++{ ++ if((epfifo < 2) || (epfifo > MAX_EP_NUM)) ++ return false; ++ ++ dma->dir = dir; ++ dma->count = count; ++ dma->epfifo = epfifo; ++ dma->l2buffer = l2buffer; ++ ++ //config dma addr ++ dma->addr = 0x70000000 + 0x1000000*(epfifo-2); ++ dma->phyaddr = phyaddr; ++ ++ return true; ++} ++ ++ ++ ++/** ++ alloc dma channel for epfifo ++*/ ++static inline struct akotg_dma * akotg_dma_alloc(struct akotg_usbhc *otg, u8 epfifo) ++{ ++ struct akotg_dma *dma; ++ u8 l2buffer; ++ ++ if((epfifo < 2) || (epfifo > MAX_EP_NUM)) ++ return NULL; ++ ++ //alloc dma channel, do simple now, ++ //we'll complete it later ++ dma = &otg->dma_channel[epfifo-2]; ++ if(dma->status != USB_DMA_CHANNEL_IDLE){ ++ return NULL; ++ } ++ ++ //alloc l2 buffer ++ l2buffer = akotg_alloc_l2_buffer(epfifo); ++ if(BUF_NULL == l2buffer){ ++ return NULL; ++ } ++ ++ //printk("<%d:%d>\n", epfifo, l2buffer); ++ ++ dma->status = USB_DMA_CHANNEL_ALLOC; ++ dma->l2buffer = l2buffer; ++ ++ return dma; ++} ++ ++/** ++ get dma struct for channel i ++*/ ++static inline struct akotg_dma *akotg_dma_get_struct(struct akotg_usbhc *otg, u8 channel) ++{ ++ if(channel > USBDMA_CHANNEL_NUM) ++ return NULL; ++ ++ return &otg->dma_channel[channel]; ++} ++ ++/** ++ the usb dma trans will be stopped if a short packet is received, ++ we can use this func to check the real number of data been received ++*/ ++static inline u32 akotg_dma_get_trans_length(struct akotg_usbhc *otg, u8 epfifo) ++{ ++ int i; ++ struct akotg_dma *dma; ++ u32 addr; ++ ++ for(i = 0; i < USBDMA_CHANNEL_NUM; i++) ++ { ++ dma = &otg->dma_channel[i]; ++ ++ if((dma->epfifo == epfifo) && (dma->status == USB_DMA_CHANNEL_TRANS)) ++ { ++ addr = hc_readl(USB_DMA_ADDR(dma->channel)); ++ return (addr - dma->addr); ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ clear dma, ++ epfifo: 0-clear all, >0 clear dma for epfifo ++*/ ++static inline void akotg_dma_clear(struct akotg_usbhc *otg, u8 epfifo) ++{ ++ int i; ++ struct akotg_dma *dma; ++ ++ for(i = 0; i < USBDMA_CHANNEL_NUM; i++) ++ { ++ dma = &otg->dma_channel[i]; ++ ++ if((epfifo != 0) && (dma->epfifo != epfifo)){ ++ continue; ++ } ++ ++ if(dma->l2buffer != BUF_NULL) ++ { ++ //l2 free ++ akotg_free_l2_buffer(dma->epfifo); ++ } ++ ++ hc_writel(0, USB_DMA_CTRL(i)); ++ ++ dma->status = USB_DMA_CHANNEL_IDLE; ++ dma->l2buffer = BUF_NULL; ++ } ++ ++ ++} ++ ++static inline void init_epfifo_mapping(struct akotghc_epfifo_mapping *akotg_mapping) ++{ ++ int i; ++ struct epfifo_mapping *mapping; ++ ++ spin_lock_init(&akotg_mapping->lock); ++ ++ for (i = 0; i < MAX_EP_NUM; i++) { ++ mapping = &akotg_mapping->mapping[i]; ++ mapping->epfifo = i + 1; /* EPFIFO 1~MAX_EP_NUM is used by AKOTG HS HCD */ ++ mapping->used = 0; ++ mapping->epnum = 0; ++ mapping->direction = 0; ++ } ++} ++ ++static inline bool __is_epnum_mapped( ++ struct akotghc_epfifo_mapping *akotg_mapping, int epnum, int direction) ++{ ++ int i; ++ struct epfifo_mapping *mapping; ++ ++ if(epnum == 0) ++ return true; ++ ++ for (i = 0; i < MAX_EP_NUM; i++) { ++ mapping = &akotg_mapping->mapping[i]; ++ if (mapping->used ++ && (mapping->epnum == epnum) ++ && (mapping->direction == direction)) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++static inline bool is_epnum_mapped(struct akotghc_epfifo_mapping *akotg_mapping, ++ int epnum, int direction) ++{ ++ bool ret; ++ unsigned long flags; ++ ++ BUG_ON(akotg_mapping == NULL); ++ ++ if(epnum == 0) ++ return true; ++ ++ spin_lock_irqsave(&akotg_mapping->lock, flags); ++ ++ ret = __is_epnum_mapped(akotg_mapping, epnum, direction); ++ ++ spin_unlock_irqrestore(&akotg_mapping->lock, flags); ++ ++ return ret; ++ ++} ++ ++static inline bool __map_epnum_to_epfifo( ++ struct akotghc_epfifo_mapping *akotg_mapping, ++ int epnum, int direction, int *epfifo) ++{ ++ int i; ++ struct epfifo_mapping *mapping; ++ ++ if (__is_epnum_mapped(akotg_mapping, epnum, direction)) ++ return false; ++ ++ //allocate 512 byte size of filo to epnum ++ for (i = 1; i < MAX_EP_NUM; i++) { ++ mapping = &akotg_mapping->mapping[i]; ++ if (!mapping->used) { ++ mapping->used = 1; ++ mapping->epnum = epnum; ++ mapping->direction = direction; ++ *epfifo = mapping->epfifo; ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++static inline bool map_epnum_to_epfifo(struct akotghc_epfifo_mapping *akotg_mapping, ++ int epnum, int direction, int *epfifo) ++{ ++ bool ret; ++ unsigned long flags; ++ ++ BUG_ON(akotg_mapping == NULL); ++ ++ if (is_epnum_mapped(akotg_mapping, epnum, direction)) ++ return false; ++ ++ spin_lock_irqsave(&akotg_mapping->lock, flags); ++ ++ ret = __map_epnum_to_epfifo(akotg_mapping, epnum, direction, epfifo); ++ ++ spin_unlock_irqrestore(&akotg_mapping->lock, flags); ++ ++ return ret; ++} ++ ++static inline bool epfifo_to_epnum(struct akotghc_epfifo_mapping *akotg_mapping, int epfifo, int *epnum, int *direction) ++{ ++ int ret; ++ unsigned long flags; ++ struct epfifo_mapping *mapping; ++ ++ ret = false; ++ ++ spin_lock_irqsave(&akotg_mapping->lock, flags); ++ ++ mapping = &akotg_mapping->mapping[epfifo]; ++ if (mapping->used) { ++ *epnum = mapping->epnum; ++ *direction = mapping->direction; ++ ret = true; ++ } else { ++ *epnum = 0; ++ *direction = 0; ++ ret = false; ++ } ++ ++ spin_unlock_irqrestore(&akotg_mapping->lock, flags); ++ ++ return ret; ++} ++ ++static inline bool epnum_to_epfifo(struct akotghc_epfifo_mapping *akotg_mapping, int epnum, int direction, int *epfifo) ++{ ++ int i; ++ unsigned long flags; ++ struct epfifo_mapping *mapping; ++ ++ if (epnum == 0) { ++ *epfifo = 0; ++ return true; ++ } ++ ++ spin_lock_irqsave(&akotg_mapping->lock, flags); ++ ++ for (i = 0; i < MAX_EP_NUM; i++) { ++ mapping = &akotg_mapping->mapping[i]; ++ if (mapping->used && (mapping->epnum == epnum) && (mapping->direction == direction)) { ++ *epfifo = mapping->epfifo; ++ spin_unlock_irqrestore(&akotg_mapping->lock, flags); ++ return true; ++ } ++ } ++ ++ spin_unlock_irqrestore(&akotg_mapping->lock, flags); ++ ++ return false; ++} ++ ++static inline void set_usb_as_host(void) ++{ ++ unsigned long con; ++ ++ con = __raw_readl(USB_OP_MOD_REG); ++ con &= ~(0xff << 6); ++ con |= (0x1 << 12)|(0x1f << 6); ++ __raw_writel(con, USB_OP_MOD_REG); ++} ++ ++static inline void usb_reset_phy(struct akotg_usbhc *otghc) ++{ ++ unsigned long con; ++ ++ con = __raw_readl(USB_OP_MOD_REG); ++ con &= ~(0x7 << 0); ++ con |= (1 << 0); ++ __raw_writel(con, USB_OP_MOD_REG); ++} ++ ++/* power up and set usb suspend */ ++static inline void usb_power_up(struct akotg_usbhc *otghc) ++{ ++ unsigned long con; ++ ++ con = __raw_readl(USB_OP_MOD_REG); ++ con &= ~(0x7 << 0); ++ con |= (3 << 1); ++ __raw_writel(con, USB_OP_MOD_REG); ++} ++ ++#endif +diff --git a/include/plat-anyka/usb-reg-define.h b/include/plat-anyka/usb-reg-define.h +new file mode 100755 +index 00000000..3b8779ad +--- /dev/null ++++ b/include/plat-anyka/usb-reg-define.h +@@ -0,0 +1,262 @@ ++#ifndef _ANYKA_USB_REG_DEFINE_H_ ++#define _ANYKA_USB_REG_DEFINE_H_ ++ ++ /** Control Registers*/ ++#define USB_REG_FADDR (0x0000) // 8-bit ++#define USB_REG_POWER (0x0001) // 8-bit ++#define USB_REG_INTRTX (0x0002) // 16-bit, read clear ++#define USB_REG_INTRRX (0x0004) // 16-bit, read clear ++#define USB_REG_INTRTXE (0x0006) // 16-bit ++#define USB_REG_INTRRXE (0x0008) // 16-bit ++#define USB_REG_INTRUSB (0x000A) // 8-bit, read clear ++#define USB_REG_INTRUSBE (0x000B) // 8-bit ++#define USB_REG_FRAME (0x000C) // 16-bit ++#define USB_REG_INDEX (0x000E) // 8-bit ++#define USB_REG_TESEMODE (0x000F) // 8-bit ++#define USB_REG_DEVCTL (0x0060) // 8-bit ++ ++/** Endpoint Control/Status Registers */ ++#define USB_REG_TXMAXP (0x0010) // 16-bit, when index == 1~4 ++#define USB_REG_TXCSR1 (0x0012) // 8-bit, when index == 1~4 ++#define USB_REG_TXCSR2 (0x0013) // 8-bit, when index == 1~4 ++#define USB_REG_RXMAXP (0x0014) // 16-bit, when index == 1~4 ++#define USB_REG_RXCSR1 (0x0016) // 8-bit, when index == 1~4 ++#define USB_REG_RXCSR2 (0x0017) // 8-bit, when index == 1~4 ++#define USB_REG_COUNT0 (0x0018) // 16-bit, when index == 0 ++#define USB_REG_RXCOUNT (0x0018) // 16-bit, when index == 1~4 ++#define USB_REG_TXTYPE (0x001A) // 8-bit, when index == 1~4, host only ++#define USB_REG_NAKLIMIT0 (0x001B) // 8-bit, when index == 0, host only ++#define USB_REG_TXINTERVAL (0x001B) // 8-bit, when index == 1~4, host only ++#define USB_REG_RXTYPE (0x001C) // 8-bit, when index == 1~4, host only ++#define USB_REG_RXINTERVAL (0x001D) // 8-bit, when index == 1~4, host only ++#define USB_REG_CONFIGDATA (0x001F) // 8-bit, when index == 0 ++#define USB_REG_FIFOSIZE (0x001F) // 8-bit, when index == 1~4 ++ ++/** USB DMA */ ++#define USB_DMA_INTR (0x0200) ++#define USB_DMA_CTRL_BASE (0x0204) ++#define USB_DMA_ADDR_BASE (0x0208) ++#define USB_DMA_COUNT_BASE (0x020c) ++#define USB_DMA_CTRL(n) (USB_DMA_CTRL_BASE+(n)*0x10) ++#define USB_DMA_ADDR(n) (USB_DMA_ADDR_BASE+(n)*0x10) ++#define USB_DMA_COUNT(n) (USB_DMA_COUNT_BASE+(n)*0x10) ++ ++#define USB_REG_REQPKTCNT_BASE (0x0304) ++#define USB_REG_REQPKTCNT(ep) (USB_REG_REQPKTCNT_BASE + (ep-1)*0x4) ++ ++/** FIFOs Entry */ ++#define USB_FIFO_EP0 (0x0020) // 8- / 16- / 32-bit ++#define USB_FIFO_EP1 (0x0024) // 8- / 16- / 32-bit ++#define USB_FIFO_EP2 (0x0028) // 8- / 16- / 32-bit ++#define USB_FIFO_EP3 (0x002C) // 8- / 16- / 32-bit ++#define USB_FIFO_EP4 (0x0030) // 8- / 16- / 32-bit ++#define USB_FIFO_EP5 (0x0034) // 8- / 16- / 32-bit ++ ++/*DMA controler registers.*/ ++#define DMA_INTR_STAT 0x0 /*DMA interrupt status.*/ ++ ++#define DMA_CTRL_REG1 0x04 /*DMA channel 1 control.*/ ++#define DMA_CTRL_REG2 0x14 /*DMA channel 2 control.*/ ++#define DMA_CTRL_REG3 0x24 /*DMA channel 3 control.*/ ++#define DMA_CTRL_REG4 0x34 /*DMA channel 4 control.*/ ++ ++#define DMA_ADDR_REG1 0x08 /*DMA channel 1 AHB memory address.*/ ++#define DMA_ADDR_REG2 0x18 /*DMA channel 2 AHB memory address.*/ ++#define DMA_ADDR_REG3 0x28 /*DMA channel 3 AHB memory address.*/ ++#define DMA_ADDR_REG4 0x38 /*DMA channel 4 AHB memory address.*/ ++ ++#define DMA_CUNT_REG1 0x0c /*DMA channel 1 byte count.*/ ++#define DMA_CUNT_REG2 0x1c /*DMA channel 2 byte count.*/ ++#define DMA_CUNT_REG3 0x2c /*DMA channel 3 byte count.*/ ++#define DMA_CUNT_REG4 0x3c /*DMA channel 4 byte count.*/ ++ ++ ++//********************** bit fields defs*********************** ++ ++#define USB_ENABLE_DMA (1) ++#define USB_DIRECTION_RX (0<<1) ++#define USB_DIRECTION_TX (1<<1) ++#define USB_DMA_MODE1 (1<<2) ++#define USB_DMA_MODE0 (0<<2) ++#define USB_DMA_INT_ENABLE (1<<3) ++#define USB_DMA_INT_DISABLE (0<<3) ++#define USB_DMA_BUS_ERROR (1<<8) ++#define USB_DMA_BUS_MODE0 (0<<9) ++#define USB_DMA_BUS_MODE1 (1<<9) ++#define USB_DMA_BUS_MODE2 (2<<9) ++#define USB_DMA_BUS_MODE3 (3<<9) ++ ++// RTC_USB_CONFIG_REG ++#define SESSEND (1 << 31) // 0:above/1:below Session End threshold ++#define AVALID (1 << 30) ++#define VBUSVALID (1 << 29) ++#define SESSEND_SEL (1 << 28) ++#define AVALID_SEL (1 << 27) ++#define VBUSVALID_SEL (1 << 26) ++ ++ ++/** POWER Control register */ ++#define USB_POWER_ENSUSPEND (1 << 0) ++#define USB_POWER_SUSPENDM (1 << 1) // host/client ++#define USB_POWER_RESUME (1 << 2) // host/client ++#define USB_POWER_RESET (1 << 3) ++#define USB_HOSG_HIGH_SPEED (1 << 5) ++ ++ /** CSR01 register */ ++ // mode-agnostic ++#define USB_CSR01_RXPKTRDY (1 << 0) // r / clear ++#define USB_CSR01_TXPKTRDY (1 << 1) // r / set ++ // Client mode ++#define USB_CSR01_P_SENTSTALL (1 << 2) ++#define USB_CSR01_P_DATAEND (1 << 3) ++#define USB_CSR01_P_SETUPEND (1 << 4) ++#define USB_CSR01_P_SENDSTALL (1 << 5) ++#define USB_CSR01_P_SVDRXPKTRDY (1 << 6) ++#define USB_CSR01_P_SVDSETUPEND (1 << 7) ++ // Host mode ++#define USB_CSR01_H_RXSTALL (1 << 2) // r / clear ++#define USB_CSR01_H_SETUPPKT (1 << 3) // r / w ++#define USB_CSR01_H_ERROR (1 << 4) // r / clear ++#define USB_CSR01_H_REQPKT (1 << 5) // r / w ++#define USB_CSR01_H_STATUSPKT (1 << 6) // r / w ++#define USB_CSR01_H_NAKTIMEOUT (1 << 7) // r / clear ++ ++ /** CSR02 register */ ++ // mode-agnostic ++#define USB_CSR02_FLUSHFIFO (1 << 0) // set ++ // Client mode (none) ++ // Host mode ++#define USB_CSR02_H_DISPING (1 << 3) // r / w ++ ++ ++ /** TXCSR1 register */ ++ // mode-agnostic ++#define USB_TXCSR1_TXPKTRDY (1 << 0) ++#define USB_TXCSR1_FIFONOTEMPTY (1 << 1) ++#define USB_TXCSR1_FLUSHFIFO (1 << 3) ++#define USB_TXCSR1_CLRDATATOG (1 << 6) ++ // Client mode ++#define USB_TXCSR1_P_UNDERRUN (1 << 2) ++#define USB_TXCSR1_P_SENDSTALL (1 << 4) ++#define USB_TXCSR1_P_SENTSTALL (1 << 5) ++#define USB_TXCSR1_P_INCOMPTX (1 << 7) ++ // Host MODE ++#define USB_TXCSR1_H_ERROR (1 << 2) ++#define USB_TXCSR1_H_RXSTALL (1 << 5) ++#define USB_TXCSR1_H_NAKTIMEOUT (1 << 7) // for Bulk Endpoint ++#define USB_TXCSR1_H_INCOMPTX (1 << 7) // for Interrupt Endpoint ++ ++ /** TXCSR2 register */ ++ // mode-agnostic ++#define USB_TXCSR2_DMAMODE1 (1 << 2) ++#define USB_TXCSR2_FRCDATATOG (1 << 3) ++#define USB_TXCSR2_DMAENAB (1 << 4) ++#define USB_TXCSR2_MODE (1 << 5) ++ // Host mode ++#define USB_TXCSR2_MODE_TX (1 << 5) ++#define USB_TXCSR2_AUTOSET (1 << 7) ++ // Client mode ++#define USB_TXCSR2_P_ISO (1 << 6) ++ ++ /** RXCSR1 register */ ++ // mode-agnostic ++#define USB_RXCSR1_RXPKTRDY (1 << 0) // r / clear ++#define USB_RXCSR1_FIFOFULL (1 << 1) // r ++#define USB_RXCSR1_DATAERR (1 << 3) // r / clear(host), for ISO only ++#define USB_RXCSR1_FLUSHFIFO (1 << 4) // set ++#define USB_RXCSR1_CLRDATATOG (1 << 7) // set ++ // Client mode ++#define USB_RXCSR1_P_OVERRUN (1 << 2) // r / clear, for ISO only ++#define USB_RXCSR1_P_SENDSTALL (1 << 5) // r / w ++#define USB_RXCSR1_P_SENTSTALL (1 << 6) // r / clear ++ // Host MODE ++#define USB_RXCSR1_H_ERROR (1 << 2) // r / clear ++#define USB_RXCSR1_H_NAKTIMEOUT (1 << 3) // r / clear, for BULK ++#define USB_RXCSR1_H_REQPKT (1 << 5) // r / w ++#define USB_RXCSR1_H_RXSTALL (1 << 6) // r / clear ++ ++ /** RXCSR2 register */ ++ // mode-agnostic ++#define USB_RXCSR2_INCOMPRX (1 << 0) // r ++#define USB_RXCSR2_DMAMODE0 (0 << 3) ++#define USB_RXCSR2_DMAMODE1 (1 << 3) // r / w ++#define USB_RXCSR2_DMAREQENAB (1 << 5) // r / w ++#define USB_RXCSR2_AUTOCLEAR (1 << 7) // r / w ++ ++#define USB_RXCSR2_AUTOREQ (1 << 6) ++#define USB_RXCSR2_DMAENAB (1 << 5) ++#define USB_RXCSR2_DISNYET (1 << 4) ++#define USB_RXCSR2_DMAMODE (1 << 3) ++ ++ // Client mode ++#define USB_RXCSR2_P_DISNYET (1 << 4) // for Bulk/Interrupt transaction ++#define USB_RXCSR2_P_PIDERROR (1 << 4) // for ISO transaction ++#define USB_RXCSR2_P_ISO (1 << 6) ++ // Host Mode ++#define USB_RXCSR2_H_PIDERROR (1 << 4) // r / w, for ISO only ++#define USB_RXCSR2_H_AUTOREQ (1 << 6) // r / w ++ ++ ++ /** IntrUSB/IntrUSBE register */ ++#define USB_INTR_SUSPEND (1 << 0) // client ++#define USB_INTR_RESUME (1 << 1) ++#define USB_INTR_RESET (1 << 2) // client ++#define USB_INTR_BABBLE (1 << 2) // host ++#define USB_INTR_SOF (1 << 3) ++#define USB_INTR_CONNECT (1 << 4) // host ++#define USB_INTR_DISCONNECT (1 << 5) // host/client ++#define USB_INTR_SESSREQ (1 << 6) // A device ++#define USB_INTR_VBUSERROR (1 << 7) // A device ++ ++ ++ /** IntrTX register */ ++ /** IntrRX register */ ++ /** IntrTXE register */ ++ /** IntrRXE register */ ++#define USB_INTR_EP0 (1 << 0) ++#define USB_INTR_EP1 (1 << 1) ++#define USB_INTR_EP2 (1 << 2) ++#define USB_INTR_EP3 (1 << 3) ++#define USB_INTR_EP4 (1 << 4) ++#define USB_INTR_EP5 (1 << 5) ++#define USB_INTR_EPX_MASK (USB_INTR_EP1|USB_INTR_EP2|USB_INTR_EP3|USB_INTR_EP4|USB_INTR_EP5) ++ ++ /** IntrUSB/IntrUSBE register */ ++#define USB_INTR_SUSPEND (1 << 0) // client ++#define USB_INTR_RESUME (1 << 1) ++#define USB_INTR_RESET (1 << 2) // client ++#define USB_INTR_BABBLE (1 << 2) // host ++#define USB_INTR_SOF (1 << 3) ++#define USB_INTR_CONNECT (1 << 4) // host ++#define USB_INTR_DISCONNECT (1 << 5) // host/client ++#define USB_INTR_SESSREQ (1 << 6) // A device ++#define USB_INTR_VBUSERROR (1 << 7) // A device ++ ++ /** RXMAXP register */ ++#define USB_RXMAXP_MASK (0x07FF) ++#define USB_RXMAXP(cnt) ((cnt) & 0x07FF) ++ ++ /** TXTYPE register */ ++#define USB_TXTYPE_EPNUM_MASK (0xf << 0) ++#define USB_TXTYPE_EPNUM(ep) (((ep)&0xf) << 0) ++#define USB_TXTYPE_PROTOCOL_ILLEGAL (0 << 4) ++#define USB_TXTYPE_PROTOCOL_ISO (1 << 4) ++#define USB_TXTYPE_PROTOCOL_BULK (2 << 4) ++#define USB_TXTYPE_PROTOCOL_INTR (3 << 4) ++ ++ /** DevCtl register */ ++#define USB_DEVCTL_SESSION (1 << 0) // host/client ++#define USB_DEVCTL_HOSTREQ (1 << 1) // B device ++#define USB_DEVCTL_HOSTMODE_MASK (1 << 2) ++ ++#define USB_DEVCTL_HOSTMODE_HOST (1 << 2) ++#define USB_DEVCTL_VBUS_MASK (3 << 3) ++ ++#define USB_DEVCTL_LSDEV (1 << 5) ++#define USB_DEVCTL_FSDEV (1 << 6) // host ++#define USB_DEVCTL_BDEVICE_MASK (1 << 7) ++ ++#define USB_DEVCTL_BDEVICE_B (1 << 7) ++ ++#endif /* _ANYKA_USB_REG_DEFINE_H_ */ ++ +diff --git a/include/plat-anyka/usbburn.h b/include/plat-anyka/usbburn.h +new file mode 100644 +index 00000000..08f2917d +--- /dev/null ++++ b/include/plat-anyka/usbburn.h +@@ -0,0 +1,16 @@ ++#ifndef __USB_BURN__ ++#define __USB_BURN__ ++ ++extern int sense_data; // the CSW status ++ ++extern struct semaphore sense_data_lock; // synchronization sense_data ++ ++/* the write function for the usb file_storage */ ++extern int usbburn_write(void *buf, size_t count); ++ ++/* the read function for the usb file_storage */ ++extern int usbburn_read(void *buf, size_t count); ++ ++extern void usbburn_ioctl(void); ++ ++#endif /* __USB_BURN__ */ +diff --git a/include/plat-anyka/wifi.h b/include/plat-anyka/wifi.h +new file mode 100755 +index 00000000..a3058815 +--- /dev/null ++++ b/include/plat-anyka/wifi.h +@@ -0,0 +1,25 @@ ++/* ++ * include/plat-anyka/wifi.h ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef __WIFI_H__ ++#define __WIFI_H__ ++ ++#include ++ ++struct akwifi_platform_data { ++ struct gpio_info gpio_on; ++ struct gpio_info gpio_off; ++ int power_on_delay; ++ int power_off_delay; ++ int total_usb_ep_num; ++ void (* gpio_init) (const struct gpio_info *); ++}; ++ ++#endif /* __WIFI_H__ */ ++ +diff --git a/include/plat-anyka/wrap_sensor.h b/include/plat-anyka/wrap_sensor.h +new file mode 100755 +index 00000000..e161d49e +--- /dev/null ++++ b/include/plat-anyka/wrap_sensor.h +@@ -0,0 +1,114 @@ ++#ifndef __WRAP_SENSOR__ ++#define __WRAP_SENSOR__ ++ ++#include ++#include ++#include ++#include ++ ++#define GPIO_I2C_SCL INVALID_GPIO ++#define GPIO_I2C_SDA INVALID_GPIO ++ ++/** ++ * @brief millisecond delay ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param[in] minisecond minisecond delay number ++ * @return T_VOID ++ */ ++T_VOID mini_delay(T_U32 minisecond); ++ ++/** ++ * @brief anyka specific printf ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param[in] level forbidden level ++ * @param[in] mStr module string ++ * @param[in] s format string ++ * @return T_S32 ++ * @retval 0 is print ok, -1 is forbidden to print ++ */ ++T_S32 akprintf(T_U8 level, T_pCSTR mStr, T_pCSTR s, ...); ++ ++/** ++ * @brief write data to SCCB device ++ * ++ * write size length data to daddr's raddr register ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param[in] daddr SCCB device address ++ * @param[in] raddr register address ++ * @param[in] data write data's point ++ * @param[in] size write data's length ++ * @return T_BOOL return write success or failed ++ * @retval AK_FALSE operate failed ++ * @retval AK_TRUE operate success ++ */ ++T_BOOL sccb_write_data(T_U8 daddr, T_U8 raddr, T_U8 *data, T_U32 size); ++T_BOOL sccb_write_short(T_U8 daddr, T_U16 raddr, T_U8 *data, T_U32 size); ++T_BOOL sccb_write_word(T_U8 daddr, T_U16 raddr, T_U16 *data, T_U32 size); ++ ++/** ++ * @brief read data from SCCB device function ++ * ++ * read data from daddr's raddr register ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param[in] daddr SCCB device address ++ * @param[in] raddr register address ++ * @return T_U8 ++ * @retval read-back data ++ */ ++T_U8 sccb_read_data(T_U8 daddr, T_U8 raddr); ++T_U8 sccb_read_short(T_U8 daddr, T_U16 raddr); ++T_U16 sccb_read_word(T_U8 daddr, T_U16 raddr); ++ ++/*@{*/ ++/** ++ * @brief SCCB interface initialize function ++ * ++ * setup SCCB interface ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param[in] pin_scl the pin assigned to SCL ++ * @param[in] pin_sda the pin assigned to SDA ++ * @return T_VOID ++ */ ++T_VOID sccb_init(T_U32 pin_scl, T_U32 pin_sda); ++ ++ ++/** ++ * @brief set client handle ++ * @author dengzhou ++ * @date 2012-03-19 ++ * @param[in] client handle of I2C open ++ * @return T_VOID ++ */ ++void sensor_set_handle(struct i2c_client *client); ++ ++/** ++ * @brief get GPIO pin value ++ * @author dengzhou ++ * @date 2012-03-16 ++ * @param GPIO pin type ++ * @return GPIO pin value ++ * @retval ++ */ ++T_U32 cam_getpin(T_CAMERA_PINTYPE pin_type); ++ ++#define GPIO_CAMERA_AVDD cam_getpin(PIN_AVDD) ++#define GPIO_CAMERA_CHIP_ENABLE cam_getpin(PIN_POWER) ++#define GPIO_CAMERA_RESET cam_getpin(PIN_RESET) ++ ++#define GPIO_DIR_INPUT AK_GPIO_DIR_INPUT ++#define GPIO_DIR_OUTPUT AK_GPIO_DIR_OUTPUT ++#define GPIO_LEVEL_LOW AK_GPIO_OUT_LOW ++#define GPIO_LEVEL_HIGH AK_GPIO_OUT_HIGH ++ ++#define gpio_set_pin_dir(pin,dir) ak_gpio_cfgpin(pin,dir) ++#define gpio_set_pin_level(pin,level) ak_gpio_setpin(pin,level) ++#define gpio_set_pin_as_gpio(pin) ak_setpin_as_gpio(pin) ++#define gpio_pin_get_ActiveLevel(pin) ak_gpio_getpin(pin) ++ ++#endif ++ +diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h +index f34a5a87..11a4fd05 100644 +--- a/include/scsi/scsi.h ++++ b/include/scsi/scsi.h +@@ -146,6 +146,13 @@ struct scsi_cmnd; + #define SYNCHRONIZE_CACHE_16 0x91 + #define WRITE_SAME_16 0x93 + #define SERVICE_ACTION_IN 0x9e ++ ++#ifdef CONFIG_USB_AKUDC_PRODUCER ++//modified by anyka Zhang Jingyuan ++#define SCSI_ANYKA_UBOOT 0xf1 ++//end of modified by anyka Zhang Jingyuan ++#endif ++ + /* values for service action in */ + #define SAI_READ_CAPACITY_16 0x10 + #define SAI_GET_LBA_STATUS 0x12 +diff --git a/include/sound/ak_pcm.h b/include/sound/ak_pcm.h +new file mode 100644 +index 00000000..559dd858 +--- /dev/null ++++ b/include/sound/ak_pcm.h +@@ -0,0 +1,51 @@ ++#ifndef _AK_PCM_H ++#define _AK_PCM_H ++ ++#include ++#include ++ ++ ++struct ak_codec_dai; ++ ++struct ak_codec_ops { ++ void (*dac_init) (struct ak_codec_dai *dai); ++ void (*dac_exit) (struct ak_codec_dai *dai); ++ ++ void (*adc_init) (struct ak_codec_dai *dai); ++ void (*adc_exit) (struct ak_codec_dai *dai); ++ ++ void (*set_dac_samplerate)(struct ak_codec_dai *dai, unsigned int rate); ++ unsigned long (*set_adc_samplerate)(struct ak_codec_dai *dai, unsigned int rate); ++ ++ void (*set_dac_channels)(struct ak_codec_dai *dai, unsigned int channels); ++ void (*set_adc_channels)(struct ak_codec_dai *dai, unsigned int channels); ++ ++ void (*playback_start)(struct ak_codec_dai *dai); ++ void (*playback_end)(struct ak_codec_dai *dai); ++ ++ void (*capture_start)(struct ak_codec_dai *dai); ++ void (*capture_end)(struct ak_codec_dai *dai); ++ ++ void (*start_to_play) (struct ak_codec_dai *dai, unsigned int channels, unsigned int rate); ++}; ++ ++struct ak_proc_entry { ++ const char *name; ++ void (*cb)(struct snd_info_entry *, struct snd_info_buffer *); ++}; ++ ++struct ak_codec_dai { ++ int num_kcontrols; ++ struct snd_kcontrol_new *kcontrols; ++ int num_pentries; ++ void *entries_private; ++ struct ak_proc_entry *pentries; ++ struct ak_codec_ops *ops; ++ int aec_flag; ++ ++}; ++ ++int ak_codec_register(struct ak_codec_dai *dai); ++void ak_codec_ctl_event(unsigned int iface, unsigned int event, const char* ctl_name); ++ ++#endif +diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h +index 8da3c240..055242e6 100644 +--- a/include/sound/soc-dapm.h ++++ b/include/sound/soc-dapm.h +@@ -432,6 +432,11 @@ enum snd_soc_dapm_type { + snd_soc_dapm_dai, /* link to DAI structure */ + }; + ++enum snd_soc_dapm_subclass { ++ SND_SOC_DAPM_CLASS_INIT = 0, ++ SND_SOC_DAPM_CLASS_PCM = 1, ++}; ++ + /* + * DAPM audio route definition. + * +diff --git a/include/sound/soc.h b/include/sound/soc.h +index 2ebf7877..66fd9bc9 100644 +--- a/include/sound/soc.h ++++ b/include/sound/soc.h +@@ -288,6 +288,11 @@ enum snd_soc_pcm_subclass { + SND_SOC_PCM_CLASS_BE = 1, + }; + ++enum snd_soc_card_subclass { ++ SND_SOC_CARD_CLASS_INIT = 0, ++ SND_SOC_CARD_CLASS_PCM = 1, ++}; ++ + int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, + int source, unsigned int freq, int dir); + int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, +@@ -800,6 +805,7 @@ struct snd_soc_card { + + struct list_head list; + struct mutex mutex; ++ struct mutex dapm_mutex; + + bool instantiated; + +diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h +new file mode 100644 +index 00000000..951e6ca1 +--- /dev/null ++++ b/include/trace/events/cpufreq_interactive.h +@@ -0,0 +1,112 @@ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM cpufreq_interactive ++ ++#if !defined(_TRACE_CPUFREQ_INTERACTIVE_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _TRACE_CPUFREQ_INTERACTIVE_H ++ ++#include ++ ++DECLARE_EVENT_CLASS(set, ++ TP_PROTO(u32 cpu_id, unsigned long targfreq, ++ unsigned long actualfreq), ++ TP_ARGS(cpu_id, targfreq, actualfreq), ++ ++ TP_STRUCT__entry( ++ __field( u32, cpu_id ) ++ __field(unsigned long, targfreq ) ++ __field(unsigned long, actualfreq ) ++ ), ++ ++ TP_fast_assign( ++ __entry->cpu_id = (u32) cpu_id; ++ __entry->targfreq = targfreq; ++ __entry->actualfreq = actualfreq; ++ ), ++ ++ TP_printk("cpu=%u targ=%lu actual=%lu", ++ __entry->cpu_id, __entry->targfreq, ++ __entry->actualfreq) ++); ++ ++DEFINE_EVENT(set, cpufreq_interactive_setspeed, ++ TP_PROTO(u32 cpu_id, unsigned long targfreq, ++ unsigned long actualfreq), ++ TP_ARGS(cpu_id, targfreq, actualfreq) ++); ++ ++DECLARE_EVENT_CLASS(loadeval, ++ TP_PROTO(unsigned long cpu_id, unsigned long load, ++ unsigned long curtarg, unsigned long curactual, ++ unsigned long newtarg), ++ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg), ++ ++ TP_STRUCT__entry( ++ __field(unsigned long, cpu_id ) ++ __field(unsigned long, load ) ++ __field(unsigned long, curtarg ) ++ __field(unsigned long, curactual ) ++ __field(unsigned long, newtarg ) ++ ), ++ ++ TP_fast_assign( ++ __entry->cpu_id = cpu_id; ++ __entry->load = load; ++ __entry->curtarg = curtarg; ++ __entry->curactual = curactual; ++ __entry->newtarg = newtarg; ++ ), ++ ++ TP_printk("cpu=%lu load=%lu cur=%lu actual=%lu targ=%lu", ++ __entry->cpu_id, __entry->load, __entry->curtarg, ++ __entry->curactual, __entry->newtarg) ++); ++ ++DEFINE_EVENT(loadeval, cpufreq_interactive_target, ++ TP_PROTO(unsigned long cpu_id, unsigned long load, ++ unsigned long curtarg, unsigned long curactual, ++ unsigned long newtarg), ++ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) ++); ++ ++DEFINE_EVENT(loadeval, cpufreq_interactive_already, ++ TP_PROTO(unsigned long cpu_id, unsigned long load, ++ unsigned long curtarg, unsigned long curactual, ++ unsigned long newtarg), ++ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) ++); ++ ++DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, ++ TP_PROTO(unsigned long cpu_id, unsigned long load, ++ unsigned long curtarg, unsigned long curactual, ++ unsigned long newtarg), ++ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) ++); ++ ++TRACE_EVENT(cpufreq_interactive_boost, ++ TP_PROTO(const char *s), ++ TP_ARGS(s), ++ TP_STRUCT__entry( ++ __string(s, s) ++ ), ++ TP_fast_assign( ++ __assign_str(s, s); ++ ), ++ TP_printk("%s", __get_str(s)) ++); ++ ++TRACE_EVENT(cpufreq_interactive_unboost, ++ TP_PROTO(const char *s), ++ TP_ARGS(s), ++ TP_STRUCT__entry( ++ __string(s, s) ++ ), ++ TP_fast_assign( ++ __assign_str(s, s); ++ ), ++ TP_printk("%s", __get_str(s)) ++); ++ ++#endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ ++ ++/* This part must be outside protection */ ++#include +diff --git a/include/trace/events/gpu.h b/include/trace/events/gpu.h +new file mode 100644 +index 00000000..09efa71d +--- /dev/null ++++ b/include/trace/events/gpu.h +@@ -0,0 +1,142 @@ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM gpu ++ ++#if !defined(_TRACE_GPU_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _TRACE_GPU_H ++ ++#include ++#include ++ ++#define show_secs_from_ns(ns) \ ++ ({ \ ++ u64 t = ns + (NSEC_PER_USEC / 2); \ ++ do_div(t, NSEC_PER_SEC); \ ++ }) ++ ++#define show_usecs_from_ns(ns) \ ++ ({ \ ++ u64 t = ns + (NSEC_PER_USEC / 2) ; \ ++ u32 rem; \ ++ do_div(t, NSEC_PER_USEC); \ ++ rem = do_div(t, USEC_PER_SEC); \ ++ }) ++ ++/* ++ * The gpu_sched_switch event indicates that a switch from one GPU context to ++ * another occurred on one of the GPU hardware blocks. ++ * ++ * The gpu_name argument identifies the GPU hardware block. Each independently ++ * scheduled GPU hardware block should have a different name. This may be used ++ * in different ways for different GPUs. For example, if a GPU includes ++ * multiple processing cores it may use names "GPU 0", "GPU 1", etc. If a GPU ++ * includes a separately scheduled 2D and 3D hardware block, it might use the ++ * names "2D" and "3D". ++ * ++ * The timestamp argument is the timestamp at which the switch occurred on the ++ * GPU. These timestamps are in units of nanoseconds and must use ++ * approximately the same time as sched_clock, though they need not come from ++ * any CPU clock. The timestamps for a single hardware block must be ++ * monotonically nondecreasing. This means that if a variable compensation ++ * offset is used to translate from some other clock to the sched_clock, then ++ * care must be taken when increasing that offset, and doing so may result in ++ * multiple events with the same timestamp. ++ * ++ * The next_ctx_id argument identifies the next context that was running on ++ * the GPU hardware block. A value of 0 indicates that the hardware block ++ * will be idle. ++ * ++ * The next_prio argument indicates the priority of the next context at the ++ * time of the event. The exact numeric values may mean different things for ++ * different GPUs, but they should follow the rule that lower values indicate a ++ * higher priority. ++ * ++ * The next_job_id argument identifies the batch of work that the GPU will be ++ * working on. This should correspond to a job_id that was previously traced ++ * as a gpu_job_enqueue event when the batch of work was created. ++ */ ++TRACE_EVENT(gpu_sched_switch, ++ ++ TP_PROTO(const char *gpu_name, u64 timestamp, ++ u32 next_ctx_id, s32 next_prio, u32 next_job_id), ++ ++ TP_ARGS(gpu_name, timestamp, next_ctx_id, next_prio, next_job_id), ++ ++ TP_STRUCT__entry( ++ __string( gpu_name, gpu_name ) ++ __field( u64, timestamp ) ++ __field( u32, next_ctx_id ) ++ __field( s32, next_prio ) ++ __field( u32, next_job_id ) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(gpu_name, gpu_name); ++ __entry->timestamp = timestamp; ++ __entry->next_ctx_id = next_ctx_id; ++ __entry->next_prio = next_prio; ++ __entry->next_job_id = next_job_id; ++ ), ++ ++ TP_printk("gpu_name=%s ts=%5llu.%06lu next_ctx_id=%lu next_prio=%ld " ++ "next_job_id=%lu", ++ __get_str(gpu_name), ++ (unsigned long long)show_secs_from_ns(__entry->timestamp), ++ (unsigned long)show_usecs_from_ns(__entry->timestamp), ++ (unsigned long)__entry->next_ctx_id, ++ (long)__entry->next_prio, ++ (unsigned long)__entry->next_job_id) ++); ++ ++/* ++ * The gpu_job_enqueue event indicates that a batch of work has been queued up ++ * to be processed by the GPU. This event is not intended to indicate that ++ * the batch of work has been submitted to the GPU hardware, but rather that ++ * it has been submitted to the GPU kernel driver. ++ * ++ * This event should be traced on the thread that initiated the work being ++ * queued. For example, if a batch of work is submitted to the kernel by a ++ * userland thread, the event should be traced on that thread. ++ * ++ * The ctx_id field identifies the GPU context in which the batch of work ++ * being queued is to be run. ++ * ++ * The job_id field identifies the batch of work being queued within the given ++ * GPU context. The first batch of work submitted for a given GPU context ++ * should have a job_id of 0, and each subsequent batch of work should ++ * increment the job_id by 1. ++ * ++ * The type field identifies the type of the job being enqueued. The job ++ * types may be different for different GPU hardware. For example, a GPU may ++ * differentiate between "2D", "3D", and "compute" jobs. ++ */ ++TRACE_EVENT(gpu_job_enqueue, ++ ++ TP_PROTO(u32 ctx_id, u32 job_id, const char *type), ++ ++ TP_ARGS(ctx_id, job_id, type), ++ ++ TP_STRUCT__entry( ++ __field( u32, ctx_id ) ++ __field( u32, job_id ) ++ __string( type, type ) ++ ), ++ ++ TP_fast_assign( ++ __entry->ctx_id = ctx_id; ++ __entry->job_id = job_id; ++ __assign_str(type, type); ++ ), ++ ++ TP_printk("ctx_id=%lu job_id=%lu type=%s", ++ (unsigned long)__entry->ctx_id, ++ (unsigned long)__entry->job_id, ++ __get_str(type)) ++); ++ ++#undef show_secs_from_ns ++#undef show_usecs_from_ns ++ ++#endif /* _TRACE_GPU_H */ ++ ++/* This part must be outside protection */ ++#include +diff --git a/include/trace/events/power.h b/include/trace/events/power.h +index cae9a94f..243c677c 100644 +--- a/include/trace/events/power.h ++++ b/include/trace/events/power.h +@@ -65,6 +65,40 @@ TRACE_EVENT(machine_suspend, + TP_printk("state=%lu", (unsigned long)__entry->state) + ); + ++DECLARE_EVENT_CLASS(wakeup_source, ++ ++ TP_PROTO(const char *name, unsigned int state), ++ ++ TP_ARGS(name, state), ++ ++ TP_STRUCT__entry( ++ __string( name, name ) ++ __field( u64, state ) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(name, name); ++ __entry->state = state; ++ ), ++ ++ TP_printk("%s state=0x%lx", __get_str(name), ++ (unsigned long)__entry->state) ++); ++ ++DEFINE_EVENT(wakeup_source, wakeup_source_activate, ++ ++ TP_PROTO(const char *name, unsigned int state), ++ ++ TP_ARGS(name, state) ++); ++ ++DEFINE_EVENT(wakeup_source, wakeup_source_deactivate, ++ ++ TP_PROTO(const char *name, unsigned int state), ++ ++ TP_ARGS(name, state) ++); ++ + #ifdef CONFIG_EVENT_POWER_TRACING_DEPRECATED + + /* +@@ -204,6 +238,25 @@ DEFINE_EVENT(clock, clock_set_rate, + TP_ARGS(name, state, cpu_id) + ); + ++TRACE_EVENT(clock_set_parent, ++ ++ TP_PROTO(const char *name, const char *parent_name), ++ ++ TP_ARGS(name, parent_name), ++ ++ TP_STRUCT__entry( ++ __string( name, name ) ++ __string( parent_name, parent_name ) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(name, name); ++ __assign_str(parent_name, parent_name); ++ ), ++ ++ TP_printk("%s parent=%s", __get_str(name), __get_str(parent_name)) ++); ++ + /* + * The power domain events are used for power domains transitions + */ +diff --git a/include/trace/events/sync.h b/include/trace/events/sync.h +new file mode 100644 +index 00000000..f31bc63c +--- /dev/null ++++ b/include/trace/events/sync.h +@@ -0,0 +1,82 @@ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM sync ++ ++#if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _TRACE_SYNC_H ++ ++#include ++#include ++ ++TRACE_EVENT(sync_timeline, ++ TP_PROTO(struct sync_timeline *timeline), ++ ++ TP_ARGS(timeline), ++ ++ TP_STRUCT__entry( ++ __string(name, timeline->name) ++ __array(char, value, 32) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(name, timeline->name); ++ if (timeline->ops->timeline_value_str) { ++ timeline->ops->timeline_value_str(timeline, ++ __entry->value, ++ sizeof(__entry->value)); ++ } else { ++ __entry->value[0] = '\0'; ++ } ++ ), ++ ++ TP_printk("name=%s value=%s", __get_str(name), __entry->value) ++); ++ ++TRACE_EVENT(sync_wait, ++ TP_PROTO(struct sync_fence *fence, int begin), ++ ++ TP_ARGS(fence, begin), ++ ++ TP_STRUCT__entry( ++ __string(name, fence->name) ++ __field(s32, status) ++ __field(u32, begin) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(name, fence->name); ++ __entry->status = fence->status; ++ __entry->begin = begin; ++ ), ++ ++ TP_printk("%s name=%s state=%d", __entry->begin ? "begin" : "end", ++ __get_str(name), __entry->status) ++); ++ ++TRACE_EVENT(sync_pt, ++ TP_PROTO(struct sync_pt *pt), ++ ++ TP_ARGS(pt), ++ ++ TP_STRUCT__entry( ++ __string(timeline, pt->parent->name) ++ __array(char, value, 32) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(timeline, pt->parent->name); ++ if (pt->parent->ops->pt_value_str) { ++ pt->parent->ops->pt_value_str(pt, ++ __entry->value, ++ sizeof(__entry->value)); ++ } else { ++ __entry->value[0] = '\0'; ++ } ++ ), ++ ++ TP_printk("name=%s value=%s", __get_str(timeline), __entry->value) ++ ); ++ ++#endif /* if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) */ ++ ++/* This part must be outside protection */ ++#include +diff --git a/init/Kconfig b/init/Kconfig +index 6cfd71d0..a7cffc83 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -953,6 +953,12 @@ config SYSCTL + config ANON_INODES + bool + ++config PANIC_TIMEOUT ++ int "Default panic timeout" ++ default 0 ++ help ++ Set default panic timeout. ++ + menuconfig EXPERT + bool "Configure standard kernel features (expert users)" + # Unhide debug options, to make the on-by-default options visible +diff --git a/kernel/cgroup.c b/kernel/cgroup.c +index a4c47d1b..c5d69222 100644 +--- a/kernel/cgroup.c ++++ b/kernel/cgroup.c +@@ -287,6 +287,33 @@ static void cgroup_release_agent(struct work_struct *work); + static DECLARE_WORK(release_agent_work, cgroup_release_agent); + static void check_for_release(struct cgroup *cgrp); + ++/* ++ * A queue for waiters to do rmdir() cgroup. A tasks will sleep when ++ * cgroup->count == 0 && list_empty(&cgroup->children) && subsys has some ++ * reference to css->refcnt. In general, this refcnt is expected to goes down ++ * to zero, soon. ++ * ++ * CGRP_WAIT_ON_RMDIR flag is set under cgroup's inode->i_mutex; ++ */ ++static DECLARE_WAIT_QUEUE_HEAD(cgroup_rmdir_waitq); ++ ++static void cgroup_wakeup_rmdir_waiter(struct cgroup *cgrp) ++{ ++ if (unlikely(test_and_clear_bit(CGRP_WAIT_ON_RMDIR, &cgrp->flags))) ++ wake_up_all(&cgroup_rmdir_waitq); ++} ++ ++void cgroup_exclude_rmdir(struct cgroup_subsys_state *css) ++{ ++ css_get(css); ++} ++ ++void cgroup_release_and_wakeup_rmdir(struct cgroup_subsys_state *css) ++{ ++ cgroup_wakeup_rmdir_waiter(css->cgroup); ++ css_put(css); ++} ++ + /* Link structure for associating css_set objects with cgroups */ + struct cg_cgroup_link { + /* +@@ -346,60 +373,51 @@ static struct hlist_head *css_set_hash(struct cgroup_subsys_state *css[]) + return &css_set_table[index]; + } + +-/* We don't maintain the lists running through each css_set to its +- * task until after the first call to cgroup_iter_start(). This +- * reduces the fork()/exit() overhead for people who have cgroups +- * compiled into their kernel but not actually in use */ +-static int use_task_css_set_links __read_mostly; +- +-static void __put_css_set(struct css_set *cg, int taskexit) ++static void free_css_set_work(struct work_struct *work) + { ++ struct css_set *cg = container_of(work, struct css_set, work); + struct cg_cgroup_link *link; + struct cg_cgroup_link *saved_link; +- /* +- * Ensure that the refcount doesn't hit zero while any readers +- * can see it. Similar to atomic_dec_and_lock(), but for an +- * rwlock +- */ +- if (atomic_add_unless(&cg->refcount, -1, 1)) +- return; +- write_lock(&css_set_lock); +- if (!atomic_dec_and_test(&cg->refcount)) { +- write_unlock(&css_set_lock); +- return; +- } +- +- /* This css_set is dead. unlink it and release cgroup refcounts */ +- hlist_del(&cg->hlist); +- css_set_count--; + ++ write_lock(&css_set_lock); + list_for_each_entry_safe(link, saved_link, &cg->cg_links, + cg_link_list) { + struct cgroup *cgrp = link->cgrp; + list_del(&link->cg_link_list); + list_del(&link->cgrp_link_list); +- + /* + * We may not be holding cgroup_mutex, and if cgrp->count is + * dropped to 0 the cgroup can be destroyed at any time, hence + * rcu_read_lock is used to keep it alive. + */ + rcu_read_lock(); +- if (atomic_dec_and_test(&cgrp->count) && +- notify_on_release(cgrp)) { +- if (taskexit) +- set_bit(CGRP_RELEASABLE, &cgrp->flags); ++ if (atomic_dec_and_test(&cgrp->count)) { + check_for_release(cgrp); ++ cgroup_wakeup_rmdir_waiter(cgrp); + } + rcu_read_unlock(); + + kfree(link); + } +- + write_unlock(&css_set_lock); +- kfree_rcu(cg, rcu_head); ++ ++ kfree(cg); + } + ++static void free_css_set_rcu(struct rcu_head *obj) ++{ ++ struct css_set *cg = container_of(obj, struct css_set, rcu_head); ++ ++ INIT_WORK(&cg->work, free_css_set_work); ++ schedule_work(&cg->work); ++} ++ ++/* We don't maintain the lists running through each css_set to its ++ * task until after the first call to cgroup_iter_start(). This ++ * reduces the fork()/exit() overhead for people who have cgroups ++ * compiled into their kernel but not actually in use */ ++static int use_task_css_set_links __read_mostly; ++ + /* + * refcounted get/put for css_set objects + */ +@@ -408,14 +426,26 @@ static inline void get_css_set(struct css_set *cg) + atomic_inc(&cg->refcount); + } + +-static inline void put_css_set(struct css_set *cg) ++static void put_css_set(struct css_set *cg) + { +- __put_css_set(cg, 0); +-} ++ /* ++ * Ensure that the refcount doesn't hit zero while any readers ++ * can see it. Similar to atomic_dec_and_lock(), but for an ++ * rwlock ++ */ ++ if (atomic_add_unless(&cg->refcount, -1, 1)) ++ return; ++ write_lock(&css_set_lock); ++ if (!atomic_dec_and_test(&cg->refcount)) { ++ write_unlock(&css_set_lock); ++ return; ++ } + +-static inline void put_css_set_taskexit(struct css_set *cg) +-{ +- __put_css_set(cg, 1); ++ hlist_del(&cg->hlist); ++ css_set_count--; ++ ++ write_unlock(&css_set_lock); ++ call_rcu(&cg->rcu_head, free_css_set_rcu); + } + + /* +@@ -747,9 +777,9 @@ static struct cgroup *task_cgroup_from_root(struct task_struct *task, + * cgroup_attach_task(), which overwrites one tasks cgroup pointer with + * another. It does so using cgroup_mutex, however there are + * several performance critical places that need to reference +- * task->cgroup without the expense of grabbing a system global ++ * task->cgroups without the expense of grabbing a system global + * mutex. Therefore except as noted below, when dereferencing or, as +- * in cgroup_attach_task(), modifying a task'ss cgroup pointer we use ++ * in cgroup_attach_task(), modifying a task's cgroups pointer we use + * task_lock(), which acts on a spinlock (task->alloc_lock) already in + * the task_struct routinely used for such matters. + * +@@ -938,33 +968,6 @@ static void cgroup_d_remove_dir(struct dentry *dentry) + remove_dir(dentry); + } + +-/* +- * A queue for waiters to do rmdir() cgroup. A tasks will sleep when +- * cgroup->count == 0 && list_empty(&cgroup->children) && subsys has some +- * reference to css->refcnt. In general, this refcnt is expected to goes down +- * to zero, soon. +- * +- * CGRP_WAIT_ON_RMDIR flag is set under cgroup's inode->i_mutex; +- */ +-static DECLARE_WAIT_QUEUE_HEAD(cgroup_rmdir_waitq); +- +-static void cgroup_wakeup_rmdir_waiter(struct cgroup *cgrp) +-{ +- if (unlikely(test_and_clear_bit(CGRP_WAIT_ON_RMDIR, &cgrp->flags))) +- wake_up_all(&cgroup_rmdir_waitq); +-} +- +-void cgroup_exclude_rmdir(struct cgroup_subsys_state *css) +-{ +- css_get(css); +-} +- +-void cgroup_release_and_wakeup_rmdir(struct cgroup_subsys_state *css) +-{ +- cgroup_wakeup_rmdir_waiter(css->cgroup); +- css_put(css); +-} +- + /* + * Call with cgroup_mutex held. Drops reference counts on modules, including + * any duplicate ones that parse_cgroupfs_options took. If this function +@@ -1896,6 +1899,7 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) + struct cgroupfs_root *root = cgrp->root; + struct cgroup_taskset tset = { }; + struct css_set *newcg; ++ struct css_set *cg; + + /* @tsk either already exited or can't exit until the end */ + if (tsk->flags & PF_EXITING) +@@ -1931,14 +1935,20 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) + goto out; + } + ++ task_lock(tsk); ++ cg = tsk->cgroups; ++ get_css_set(cg); ++ task_unlock(tsk); ++ + cgroup_task_migrate(cgrp, oldcgrp, tsk, newcg); + + for_each_subsys(root, ss) { + if (ss->attach) + ss->attach(cgrp, &tset); + } +- +- synchronize_rcu(); ++ set_bit(CGRP_RELEASABLE, &cgrp->flags); ++ /* put_css_set will not destroy cg until after an RCU grace period */ ++ put_css_set(cg); + + /* + * wake up rmdir() waiter. the rmdir should fail since the cgroup +@@ -2139,6 +2149,24 @@ out_free_group_list: + return retval; + } + ++static int cgroup_allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) ++{ ++ struct cgroup_subsys *ss; ++ int ret; ++ ++ for_each_subsys(cgrp->root, ss) { ++ if (ss->allow_attach) { ++ ret = ss->allow_attach(cgrp, tset); ++ if (ret) ++ return ret; ++ } else { ++ return -EACCES; ++ } ++ } ++ ++ return 0; ++} ++ + /* + * Find the task_struct of the task to attach by vpid and pass it along to the + * function to attach either it or all tasks in its threadgroup. Will lock +@@ -2170,9 +2198,18 @@ retry_find_task: + if (cred->euid && + cred->euid != tcred->uid && + cred->euid != tcred->suid) { +- rcu_read_unlock(); +- ret = -EACCES; +- goto out_unlock_cgroup; ++ /* ++ * if the default permission check fails, give each ++ * cgroup a chance to extend the permission check ++ */ ++ struct cgroup_taskset tset = { }; ++ tset.single.task = tsk; ++ tset.single.cgrp = cgrp; ++ ret = cgroup_allow_attach(cgrp, &tset); ++ if (ret) { ++ rcu_read_unlock(); ++ goto out_unlock_cgroup; ++ } + } + } else + tsk = current; +@@ -3789,6 +3826,8 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, + if (err < 0) + goto err_remove; + ++ set_bit(CGRP_RELEASABLE, &parent->flags); ++ + /* The cgroup directory was pre-locked for us */ + BUG_ON(!mutex_is_locked(&cgrp->dentry->d_inode->i_mutex)); + +@@ -3920,6 +3959,21 @@ static int cgroup_clear_css_refs(struct cgroup *cgrp) + return !failed; + } + ++/* checks if all of the css_sets attached to a cgroup have a refcount of 0. ++ * Must be called with css_set_lock held */ ++static int cgroup_css_sets_empty(struct cgroup *cgrp) ++{ ++ struct cg_cgroup_link *link; ++ ++ list_for_each_entry(link, &cgrp->css_sets, cgrp_link_list) { ++ struct css_set *cg = link->cg; ++ if (atomic_read(&cg->refcount) > 0) ++ return 0; ++ } ++ ++ return 1; ++} ++ + static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry) + { + struct cgroup *cgrp = dentry->d_fsdata; +@@ -3932,7 +3986,7 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry) + /* the vfs holds both inode->i_mutex already */ + again: + mutex_lock(&cgroup_mutex); +- if (atomic_read(&cgrp->count) != 0) { ++ if (!cgroup_css_sets_empty(cgrp)) { + mutex_unlock(&cgroup_mutex); + return -EBUSY; + } +@@ -3965,7 +4019,7 @@ again: + + mutex_lock(&cgroup_mutex); + parent = cgrp->parent; +- if (atomic_read(&cgrp->count) || !list_empty(&cgrp->children)) { ++ if (!cgroup_css_sets_empty(cgrp) || !list_empty(&cgrp->children)) { + clear_bit(CGRP_WAIT_ON_RMDIR, &cgrp->flags); + mutex_unlock(&cgroup_mutex); + return -EBUSY; +@@ -4005,7 +4059,6 @@ again: + cgroup_d_remove_dir(d); + dput(d); + +- set_bit(CGRP_RELEASABLE, &parent->flags); + check_for_release(parent); + + /* +@@ -4616,7 +4669,7 @@ void cgroup_exit(struct task_struct *tsk, int run_callbacks) + task_unlock(tsk); + + if (cg) +- put_css_set_taskexit(cg); ++ put_css_set(cg); + } + + /** +@@ -4669,6 +4722,14 @@ static void check_for_release(struct cgroup *cgrp) + } + } + ++/* Caller must verify that the css is not for root cgroup */ ++void __css_get(struct cgroup_subsys_state *css, int count) ++{ ++ atomic_add(count, &css->refcnt); ++ set_bit(CGRP_RELEASABLE, &css->cgroup->flags); ++} ++EXPORT_SYMBOL_GPL(__css_get); ++ + /* Caller must verify that the css is not for root cgroup */ + void __css_put(struct cgroup_subsys_state *css, int count) + { +@@ -4677,10 +4738,7 @@ void __css_put(struct cgroup_subsys_state *css, int count) + rcu_read_lock(); + val = atomic_sub_return(count, &css->refcnt); + if (val == 1) { +- if (notify_on_release(cgrp)) { +- set_bit(CGRP_RELEASABLE, &cgrp->flags); +- check_for_release(cgrp); +- } ++ check_for_release(cgrp); + cgroup_wakeup_rmdir_waiter(cgrp); + } + rcu_read_unlock(); +diff --git a/kernel/cpu.c b/kernel/cpu.c +index 2060c6e5..fb4a5acc 100644 +--- a/kernel/cpu.c ++++ b/kernel/cpu.c +@@ -668,3 +668,23 @@ void init_cpu_online(const struct cpumask *src) + { + cpumask_copy(to_cpumask(cpu_online_bits), src); + } ++ ++static ATOMIC_NOTIFIER_HEAD(idle_notifier); ++ ++void idle_notifier_register(struct notifier_block *n) ++{ ++ atomic_notifier_chain_register(&idle_notifier, n); ++} ++EXPORT_SYMBOL_GPL(idle_notifier_register); ++ ++void idle_notifier_unregister(struct notifier_block *n) ++{ ++ atomic_notifier_chain_unregister(&idle_notifier, n); ++} ++EXPORT_SYMBOL_GPL(idle_notifier_unregister); ++ ++void idle_notifier_call_chain(unsigned long val) ++{ ++ atomic_notifier_call_chain(&idle_notifier, val, NULL); ++} ++EXPORT_SYMBOL_GPL(idle_notifier_call_chain); +diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c +index 0557f24c..35b94ace 100644 +--- a/kernel/debug/debug_core.c ++++ b/kernel/debug/debug_core.c +@@ -85,6 +85,10 @@ static int kgdb_use_con; + bool dbg_is_early = true; + /* Next cpu to become the master debug core */ + int dbg_switch_cpu; ++/* Flag for entering kdb when a panic occurs */ ++static bool break_on_panic = true; ++/* Flag for entering kdb when an exception occurs */ ++static bool break_on_exception = true; + + /* Use kdb or gdbserver mode */ + int dbg_kdb_mode = 1; +@@ -99,6 +103,8 @@ early_param("kgdbcon", opt_kgdb_con); + + module_param(kgdb_use_con, int, 0644); + module_param(kgdbreboot, int, 0644); ++module_param(break_on_panic, bool, 0644); ++module_param(break_on_exception, bool, 0644); + + /* + * Holds information about breakpoints in a kernel. These breakpoints are +@@ -673,6 +679,9 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs) + struct kgdb_state kgdb_var; + struct kgdb_state *ks = &kgdb_var; + ++ if (unlikely(signo != SIGTRAP && !break_on_exception)) ++ return 1; ++ + ks->cpu = raw_smp_processor_id(); + ks->ex_vector = evector; + ks->signo = signo; +@@ -759,6 +768,9 @@ static int kgdb_panic_event(struct notifier_block *self, + unsigned long val, + void *data) + { ++ if (!break_on_panic) ++ return NOTIFY_DONE; ++ + if (dbg_kdb_mode) + kdb_printf("PANIC: %s\n", (char *)data); + kgdb_breakpoint(); +diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c +index 572e604c..81b31bba 100644 +--- a/kernel/debug/kdb/kdb_io.c ++++ b/kernel/debug/kdb/kdb_io.c +@@ -216,7 +216,7 @@ static char *kdb_read(char *buffer, size_t bufsize) + int i; + int diag, dtab_count; + int key; +- ++ static int last_crlf; + + diag = kdbgetintenv("DTABCOUNT", &dtab_count); + if (diag) +@@ -237,6 +237,9 @@ poll_again: + return buffer; + if (key != 9) + tab = 0; ++ if (key != 10 && key != 13) ++ last_crlf = 0; ++ + switch (key) { + case 8: /* backspace */ + if (cp > buffer) { +@@ -254,7 +257,12 @@ poll_again: + *cp = tmp; + } + break; +- case 13: /* enter */ ++ case 10: /* new line */ ++ case 13: /* carriage return */ ++ /* handle \n after \r */ ++ if (last_crlf && last_crlf != key) ++ break; ++ last_crlf = key; + *lastchar++ = '\n'; + *lastchar++ = '\0'; + if (!KDB_STATE(KGDB_TRANS)) { +diff --git a/kernel/fork.c b/kernel/fork.c +index 81633337..bc3398ee 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -158,6 +158,9 @@ struct kmem_cache *vm_area_cachep; + /* SLAB cache for mm_struct structures (tsk->mm) */ + static struct kmem_cache *mm_cachep; + ++/* Notifier list called when a task struct is freed */ ++static ATOMIC_NOTIFIER_HEAD(task_free_notifier); ++ + static void account_kernel_stack(struct thread_info *ti, int account) + { + struct zone *zone = page_zone(virt_to_page(ti)); +@@ -188,6 +191,18 @@ static inline void put_signal_struct(struct signal_struct *sig) + free_signal_struct(sig); + } + ++int task_free_register(struct notifier_block *n) ++{ ++ return atomic_notifier_chain_register(&task_free_notifier, n); ++} ++EXPORT_SYMBOL(task_free_register); ++ ++int task_free_unregister(struct notifier_block *n) ++{ ++ return atomic_notifier_chain_unregister(&task_free_notifier, n); ++} ++EXPORT_SYMBOL(task_free_unregister); ++ + void __put_task_struct(struct task_struct *tsk) + { + WARN_ON(!tsk->exit_state); +@@ -199,6 +214,7 @@ void __put_task_struct(struct task_struct *tsk) + delayacct_tsk_free(tsk); + put_signal_struct(tsk->signal); + ++ atomic_notifier_call_chain(&task_free_notifier, 0, tsk); + if (!profile_handoff_task(tsk)) + free_task(tsk); + } +@@ -677,7 +693,8 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) + + mm = get_task_mm(task); + if (mm && mm != current->mm && +- !ptrace_may_access(task, mode)) { ++ !ptrace_may_access(task, mode) && ++ !capable(CAP_SYS_RESOURCE)) { + mmput(mm); + mm = ERR_PTR(-EACCES); + } +diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c +index 15e53b17..fe4b09cf 100644 +--- a/kernel/irq/pm.c ++++ b/kernel/irq/pm.c +@@ -104,8 +104,13 @@ int check_wakeup_irqs(void) + + for_each_irq_desc(irq, desc) { + if (irqd_is_wakeup_set(&desc->irq_data)) { +- if (desc->istate & IRQS_PENDING) ++ if (desc->istate & IRQS_PENDING) { ++ pr_info("Wakeup IRQ %d %s pending, suspend aborted\n", ++ irq, ++ desc->action && desc->action->name ? ++ desc->action->name : ""); + return -EBUSY; ++ } + continue; + } + /* +diff --git a/kernel/panic.c b/kernel/panic.c +index 9ed023b8..90fd4431 100644 +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -27,13 +27,19 @@ + #define PANIC_TIMER_STEP 100 + #define PANIC_BLINK_SPD 18 + ++/* Machine specific panic information string */ ++char *mach_panic_string; ++ + int panic_on_oops; + static unsigned long tainted_mask; + static int pause_on_oops; + static int pause_on_oops_flag; + static DEFINE_SPINLOCK(pause_on_oops_lock); + +-int panic_timeout; ++#ifndef CONFIG_PANIC_TIMEOUT ++#define CONFIG_PANIC_TIMEOUT 0 ++#endif ++int panic_timeout = CONFIG_PANIC_TIMEOUT; + EXPORT_SYMBOL_GPL(panic_timeout); + + ATOMIC_NOTIFIER_HEAD(panic_notifier_list); +@@ -375,6 +381,11 @@ late_initcall(init_oops_id); + void print_oops_end_marker(void) + { + init_oops_id(); ++ ++ if (mach_panic_string) ++ printk(KERN_WARNING "Board Information: %s\n", ++ mach_panic_string); ++ + printk(KERN_WARNING "---[ end trace %016llx ]---\n", + (unsigned long long)oops_id); + } +diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig +index deb5461e..904fc660 100644 +--- a/kernel/power/Kconfig ++++ b/kernel/power/Kconfig +@@ -18,6 +18,14 @@ config SUSPEND_FREEZER + + Turning OFF this setting is NOT recommended! If in doubt, say Y. + ++config HAS_WAKELOCK ++ bool ++ default y ++ ++config WAKELOCK ++ bool ++ default y ++ + config HIBERNATE_CALLBACKS + bool + +@@ -103,6 +111,33 @@ config PM_SLEEP_SMP + select HOTPLUG + select HOTPLUG_CPU + ++config PM_AUTOSLEEP ++ bool "Opportunistic sleep" ++ depends on PM_SLEEP ++ default n ++ ---help--- ++ Allow the kernel to trigger a system transition into a global sleep ++ state automatically whenever there are no active wakeup sources. ++ ++config PM_WAKELOCKS ++ bool "User space wakeup sources interface" ++ depends on PM_SLEEP ++ default n ++ ---help--- ++ Allow user space to create, activate and deactivate wakeup source ++ objects with the help of a sysfs-based interface. ++ ++config PM_WAKELOCKS_LIMIT ++ int "Maximum number of user space wakeup sources (0 = no limit)" ++ range 0 100000 ++ default 100 ++ depends on PM_WAKELOCKS ++ ++config PM_WAKELOCKS_GC ++ bool "Garbage collector for user space wakeup sources" ++ depends on PM_WAKELOCKS ++ default y ++ + config PM_RUNTIME + bool "Run-time PM core functionality" + depends on !IA64_HP_SIM +@@ -243,3 +278,10 @@ config PM_GENERIC_DOMAINS_RUNTIME + config CPU_PM + bool + depends on SUSPEND || CPU_IDLE ++ ++config SUSPEND_TIME ++ bool "Log time spent in suspend" ++ ---help--- ++ Prints the time spent in suspend in the kernel log, and ++ keeps statistics on the time spent in suspend in ++ /sys/kernel/debug/suspend_time +diff --git a/kernel/power/Makefile b/kernel/power/Makefile +index 66d808ec..8450b85d 100644 +--- a/kernel/power/Makefile ++++ b/kernel/power/Makefile +@@ -9,5 +9,8 @@ obj-$(CONFIG_SUSPEND) += suspend.o + obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o + obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ + block_io.o ++obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o ++obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o ++obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o + + obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o +diff --git a/kernel/power/autosleep.c b/kernel/power/autosleep.c +new file mode 100644 +index 00000000..ca304046 +--- /dev/null ++++ b/kernel/power/autosleep.c +@@ -0,0 +1,127 @@ ++/* ++ * kernel/power/autosleep.c ++ * ++ * Opportunistic sleep support. ++ * ++ * Copyright (C) 2012 Rafael J. Wysocki ++ */ ++ ++#include ++#include ++#include ++ ++#include "power.h" ++ ++static suspend_state_t autosleep_state; ++static struct workqueue_struct *autosleep_wq; ++/* ++ * Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source ++ * is active, otherwise a deadlock with try_to_suspend() is possible. ++ * Alternatively mutex_lock_interruptible() can be used. This will then fail ++ * if an auto_sleep cycle tries to freeze processes. ++ */ ++static DEFINE_MUTEX(autosleep_lock); ++static struct wakeup_source *autosleep_ws; ++ ++static void try_to_suspend(struct work_struct *work) ++{ ++ unsigned int initial_count, final_count; ++ ++ if (!pm_get_wakeup_count(&initial_count, true)) ++ goto out; ++ ++ mutex_lock(&autosleep_lock); ++ ++ if (!pm_save_wakeup_count(initial_count)) { ++ mutex_unlock(&autosleep_lock); ++ goto out; ++ } ++ ++ if (autosleep_state == PM_SUSPEND_ON) { ++ mutex_unlock(&autosleep_lock); ++ return; ++ } ++ if (autosleep_state >= PM_SUSPEND_MAX) ++ hibernate(); ++ else ++ pm_suspend(autosleep_state); ++ ++ mutex_unlock(&autosleep_lock); ++ ++ if (!pm_get_wakeup_count(&final_count, false)) ++ goto out; ++ ++ /* ++ * If the wakeup occured for an unknown reason, wait to prevent the ++ * system from trying to suspend and waking up in a tight loop. ++ */ ++ if (final_count == initial_count) ++ schedule_timeout_uninterruptible(HZ / 2); ++ ++ out: ++ queue_up_suspend_work(); ++} ++ ++static DECLARE_WORK(suspend_work, try_to_suspend); ++ ++void queue_up_suspend_work(void) ++{ ++ if (!work_pending(&suspend_work) && autosleep_state > PM_SUSPEND_ON) ++ queue_work(autosleep_wq, &suspend_work); ++} ++ ++suspend_state_t pm_autosleep_state(void) ++{ ++ return autosleep_state; ++} ++ ++int pm_autosleep_lock(void) ++{ ++ return mutex_lock_interruptible(&autosleep_lock); ++} ++ ++void pm_autosleep_unlock(void) ++{ ++ mutex_unlock(&autosleep_lock); ++} ++ ++int pm_autosleep_set_state(suspend_state_t state) ++{ ++ ++#ifndef CONFIG_HIBERNATION ++ if (state >= PM_SUSPEND_MAX) ++ return -EINVAL; ++#endif ++ ++ __pm_stay_awake(autosleep_ws); ++ ++ mutex_lock(&autosleep_lock); ++ ++ autosleep_state = state; ++ ++ __pm_relax(autosleep_ws); ++ ++ if (state > PM_SUSPEND_ON) { ++ pm_wakep_autosleep_enabled(true); ++ queue_up_suspend_work(); ++ } else { ++ pm_wakep_autosleep_enabled(false); ++ } ++ ++ mutex_unlock(&autosleep_lock); ++ return 0; ++} ++ ++int __init pm_autosleep_init(void) ++{ ++ autosleep_ws = wakeup_source_register("autosleep"); ++ if (!autosleep_ws) ++ return -ENOMEM; ++ ++ autosleep_wq = alloc_ordered_workqueue("autosleep", 0); ++ if (autosleep_wq) ++ return 0; ++ ++ wakeup_source_unregister(autosleep_ws); ++ return -ENOMEM; ++} +diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c +index 52a18173..586521aa 100644 +--- a/kernel/power/hibernate.c ++++ b/kernel/power/hibernate.c +@@ -25,6 +25,8 @@ + #include + #include + #include ++#include ++#include + #include + + #include "power.h" +@@ -728,6 +730,17 @@ static int software_resume(void) + + /* Check if the device is there */ + swsusp_resume_device = name_to_dev_t(resume_file); ++ ++ /* ++ * name_to_dev_t is ineffective to verify parition if resume_file is in ++ * integer format. (e.g. major:minor) ++ */ ++ if (isdigit(resume_file[0]) && resume_wait) { ++ int partno; ++ while (!get_gendisk(swsusp_resume_device, &partno)) ++ msleep(10); ++ } ++ + if (!swsusp_resume_device) { + /* + * Some device discovery might still be in progress; we need +diff --git a/kernel/power/main.c b/kernel/power/main.c +index 1c12581f..428f8a03 100644 +--- a/kernel/power/main.c ++++ b/kernel/power/main.c +@@ -269,8 +269,7 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, + return (s - buf); + } + +-static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, +- const char *buf, size_t n) ++static suspend_state_t decode_state(const char *buf, size_t n) + { + #ifdef CONFIG_SUSPEND + suspend_state_t state = PM_SUSPEND_STANDBY; +@@ -278,27 +277,48 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, + #endif + char *p; + int len; +- int error = -EINVAL; + + p = memchr(buf, '\n', n); + len = p ? p - buf : n; + +- /* First, check if we are requested to hibernate */ +- if (len == 4 && !strncmp(buf, "disk", len)) { +- error = hibernate(); +- goto Exit; +- } ++ /* Check hibernation first. */ ++ if (len == 4 && !strncmp(buf, "disk", len)) ++ return PM_SUSPEND_MAX; + + #ifdef CONFIG_SUSPEND +- for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) { +- if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) { +- error = pm_suspend(state); +- break; +- } +- } ++ for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) ++ if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) ++ return state; + #endif + +- Exit: ++ return PM_SUSPEND_ON; ++} ++ ++static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ suspend_state_t state; ++ int error; ++ ++ error = pm_autosleep_lock(); ++ if (error) ++ return error; ++ ++ if (pm_autosleep_state() > PM_SUSPEND_ON) { ++ error = -EBUSY; ++ goto out; ++ } ++ ++ state = decode_state(buf, n); ++ if (state < PM_SUSPEND_MAX) ++ error = pm_suspend(state); ++ else if (state == PM_SUSPEND_MAX) ++ error = hibernate(); ++ else ++ error = -EINVAL; ++ ++ out: ++ pm_autosleep_unlock(); + return error ? error : n; + } + +@@ -339,7 +359,8 @@ static ssize_t wakeup_count_show(struct kobject *kobj, + { + unsigned int val; + +- return pm_get_wakeup_count(&val) ? sprintf(buf, "%u\n", val) : -EINTR; ++ return pm_get_wakeup_count(&val, true) ? ++ sprintf(buf, "%u\n", val) : -EINTR; + } + + static ssize_t wakeup_count_store(struct kobject *kobj, +@@ -347,15 +368,106 @@ static ssize_t wakeup_count_store(struct kobject *kobj, + const char *buf, size_t n) + { + unsigned int val; ++ int error; ++ ++ error = pm_autosleep_lock(); ++ if (error) ++ return error; ++ ++ if (pm_autosleep_state() > PM_SUSPEND_ON) { ++ error = -EBUSY; ++ goto out; ++ } + ++ error = -EINVAL; + if (sscanf(buf, "%u", &val) == 1) { + if (pm_save_wakeup_count(val)) +- return n; ++ error = n; + } +- return -EINVAL; ++ ++ out: ++ pm_autosleep_unlock(); ++ return error; + } + + power_attr(wakeup_count); ++ ++#ifdef CONFIG_PM_AUTOSLEEP ++static ssize_t autosleep_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ suspend_state_t state = pm_autosleep_state(); ++ ++ if (state == PM_SUSPEND_ON) ++ return sprintf(buf, "off\n"); ++ ++#ifdef CONFIG_SUSPEND ++ if (state < PM_SUSPEND_MAX) ++ return sprintf(buf, "%s\n", valid_state(state) ? ++ pm_states[state] : "error"); ++#endif ++#ifdef CONFIG_HIBERNATION ++ return sprintf(buf, "disk\n"); ++#else ++ return sprintf(buf, "error"); ++#endif ++} ++ ++static ssize_t autosleep_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ suspend_state_t state = decode_state(buf, n); ++ int error; ++ ++ if (state == PM_SUSPEND_ON ++ && strcmp(buf, "off") && strcmp(buf, "off\n")) ++ return -EINVAL; ++ ++ error = pm_autosleep_set_state(state); ++ return error ? error : n; ++} ++ ++power_attr(autosleep); ++#endif /* CONFIG_PM_AUTOSLEEP */ ++ ++#ifdef CONFIG_PM_WAKELOCKS ++static ssize_t wake_lock_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ return pm_show_wakelocks(buf, true); ++} ++ ++static ssize_t wake_lock_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ int error = pm_wake_lock(buf); ++ return error ? error : n; ++} ++ ++power_attr(wake_lock); ++ ++static ssize_t wake_unlock_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ return pm_show_wakelocks(buf, false); ++} ++ ++static ssize_t wake_unlock_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ int error = pm_wake_unlock(buf); ++ return error ? error : n; ++} ++ ++power_attr(wake_unlock); ++ ++#endif /* CONFIG_PM_WAKELOCKS */ + #endif /* CONFIG_PM_SLEEP */ + + #ifdef CONFIG_PM_TRACE +@@ -409,6 +521,13 @@ static struct attribute * g[] = { + #ifdef CONFIG_PM_SLEEP + &pm_async_attr.attr, + &wakeup_count_attr.attr, ++#ifdef CONFIG_PM_AUTOSLEEP ++ &autosleep_attr.attr, ++#endif ++#ifdef CONFIG_PM_WAKELOCKS ++ &wake_lock_attr.attr, ++ &wake_unlock_attr.attr, ++#endif + #ifdef CONFIG_PM_DEBUG + &pm_test_attr.attr, + #endif +@@ -444,7 +563,10 @@ static int __init pm_init(void) + power_kobj = kobject_create_and_add("power", NULL); + if (!power_kobj) + return -ENOMEM; +- return sysfs_create_group(power_kobj, &attr_group); ++ error = sysfs_create_group(power_kobj, &attr_group); ++ if (error) ++ return error; ++ return pm_autosleep_init(); + } + + core_initcall(pm_init); +diff --git a/kernel/power/power.h b/kernel/power/power.h +index 98f3622d..b0bd4bea 100644 +--- a/kernel/power/power.h ++++ b/kernel/power/power.h +@@ -264,3 +264,30 @@ static inline void suspend_thaw_processes(void) + { + } + #endif ++ ++#ifdef CONFIG_PM_AUTOSLEEP ++ ++/* kernel/power/autosleep.c */ ++extern int pm_autosleep_init(void); ++extern int pm_autosleep_lock(void); ++extern void pm_autosleep_unlock(void); ++extern suspend_state_t pm_autosleep_state(void); ++extern int pm_autosleep_set_state(suspend_state_t state); ++ ++#else /* !CONFIG_PM_AUTOSLEEP */ ++ ++static inline int pm_autosleep_init(void) { return 0; } ++static inline int pm_autosleep_lock(void) { return 0; } ++static inline void pm_autosleep_unlock(void) {} ++static inline suspend_state_t pm_autosleep_state(void) { return PM_SUSPEND_ON; } ++ ++#endif /* !CONFIG_PM_AUTOSLEEP */ ++ ++#ifdef CONFIG_PM_WAKELOCKS ++ ++/* kernel/power/wakelock.c */ ++extern ssize_t pm_show_wakelocks(char *buf, bool show_active); ++extern int pm_wake_lock(const char *buf); ++extern int pm_wake_unlock(const char *buf); ++ ++#endif /* !CONFIG_PM_WAKELOCKS */ +diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c +index c8b7446b..e97a689f 100644 +--- a/kernel/power/suspend.c ++++ b/kernel/power/suspend.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + + #include "power.h" +@@ -303,6 +304,18 @@ static int enter_state(suspend_state_t state) + return error; + } + ++static void pm_suspend_marker(char *annotation) ++{ ++ struct timespec ts; ++ struct rtc_time tm; ++ ++ getnstimeofday(&ts); ++ rtc_time_to_tm(ts.tv_sec, &tm); ++ pr_info("PM: suspend %s %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n", ++ annotation, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ++ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); ++} ++ + /** + * pm_suspend - Externally visible function for suspending the system. + * @state: System sleep state to enter. +@@ -317,6 +330,7 @@ int pm_suspend(suspend_state_t state) + if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX) + return -EINVAL; + ++ pm_suspend_marker("entry"); + error = enter_state(state); + if (error) { + suspend_stats.fail++; +@@ -324,6 +338,7 @@ int pm_suspend(suspend_state_t state) + } else { + suspend_stats.success++; + } ++ pm_suspend_marker("exit"); + return error; + } + EXPORT_SYMBOL(pm_suspend); +diff --git a/kernel/power/suspend_time.c b/kernel/power/suspend_time.c +new file mode 100644 +index 00000000..d2a65da9 +--- /dev/null ++++ b/kernel/power/suspend_time.c +@@ -0,0 +1,111 @@ ++/* ++ * debugfs file to track time spent in suspend ++ * ++ * Copyright (c) 2011, Google, Inc. ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct timespec suspend_time_before; ++static unsigned int time_in_suspend_bins[32]; ++ ++#ifdef CONFIG_DEBUG_FS ++static int suspend_time_debug_show(struct seq_file *s, void *data) ++{ ++ int bin; ++ seq_printf(s, "time (secs) count\n"); ++ seq_printf(s, "------------------\n"); ++ for (bin = 0; bin < 32; bin++) { ++ if (time_in_suspend_bins[bin] == 0) ++ continue; ++ seq_printf(s, "%4d - %4d %4u\n", ++ bin ? 1 << (bin - 1) : 0, 1 << bin, ++ time_in_suspend_bins[bin]); ++ } ++ return 0; ++} ++ ++static int suspend_time_debug_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, suspend_time_debug_show, NULL); ++} ++ ++static const struct file_operations suspend_time_debug_fops = { ++ .open = suspend_time_debug_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int __init suspend_time_debug_init(void) ++{ ++ struct dentry *d; ++ ++ d = debugfs_create_file("suspend_time", 0755, NULL, NULL, ++ &suspend_time_debug_fops); ++ if (!d) { ++ pr_err("Failed to create suspend_time debug file\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++late_initcall(suspend_time_debug_init); ++#endif ++ ++static int suspend_time_syscore_suspend(void) ++{ ++ read_persistent_clock(&suspend_time_before); ++ ++ return 0; ++} ++ ++static void suspend_time_syscore_resume(void) ++{ ++ struct timespec after; ++ ++ read_persistent_clock(&after); ++ ++ after = timespec_sub(after, suspend_time_before); ++ ++ time_in_suspend_bins[fls(after.tv_sec)]++; ++ ++ pr_info("Suspended for %lu.%03lu seconds\n", after.tv_sec, ++ after.tv_nsec / NSEC_PER_MSEC); ++} ++ ++static struct syscore_ops suspend_time_syscore_ops = { ++ .suspend = suspend_time_syscore_suspend, ++ .resume = suspend_time_syscore_resume, ++}; ++ ++static int suspend_time_syscore_init(void) ++{ ++ register_syscore_ops(&suspend_time_syscore_ops); ++ ++ return 0; ++} ++ ++static void suspend_time_syscore_exit(void) ++{ ++ unregister_syscore_ops(&suspend_time_syscore_ops); ++} ++module_init(suspend_time_syscore_init); ++module_exit(suspend_time_syscore_exit); +diff --git a/kernel/power/swap.c b/kernel/power/swap.c +index eef311a5..11e22c06 100644 +--- a/kernel/power/swap.c ++++ b/kernel/power/swap.c +@@ -6,7 +6,7 @@ + * + * Copyright (C) 1998,2001-2005 Pavel Machek + * Copyright (C) 2006 Rafael J. Wysocki +- * Copyright (C) 2010 Bojan Smojver ++ * Copyright (C) 2010-2012 Bojan Smojver + * + * This file is released under the GPLv2. + * +@@ -282,14 +282,17 @@ static int write_page(void *buf, sector_t offset, struct bio **bio_chain) + return -ENOSPC; + + if (bio_chain) { +- src = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH); ++ src = (void *)__get_free_page(__GFP_WAIT | __GFP_NOWARN | ++ __GFP_NORETRY); + if (src) { + copy_page(src, buf); + } else { + ret = hib_wait_on_bio_chain(bio_chain); /* Free pages */ + if (ret) + return ret; +- src = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH); ++ src = (void *)__get_free_page(__GFP_WAIT | ++ __GFP_NOWARN | ++ __GFP_NORETRY); + if (src) { + copy_page(src, buf); + } else { +@@ -367,12 +370,17 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, + clear_page(handle->cur); + handle->cur_swap = offset; + handle->k = 0; +- } +- if (bio_chain && low_free_pages() <= handle->reqd_free_pages) { +- error = hib_wait_on_bio_chain(bio_chain); +- if (error) +- goto out; +- handle->reqd_free_pages = reqd_free_pages(); ++ ++ if (bio_chain && low_free_pages() <= handle->reqd_free_pages) { ++ error = hib_wait_on_bio_chain(bio_chain); ++ if (error) ++ goto out; ++ /* ++ * Recalculate the number of required free pages, to ++ * make sure we never take more than half. ++ */ ++ handle->reqd_free_pages = reqd_free_pages(); ++ } + } + out: + return error; +@@ -419,8 +427,9 @@ static int swap_writer_finish(struct swap_map_handle *handle, + /* Maximum number of threads for compression/decompression. */ + #define LZO_THREADS 3 + +-/* Maximum number of pages for read buffering. */ +-#define LZO_READ_PAGES (MAP_PAGE_ENTRIES * 8) ++/* Minimum/maximum number of pages for read buffering. */ ++#define LZO_MIN_RD_PAGES 1024 ++#define LZO_MAX_RD_PAGES 8192 + + + /** +@@ -630,12 +639,6 @@ static int save_image_lzo(struct swap_map_handle *handle, + } + } + +- /* +- * Adjust number of free pages after all allocations have been done. +- * We don't want to run out of pages when writing. +- */ +- handle->reqd_free_pages = reqd_free_pages(); +- + /* + * Start the CRC32 thread. + */ +@@ -657,6 +660,12 @@ static int save_image_lzo(struct swap_map_handle *handle, + goto out_clean; + } + ++ /* ++ * Adjust the number of required free pages after all allocations have ++ * been done. We don't want to run out of pages when writing. ++ */ ++ handle->reqd_free_pages = reqd_free_pages(); ++ + printk(KERN_INFO + "PM: Using %u thread(s) for compression.\n" + "PM: Compressing and saving image data (%u pages) ... ", +@@ -1067,7 +1076,7 @@ static int load_image_lzo(struct swap_map_handle *handle, + unsigned i, thr, run_threads, nr_threads; + unsigned ring = 0, pg = 0, ring_size = 0, + have = 0, want, need, asked = 0; +- unsigned long read_pages; ++ unsigned long read_pages = 0; + unsigned char **page = NULL; + struct dec_data *data = NULL; + struct crc_data *crc = NULL; +@@ -1079,7 +1088,7 @@ static int load_image_lzo(struct swap_map_handle *handle, + nr_threads = num_online_cpus() - 1; + nr_threads = clamp_val(nr_threads, 1, LZO_THREADS); + +- page = vmalloc(sizeof(*page) * LZO_READ_PAGES); ++ page = vmalloc(sizeof(*page) * LZO_MAX_RD_PAGES); + if (!page) { + printk(KERN_ERR "PM: Failed to allocate LZO page\n"); + ret = -ENOMEM; +@@ -1144,15 +1153,22 @@ static int load_image_lzo(struct swap_map_handle *handle, + } + + /* +- * Adjust number of pages for read buffering, in case we are short. ++ * Set the number of pages for read buffering. ++ * This is complete guesswork, because we'll only know the real ++ * picture once prepare_image() is called, which is much later on ++ * during the image load phase. We'll assume the worst case and ++ * say that none of the image pages are from high memory. + */ +- read_pages = (nr_free_pages() - snapshot_get_image_size()) >> 1; +- read_pages = clamp_val(read_pages, LZO_CMP_PAGES, LZO_READ_PAGES); ++ if (low_free_pages() > snapshot_get_image_size()) ++ read_pages = (low_free_pages() - snapshot_get_image_size()) / 2; ++ read_pages = clamp_val(read_pages, LZO_MIN_RD_PAGES, LZO_MAX_RD_PAGES); + + for (i = 0; i < read_pages; i++) { + page[i] = (void *)__get_free_page(i < LZO_CMP_PAGES ? + __GFP_WAIT | __GFP_HIGH : +- __GFP_WAIT); ++ __GFP_WAIT | __GFP_NOWARN | ++ __GFP_NORETRY); ++ + if (!page[i]) { + if (i < LZO_CMP_PAGES) { + ring_size = i; +diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c +new file mode 100644 +index 00000000..c8fba338 +--- /dev/null ++++ b/kernel/power/wakelock.c +@@ -0,0 +1,259 @@ ++/* ++ * kernel/power/wakelock.c ++ * ++ * User space wakeup sources support. ++ * ++ * Copyright (C) 2012 Rafael J. Wysocki ++ * ++ * This code is based on the analogous interface allowing user space to ++ * manipulate wakelocks on Android. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static DEFINE_MUTEX(wakelocks_lock); ++ ++struct wakelock { ++ char *name; ++ struct rb_node node; ++ struct wakeup_source ws; ++#ifdef CONFIG_PM_WAKELOCKS_GC ++ struct list_head lru; ++#endif ++}; ++ ++static struct rb_root wakelocks_tree = RB_ROOT; ++ ++ssize_t pm_show_wakelocks(char *buf, bool show_active) ++{ ++ struct rb_node *node; ++ struct wakelock *wl; ++ char *str = buf; ++ char *end = buf + PAGE_SIZE; ++ ++ mutex_lock(&wakelocks_lock); ++ ++ for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) { ++ wl = rb_entry(node, struct wakelock, node); ++ if (wl->ws.active == show_active) ++ str += scnprintf(str, end - str, "%s ", wl->name); ++ } ++ if (str > buf) ++ str--; ++ ++ str += scnprintf(str, end - str, "\n"); ++ ++ mutex_unlock(&wakelocks_lock); ++ return (str - buf); ++} ++ ++#if CONFIG_PM_WAKELOCKS_LIMIT > 0 ++static unsigned int number_of_wakelocks; ++ ++static inline bool wakelocks_limit_exceeded(void) ++{ ++ return number_of_wakelocks > CONFIG_PM_WAKELOCKS_LIMIT; ++} ++ ++static inline void increment_wakelocks_number(void) ++{ ++ number_of_wakelocks++; ++} ++ ++static inline void decrement_wakelocks_number(void) ++{ ++ number_of_wakelocks--; ++} ++#else /* CONFIG_PM_WAKELOCKS_LIMIT = 0 */ ++static inline bool wakelocks_limit_exceeded(void) { return false; } ++static inline void increment_wakelocks_number(void) {} ++static inline void decrement_wakelocks_number(void) {} ++#endif /* CONFIG_PM_WAKELOCKS_LIMIT */ ++ ++#ifdef CONFIG_PM_WAKELOCKS_GC ++#define WL_GC_COUNT_MAX 100 ++#define WL_GC_TIME_SEC 300 ++ ++static LIST_HEAD(wakelocks_lru_list); ++static unsigned int wakelocks_gc_count; ++ ++static inline void wakelocks_lru_add(struct wakelock *wl) ++{ ++ list_add(&wl->lru, &wakelocks_lru_list); ++} ++ ++static inline void wakelocks_lru_most_recent(struct wakelock *wl) ++{ ++ list_move(&wl->lru, &wakelocks_lru_list); ++} ++ ++static void wakelocks_gc(void) ++{ ++ struct wakelock *wl, *aux; ++ ktime_t now; ++ ++ if (++wakelocks_gc_count <= WL_GC_COUNT_MAX) ++ return; ++ ++ now = ktime_get(); ++ list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) { ++ u64 idle_time_ns; ++ bool active; ++ ++ spin_lock_irq(&wl->ws.lock); ++ idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time)); ++ active = wl->ws.active; ++ spin_unlock_irq(&wl->ws.lock); ++ ++ if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC)) ++ break; ++ ++ if (!active) { ++ wakeup_source_remove(&wl->ws); ++ rb_erase(&wl->node, &wakelocks_tree); ++ list_del(&wl->lru); ++ kfree(wl->name); ++ kfree(wl); ++ decrement_wakelocks_number(); ++ } ++ } ++ wakelocks_gc_count = 0; ++} ++#else /* !CONFIG_PM_WAKELOCKS_GC */ ++static inline void wakelocks_lru_add(struct wakelock *wl) {} ++static inline void wakelocks_lru_most_recent(struct wakelock *wl) {} ++static inline void wakelocks_gc(void) {} ++#endif /* !CONFIG_PM_WAKELOCKS_GC */ ++ ++static struct wakelock *wakelock_lookup_add(const char *name, size_t len, ++ bool add_if_not_found) ++{ ++ struct rb_node **node = &wakelocks_tree.rb_node; ++ struct rb_node *parent = *node; ++ struct wakelock *wl; ++ ++ while (*node) { ++ int diff; ++ ++ parent = *node; ++ wl = rb_entry(*node, struct wakelock, node); ++ diff = strncmp(name, wl->name, len); ++ if (diff == 0) { ++ if (wl->name[len]) ++ diff = -1; ++ else ++ return wl; ++ } ++ if (diff < 0) ++ node = &(*node)->rb_left; ++ else ++ node = &(*node)->rb_right; ++ } ++ if (!add_if_not_found) ++ return ERR_PTR(-EINVAL); ++ ++ if (wakelocks_limit_exceeded()) ++ return ERR_PTR(-ENOSPC); ++ ++ /* Not found, we have to add a new one. */ ++ wl = kzalloc(sizeof(*wl), GFP_KERNEL); ++ if (!wl) ++ return ERR_PTR(-ENOMEM); ++ ++ wl->name = kstrndup(name, len, GFP_KERNEL); ++ if (!wl->name) { ++ kfree(wl); ++ return ERR_PTR(-ENOMEM); ++ } ++ wl->ws.name = wl->name; ++ wakeup_source_add(&wl->ws); ++ rb_link_node(&wl->node, parent, node); ++ rb_insert_color(&wl->node, &wakelocks_tree); ++ wakelocks_lru_add(wl); ++ increment_wakelocks_number(); ++ return wl; ++} ++ ++int pm_wake_lock(const char *buf) ++{ ++ const char *str = buf; ++ struct wakelock *wl; ++ u64 timeout_ns = 0; ++ size_t len; ++ int ret = 0; ++ ++ while (*str && !isspace(*str)) ++ str++; ++ ++ len = str - buf; ++ if (!len) ++ return -EINVAL; ++ ++ if (*str && *str != '\n') { ++ /* Find out if there's a valid timeout string appended. */ ++ ret = kstrtou64(skip_spaces(str), 10, &timeout_ns); ++ if (ret) ++ return -EINVAL; ++ } ++ ++ mutex_lock(&wakelocks_lock); ++ ++ wl = wakelock_lookup_add(buf, len, true); ++ if (IS_ERR(wl)) { ++ ret = PTR_ERR(wl); ++ goto out; ++ } ++ if (timeout_ns) { ++ u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1; ++ ++ do_div(timeout_ms, NSEC_PER_MSEC); ++ __pm_wakeup_event(&wl->ws, timeout_ms); ++ } else { ++ __pm_stay_awake(&wl->ws); ++ } ++ ++ wakelocks_lru_most_recent(wl); ++ ++ out: ++ mutex_unlock(&wakelocks_lock); ++ return ret; ++} ++ ++int pm_wake_unlock(const char *buf) ++{ ++ struct wakelock *wl; ++ size_t len; ++ int ret = 0; ++ ++ len = strlen(buf); ++ if (!len) ++ return -EINVAL; ++ ++ if (buf[len-1] == '\n') ++ len--; ++ ++ if (!len) ++ return -EINVAL; ++ ++ mutex_lock(&wakelocks_lock); ++ ++ wl = wakelock_lookup_add(buf, len, false); ++ if (IS_ERR(wl)) { ++ ret = PTR_ERR(wl); ++ goto out; ++ } ++ __pm_relax(&wl->ws); ++ ++ wakelocks_lru_most_recent(wl); ++ wakelocks_gc(); ++ ++ out: ++ mutex_unlock(&wakelocks_lock); ++ return ret; ++} +diff --git a/kernel/printk.c b/kernel/printk.c +index e95c6622..ffe4b65a 100644 +--- a/kernel/printk.c ++++ b/kernel/printk.c +@@ -56,6 +56,10 @@ void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...) + + #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) + ++#ifdef CONFIG_DEBUG_LL ++extern void printascii(char *); ++#endif ++ + /* printk's without a loglevel use this.. */ + #define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL + +@@ -293,6 +297,53 @@ static inline void boot_delay_msec(void) + } + #endif + ++/* ++ * Return the number of unread characters in the log buffer. ++ */ ++static int log_buf_get_len(void) ++{ ++ return logged_chars; ++} ++ ++/* ++ * Clears the ring-buffer ++ */ ++void log_buf_clear(void) ++{ ++ logged_chars = 0; ++} ++ ++/* ++ * Copy a range of characters from the log buffer. ++ */ ++int log_buf_copy(char *dest, int idx, int len) ++{ ++ int ret, max; ++ bool took_lock = false; ++ ++ if (!oops_in_progress) { ++ raw_spin_lock_irq(&logbuf_lock); ++ took_lock = true; ++ } ++ ++ max = log_buf_get_len(); ++ if (idx < 0 || idx >= max) { ++ ret = -1; ++ } else { ++ if (len > max - idx) ++ len = max - idx; ++ ret = len; ++ idx += (log_end - max); ++ while (len-- > 0) ++ dest[len] = LOG_BUF(idx + len); ++ } ++ ++ if (took_lock) ++ raw_spin_unlock_irq(&logbuf_lock); ++ ++ return ret; ++} ++ + #ifdef CONFIG_SECURITY_DMESG_RESTRICT + int dmesg_restrict = 1; + #else +@@ -895,6 +946,10 @@ asmlinkage int vprintk(const char *fmt, va_list args) + printed_len += vscnprintf(printk_buf + printed_len, + sizeof(printk_buf) - printed_len, fmt, args); + ++#ifdef CONFIG_DEBUG_LL ++ printascii(printk_buf); ++#endif ++ + p = printk_buf; + + /* Read log level and handle special printk prefix */ +@@ -1172,7 +1227,6 @@ static int __cpuinit console_cpu_notify(struct notifier_block *self, + switch (action) { + case CPU_ONLINE: + case CPU_DEAD: +- case CPU_DYING: + case CPU_DOWN_FAILED: + case CPU_UP_CANCELED: + console_lock(); +diff --git a/kernel/sched/core.c b/kernel/sched/core.c +index e1718bc3..d10a575b 100644 +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -7243,13 +7243,24 @@ static inline int preempt_count_equals(int preempt_offset) + return (nested == preempt_offset); + } + ++static int __might_sleep_init_called; ++int __init __might_sleep_init(void) ++{ ++ __might_sleep_init_called = 1; ++ return 0; ++} ++early_initcall(__might_sleep_init); ++ + void __might_sleep(const char *file, int line, int preempt_offset) + { + static unsigned long prev_jiffy; /* ratelimiting */ + + rcu_sleep_check(); /* WARN_ON_ONCE() by default, no rate limit reqd. */ + if ((preempt_count_equals(preempt_offset) && !irqs_disabled()) || +- system_state != SYSTEM_RUNNING || oops_in_progress) ++ oops_in_progress) ++ return; ++ if (system_state != SYSTEM_RUNNING && ++ (!__might_sleep_init_called || system_state != SYSTEM_BOOTING)) + return; + if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy) + return; +@@ -7809,6 +7820,23 @@ static void cpu_cgroup_destroy(struct cgroup *cgrp) + sched_destroy_group(tg); + } + ++static int ++cpu_cgroup_allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) ++{ ++ const struct cred *cred = current_cred(), *tcred; ++ struct task_struct *task; ++ ++ cgroup_taskset_for_each(task, cgrp, tset) { ++ tcred = __task_cred(task); ++ ++ if ((current != task) && !capable(CAP_SYS_NICE) && ++ cred->euid != tcred->uid && cred->euid != tcred->suid) ++ return -EACCES; ++ } ++ ++ return 0; ++} ++ + static int cpu_cgroup_can_attach(struct cgroup *cgrp, + struct cgroup_taskset *tset) + { +@@ -8170,6 +8198,7 @@ struct cgroup_subsys cpu_cgroup_subsys = { + .destroy = cpu_cgroup_destroy, + .can_attach = cpu_cgroup_can_attach, + .attach = cpu_cgroup_attach, ++ .allow_attach = cpu_cgroup_allow_attach, + .exit = cpu_cgroup_exit, + .populate = cpu_cgroup_populate, + .subsys_id = cpu_cgroup_subsys_id, +diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c +index ead03360..e8a7a2c0 100644 +--- a/kernel/sched/rt.c ++++ b/kernel/sched/rt.c +@@ -1983,6 +1983,8 @@ static void watchdog(struct rq *rq, struct task_struct *p) + + static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued) + { ++ struct sched_rt_entity *rt_se = &p->rt; ++ + update_curr_rt(rq); + + watchdog(rq, p); +@@ -2000,12 +2002,15 @@ static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued) + p->rt.time_slice = RR_TIMESLICE; + + /* +- * Requeue to the end of queue if we are not the only element +- * on the queue: ++ * Requeue to the end of queue if we (and all of our ancestors) are the ++ * only element on the queue + */ +- if (p->rt.run_list.prev != p->rt.run_list.next) { +- requeue_task_rt(rq, p, 0); +- set_tsk_need_resched(p); ++ for_each_sched_rt_entity(rt_se) { ++ if (rt_se->run_list.prev != rt_se->run_list.next) { ++ requeue_task_rt(rq, p, 0); ++ set_tsk_need_resched(p); ++ return; ++ } + } + } + +diff --git a/kernel/signal.c b/kernel/signal.c +index a4363a98..563b7711 100644 +--- a/kernel/signal.c ++++ b/kernel/signal.c +@@ -2208,7 +2208,7 @@ relock: + * Now that we woke up, it's crucial if we're supposed to be + * frozen that we freeze now before running anything substantial. + */ +- try_to_freeze(); ++ try_to_freeze_nowarn(); + + spin_lock_irq(&sighand->siglock); + /* +diff --git a/kernel/sysctl.c b/kernel/sysctl.c +index 4ab11879..49f47258 100644 +--- a/kernel/sysctl.c ++++ b/kernel/sysctl.c +@@ -102,6 +102,7 @@ extern char core_pattern[]; + extern unsigned int core_pipe_limit; + extern int pid_max; + extern int min_free_kbytes; ++extern int min_free_order_shift; + extern int pid_max_min, pid_max_max; + extern int sysctl_drop_caches; + extern int percpu_pagelist_fraction; +@@ -1198,6 +1199,13 @@ static struct ctl_table vm_table[] = { + .proc_handler = min_free_kbytes_sysctl_handler, + .extra1 = &zero, + }, ++ { ++ .procname = "min_free_order_shift", ++ .data = &min_free_order_shift, ++ .maxlen = sizeof(min_free_order_shift), ++ .mode = 0644, ++ .proc_handler = &proc_dointvec ++ }, + { + .procname = "percpu_pagelist_fraction", + .data = &percpu_pagelist_fraction, +diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c +index 8a538c55..f979d852 100644 +--- a/kernel/time/alarmtimer.c ++++ b/kernel/time/alarmtimer.c +@@ -46,6 +46,8 @@ static struct alarm_base { + static ktime_t freezer_delta; + static DEFINE_SPINLOCK(freezer_delta_lock); + ++static struct wakeup_source *ws; ++ + #ifdef CONFIG_RTC_CLASS + /* rtc timer and device for setting alarm wakeups at suspend */ + static struct rtc_timer rtctimer; +@@ -59,7 +61,7 @@ static DEFINE_SPINLOCK(rtcdev_lock); + * If one has not already been chosen, it checks to see if a + * functional rtc device is available. + */ +-static struct rtc_device *alarmtimer_get_rtcdev(void) ++struct rtc_device *alarmtimer_get_rtcdev(void) + { + unsigned long flags; + struct rtc_device *ret; +@@ -115,7 +117,7 @@ static void alarmtimer_rtc_interface_remove(void) + class_interface_unregister(&alarmtimer_rtc_interface); + } + #else +-static inline struct rtc_device *alarmtimer_get_rtcdev(void) ++struct rtc_device *alarmtimer_get_rtcdev(void) + { + return NULL; + } +@@ -250,6 +252,7 @@ static int alarmtimer_suspend(struct device *dev) + unsigned long flags; + struct rtc_device *rtc; + int i; ++ int ret; + + spin_lock_irqsave(&freezer_delta_lock, flags); + min = freezer_delta; +@@ -279,8 +282,10 @@ static int alarmtimer_suspend(struct device *dev) + if (min.tv64 == 0) + return 0; + +- /* XXX - Should we enforce a minimum sleep time? */ +- WARN_ON(min.tv64 < NSEC_PER_SEC); ++ if (ktime_to_ns(min) < 2 * NSEC_PER_SEC) { ++ __pm_wakeup_event(ws, 2 * MSEC_PER_SEC); ++ return -EBUSY; ++ } + + /* Setup an rtc timer to fire that far in the future */ + rtc_timer_cancel(rtc, &rtctimer); +@@ -288,9 +293,11 @@ static int alarmtimer_suspend(struct device *dev) + now = rtc_tm_to_ktime(tm); + now = ktime_add(now, min); + +- rtc_timer_start(rtc, &rtctimer, now, ktime_set(0, 0)); +- +- return 0; ++ /* Set alarm, if in the past reject suspend briefly to handle */ ++ ret = rtc_timer_start(rtc, &rtctimer, now, ktime_set(0, 0)); ++ if (ret < 0) ++ __pm_wakeup_event(ws, 1 * MSEC_PER_SEC); ++ return ret; + } + #else + static int alarmtimer_suspend(struct device *dev) +@@ -821,6 +828,7 @@ static int __init alarmtimer_init(void) + error = PTR_ERR(pdev); + goto out_drv; + } ++ ws = wakeup_source_register("alarmtimer"); + return 0; + + out_drv: +diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c +index eff0b1e9..0a462de7 100644 +--- a/kernel/time/timekeeping.c ++++ b/kernel/time/timekeeping.c +@@ -1193,7 +1193,7 @@ void get_monotonic_boottime(struct timespec *ts) + } while (read_seqretry(&timekeeper.lock, seq)); + + set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec, +- ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec + nsecs); ++ (s64)ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec + nsecs); + } + EXPORT_SYMBOL_GPL(get_monotonic_boottime); + +diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig +index a1d2849f..59a80f8a 100644 +--- a/kernel/trace/Kconfig ++++ b/kernel/trace/Kconfig +@@ -69,6 +69,9 @@ config EVENT_TRACING + select CONTEXT_SWITCH_TRACER + bool + ++config GPU_TRACEPOINTS ++ bool ++ + config EVENT_POWER_TRACING_DEPRECATED + depends on EVENT_TRACING + bool "Deprecated power event trace API, to be removed" +diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile +index 5f39a07f..fedcd61d 100644 +--- a/kernel/trace/Makefile ++++ b/kernel/trace/Makefile +@@ -61,5 +61,6 @@ endif + ifeq ($(CONFIG_TRACING),y) + obj-$(CONFIG_KGDB_KDB) += trace_kdb.o + endif ++obj-$(CONFIG_GPU_TRACEPOINTS) += gpu-traces.o + + libftrace-y := ftrace.o +diff --git a/kernel/trace/gpu-traces.c b/kernel/trace/gpu-traces.c +new file mode 100644 +index 00000000..a4b3f00f +--- /dev/null ++++ b/kernel/trace/gpu-traces.c +@@ -0,0 +1,23 @@ ++/* ++ * GPU tracepoints ++ * ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++ ++#define CREATE_TRACE_POINTS ++#include ++ ++EXPORT_TRACEPOINT_SYMBOL(gpu_sched_switch); ++EXPORT_TRACEPOINT_SYMBOL(gpu_job_enqueue); +diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c +index 55e4d4c5..418c79b0 100644 +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -482,6 +482,7 @@ static const char *trace_options[] = { + "overwrite", + "disable_on_free", + "irq-info", ++ "print-tgid", + NULL + }; + +@@ -970,6 +971,7 @@ void tracing_reset_current_online_cpus(void) + static unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1]; + static unsigned map_cmdline_to_pid[SAVED_CMDLINES]; + static char saved_cmdlines[SAVED_CMDLINES][TASK_COMM_LEN]; ++static unsigned saved_tgids[SAVED_CMDLINES]; + static int cmdline_idx; + static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED; + +@@ -1117,6 +1119,7 @@ static void trace_save_cmdline(struct task_struct *tsk) + } + + memcpy(&saved_cmdlines[idx], tsk->comm, TASK_COMM_LEN); ++ saved_tgids[idx] = tsk->tgid; + + arch_spin_unlock(&trace_cmdline_lock); + } +@@ -1152,6 +1155,25 @@ void trace_find_cmdline(int pid, char comm[]) + preempt_enable(); + } + ++int trace_find_tgid(int pid) ++{ ++ unsigned map; ++ int tgid; ++ ++ preempt_disable(); ++ arch_spin_lock(&trace_cmdline_lock); ++ map = map_pid_to_cmdline[pid]; ++ if (map != NO_CMDLINE_MAP) ++ tgid = saved_tgids[map]; ++ else ++ tgid = -1; ++ ++ arch_spin_unlock(&trace_cmdline_lock); ++ preempt_enable(); ++ ++ return tgid; ++} ++ + void tracing_record_cmdline(struct task_struct *tsk) + { + if (atomic_read(&trace_record_cmdline_disabled) || !tracer_enabled || +@@ -1960,6 +1982,13 @@ static void print_func_help_header(struct trace_array *tr, struct seq_file *m) + seq_puts(m, "# | | | | |\n"); + } + ++static void print_func_help_header_tgid(struct trace_array *tr, struct seq_file *m) ++{ ++ print_event_info(tr, m); ++ seq_puts(m, "# TASK-PID TGID CPU# TIMESTAMP FUNCTION\n"); ++ seq_puts(m, "# | | | | | |\n"); ++} ++ + static void print_func_help_header_irq(struct trace_array *tr, struct seq_file *m) + { + print_event_info(tr, m); +@@ -1972,6 +2001,18 @@ static void print_func_help_header_irq(struct trace_array *tr, struct seq_file * + seq_puts(m, "# | | | |||| | |\n"); + } + ++static void print_func_help_header_irq_tgid(struct trace_array *tr, struct seq_file *m) ++{ ++ print_event_info(tr, m); ++ seq_puts(m, "# _-----=> irqs-off\n"); ++ seq_puts(m, "# / _----=> need-resched\n"); ++ seq_puts(m, "# | / _---=> hardirq/softirq\n"); ++ seq_puts(m, "# || / _--=> preempt-depth\n"); ++ seq_puts(m, "# ||| / delay\n"); ++ seq_puts(m, "# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION\n"); ++ seq_puts(m, "# | | | | |||| | |\n"); ++} ++ + void + print_trace_header(struct seq_file *m, struct trace_iterator *iter) + { +@@ -2264,9 +2305,15 @@ void trace_default_header(struct seq_file *m) + } else { + if (!(trace_flags & TRACE_ITER_VERBOSE)) { + if (trace_flags & TRACE_ITER_IRQ_INFO) +- print_func_help_header_irq(iter->tr, m); ++ if (trace_flags & TRACE_ITER_TGID) ++ print_func_help_header_irq_tgid(iter->tr, m); ++ else ++ print_func_help_header_irq(iter->tr, m); + else +- print_func_help_header(iter->tr, m); ++ if (trace_flags & TRACE_ITER_TGID) ++ print_func_help_header_tgid(iter->tr, m); ++ else ++ print_func_help_header(iter->tr, m); + } + } + } +@@ -2898,9 +2945,53 @@ tracing_saved_cmdlines_read(struct file *file, char __user *ubuf, + } + + static const struct file_operations tracing_saved_cmdlines_fops = { +- .open = tracing_open_generic, +- .read = tracing_saved_cmdlines_read, +- .llseek = generic_file_llseek, ++ .open = tracing_open_generic, ++ .read = tracing_saved_cmdlines_read, ++ .llseek = generic_file_llseek, ++}; ++ ++static ssize_t ++tracing_saved_tgids_read(struct file *file, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ char *file_buf; ++ char *buf; ++ int len = 0; ++ int pid; ++ int i; ++ ++ file_buf = kmalloc(SAVED_CMDLINES*(16+1+16), GFP_KERNEL); ++ if (!file_buf) ++ return -ENOMEM; ++ ++ buf = file_buf; ++ ++ for (i = 0; i < SAVED_CMDLINES; i++) { ++ int tgid; ++ int r; ++ ++ pid = map_cmdline_to_pid[i]; ++ if (pid == -1 || pid == NO_CMDLINE_MAP) ++ continue; ++ ++ tgid = trace_find_tgid(pid); ++ r = sprintf(buf, "%d %d\n", pid, tgid); ++ buf += r; ++ len += r; ++ } ++ ++ len = simple_read_from_buffer(ubuf, cnt, ppos, ++ file_buf, len); ++ ++ kfree(file_buf); ++ ++ return len; ++} ++ ++static const struct file_operations tracing_saved_tgids_fops = { ++ .open = tracing_open_generic, ++ .read = tracing_saved_tgids_read, ++ .llseek = generic_file_llseek, + }; + + static ssize_t +@@ -4736,6 +4827,9 @@ static __init int tracer_init_debugfs(void) + trace_create_file("saved_cmdlines", 0444, d_tracer, + NULL, &tracing_saved_cmdlines_fops); + ++ trace_create_file("saved_tgids", 0444, d_tracer, ++ NULL, &tracing_saved_tgids_fops); ++ + trace_create_file("trace_clock", 0644, d_tracer, NULL, + &trace_clock_fops); + +diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h +index f95d65da..6d83991c 100644 +--- a/kernel/trace/trace.h ++++ b/kernel/trace/trace.h +@@ -456,6 +456,7 @@ static inline void __trace_stack(struct trace_array *tr, unsigned long flags, + extern cycle_t ftrace_now(int cpu); + + extern void trace_find_cmdline(int pid, char comm[]); ++extern int trace_find_tgid(int pid); + + #ifdef CONFIG_DYNAMIC_FTRACE + extern unsigned long ftrace_update_tot_cnt; +@@ -667,6 +668,7 @@ enum trace_iterator_flags { + TRACE_ITER_OVERWRITE = 0x200000, + TRACE_ITER_STOP_ON_FREE = 0x400000, + TRACE_ITER_IRQ_INFO = 0x800000, ++ TRACE_ITER_TGID = 0x1000000, + }; + + /* +diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c +index a7d2a4c6..f7f1f37a 100644 +--- a/kernel/trace/trace_functions_graph.c ++++ b/kernel/trace/trace_functions_graph.c +@@ -46,6 +46,8 @@ struct fgraph_data { + #define TRACE_GRAPH_PRINT_DURATION 0x10 + #define TRACE_GRAPH_PRINT_ABS_TIME 0x20 + #define TRACE_GRAPH_PRINT_IRQS 0x40 ++#define TRACE_GRAPH_PRINT_FLAT 0x80 ++ + + static struct tracer_opt trace_opts[] = { + /* Display overruns? (for self-debug purpose) */ +@@ -62,6 +64,8 @@ static struct tracer_opt trace_opts[] = { + { TRACER_OPT(funcgraph-abstime, TRACE_GRAPH_PRINT_ABS_TIME) }, + /* Display interrupts */ + { TRACER_OPT(funcgraph-irqs, TRACE_GRAPH_PRINT_IRQS) }, ++ /* Use standard trace formatting rather than hierarchical */ ++ { TRACER_OPT(funcgraph-flat, TRACE_GRAPH_PRINT_FLAT) }, + { } /* Empty entry */ + }; + +@@ -1222,6 +1226,9 @@ print_graph_function_flags(struct trace_iterator *iter, u32 flags) + int cpu = iter->cpu; + int ret; + ++ if (flags & TRACE_GRAPH_PRINT_FLAT) ++ return TRACE_TYPE_UNHANDLED; ++ + if (data && per_cpu_ptr(data->cpu_data, cpu)->ignore) { + per_cpu_ptr(data->cpu_data, cpu)->ignore = 0; + return TRACE_TYPE_HANDLED; +@@ -1279,13 +1286,6 @@ print_graph_function(struct trace_iterator *iter) + return print_graph_function_flags(iter, tracer_flags.val); + } + +-static enum print_line_t +-print_graph_function_event(struct trace_iterator *iter, int flags, +- struct trace_event *event) +-{ +- return print_graph_function(iter); +-} +- + static void print_lat_header(struct seq_file *s, u32 flags) + { + static const char spaces[] = " " /* 16 spaces */ +@@ -1352,6 +1352,11 @@ void print_graph_headers_flags(struct seq_file *s, u32 flags) + { + struct trace_iterator *iter = s->private; + ++ if (flags & TRACE_GRAPH_PRINT_FLAT) { ++ trace_default_header(s); ++ return; ++ } ++ + if (!(trace_flags & TRACE_ITER_CONTEXT_INFO)) + return; + +@@ -1422,20 +1427,6 @@ static int func_graph_set_flag(u32 old_flags, u32 bit, int set) + return 0; + } + +-static struct trace_event_functions graph_functions = { +- .trace = print_graph_function_event, +-}; +- +-static struct trace_event graph_trace_entry_event = { +- .type = TRACE_GRAPH_ENT, +- .funcs = &graph_functions, +-}; +- +-static struct trace_event graph_trace_ret_event = { +- .type = TRACE_GRAPH_RET, +- .funcs = &graph_functions +-}; +- + static struct tracer graph_trace __read_mostly = { + .name = "function_graph", + .open = graph_trace_open, +@@ -1458,16 +1449,6 @@ static __init int init_graph_trace(void) + { + max_bytes_for_cpu = snprintf(NULL, 0, "%d", nr_cpu_ids - 1); + +- if (!register_ftrace_event(&graph_trace_entry_event)) { +- pr_warning("Warning: could not register graph trace events\n"); +- return 1; +- } +- +- if (!register_ftrace_event(&graph_trace_ret_event)) { +- pr_warning("Warning: could not register graph trace events\n"); +- return 1; +- } +- + return register_tracer(&graph_trace); + } + +diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c +index df611a0e..cb29ce20 100644 +--- a/kernel/trace/trace_output.c ++++ b/kernel/trace/trace_output.c +@@ -630,11 +630,25 @@ int trace_print_context(struct trace_iterator *iter) + unsigned long secs = (unsigned long)t; + char comm[TASK_COMM_LEN]; + int ret; ++ int tgid; + + trace_find_cmdline(entry->pid, comm); + +- ret = trace_seq_printf(s, "%16s-%-5d [%03d] ", +- comm, entry->pid, iter->cpu); ++ ret = trace_seq_printf(s, "%16s-%-5d ", comm, entry->pid); ++ if (!ret) ++ return 0; ++ ++ if (trace_flags & TRACE_ITER_TGID) { ++ tgid = trace_find_tgid(entry->pid); ++ if (tgid < 0) ++ ret = trace_seq_puts(s, "(-----) "); ++ else ++ ret = trace_seq_printf(s, "(%5d) ", tgid); ++ if (!ret) ++ return 0; ++ } ++ ++ ret = trace_seq_printf(s, "[%03d] ", iter->cpu); + if (!ret) + return 0; + +@@ -966,6 +980,168 @@ static struct trace_event trace_fn_event = { + .funcs = &trace_fn_funcs, + }; + ++/* TRACE_GRAPH_ENT */ ++static enum print_line_t trace_graph_ent_trace(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct trace_seq *s = &iter->seq; ++ struct ftrace_graph_ent_entry *field; ++ ++ trace_assign_type(field, iter->ent); ++ ++ if (!trace_seq_puts(s, "graph_ent: func=")) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ if (!seq_print_ip_sym(s, field->graph_ent.func, flags)) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ if (!trace_seq_puts(s, "\n")) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t trace_graph_ent_raw(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct ftrace_graph_ent_entry *field; ++ ++ trace_assign_type(field, iter->ent); ++ ++ if (!trace_seq_printf(&iter->seq, "%lx %d\n", ++ field->graph_ent.func, ++ field->graph_ent.depth)) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t trace_graph_ent_hex(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct ftrace_graph_ent_entry *field; ++ struct trace_seq *s = &iter->seq; ++ ++ trace_assign_type(field, iter->ent); ++ ++ SEQ_PUT_HEX_FIELD_RET(s, field->graph_ent.func); ++ SEQ_PUT_HEX_FIELD_RET(s, field->graph_ent.depth); ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t trace_graph_ent_bin(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct ftrace_graph_ent_entry *field; ++ struct trace_seq *s = &iter->seq; ++ ++ trace_assign_type(field, iter->ent); ++ ++ SEQ_PUT_FIELD_RET(s, field->graph_ent.func); ++ SEQ_PUT_FIELD_RET(s, field->graph_ent.depth); ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static struct trace_event_functions trace_graph_ent_funcs = { ++ .trace = trace_graph_ent_trace, ++ .raw = trace_graph_ent_raw, ++ .hex = trace_graph_ent_hex, ++ .binary = trace_graph_ent_bin, ++}; ++ ++static struct trace_event trace_graph_ent_event = { ++ .type = TRACE_GRAPH_ENT, ++ .funcs = &trace_graph_ent_funcs, ++}; ++ ++/* TRACE_GRAPH_RET */ ++static enum print_line_t trace_graph_ret_trace(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct trace_seq *s = &iter->seq; ++ struct trace_entry *entry = iter->ent; ++ struct ftrace_graph_ret_entry *field; ++ ++ trace_assign_type(field, entry); ++ ++ if (!trace_seq_puts(s, "graph_ret: func=")) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ if (!seq_print_ip_sym(s, field->ret.func, flags)) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ if (!trace_seq_puts(s, "\n")) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t trace_graph_ret_raw(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct ftrace_graph_ret_entry *field; ++ ++ trace_assign_type(field, iter->ent); ++ ++ if (!trace_seq_printf(&iter->seq, "%lx %lld %lld %ld %d\n", ++ field->ret.func, ++ field->ret.calltime, ++ field->ret.rettime, ++ field->ret.overrun, ++ field->ret.depth)); ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t trace_graph_ret_hex(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct ftrace_graph_ret_entry *field; ++ struct trace_seq *s = &iter->seq; ++ ++ trace_assign_type(field, iter->ent); ++ ++ SEQ_PUT_HEX_FIELD_RET(s, field->ret.func); ++ SEQ_PUT_HEX_FIELD_RET(s, field->ret.calltime); ++ SEQ_PUT_HEX_FIELD_RET(s, field->ret.rettime); ++ SEQ_PUT_HEX_FIELD_RET(s, field->ret.overrun); ++ SEQ_PUT_HEX_FIELD_RET(s, field->ret.depth); ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t trace_graph_ret_bin(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct ftrace_graph_ret_entry *field; ++ struct trace_seq *s = &iter->seq; ++ ++ trace_assign_type(field, iter->ent); ++ ++ SEQ_PUT_FIELD_RET(s, field->ret.func); ++ SEQ_PUT_FIELD_RET(s, field->ret.calltime); ++ SEQ_PUT_FIELD_RET(s, field->ret.rettime); ++ SEQ_PUT_FIELD_RET(s, field->ret.overrun); ++ SEQ_PUT_FIELD_RET(s, field->ret.depth); ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static struct trace_event_functions trace_graph_ret_funcs = { ++ .trace = trace_graph_ret_trace, ++ .raw = trace_graph_ret_raw, ++ .hex = trace_graph_ret_hex, ++ .binary = trace_graph_ret_bin, ++}; ++ ++static struct trace_event trace_graph_ret_event = { ++ .type = TRACE_GRAPH_RET, ++ .funcs = &trace_graph_ret_funcs, ++}; ++ + /* TRACE_CTX an TRACE_WAKE */ + static enum print_line_t trace_ctxwake_print(struct trace_iterator *iter, + char *delim) +@@ -1298,6 +1474,8 @@ static struct trace_event trace_print_event = { + + static struct trace_event *events[] __initdata = { + &trace_fn_event, ++ &trace_graph_ent_event, ++ &trace_graph_ret_event, + &trace_ctx_event, + &trace_wake_event, + &trace_stack_event, +diff --git a/kernel/watchdog.c b/kernel/watchdog.c +index 991aa938..19c81d8e 100644 +--- a/kernel/watchdog.c ++++ b/kernel/watchdog.c +@@ -39,6 +39,8 @@ static DEFINE_PER_CPU(bool, hard_watchdog_warn); + static DEFINE_PER_CPU(bool, watchdog_nmi_touch); + static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts); + static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved); ++#endif ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI + static DEFINE_PER_CPU(struct perf_event *, watchdog_ev); + #endif + +@@ -174,7 +176,7 @@ void touch_softlockup_watchdog_sync(void) + __raw_get_cpu_var(watchdog_touch_ts) = 0; + } + +-#ifdef CONFIG_HARDLOCKUP_DETECTOR ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI + /* watchdog detector functions */ + static int is_hardlockup(void) + { +@@ -188,6 +190,61 @@ static int is_hardlockup(void) + } + #endif + ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU ++static int is_hardlockup_other_cpu(int cpu) ++{ ++ unsigned long hrint = per_cpu(hrtimer_interrupts, cpu); ++ ++ if (per_cpu(hrtimer_interrupts_saved, cpu) == hrint) ++ return 1; ++ ++ per_cpu(hrtimer_interrupts_saved, cpu) = hrint; ++ return 0; ++} ++ ++static void watchdog_check_hardlockup_other_cpu(void) ++{ ++ int cpu; ++ ++ /* ++ * Test for hardlockups every 3 samples. The sample period is ++ * watchdog_thresh * 2 / 5, so 3 samples gets us back to slightly over ++ * watchdog_thresh (over by 20%). ++ */ ++ if (__this_cpu_read(hrtimer_interrupts) % 3 != 0) ++ return; ++ ++ /* check for a hardlockup on the next cpu */ ++ cpu = cpumask_next(smp_processor_id(), cpu_online_mask); ++ if (cpu >= nr_cpu_ids) ++ cpu = cpumask_first(cpu_online_mask); ++ if (cpu == smp_processor_id()) ++ return; ++ ++ if (per_cpu(watchdog_nmi_touch, cpu) == true) { ++ per_cpu(watchdog_nmi_touch, cpu) = false; ++ return; ++ } ++ ++ if (is_hardlockup_other_cpu(cpu)) { ++ /* only warn once */ ++ if (per_cpu(hard_watchdog_warn, cpu) == true) ++ return; ++ ++ if (hardlockup_panic) ++ panic("Watchdog detected hard LOCKUP on cpu %d", cpu); ++ else ++ WARN(1, "Watchdog detected hard LOCKUP on cpu %d", cpu); ++ ++ per_cpu(hard_watchdog_warn, cpu) = true; ++ } else { ++ per_cpu(hard_watchdog_warn, cpu) = false; ++ } ++} ++#else ++static inline void watchdog_check_hardlockup_other_cpu(void) { return; } ++#endif ++ + static int is_softlockup(unsigned long touch_ts) + { + unsigned long now = get_timestamp(smp_processor_id()); +@@ -199,7 +256,7 @@ static int is_softlockup(unsigned long touch_ts) + return 0; + } + +-#ifdef CONFIG_HARDLOCKUP_DETECTOR ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI + + static struct perf_event_attr wd_hw_attr = { + .type = PERF_TYPE_HARDWARE, +@@ -247,6 +304,9 @@ static void watchdog_overflow_callback(struct perf_event *event, + __this_cpu_write(hard_watchdog_warn, false); + return; + } ++#endif ++ ++#ifdef CONFIG_HARDLOCKUP_DETECTOR + static void watchdog_interrupt_count(void) + { + __this_cpu_inc(hrtimer_interrupts); +@@ -265,6 +325,9 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) + /* kick the hardlockup detector */ + watchdog_interrupt_count(); + ++ /* test for hardlockups on the next cpu */ ++ watchdog_check_hardlockup_other_cpu(); ++ + /* kick the softlockup detector */ + wake_up_process(__this_cpu_read(softlockup_watchdog)); + +@@ -359,7 +422,7 @@ static int watchdog(void *unused) + } + + +-#ifdef CONFIG_HARDLOCKUP_DETECTOR ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI + static int watchdog_nmi_enable(int cpu) + { + struct perf_event_attr *wd_attr; +@@ -430,6 +493,18 @@ static void watchdog_prepare_cpu(int cpu) + WARN_ON(per_cpu(softlockup_watchdog, cpu)); + hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer->function = watchdog_timer_fn; ++ ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU ++ /* ++ * The new cpu will be marked online before the first hrtimer interrupt ++ * runs on it. If another cpu tests for a hardlockup on the new cpu ++ * before it has run its first hrtimer, it will get a false positive. ++ * Touch the watchdog on the new cpu to delay the first check for at ++ * least 3 sampling periods to guarantee one hrtimer has run on the new ++ * cpu. ++ */ ++ per_cpu(watchdog_nmi_touch, cpu) = true; ++#endif + } + + static int watchdog_enable(int cpu) +diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug +index 6777153f..bbdea1d8 100644 +--- a/lib/Kconfig.debug ++++ b/lib/Kconfig.debug +@@ -178,14 +178,24 @@ config LOCKUP_DETECTOR + The overhead should be minimal. A periodic hrtimer runs to + generate interrupts and kick the watchdog task every 4 seconds. + An NMI is generated every 10 seconds or so to check for hardlockups. ++ If NMIs are not available on the platform, every 12 seconds the ++ hrtimer interrupt on one cpu will be used to check for hardlockups ++ on the next cpu. + + The frequency of hrtimer and NMI events and the soft and hard lockup + thresholds can be controlled through the sysctl watchdog_thresh. + +-config HARDLOCKUP_DETECTOR ++config HARDLOCKUP_DETECTOR_NMI + def_bool LOCKUP_DETECTOR && PERF_EVENTS && HAVE_PERF_EVENTS_NMI && \ + !HAVE_NMI_WATCHDOG + ++config HARDLOCKUP_DETECTOR_OTHER_CPU ++ def_bool LOCKUP_DETECTOR && SMP && !HARDLOCKUP_DETECTOR_NMI && \ ++ !HAVE_NMI_WATCHDOG ++ ++config HARDLOCKUP_DETECTOR ++ def_bool HARDLOCKUP_DETECTOR_NMI || HARDLOCKUP_DETECTOR_OTHER_CPU ++ + config BOOTPARAM_HARDLOCKUP_PANIC + bool "Panic (Reboot) On Hard Lockups" + depends on LOCKUP_DETECTOR +@@ -676,8 +686,9 @@ config DEBUG_LOCKING_API_SELFTESTS + mutexes and rwsems. + + config STACKTRACE +- bool ++ bool "Stacktrace" + depends on STACKTRACE_SUPPORT ++ default y + + config DEBUG_STACK_USAGE + bool "Stack utilization instrumentation" +diff --git a/mm/page_alloc.c b/mm/page_alloc.c +index 533ea80f..035fbe94 100644 +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -192,6 +192,7 @@ static char * const zone_names[MAX_NR_ZONES] = { + }; + + int min_free_kbytes = 1024; ++int min_free_order_shift = 1; + + static unsigned long __meminitdata nr_kernel_pages; + static unsigned long __meminitdata nr_all_pages; +@@ -1565,7 +1566,7 @@ static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark, + free_pages -= z->free_area[o].nr_free << o; + + /* Require fewer higher order pages to be free */ +- min >>= 1; ++ min >>= min_free_order_shift; + + if (free_pages <= min) + return false; +diff --git a/mm/shmem.c b/mm/shmem.c +index 58c4a477..44895669 100644 +--- a/mm/shmem.c ++++ b/mm/shmem.c +@@ -2621,6 +2621,15 @@ put_memory: + } + EXPORT_SYMBOL_GPL(shmem_file_setup); + ++void shmem_set_file(struct vm_area_struct *vma, struct file *file) ++{ ++ if (vma->vm_file) ++ fput(vma->vm_file); ++ vma->vm_file = file; ++ vma->vm_ops = &shmem_vm_ops; ++ vma->vm_flags |= VM_CAN_NONLINEAR; ++} ++ + /** + * shmem_zero_setup - setup a shared anonymous mapping + * @vma: the vma to be mmapped is prepared by do_mmap_pgoff +@@ -2634,11 +2643,7 @@ int shmem_zero_setup(struct vm_area_struct *vma) + if (IS_ERR(file)) + return PTR_ERR(file); + +- if (vma->vm_file) +- fput(vma->vm_file); +- vma->vm_file = file; +- vma->vm_ops = &shmem_vm_ops; +- vma->vm_flags |= VM_CAN_NONLINEAR; ++ shmem_set_file(vma, file); + return 0; + } + +diff --git a/mm/vmscan.c b/mm/vmscan.c +index e6ca5051..7fa5fc87 100644 +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -201,6 +202,39 @@ static unsigned long zone_nr_lru_pages(struct mem_cgroup_zone *mz, + return zone_page_state(mz->zone, NR_LRU_BASE + lru); + } + ++struct dentry *debug_file; ++ ++static int debug_shrinker_show(struct seq_file *s, void *unused) ++{ ++ struct shrinker *shrinker; ++ struct shrink_control sc; ++ ++ sc.gfp_mask = -1; ++ sc.nr_to_scan = 0; ++ ++ down_read(&shrinker_rwsem); ++ list_for_each_entry(shrinker, &shrinker_list, list) { ++ char name[64]; ++ int num_objs; ++ ++ num_objs = shrinker->shrink(shrinker, &sc); ++ seq_printf(s, "%pf %d\n", shrinker->shrink, num_objs); ++ } ++ up_read(&shrinker_rwsem); ++ return 0; ++} ++ ++static int debug_shrinker_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, debug_shrinker_show, inode->i_private); ++} ++ ++static const struct file_operations debug_shrinker_fops = { ++ .open = debug_shrinker_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; + + /* + * Add a shrinker callback to be called from the vm +@@ -214,6 +248,15 @@ void register_shrinker(struct shrinker *shrinker) + } + EXPORT_SYMBOL(register_shrinker); + ++static int __init add_shrinker_debug(void) ++{ ++ debugfs_create_file("shrinker", 0644, NULL, NULL, ++ &debug_shrinker_fops); ++ return 0; ++} ++ ++late_initcall(add_shrinker_debug); ++ + /* + * Remove one + */ +diff --git a/net/Kconfig b/net/Kconfig +index e07272d0..c8ee300c 100644 +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -79,6 +79,20 @@ source "net/netlabel/Kconfig" + + endif # if INET + ++config ANDROID_PARANOID_NETWORK ++ bool "Only allow certain groups to create sockets" ++ default y ++ help ++ none ++ ++config NET_ACTIVITY_STATS ++ bool "Network activity statistics tracking" ++ default y ++ help ++ Network activity statistics are useful for tracking wireless ++ modem activity on 2G, 3G, 4G wireless networks. Counts number of ++ transmissions and groups them in specified time buckets. ++ + config NETWORK_SECMARK + bool "Security Marking" + help +@@ -218,7 +232,7 @@ source "net/batman-adv/Kconfig" + source "net/openvswitch/Kconfig" + + config RPS +- boolean ++ boolean "RPS" + depends on SMP && SYSFS && USE_GENERIC_SMP_HELPERS + default y + +diff --git a/net/Makefile b/net/Makefile +index ad432fa4..6865dab6 100644 +--- a/net/Makefile ++++ b/net/Makefile +@@ -70,3 +70,4 @@ obj-$(CONFIG_CEPH_LIB) += ceph/ + obj-$(CONFIG_BATMAN_ADV) += batman-adv/ + obj-$(CONFIG_NFC) += nfc/ + obj-$(CONFIG_OPENVSWITCH) += openvswitch/ ++obj-$(CONFIG_NET_ACTIVITY_STATS) += activity_stats.o +diff --git a/net/activity_stats.c b/net/activity_stats.c +new file mode 100644 +index 00000000..8a3e9347 +--- /dev/null ++++ b/net/activity_stats.c +@@ -0,0 +1,115 @@ ++/* net/activity_stats.c ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Author: Mike Chan (mike@android.com) ++ */ ++ ++#include ++#include ++#include ++ ++/* ++ * Track transmission rates in buckets (power of 2). ++ * 1,2,4,8...512 seconds. ++ * ++ * Buckets represent the count of network transmissions at least ++ * N seconds apart, where N is 1 << bucket index. ++ */ ++#define BUCKET_MAX 10 ++ ++/* Track network activity frequency */ ++static unsigned long activity_stats[BUCKET_MAX]; ++static ktime_t last_transmit; ++static ktime_t suspend_time; ++static DEFINE_SPINLOCK(activity_lock); ++ ++void activity_stats_update(void) ++{ ++ int i; ++ unsigned long flags; ++ ktime_t now; ++ s64 delta; ++ ++ spin_lock_irqsave(&activity_lock, flags); ++ now = ktime_get(); ++ delta = ktime_to_ns(ktime_sub(now, last_transmit)); ++ ++ for (i = BUCKET_MAX - 1; i >= 0; i--) { ++ /* ++ * Check if the time delta between network activity is within the ++ * minimum bucket range. ++ */ ++ if (delta < (1000000000ULL << i)) ++ continue; ++ ++ activity_stats[i]++; ++ last_transmit = now; ++ break; ++ } ++ spin_unlock_irqrestore(&activity_lock, flags); ++} ++ ++static int activity_stats_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int i; ++ int len; ++ char *p = page; ++ ++ /* Only print if offset is 0, or we have enough buffer space */ ++ if (off || count < (30 * BUCKET_MAX + 22)) ++ return -ENOMEM; ++ ++ len = snprintf(p, count, "Min Bucket(sec) Count\n"); ++ count -= len; ++ p += len; ++ ++ for (i = 0; i < BUCKET_MAX; i++) { ++ len = snprintf(p, count, "%15d %lu\n", 1 << i, activity_stats[i]); ++ count -= len; ++ p += len; ++ } ++ *eof = 1; ++ ++ return p - page; ++} ++ ++static int activity_stats_notifier(struct notifier_block *nb, ++ unsigned long event, void *dummy) ++{ ++ switch (event) { ++ case PM_SUSPEND_PREPARE: ++ suspend_time = ktime_get_real(); ++ break; ++ ++ case PM_POST_SUSPEND: ++ suspend_time = ktime_sub(ktime_get_real(), suspend_time); ++ last_transmit = ktime_sub(last_transmit, suspend_time); ++ } ++ ++ return 0; ++} ++ ++static struct notifier_block activity_stats_notifier_block = { ++ .notifier_call = activity_stats_notifier, ++}; ++ ++static int __init activity_stats_init(void) ++{ ++ create_proc_read_entry("activity", S_IRUGO, ++ init_net.proc_net_stat, activity_stats_read_proc, NULL); ++ return register_pm_notifier(&activity_stats_notifier_block); ++} ++ ++subsys_initcall(activity_stats_init); ++ +diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c +index 6fb68a97..f7fa8087 100644 +--- a/net/bluetooth/af_bluetooth.c ++++ b/net/bluetooth/af_bluetooth.c +@@ -40,6 +40,11 @@ + + #include + ++#ifndef CONFIG_BT_SOCK_DEBUG ++#undef BT_DBG ++#define BT_DBG(D...) ++#endif ++ + #define VERSION "2.16" + + /* Bluetooth sockets */ +@@ -122,11 +127,40 @@ int bt_sock_unregister(int proto) + } + EXPORT_SYMBOL(bt_sock_unregister); + ++#ifdef CONFIG_PARANOID_NETWORK ++static inline int current_has_bt_admin(void) ++{ ++ return !current_euid(); ++} ++ ++static inline int current_has_bt(void) ++{ ++ return current_has_bt_admin(); ++} ++# else ++static inline int current_has_bt_admin(void) ++{ ++ return 1; ++} ++ ++static inline int current_has_bt(void) ++{ ++ return 1; ++} ++#endif ++ + static int bt_sock_create(struct net *net, struct socket *sock, int proto, + int kern) + { + int err; + ++ if (proto == BTPROTO_RFCOMM || proto == BTPROTO_SCO || ++ proto == BTPROTO_L2CAP) { ++ if (!current_has_bt()) ++ return -EPERM; ++ } else if (!current_has_bt_admin()) ++ return -EPERM; ++ + if (net != &init_net) + return -EAFNOSUPPORT; + +diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c +index 39b2baf6..0e8f89f9 100644 +--- a/net/bluetooth/hci_conn.c ++++ b/net/bluetooth/hci_conn.c +@@ -361,7 +361,8 @@ static void hci_conn_auto_accept(unsigned long arg) + &conn->dst); + } + +-struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) ++struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, ++ __u16 pkt_type, bdaddr_t *dst) + { + struct hci_conn *conn; + +@@ -389,14 +390,22 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) + conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK; + break; + case SCO_LINK: +- if (lmp_esco_capable(hdev)) +- conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | +- (hdev->esco_type & EDR_ESCO_MASK); +- else +- conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK; +- break; ++ if (!pkt_type) ++ pkt_type = SCO_ESCO_MASK; + case ESCO_LINK: +- conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK; ++ if (!pkt_type) ++ pkt_type = ALL_ESCO_MASK; ++ if (lmp_esco_capable(hdev)) { ++ /* HCI Setup Synchronous Connection Command uses ++ reverse logic on the EDR_ESCO_MASK bits */ ++ conn->pkt_type = (pkt_type ^ EDR_ESCO_MASK) & ++ hdev->esco_type; ++ } else { ++ /* Legacy HCI Add Sco Connection Command uses a ++ shifted bitmask */ ++ conn->pkt_type = (pkt_type << 5) & hdev->pkt_type & ++ SCO_PTYPE_MASK; ++ } + break; + } + +@@ -514,7 +523,9 @@ EXPORT_SYMBOL(hci_get_route); + + /* Create SCO, ACL or LE connection. + * Device _must_ be locked */ +-struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type) ++struct hci_conn *hci_connect(struct hci_dev *hdev, int type, ++ __u16 pkt_type, bdaddr_t *dst, ++ __u8 sec_level, __u8 auth_type) + { + struct hci_conn *acl; + struct hci_conn *sco; +@@ -533,7 +544,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 + if (!entry) + return ERR_PTR(-EHOSTUNREACH); + +- le = hci_conn_add(hdev, LE_LINK, dst); ++ le = hci_conn_add(hdev, LE_LINK, 0, dst); + if (!le) + return ERR_PTR(-ENOMEM); + +@@ -548,7 +559,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 + + acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); + if (!acl) { +- acl = hci_conn_add(hdev, ACL_LINK, dst); ++ acl = hci_conn_add(hdev, ACL_LINK, 0, dst); + if (!acl) + return ERR_PTR(-ENOMEM); + } +@@ -567,7 +578,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 + + sco = hci_conn_hash_lookup_ba(hdev, type, dst); + if (!sco) { +- sco = hci_conn_add(hdev, type, dst); ++ sco = hci_conn_add(hdev, type, pkt_type, dst); + if (!sco) { + hci_conn_put(acl); + return ERR_PTR(-ENOMEM); +@@ -874,6 +885,15 @@ int hci_get_conn_list(void __user *arg) + (ci + n)->out = c->out; + (ci + n)->state = c->state; + (ci + n)->link_mode = c->link_mode; ++ if (c->type == SCO_LINK) { ++ (ci + n)->mtu = hdev->sco_mtu; ++ (ci + n)->cnt = hdev->sco_cnt; ++ (ci + n)->pkts = hdev->sco_pkts; ++ } else { ++ (ci + n)->mtu = hdev->acl_mtu; ++ (ci + n)->cnt = hdev->acl_cnt; ++ (ci + n)->pkts = hdev->acl_pkts; ++ } + if (++n >= req.conn_num) + break; + } +@@ -910,6 +930,15 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg) + ci.out = conn->out; + ci.state = conn->state; + ci.link_mode = conn->link_mode; ++ if (req.type == SCO_LINK) { ++ ci.mtu = hdev->sco_mtu; ++ ci.cnt = hdev->sco_cnt; ++ ci.pkts = hdev->sco_pkts; ++ } else { ++ ci.mtu = hdev->acl_mtu; ++ ci.cnt = hdev->acl_cnt; ++ ci.pkts = hdev->acl_pkts; ++ } + } + hci_dev_unlock(hdev); + +diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c +old mode 100644 +new mode 100755 +index ef45e10c..aba4fccd +--- a/net/bluetooth/hci_event.c ++++ b/net/bluetooth/hci_event.c +@@ -1213,7 +1213,7 @@ static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) + } + } else { + if (!conn) { +- conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr); ++ conn = hci_conn_add(hdev, ACL_LINK, 0, &cp->bdaddr); + if (conn) { + conn->out = true; + conn->link_mode |= HCI_LM_MASTER; +@@ -1641,7 +1641,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) + } + } else { + if (!conn) { +- conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr); ++ conn = hci_conn_add(hdev, LE_LINK, 0, &cp->peer_addr); + if (conn) { + conn->dst_type = cp->peer_addr_type; + conn->out = true; +@@ -1816,6 +1816,15 @@ unlock: + hci_conn_check_pending(hdev); + } + ++static inline bool is_sco_active(struct hci_dev *hdev) ++{ ++ if (hci_conn_hash_lookup_state(hdev, SCO_LINK, BT_CONNECTED) || ++ (hci_conn_hash_lookup_state(hdev, ESCO_LINK, ++ BT_CONNECTED))) ++ return true; ++ return false; ++} ++ + static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) + { + struct hci_ev_conn_request *ev = (void *) skb->data; +@@ -1840,7 +1849,8 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk + + conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); + if (!conn) { +- conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr); ++ /* pkt_type not yet used for incoming connections */ ++ conn = hci_conn_add(hdev, ev->link_type, 0, &ev->bdaddr); + if (!conn) { + BT_ERR("No memory for new connection"); + hci_dev_unlock(hdev); +@@ -1858,7 +1868,8 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk + + bacpy(&cp.bdaddr, &ev->bdaddr); + +- if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) ++ if (lmp_rswitch_capable(hdev) && ((mask & HCI_LM_MASTER) ++ || is_sco_active(hdev))) + cp.role = 0x00; /* Become master */ + else + cp.role = 0x01; /* Remain slave */ +@@ -2945,6 +2956,7 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu + hci_conn_add_sysfs(conn); + break; + ++ case 0x10: /* Connection Accept Timeout */ + case 0x11: /* Unsupported Feature or Parameter Value */ + case 0x1c: /* SCO interval rejected */ + case 0x1a: /* Unsupported Remote Feature */ +@@ -3290,7 +3302,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff + + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr); + if (!conn) { +- conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr); ++ conn = hci_conn_add(hdev, LE_LINK, 0, &ev->bdaddr); + if (!conn) { + BT_ERR("No memory for new connection"); + hci_dev_unlock(hdev); +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index fa07aed3..e49f200c 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -1220,10 +1220,10 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d + auth_type = l2cap_get_auth_type(chan); + + if (chan->dcid == L2CAP_CID_LE_DATA) +- hcon = hci_connect(hdev, LE_LINK, dst, ++ hcon = hci_connect(hdev, LE_LINK, 0, dst, + chan->sec_level, auth_type); + else +- hcon = hci_connect(hdev, ACL_LINK, dst, ++ hcon = hci_connect(hdev, ACL_LINK, 0, dst, + chan->sec_level, auth_type); + + if (IS_ERR(hcon)) { +diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c +index 8f3d9dc7..00879b82 100644 +--- a/net/bluetooth/mgmt.c ++++ b/net/bluetooth/mgmt.c +@@ -1891,11 +1891,11 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, + auth_type = HCI_AT_DEDICATED_BONDING_MITM; + + if (cp->addr.type == MGMT_ADDR_BREDR) +- conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level, +- auth_type); ++ conn = hci_connect(hdev, ACL_LINK, 0, &cp->addr.bdaddr, ++ sec_level, auth_type); + else +- conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level, +- auth_type); ++ conn = hci_connect(hdev, LE_LINK, 0, &cp->addr.bdaddr, ++ sec_level, auth_type); + + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); +diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c +index 8a602388..9652707b 100644 +--- a/net/bluetooth/rfcomm/core.c ++++ b/net/bluetooth/rfcomm/core.c +@@ -462,7 +462,6 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) + + switch (d->state) { + case BT_CONNECT: +- case BT_CONFIG: + if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { + set_bit(RFCOMM_AUTH_REJECT, &d->flags); + rfcomm_schedule(); +diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c +index f6ab1290..458cec07 100644 +--- a/net/bluetooth/sco.c ++++ b/net/bluetooth/sco.c +@@ -177,6 +177,7 @@ static int sco_connect(struct sock *sk) + { + bdaddr_t *src = &bt_sk(sk)->src; + bdaddr_t *dst = &bt_sk(sk)->dst; ++ __u16 pkt_type = sco_pi(sk)->pkt_type; + struct sco_conn *conn; + struct hci_conn *hcon; + struct hci_dev *hdev; +@@ -192,10 +193,12 @@ static int sco_connect(struct sock *sk) + + if (lmp_esco_capable(hdev) && !disable_esco) + type = ESCO_LINK; +- else ++ else { + type = SCO_LINK; ++ pkt_type &= SCO_ESCO_MASK; ++ } + +- hcon = hci_connect(hdev, type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING); ++ hcon = hci_connect(hdev, type, pkt_type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING); + if (IS_ERR(hcon)) { + err = PTR_ERR(hcon); + goto done; +@@ -462,18 +465,22 @@ static int sco_sock_create(struct net *net, struct socket *sock, int protocol, + return 0; + } + +-static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) ++static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) + { +- struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; ++ struct sockaddr_sco sa; + struct sock *sk = sock->sk; +- bdaddr_t *src = &sa->sco_bdaddr; +- int err = 0; ++ bdaddr_t *src = &sa.sco_bdaddr; ++ int len, err = 0; + +- BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr)); ++ BT_DBG("sk %p %s", sk, batostr(&sa.sco_bdaddr)); + + if (!addr || addr->sa_family != AF_BLUETOOTH) + return -EINVAL; + ++ memset(&sa, 0, sizeof(sa)); ++ len = min_t(unsigned int, sizeof(sa), alen); ++ memcpy(&sa, addr, len); ++ + lock_sock(sk); + + if (sk->sk_state != BT_OPEN) { +@@ -487,7 +494,8 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le + err = -EADDRINUSE; + } else { + /* Save source address */ +- bacpy(&bt_sk(sk)->src, &sa->sco_bdaddr); ++ bacpy(&bt_sk(sk)->src, &sa.sco_bdaddr); ++ sco_pi(sk)->pkt_type = sa.sco_pkt_type; + sk->sk_state = BT_BOUND; + } + +@@ -500,27 +508,34 @@ done: + + static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) + { +- struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; + struct sock *sk = sock->sk; +- int err = 0; +- ++ struct sockaddr_sco sa; ++ int len, err = 0; + + BT_DBG("sk %p", sk); + +- if (alen < sizeof(struct sockaddr_sco) || +- addr->sa_family != AF_BLUETOOTH) ++ if (!addr || addr->sa_family != AF_BLUETOOTH) + return -EINVAL; + +- if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) +- return -EBADFD; +- +- if (sk->sk_type != SOCK_SEQPACKET) +- return -EINVAL; ++ memset(&sa, 0, sizeof(sa)); ++ len = min_t(unsigned int, sizeof(sa), alen); ++ memcpy(&sa, addr, len); + + lock_sock(sk); + ++ if (sk->sk_type != SOCK_SEQPACKET) { ++ err = -EINVAL; ++ goto done; ++ } ++ ++ if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { ++ err = -EBADFD; ++ goto done; ++ } ++ + /* Set destination address and psm */ +- bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr); ++ bacpy(&bt_sk(sk)->dst, &sa.sco_bdaddr); ++ sco_pi(sk)->pkt_type = sa.sco_pkt_type; + + err = sco_connect(sk); + if (err) +@@ -627,6 +642,7 @@ static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len + bacpy(&sa->sco_bdaddr, &bt_sk(sk)->dst); + else + bacpy(&sa->sco_bdaddr, &bt_sk(sk)->src); ++ sa->sco_pkt_type = sco_pi(sk)->pkt_type; + + return 0; + } +diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c +index ba829de8..9ec0822f 100644 +--- a/net/bridge/br_device.c ++++ b/net/bridge/br_device.c +@@ -38,16 +38,17 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) + } + #endif + +- u64_stats_update_begin(&brstats->syncp); +- brstats->tx_packets++; +- brstats->tx_bytes += skb->len; +- u64_stats_update_end(&brstats->syncp); +- + BR_INPUT_SKB_CB(skb)->brdev = dev; + + skb_reset_mac_header(skb); + skb_pull(skb, ETH_HLEN); + ++ u64_stats_update_begin(&brstats->syncp); ++ brstats->tx_packets++; ++ /* Exclude ETH_HLEN from byte stats for consistency with Rx chain */ ++ brstats->tx_bytes += skb->len; ++ u64_stats_update_end(&brstats->syncp); ++ + rcu_read_lock(); + if (is_broadcast_ether_addr(dest)) + br_flood_deliver(br, skb); +diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile +index ff75d3bb..c6f177cf 100644 +--- a/net/ipv4/Makefile ++++ b/net/ipv4/Makefile +@@ -14,6 +14,7 @@ obj-y := route.o inetpeer.o protocol.o \ + inet_fragment.o ping.o + + obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o ++obj-$(CONFIG_SYSFS) += sysfs_net_ipv4.o + obj-$(CONFIG_PROC_FS) += proc.o + obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o + obj-$(CONFIG_IP_MROUTE) += ipmr.o +diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c +index 78ec2980..de5ec030 100644 +--- a/net/ipv4/af_inet.c ++++ b/net/ipv4/af_inet.c +@@ -119,6 +119,19 @@ + #include + #endif + ++#ifdef CONFIG_ANDROID_PARANOID_NETWORK ++#include ++ ++static inline int current_has_network(void) ++{ ++ return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); ++} ++#else ++static inline int current_has_network(void) ++{ ++ return 1; ++} ++#endif + + /* The inetsw table contains everything that inet_create needs to + * build a new socket. +@@ -264,6 +277,7 @@ static inline int inet_netns_ok(struct net *net, int protocol) + return ipprot->netns_ok; + } + ++ + /* + * Create an inet socket. + */ +@@ -280,6 +294,9 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, + int try_loading_module = 0; + int err; + ++ if (!current_has_network()) ++ return -EACCES; ++ + if (unlikely(!inet_ehash_secret)) + if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) + build_ehash_secret(); +@@ -886,6 +903,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) + case SIOCSIFPFLAGS: + case SIOCGIFPFLAGS: + case SIOCSIFFLAGS: ++ case SIOCKILLADDR: + err = devinet_ioctl(net, cmd, (void __user *)arg); + break; + default: +diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c +index 6e447ff9..8a9aab37 100644 +--- a/net/ipv4/devinet.c ++++ b/net/ipv4/devinet.c +@@ -58,6 +58,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -734,6 +735,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) + case SIOCSIFBRDADDR: /* Set the broadcast address */ + case SIOCSIFDSTADDR: /* Set the destination address */ + case SIOCSIFNETMASK: /* Set the netmask for the interface */ ++ case SIOCKILLADDR: /* Nuke all sockets on this address */ + ret = -EACCES; + if (!capable(CAP_NET_ADMIN)) + goto out; +@@ -785,7 +787,8 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) + } + + ret = -EADDRNOTAVAIL; +- if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) ++ if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS ++ && cmd != SIOCKILLADDR) + goto done; + + switch (cmd) { +@@ -911,6 +914,9 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) + inet_insert_ifa(ifa); + } + break; ++ case SIOCKILLADDR: /* Nuke all connections on this address */ ++ ret = tcp_nuke_addr(net, (struct sockaddr *) sin); ++ break; + } + done: + rtnl_unlock(); +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index fcc543cd..a1dce8a2 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -123,6 +123,18 @@ config IP_NF_TARGET_REJECT + + To compile it as a module, choose M here. If unsure, say N. + ++config IP_NF_TARGET_REJECT_SKERR ++ bool "Force socket error when rejecting with icmp*" ++ depends on IP_NF_TARGET_REJECT ++ default n ++ help ++ This option enables turning a "--reject-with icmp*" into a matching ++ socket error also. ++ The REJECT target normally allows sending an ICMP message. But it ++ leaves the local socket unaware of any ingress rejects. ++ ++ If unsure, say N. ++ + config IP_NF_TARGET_ULOG + tristate "ULOG target support" + default m if NETFILTER_ADVANCED=n +diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c +index 51f13f8e..9dd754c7 100644 +--- a/net/ipv4/netfilter/ipt_REJECT.c ++++ b/net/ipv4/netfilter/ipt_REJECT.c +@@ -128,6 +128,14 @@ static void send_reset(struct sk_buff *oldskb, int hook) + static inline void send_unreach(struct sk_buff *skb_in, int code) + { + icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); ++#ifdef CONFIG_IP_NF_TARGET_REJECT_SKERR ++ if (skb_in->sk) { ++ skb_in->sk->sk_err = icmp_err_convert[code].errno; ++ skb_in->sk->sk_error_report(skb_in->sk); ++ pr_debug("ipt_REJECT: sk_err=%d for skb=%p sk=%p\n", ++ skb_in->sk->sk_err, skb_in, skb_in->sk); ++ } ++#endif + } + + static unsigned int +diff --git a/net/ipv4/sysfs_net_ipv4.c b/net/ipv4/sysfs_net_ipv4.c +new file mode 100644 +index 00000000..0cbbf100 +--- /dev/null ++++ b/net/ipv4/sysfs_net_ipv4.c +@@ -0,0 +1,88 @@ ++/* ++ * net/ipv4/sysfs_net_ipv4.c ++ * ++ * sysfs-based networking knobs (so we can, unlike with sysctl, control perms) ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * Robert Love ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define CREATE_IPV4_FILE(_name, _var) \ ++static ssize_t _name##_show(struct kobject *kobj, \ ++ struct kobj_attribute *attr, char *buf) \ ++{ \ ++ return sprintf(buf, "%d\n", _var); \ ++} \ ++static ssize_t _name##_store(struct kobject *kobj, \ ++ struct kobj_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ int val, ret; \ ++ ret = sscanf(buf, "%d", &val); \ ++ if (ret != 1) \ ++ return -EINVAL; \ ++ if (val < 0) \ ++ return -EINVAL; \ ++ _var = val; \ ++ return count; \ ++} \ ++static struct kobj_attribute _name##_attr = \ ++ __ATTR(_name, 0644, _name##_show, _name##_store) ++ ++CREATE_IPV4_FILE(tcp_wmem_min, sysctl_tcp_wmem[0]); ++CREATE_IPV4_FILE(tcp_wmem_def, sysctl_tcp_wmem[1]); ++CREATE_IPV4_FILE(tcp_wmem_max, sysctl_tcp_wmem[2]); ++ ++CREATE_IPV4_FILE(tcp_rmem_min, sysctl_tcp_rmem[0]); ++CREATE_IPV4_FILE(tcp_rmem_def, sysctl_tcp_rmem[1]); ++CREATE_IPV4_FILE(tcp_rmem_max, sysctl_tcp_rmem[2]); ++ ++static struct attribute *ipv4_attrs[] = { ++ &tcp_wmem_min_attr.attr, ++ &tcp_wmem_def_attr.attr, ++ &tcp_wmem_max_attr.attr, ++ &tcp_rmem_min_attr.attr, ++ &tcp_rmem_def_attr.attr, ++ &tcp_rmem_max_attr.attr, ++ NULL ++}; ++ ++static struct attribute_group ipv4_attr_group = { ++ .attrs = ipv4_attrs, ++}; ++ ++static __init int sysfs_ipv4_init(void) ++{ ++ struct kobject *ipv4_kobject; ++ int ret; ++ ++ ipv4_kobject = kobject_create_and_add("ipv4", kernel_kobj); ++ if (!ipv4_kobject) ++ return -ENOMEM; ++ ++ ret = sysfs_create_group(ipv4_kobject, &ipv4_attr_group); ++ if (ret) { ++ kobject_put(ipv4_kobject); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++subsys_initcall(sysfs_ipv4_init); +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index 01870bd2..8429ac5e 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -268,11 +268,15 @@ + #include + #include + #include ++#include + + #include + #include + #include + #include ++#include ++#include ++#include + #include + #include + +@@ -1115,6 +1119,9 @@ out: + if (copied) + tcp_push(sk, flags, mss_now, tp->nonagle); + release_sock(sk); ++ ++ if (copied > 0) ++ uid_stat_tcp_snd(current_uid(), copied); + return copied; + + do_fault: +@@ -1389,8 +1396,11 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, + tcp_rcv_space_adjust(sk); + + /* Clean up data we have read: This will do ACK frames. */ +- if (copied > 0) ++ if (copied > 0) { + tcp_cleanup_rbuf(sk, copied); ++ uid_stat_tcp_rcv(current_uid(), copied); ++ } ++ + return copied; + } + EXPORT_SYMBOL(tcp_read_sock); +@@ -1779,6 +1789,9 @@ skip_copy: + tcp_cleanup_rbuf(sk, copied); + + release_sock(sk); ++ ++ if (copied > 0) ++ uid_stat_tcp_rcv(current_uid(), copied); + return copied; + + out: +@@ -1787,6 +1800,8 @@ out: + + recv_urg: + err = tcp_recv_urg(sk, msg, len, flags); ++ if (err > 0) ++ uid_stat_tcp_rcv(current_uid(), err); + goto out; + } + EXPORT_SYMBOL(tcp_recvmsg); +@@ -3336,3 +3351,107 @@ void __init tcp_init(void) + tcp_secret_retiring = &tcp_secret_two; + tcp_secret_secondary = &tcp_secret_two; + } ++ ++static int tcp_is_local(struct net *net, __be32 addr) { ++ struct rtable *rt; ++ struct flowi4 fl4 = { .daddr = addr }; ++ rt = ip_route_output_key(net, &fl4); ++ if (IS_ERR_OR_NULL(rt)) ++ return 0; ++ return rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK); ++} ++ ++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++static int tcp_is_local6(struct net *net, struct in6_addr *addr) { ++ struct rt6_info *rt6 = rt6_lookup(net, addr, addr, 0, 0); ++ return rt6 && rt6->dst.dev && (rt6->dst.dev->flags & IFF_LOOPBACK); ++} ++#endif ++ ++/* ++ * tcp_nuke_addr - destroy all sockets on the given local address ++ * if local address is the unspecified address (0.0.0.0 or ::), destroy all ++ * sockets with local addresses that are not configured. ++ */ ++int tcp_nuke_addr(struct net *net, struct sockaddr *addr) ++{ ++ int family = addr->sa_family; ++ unsigned int bucket; ++ ++ struct in_addr *in; ++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++ struct in6_addr *in6; ++#endif ++ if (family == AF_INET) { ++ in = &((struct sockaddr_in *)addr)->sin_addr; ++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++ } else if (family == AF_INET6) { ++ in6 = &((struct sockaddr_in6 *)addr)->sin6_addr; ++#endif ++ } else { ++ return -EAFNOSUPPORT; ++ } ++ ++ for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) { ++ struct hlist_nulls_node *node; ++ struct sock *sk; ++ spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket); ++ ++restart: ++ spin_lock_bh(lock); ++ sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) { ++ struct inet_sock *inet = inet_sk(sk); ++ ++ if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT) ++ continue; ++ if (sock_flag(sk, SOCK_DEAD)) ++ continue; ++ ++ if (family == AF_INET) { ++ __be32 s4 = inet->inet_rcv_saddr; ++ if (s4 == LOOPBACK4_IPV6) ++ continue; ++ ++ if (in->s_addr != s4 && ++ !(in->s_addr == INADDR_ANY && ++ !tcp_is_local(net, s4))) ++ continue; ++ } ++ ++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++ if (family == AF_INET6) { ++ struct in6_addr *s6; ++ if (!inet->pinet6) ++ continue; ++ ++ s6 = &inet->pinet6->rcv_saddr; ++ if (ipv6_addr_type(s6) == IPV6_ADDR_MAPPED) ++ continue; ++ ++ if (!ipv6_addr_equal(in6, s6) && ++ !(ipv6_addr_equal(in6, &in6addr_any) && ++ !tcp_is_local6(net, s6))) ++ continue; ++ } ++#endif ++ ++ sock_hold(sk); ++ spin_unlock_bh(lock); ++ ++ local_bh_disable(); ++ bh_lock_sock(sk); ++ sk->sk_err = ETIMEDOUT; ++ sk->sk_error_report(sk); ++ ++ tcp_done(sk); ++ bh_unlock_sock(sk); ++ local_bh_enable(); ++ sock_put(sk); ++ ++ goto restart; ++ } ++ spin_unlock_bh(lock); ++ } ++ ++ return 0; ++} +diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c +index 8ed1b930..29625e9a 100644 +--- a/net/ipv6/af_inet6.c ++++ b/net/ipv6/af_inet6.c +@@ -62,6 +62,20 @@ + #include + #include + ++#ifdef CONFIG_ANDROID_PARANOID_NETWORK ++#include ++ ++static inline int current_has_network(void) ++{ ++ return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); ++} ++#else ++static inline int current_has_network(void) ++{ ++ return 1; ++} ++#endif ++ + MODULE_AUTHOR("Cast of dozens"); + MODULE_DESCRIPTION("IPv6 protocol stack for Linux"); + MODULE_LICENSE("GPL"); +@@ -108,6 +122,9 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, + int try_loading_module = 0; + int err; + ++ if (!current_has_network()) ++ return -EACCES; ++ + if (sock->type != SOCK_RAW && + sock->type != SOCK_DGRAM && + !inet_ehash_secret) +@@ -477,6 +494,21 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr, + + EXPORT_SYMBOL(inet6_getname); + ++int inet6_killaddr_ioctl(struct net *net, void __user *arg) { ++ struct in6_ifreq ireq; ++ struct sockaddr_in6 sin6; ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EACCES; ++ ++ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) ++ return -EFAULT; ++ ++ sin6.sin6_family = AF_INET6; ++ sin6.sin6_addr = ireq.ifr6_addr; ++ return tcp_nuke_addr(net, (struct sockaddr *) &sin6); ++} ++ + int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) + { + struct sock *sk = sock->sk; +@@ -501,6 +533,8 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) + return addrconf_del_ifaddr(net, (void __user *) arg); + case SIOCSIFDSTADDR: + return addrconf_set_dstaddr(net, (void __user *) arg); ++ case SIOCKILLADDR: ++ return inet6_killaddr_ioctl(net, (void __user *) arg); + default: + if (!sk->sk_prot->ioctl) + return -ENOIOCTLCMD; +diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig +index d33cddd1..acd7c9e1 100644 +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -175,6 +175,18 @@ config IP6_NF_TARGET_REJECT + + To compile it as a module, choose M here. If unsure, say N. + ++config IP6_NF_TARGET_REJECT_SKERR ++ bool "Force socket error when rejecting with icmp*" ++ depends on IP6_NF_TARGET_REJECT ++ default n ++ help ++ This option enables turning a "--reject-with icmp*" into a matching ++ socket error also. ++ The REJECT target normally allows sending an ICMP message. But it ++ leaves the local socket unaware of any ingress rejects. ++ ++ If unsure, say N. ++ + config IP6_NF_MANGLE + tristate "Packet mangling" + default m if NETFILTER_ADVANCED=n +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index 9d4e1555..e641f8fa 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -2279,16 +2279,15 @@ static void __exit ip6_tables_fini(void) + * "No next header". + * + * If target header is found, its offset is set in *offset and return protocol +- * number. Otherwise, return -1. ++ * number. Otherwise, return -ENOENT or -EBADMSG. + * + * If the first fragment doesn't contain the final protocol header or + * NEXTHDR_NONE it is considered invalid. + * + * Note that non-1st fragment is special case that "the protocol number + * of last header" is "next header" field in Fragment header. In this case, +- * *offset is meaningless and fragment offset is stored in *fragoff if fragoff +- * isn't NULL. +- * ++ * *offset is meaningless. If fragoff is not NULL, the fragment offset is ++ * stored in *fragoff; if it is NULL, return -EINVAL. + */ + int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, + int target, unsigned short *fragoff) +@@ -2329,9 +2328,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, + if (target < 0 && + ((!ipv6_ext_hdr(hp->nexthdr)) || + hp->nexthdr == NEXTHDR_NONE)) { +- if (fragoff) ++ if (fragoff) { + *fragoff = _frag_off; +- return hp->nexthdr; ++ return hp->nexthdr; ++ } else { ++ return -EINVAL; ++ } + } + return -ENOENT; + } +diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c +index aad2fa41..09155e34 100644 +--- a/net/ipv6/netfilter/ip6t_REJECT.c ++++ b/net/ipv6/netfilter/ip6t_REJECT.c +@@ -178,6 +178,15 @@ send_unreach(struct net *net, struct sk_buff *skb_in, unsigned char code, + skb_in->dev = net->loopback_dev; + + icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0); ++#ifdef CONFIG_IP6_NF_TARGET_REJECT_SKERR ++ if (skb_in->sk) { ++ icmpv6_err_convert(ICMPV6_DEST_UNREACH, code, ++ &skb_in->sk->sk_err); ++ skb_in->sk->sk_error_report(skb_in->sk); ++ pr_debug("ip6t_REJECT: sk_err=%d for skb=%p sk=%p\n", ++ skb_in->sk->sk_err, skb_in, skb_in->sk); ++ } ++#endif + } + + static unsigned int +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index 493490f0..5a272c65 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -1973,7 +1973,8 @@ void rt6_purge_dflt_routers(struct net *net) + restart: + read_lock_bh(&table->tb6_lock); + for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { +- if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { ++ if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && ++ (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { + dst_hold(&rt->dst); + read_unlock_bh(&table->tb6_lock); + ip6_del_rt(rt); +diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig +index 0c6f67e8..ce2976c0 100644 +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -967,6 +967,8 @@ config NETFILTER_XT_MATCH_OWNER + based on who created the socket: the user or group. It is also + possible to check whether a socket actually exists. + ++ Conflicts with '"quota, tag, uid" match' ++ + config NETFILTER_XT_MATCH_POLICY + tristate 'IPsec "policy" match support' + depends on XFRM +@@ -1000,6 +1002,22 @@ config NETFILTER_XT_MATCH_PKTTYPE + + To compile it as a module, choose M here. If unsure, say N. + ++config NETFILTER_XT_MATCH_QTAGUID ++ bool '"quota, tag, owner" match and stats support' ++ depends on NETFILTER_XT_MATCH_SOCKET ++ depends on NETFILTER_XT_MATCH_OWNER=n ++ help ++ This option replaces the `owner' match. In addition to matching ++ on uid, it keeps stats based on a tag assigned to a socket. ++ The full tag is comprised of a UID and an accounting tag. ++ The tags are assignable to sockets from user space (e.g. a download ++ manager can assign the socket to another UID for accounting). ++ Stats and control are done via /proc/net/xt_qtaguid/. ++ It replaces owner as it takes the same arguments, but should ++ really be recognized by the iptables tool. ++ ++ If unsure, say `N'. ++ + config NETFILTER_XT_MATCH_QUOTA + tristate '"quota" match support' + depends on NETFILTER_ADVANCED +@@ -1010,6 +1028,30 @@ config NETFILTER_XT_MATCH_QUOTA + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + ++config NETFILTER_XT_MATCH_QUOTA2 ++ tristate '"quota2" match support' ++ depends on NETFILTER_ADVANCED ++ help ++ This option adds a `quota2' match, which allows to match on a ++ byte counter correctly and not per CPU. ++ It allows naming the quotas. ++ This is based on http://xtables-addons.git.sourceforge.net ++ ++ If you want to compile it as a module, say M here and read ++ . If unsure, say `N'. ++ ++config NETFILTER_XT_MATCH_QUOTA2_LOG ++ bool '"quota2" Netfilter LOG support' ++ depends on NETFILTER_XT_MATCH_QUOTA2 ++ depends on IP_NF_TARGET_ULOG=n # not yes, not module, just no ++ default n ++ help ++ This option allows `quota2' to log ONCE when a quota limit ++ is passed. It logs via NETLINK using the NETLINK_NFLOG family. ++ It logs similarly to how ipt_ULOG would without data. ++ ++ If unsure, say `N'. ++ + config NETFILTER_XT_MATCH_RATEEST + tristate '"rateest" match support' + depends on NETFILTER_ADVANCED +diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile +index ca367658..452e84de 100644 +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -101,7 +101,9 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o + obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o + obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o + obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o ++obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid_print.o xt_qtaguid.o + obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o ++obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA2) += xt_quota2.o + obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o + obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o + obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o +diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c +index f407ebc1..f4ba8634 100644 +--- a/net/netfilter/xt_IDLETIMER.c ++++ b/net/netfilter/xt_IDLETIMER.c +@@ -5,6 +5,7 @@ + * After timer expires a kevent will be sent. + * + * Copyright (C) 2004, 2010 Nokia Corporation ++ * + * Written by Timo Teras + * + * Converted to x_tables and reworked for upstream inclusion +@@ -38,8 +39,10 @@ + #include + #include + #include ++#include + #include + #include ++#include + + struct idletimer_tg_attr { + struct attribute attr; +@@ -56,6 +59,8 @@ struct idletimer_tg { + struct idletimer_tg_attr attr; + + unsigned int refcnt; ++ bool send_nl_msg; ++ bool active; + }; + + static LIST_HEAD(idletimer_tg_list); +@@ -63,6 +68,32 @@ static DEFINE_MUTEX(list_mutex); + + static struct kobject *idletimer_tg_kobj; + ++static void notify_netlink_uevent(const char *label, struct idletimer_tg *timer) ++{ ++ char label_msg[NLMSG_MAX_SIZE]; ++ char state_msg[NLMSG_MAX_SIZE]; ++ char *envp[] = { label_msg, state_msg, NULL }; ++ int res; ++ ++ res = snprintf(label_msg, NLMSG_MAX_SIZE, "LABEL=%s", ++ label); ++ if (NLMSG_MAX_SIZE <= res) { ++ pr_err("message too long (%d)", res); ++ return; ++ } ++ res = snprintf(state_msg, NLMSG_MAX_SIZE, "STATE=%s", ++ timer->active ? "active" : "inactive"); ++ if (NLMSG_MAX_SIZE <= res) { ++ pr_err("message too long (%d)", res); ++ return; ++ } ++ pr_debug("putting nlmsg: <%s> <%s>\n", label_msg, state_msg); ++ kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); ++ return; ++ ++ ++} ++ + static + struct idletimer_tg *__idletimer_tg_find_by_label(const char *label) + { +@@ -83,6 +114,7 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, + { + struct idletimer_tg *timer; + unsigned long expires = 0; ++ unsigned long now = jiffies; + + mutex_lock(&list_mutex); + +@@ -92,11 +124,15 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, + + mutex_unlock(&list_mutex); + +- if (time_after(expires, jiffies)) ++ if (time_after(expires, now)) + return sprintf(buf, "%u\n", +- jiffies_to_msecs(expires - jiffies) / 1000); ++ jiffies_to_msecs(expires - now) / 1000); + +- return sprintf(buf, "0\n"); ++ if (timer->send_nl_msg) ++ return sprintf(buf, "0 %d\n", ++ jiffies_to_msecs(now - expires) / 1000); ++ else ++ return sprintf(buf, "0\n"); + } + + static void idletimer_tg_work(struct work_struct *work) +@@ -105,6 +141,9 @@ static void idletimer_tg_work(struct work_struct *work) + work); + + sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name); ++ ++ if (timer->send_nl_msg) ++ notify_netlink_uevent(timer->attr.attr.name, timer); + } + + static void idletimer_tg_expired(unsigned long data) +@@ -113,6 +152,7 @@ static void idletimer_tg_expired(unsigned long data) + + pr_debug("timer %s expired\n", timer->attr.attr.name); + ++ timer->active = false; + schedule_work(&timer->work); + } + +@@ -145,6 +185,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) + setup_timer(&info->timer->timer, idletimer_tg_expired, + (unsigned long) info->timer); + info->timer->refcnt = 1; ++ info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true; ++ info->timer->active = true; + + mod_timer(&info->timer->timer, + msecs_to_jiffies(info->timeout * 1000) + jiffies); +@@ -168,14 +210,24 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, + const struct xt_action_param *par) + { + const struct idletimer_tg_info *info = par->targinfo; ++ unsigned long now = jiffies; + + pr_debug("resetting timer %s, timeout period %u\n", + info->label, info->timeout); + + BUG_ON(!info->timer); + ++ info->timer->active = true; ++ ++ if (time_before(info->timer->timer.expires, now)) { ++ schedule_work(&info->timer->work); ++ pr_debug("Starting timer %s (Expired, Jiffies): %lu, %lu\n", ++ info->label, info->timer->timer.expires, now); ++ } ++ ++ /* TODO: Avoid modifying timers on each packet */ + mod_timer(&info->timer->timer, +- msecs_to_jiffies(info->timeout * 1000) + jiffies); ++ msecs_to_jiffies(info->timeout * 1000) + now); + + return XT_CONTINUE; + } +@@ -184,8 +236,9 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) + { + struct idletimer_tg_info *info = par->targinfo; + int ret; ++ unsigned long now = jiffies; + +- pr_debug("checkentry targinfo%s\n", info->label); ++ pr_debug("checkentry targinfo %s\n", info->label); + + if (info->timeout == 0) { + pr_debug("timeout value is zero\n"); +@@ -204,8 +257,16 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) + info->timer = __idletimer_tg_find_by_label(info->label); + if (info->timer) { + info->timer->refcnt++; ++ info->timer->active = true; ++ ++ if (time_before(info->timer->timer.expires, now)) { ++ schedule_work(&info->timer->work); ++ pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", ++ info->timer->timer.expires, now); ++ } ++ + mod_timer(&info->timer->timer, +- msecs_to_jiffies(info->timeout * 1000) + jiffies); ++ msecs_to_jiffies(info->timeout * 1000) + now); + + pr_debug("increased refcnt of timer %s to %u\n", + info->label, info->timer->refcnt); +@@ -219,6 +280,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) + } + + mutex_unlock(&list_mutex); ++ + return 0; + } + +@@ -240,7 +302,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) + kfree(info->timer); + } else { + pr_debug("decreased refcnt of timer %s to %u\n", +- info->label, info->timer->refcnt); ++ info->label, info->timer->refcnt); + } + + mutex_unlock(&list_mutex); +@@ -248,6 +310,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) + + static struct xt_target idletimer_tg __read_mostly = { + .name = "IDLETIMER", ++ .revision = 1, + .family = NFPROTO_UNSPEC, + .target = idletimer_tg_target, + .targetsize = sizeof(struct idletimer_tg_info), +@@ -313,3 +376,4 @@ MODULE_DESCRIPTION("Xtables: idle time monitor"); + MODULE_LICENSE("GPL v2"); + MODULE_ALIAS("ipt_IDLETIMER"); + MODULE_ALIAS("ip6t_IDLETIMER"); ++MODULE_ALIAS("arpt_IDLETIMER"); +diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c +new file mode 100644 +index 00000000..495b62ea +--- /dev/null ++++ b/net/netfilter/xt_qtaguid.c +@@ -0,0 +1,2990 @@ ++/* ++ * Kernel iptables module to track stats for packets based on user tags. ++ * ++ * (C) 2011 Google, Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * There are run-time debug flags enabled via the debug_mask module param, or ++ * via the DEFAULT_DEBUG_MASK. See xt_qtaguid_internal.h. ++ */ ++#define DEBUG ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) ++#include ++#endif ++ ++#include ++#include "xt_qtaguid_internal.h" ++#include "xt_qtaguid_print.h" ++ ++/* ++ * We only use the xt_socket funcs within a similar context to avoid unexpected ++ * return values. ++ */ ++#define XT_SOCKET_SUPPORTED_HOOKS \ ++ ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) ++ ++ ++static const char *module_procdirname = "xt_qtaguid"; ++static struct proc_dir_entry *xt_qtaguid_procdir; ++ ++static unsigned int proc_iface_perms = S_IRUGO; ++module_param_named(iface_perms, proc_iface_perms, uint, S_IRUGO | S_IWUSR); ++ ++static struct proc_dir_entry *xt_qtaguid_stats_file; ++static unsigned int proc_stats_perms = S_IRUGO; ++module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR); ++ ++static struct proc_dir_entry *xt_qtaguid_ctrl_file; ++ ++/* Everybody can write. But proc_ctrl_write_limited is true by default which ++ * limits what can be controlled. See the can_*() functions. ++ */ ++static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO; ++module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR); ++ ++/* Limited by default, so the gid of the ctrl and stats proc entries ++ * will limit what can be done. See the can_*() functions. ++ */ ++static bool proc_stats_readall_limited = true; ++static bool proc_ctrl_write_limited = true; ++ ++module_param_named(stats_readall_limited, proc_stats_readall_limited, bool, ++ S_IRUGO | S_IWUSR); ++module_param_named(ctrl_write_limited, proc_ctrl_write_limited, bool, ++ S_IRUGO | S_IWUSR); ++ ++/* ++ * Limit the number of active tags (via socket tags) for a given UID. ++ * Multiple processes could share the UID. ++ */ ++static int max_sock_tags = DEFAULT_MAX_SOCK_TAGS; ++module_param(max_sock_tags, int, S_IRUGO | S_IWUSR); ++ ++/* ++ * After the kernel has initiallized this module, it is still possible ++ * to make it passive. ++ * Setting passive to Y: ++ * - the iface stats handling will not act on notifications. ++ * - iptables matches will never match. ++ * - ctrl commands silently succeed. ++ * - stats are always empty. ++ * This is mostly usefull when a bug is suspected. ++ */ ++static bool module_passive; ++module_param_named(passive, module_passive, bool, S_IRUGO | S_IWUSR); ++ ++/* ++ * Control how qtaguid data is tracked per proc/uid. ++ * Setting tag_tracking_passive to Y: ++ * - don't create proc specific structs to track tags ++ * - don't check that active tag stats exceed some limits. ++ * - don't clean up socket tags on process exits. ++ * This is mostly usefull when a bug is suspected. ++ */ ++static bool qtu_proc_handling_passive; ++module_param_named(tag_tracking_passive, qtu_proc_handling_passive, bool, ++ S_IRUGO | S_IWUSR); ++ ++#define QTU_DEV_NAME "xt_qtaguid" ++ ++uint qtaguid_debug_mask = DEFAULT_DEBUG_MASK; ++module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR); ++ ++/*---------------------------------------------------------------------------*/ ++static const char *iface_stat_procdirname = "iface_stat"; ++static struct proc_dir_entry *iface_stat_procdir; ++/* ++ * The iface_stat_all* will go away once userspace gets use to the new fields ++ * that have a format line. ++ */ ++static const char *iface_stat_all_procfilename = "iface_stat_all"; ++static struct proc_dir_entry *iface_stat_all_procfile; ++static const char *iface_stat_fmt_procfilename = "iface_stat_fmt"; ++static struct proc_dir_entry *iface_stat_fmt_procfile; ++ ++ ++/* ++ * Ordering of locks: ++ * outer locks: ++ * iface_stat_list_lock ++ * sock_tag_list_lock ++ * inner locks: ++ * uid_tag_data_tree_lock ++ * tag_counter_set_list_lock ++ * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock ++ * is acquired. ++ * ++ * Call tree with all lock holders as of 2012-04-27: ++ * ++ * iface_stat_fmt_proc_read() ++ * iface_stat_list_lock ++ * (struct iface_stat) ++ * ++ * qtaguid_ctrl_proc_read() ++ * sock_tag_list_lock ++ * (sock_tag_tree) ++ * (struct proc_qtu_data->sock_tag_list) ++ * prdebug_full_state() ++ * sock_tag_list_lock ++ * (sock_tag_tree) ++ * uid_tag_data_tree_lock ++ * (uid_tag_data_tree) ++ * (proc_qtu_data_tree) ++ * iface_stat_list_lock ++ * ++ * qtaguid_stats_proc_read() ++ * iface_stat_list_lock ++ * struct iface_stat->tag_stat_list_lock ++ * ++ * qtudev_open() ++ * uid_tag_data_tree_lock ++ * ++ * qtudev_release() ++ * sock_tag_data_list_lock ++ * uid_tag_data_tree_lock ++ * prdebug_full_state() ++ * sock_tag_list_lock ++ * uid_tag_data_tree_lock ++ * iface_stat_list_lock ++ * ++ * iface_netdev_event_handler() ++ * iface_stat_create() ++ * iface_stat_list_lock ++ * iface_stat_update() ++ * iface_stat_list_lock ++ * ++ * iface_inetaddr_event_handler() ++ * iface_stat_create() ++ * iface_stat_list_lock ++ * iface_stat_update() ++ * iface_stat_list_lock ++ * ++ * iface_inet6addr_event_handler() ++ * iface_stat_create_ipv6() ++ * iface_stat_list_lock ++ * iface_stat_update() ++ * iface_stat_list_lock ++ * ++ * qtaguid_mt() ++ * account_for_uid() ++ * if_tag_stat_update() ++ * get_sock_stat() ++ * sock_tag_list_lock ++ * struct iface_stat->tag_stat_list_lock ++ * tag_stat_update() ++ * get_active_counter_set() ++ * tag_counter_set_list_lock ++ * tag_stat_update() ++ * get_active_counter_set() ++ * tag_counter_set_list_lock ++ * ++ * ++ * qtaguid_ctrl_parse() ++ * ctrl_cmd_delete() ++ * sock_tag_list_lock ++ * tag_counter_set_list_lock ++ * iface_stat_list_lock ++ * struct iface_stat->tag_stat_list_lock ++ * uid_tag_data_tree_lock ++ * ctrl_cmd_counter_set() ++ * tag_counter_set_list_lock ++ * ctrl_cmd_tag() ++ * sock_tag_list_lock ++ * (sock_tag_tree) ++ * get_tag_ref() ++ * uid_tag_data_tree_lock ++ * (uid_tag_data_tree) ++ * uid_tag_data_tree_lock ++ * (proc_qtu_data_tree) ++ * ctrl_cmd_untag() ++ * sock_tag_list_lock ++ * uid_tag_data_tree_lock ++ * ++ */ ++static LIST_HEAD(iface_stat_list); ++static DEFINE_SPINLOCK(iface_stat_list_lock); ++ ++static struct rb_root sock_tag_tree = RB_ROOT; ++static DEFINE_SPINLOCK(sock_tag_list_lock); ++ ++static struct rb_root tag_counter_set_tree = RB_ROOT; ++static DEFINE_SPINLOCK(tag_counter_set_list_lock); ++ ++static struct rb_root uid_tag_data_tree = RB_ROOT; ++static DEFINE_SPINLOCK(uid_tag_data_tree_lock); ++ ++static struct rb_root proc_qtu_data_tree = RB_ROOT; ++/* No proc_qtu_data_tree_lock; use uid_tag_data_tree_lock */ ++ ++static struct qtaguid_event_counts qtu_events; ++/*----------------------------------------------*/ ++static bool can_manipulate_uids(void) ++{ ++ /* root pwnd */ ++ return in_egroup_p(xt_qtaguid_ctrl_file->gid) ++ || unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_limited) ++ || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); ++} ++ ++static bool can_impersonate_uid(uid_t uid) ++{ ++ return uid == current_fsuid() || can_manipulate_uids(); ++} ++ ++static bool can_read_other_uid_stats(uid_t uid) ++{ ++ /* root pwnd */ ++ return in_egroup_p(xt_qtaguid_stats_file->gid) ++ || unlikely(!current_fsuid()) || uid == current_fsuid() ++ || unlikely(!proc_stats_readall_limited) ++ || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); ++} ++ ++static inline void dc_add_byte_packets(struct data_counters *counters, int set, ++ enum ifs_tx_rx direction, ++ enum ifs_proto ifs_proto, ++ int bytes, ++ int packets) ++{ ++ counters->bpc[set][direction][ifs_proto].bytes += bytes; ++ counters->bpc[set][direction][ifs_proto].packets += packets; ++} ++ ++static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) ++{ ++ struct rb_node *node = root->rb_node; ++ ++ while (node) { ++ struct tag_node *data = rb_entry(node, struct tag_node, node); ++ int result; ++ RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " ++ " node=%p data=%p\n", tag, node, data); ++ result = tag_compare(tag, data->tag); ++ RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " ++ " data.tag=0x%llx (uid=%u) res=%d\n", ++ tag, data->tag, get_uid_from_tag(data->tag), result); ++ if (result < 0) ++ node = node->rb_left; ++ else if (result > 0) ++ node = node->rb_right; ++ else ++ return data; ++ } ++ return NULL; ++} ++ ++static void tag_node_tree_insert(struct tag_node *data, struct rb_root *root) ++{ ++ struct rb_node **new = &(root->rb_node), *parent = NULL; ++ ++ /* Figure out where to put new node */ ++ while (*new) { ++ struct tag_node *this = rb_entry(*new, struct tag_node, ++ node); ++ int result = tag_compare(data->tag, this->tag); ++ RB_DEBUG("qtaguid: %s(): tag=0x%llx" ++ " (uid=%u)\n", __func__, ++ this->tag, ++ get_uid_from_tag(this->tag)); ++ parent = *new; ++ if (result < 0) ++ new = &((*new)->rb_left); ++ else if (result > 0) ++ new = &((*new)->rb_right); ++ else ++ BUG(); ++ } ++ ++ /* Add new node and rebalance tree. */ ++ rb_link_node(&data->node, parent, new); ++ rb_insert_color(&data->node, root); ++} ++ ++static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) ++{ ++ tag_node_tree_insert(&data->tn, root); ++} ++ ++static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) ++{ ++ struct tag_node *node = tag_node_tree_search(root, tag); ++ if (!node) ++ return NULL; ++ return rb_entry(&node->node, struct tag_stat, tn.node); ++} ++ ++static void tag_counter_set_tree_insert(struct tag_counter_set *data, ++ struct rb_root *root) ++{ ++ tag_node_tree_insert(&data->tn, root); ++} ++ ++static struct tag_counter_set *tag_counter_set_tree_search(struct rb_root *root, ++ tag_t tag) ++{ ++ struct tag_node *node = tag_node_tree_search(root, tag); ++ if (!node) ++ return NULL; ++ return rb_entry(&node->node, struct tag_counter_set, tn.node); ++ ++} ++ ++static void tag_ref_tree_insert(struct tag_ref *data, struct rb_root *root) ++{ ++ tag_node_tree_insert(&data->tn, root); ++} ++ ++static struct tag_ref *tag_ref_tree_search(struct rb_root *root, tag_t tag) ++{ ++ struct tag_node *node = tag_node_tree_search(root, tag); ++ if (!node) ++ return NULL; ++ return rb_entry(&node->node, struct tag_ref, tn.node); ++} ++ ++static struct sock_tag *sock_tag_tree_search(struct rb_root *root, ++ const struct sock *sk) ++{ ++ struct rb_node *node = root->rb_node; ++ ++ while (node) { ++ struct sock_tag *data = rb_entry(node, struct sock_tag, ++ sock_node); ++ if (sk < data->sk) ++ node = node->rb_left; ++ else if (sk > data->sk) ++ node = node->rb_right; ++ else ++ return data; ++ } ++ return NULL; ++} ++ ++static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) ++{ ++ struct rb_node **new = &(root->rb_node), *parent = NULL; ++ ++ /* Figure out where to put new node */ ++ while (*new) { ++ struct sock_tag *this = rb_entry(*new, struct sock_tag, ++ sock_node); ++ parent = *new; ++ if (data->sk < this->sk) ++ new = &((*new)->rb_left); ++ else if (data->sk > this->sk) ++ new = &((*new)->rb_right); ++ else ++ BUG(); ++ } ++ ++ /* Add new node and rebalance tree. */ ++ rb_link_node(&data->sock_node, parent, new); ++ rb_insert_color(&data->sock_node, root); ++} ++ ++static void sock_tag_tree_erase(struct rb_root *st_to_free_tree) ++{ ++ struct rb_node *node; ++ struct sock_tag *st_entry; ++ ++ node = rb_first(st_to_free_tree); ++ while (node) { ++ st_entry = rb_entry(node, struct sock_tag, sock_node); ++ node = rb_next(node); ++ CT_DEBUG("qtaguid: %s(): " ++ "erase st: sk=%p tag=0x%llx (uid=%u)\n", __func__, ++ st_entry->sk, ++ st_entry->tag, ++ get_uid_from_tag(st_entry->tag)); ++ rb_erase(&st_entry->sock_node, st_to_free_tree); ++ sockfd_put(st_entry->socket); ++ kfree(st_entry); ++ } ++} ++ ++static struct proc_qtu_data *proc_qtu_data_tree_search(struct rb_root *root, ++ const pid_t pid) ++{ ++ struct rb_node *node = root->rb_node; ++ ++ while (node) { ++ struct proc_qtu_data *data = rb_entry(node, ++ struct proc_qtu_data, ++ node); ++ if (pid < data->pid) ++ node = node->rb_left; ++ else if (pid > data->pid) ++ node = node->rb_right; ++ else ++ return data; ++ } ++ return NULL; ++} ++ ++static void proc_qtu_data_tree_insert(struct proc_qtu_data *data, ++ struct rb_root *root) ++{ ++ struct rb_node **new = &(root->rb_node), *parent = NULL; ++ ++ /* Figure out where to put new node */ ++ while (*new) { ++ struct proc_qtu_data *this = rb_entry(*new, ++ struct proc_qtu_data, ++ node); ++ parent = *new; ++ if (data->pid < this->pid) ++ new = &((*new)->rb_left); ++ else if (data->pid > this->pid) ++ new = &((*new)->rb_right); ++ else ++ BUG(); ++ } ++ ++ /* Add new node and rebalance tree. */ ++ rb_link_node(&data->node, parent, new); ++ rb_insert_color(&data->node, root); ++} ++ ++static void uid_tag_data_tree_insert(struct uid_tag_data *data, ++ struct rb_root *root) ++{ ++ struct rb_node **new = &(root->rb_node), *parent = NULL; ++ ++ /* Figure out where to put new node */ ++ while (*new) { ++ struct uid_tag_data *this = rb_entry(*new, ++ struct uid_tag_data, ++ node); ++ parent = *new; ++ if (data->uid < this->uid) ++ new = &((*new)->rb_left); ++ else if (data->uid > this->uid) ++ new = &((*new)->rb_right); ++ else ++ BUG(); ++ } ++ ++ /* Add new node and rebalance tree. */ ++ rb_link_node(&data->node, parent, new); ++ rb_insert_color(&data->node, root); ++} ++ ++static struct uid_tag_data *uid_tag_data_tree_search(struct rb_root *root, ++ uid_t uid) ++{ ++ struct rb_node *node = root->rb_node; ++ ++ while (node) { ++ struct uid_tag_data *data = rb_entry(node, ++ struct uid_tag_data, ++ node); ++ if (uid < data->uid) ++ node = node->rb_left; ++ else if (uid > data->uid) ++ node = node->rb_right; ++ else ++ return data; ++ } ++ return NULL; ++} ++ ++/* ++ * Allocates a new uid_tag_data struct if needed. ++ * Returns a pointer to the found or allocated uid_tag_data. ++ * Returns a PTR_ERR on failures, and lock is not held. ++ * If found is not NULL: ++ * sets *found to true if not allocated. ++ * sets *found to false if allocated. ++ */ ++struct uid_tag_data *get_uid_data(uid_t uid, bool *found_res) ++{ ++ struct uid_tag_data *utd_entry; ++ ++ /* Look for top level uid_tag_data for the UID */ ++ utd_entry = uid_tag_data_tree_search(&uid_tag_data_tree, uid); ++ DR_DEBUG("qtaguid: get_uid_data(%u) utd=%p\n", uid, utd_entry); ++ ++ if (found_res) ++ *found_res = utd_entry; ++ if (utd_entry) ++ return utd_entry; ++ ++ utd_entry = kzalloc(sizeof(*utd_entry), GFP_ATOMIC); ++ if (!utd_entry) { ++ pr_err("qtaguid: get_uid_data(%u): " ++ "tag data alloc failed\n", uid); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ utd_entry->uid = uid; ++ utd_entry->tag_ref_tree = RB_ROOT; ++ uid_tag_data_tree_insert(utd_entry, &uid_tag_data_tree); ++ DR_DEBUG("qtaguid: get_uid_data(%u) new utd=%p\n", uid, utd_entry); ++ return utd_entry; ++} ++ ++/* Never returns NULL. Either PTR_ERR or a valid ptr. */ ++static struct tag_ref *new_tag_ref(tag_t new_tag, ++ struct uid_tag_data *utd_entry) ++{ ++ struct tag_ref *tr_entry; ++ int res; ++ ++ if (utd_entry->num_active_tags + 1 > max_sock_tags) { ++ pr_info("qtaguid: new_tag_ref(0x%llx): " ++ "tag ref alloc quota exceeded. max=%d\n", ++ new_tag, max_sock_tags); ++ res = -EMFILE; ++ goto err_res; ++ ++ } ++ ++ tr_entry = kzalloc(sizeof(*tr_entry), GFP_ATOMIC); ++ if (!tr_entry) { ++ pr_err("qtaguid: new_tag_ref(0x%llx): " ++ "tag ref alloc failed\n", ++ new_tag); ++ res = -ENOMEM; ++ goto err_res; ++ } ++ tr_entry->tn.tag = new_tag; ++ /* tr_entry->num_sock_tags handled by caller */ ++ utd_entry->num_active_tags++; ++ tag_ref_tree_insert(tr_entry, &utd_entry->tag_ref_tree); ++ DR_DEBUG("qtaguid: new_tag_ref(0x%llx): " ++ " inserted new tag ref %p\n", ++ new_tag, tr_entry); ++ return tr_entry; ++ ++err_res: ++ return ERR_PTR(res); ++} ++ ++static struct tag_ref *lookup_tag_ref(tag_t full_tag, ++ struct uid_tag_data **utd_res) ++{ ++ struct uid_tag_data *utd_entry; ++ struct tag_ref *tr_entry; ++ bool found_utd; ++ uid_t uid = get_uid_from_tag(full_tag); ++ ++ DR_DEBUG("qtaguid: lookup_tag_ref(tag=0x%llx (uid=%u))\n", ++ full_tag, uid); ++ ++ utd_entry = get_uid_data(uid, &found_utd); ++ if (IS_ERR_OR_NULL(utd_entry)) { ++ if (utd_res) ++ *utd_res = utd_entry; ++ return NULL; ++ } ++ ++ tr_entry = tag_ref_tree_search(&utd_entry->tag_ref_tree, full_tag); ++ if (utd_res) ++ *utd_res = utd_entry; ++ DR_DEBUG("qtaguid: lookup_tag_ref(0x%llx) utd_entry=%p tr_entry=%p\n", ++ full_tag, utd_entry, tr_entry); ++ return tr_entry; ++} ++ ++/* Never returns NULL. Either PTR_ERR or a valid ptr. */ ++static struct tag_ref *get_tag_ref(tag_t full_tag, ++ struct uid_tag_data **utd_res) ++{ ++ struct uid_tag_data *utd_entry; ++ struct tag_ref *tr_entry; ++ ++ DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n", ++ full_tag); ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ tr_entry = lookup_tag_ref(full_tag, &utd_entry); ++ BUG_ON(IS_ERR_OR_NULL(utd_entry)); ++ if (!tr_entry) ++ tr_entry = new_tag_ref(full_tag, utd_entry); ++ ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ if (utd_res) ++ *utd_res = utd_entry; ++ DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n", ++ full_tag, utd_entry, tr_entry); ++ return tr_entry; ++} ++ ++/* Checks and maybe frees the UID Tag Data entry */ ++static void put_utd_entry(struct uid_tag_data *utd_entry) ++{ ++ /* Are we done with the UID tag data entry? */ ++ if (RB_EMPTY_ROOT(&utd_entry->tag_ref_tree) && ++ !utd_entry->num_pqd) { ++ DR_DEBUG("qtaguid: %s(): " ++ "erase utd_entry=%p uid=%u " ++ "by pid=%u tgid=%u uid=%u\n", __func__, ++ utd_entry, utd_entry->uid, ++ current->pid, current->tgid, current_fsuid()); ++ BUG_ON(utd_entry->num_active_tags); ++ rb_erase(&utd_entry->node, &uid_tag_data_tree); ++ kfree(utd_entry); ++ } else { ++ DR_DEBUG("qtaguid: %s(): " ++ "utd_entry=%p still has %d tags %d proc_qtu_data\n", ++ __func__, utd_entry, utd_entry->num_active_tags, ++ utd_entry->num_pqd); ++ BUG_ON(!(utd_entry->num_active_tags || ++ utd_entry->num_pqd)); ++ } ++} ++ ++/* ++ * If no sock_tags are using this tag_ref, ++ * decrements refcount of utd_entry, removes tr_entry ++ * from utd_entry->tag_ref_tree and frees. ++ */ ++static void free_tag_ref_from_utd_entry(struct tag_ref *tr_entry, ++ struct uid_tag_data *utd_entry) ++{ ++ DR_DEBUG("qtaguid: %s(): %p tag=0x%llx (uid=%u)\n", __func__, ++ tr_entry, tr_entry->tn.tag, ++ get_uid_from_tag(tr_entry->tn.tag)); ++ if (!tr_entry->num_sock_tags) { ++ BUG_ON(!utd_entry->num_active_tags); ++ utd_entry->num_active_tags--; ++ rb_erase(&tr_entry->tn.node, &utd_entry->tag_ref_tree); ++ DR_DEBUG("qtaguid: %s(): erased %p\n", __func__, tr_entry); ++ kfree(tr_entry); ++ } ++} ++ ++static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry) ++{ ++ struct rb_node *node; ++ struct tag_ref *tr_entry; ++ tag_t acct_tag; ++ ++ DR_DEBUG("qtaguid: %s(tag=0x%llx (uid=%u))\n", __func__, ++ full_tag, get_uid_from_tag(full_tag)); ++ acct_tag = get_atag_from_tag(full_tag); ++ node = rb_first(&utd_entry->tag_ref_tree); ++ while (node) { ++ tr_entry = rb_entry(node, struct tag_ref, tn.node); ++ node = rb_next(node); ++ if (!acct_tag || tr_entry->tn.tag == full_tag) ++ free_tag_ref_from_utd_entry(tr_entry, utd_entry); ++ } ++} ++ ++static int read_proc_u64(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len; ++ uint64_t value; ++ char *p = page; ++ uint64_t *iface_entry = data; ++ ++ if (!data) ++ return 0; ++ ++ value = *iface_entry; ++ p += sprintf(p, "%llu\n", value); ++ len = (p - page) - off; ++ *eof = (len <= count) ? 1 : 0; ++ *start = page + off; ++ return len; ++} ++ ++static int read_proc_bool(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len; ++ bool value; ++ char *p = page; ++ bool *bool_entry = data; ++ ++ if (!data) ++ return 0; ++ ++ value = *bool_entry; ++ p += sprintf(p, "%u\n", value); ++ len = (p - page) - off; ++ *eof = (len <= count) ? 1 : 0; ++ *start = page + off; ++ return len; ++} ++ ++static int get_active_counter_set(tag_t tag) ++{ ++ int active_set = 0; ++ struct tag_counter_set *tcs; ++ ++ MT_DEBUG("qtaguid: get_active_counter_set(tag=0x%llx)" ++ " (uid=%u)\n", ++ tag, get_uid_from_tag(tag)); ++ /* For now we only handle UID tags for active sets */ ++ tag = get_utag_from_tag(tag); ++ spin_lock_bh(&tag_counter_set_list_lock); ++ tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); ++ if (tcs) ++ active_set = tcs->active_set; ++ spin_unlock_bh(&tag_counter_set_list_lock); ++ return active_set; ++} ++ ++/* ++ * Find the entry for tracking the specified interface. ++ * Caller must hold iface_stat_list_lock ++ */ ++static struct iface_stat *get_iface_entry(const char *ifname) ++{ ++ struct iface_stat *iface_entry; ++ ++ /* Find the entry for tracking the specified tag within the interface */ ++ if (ifname == NULL) { ++ pr_info("qtaguid: iface_stat: get() NULL device name\n"); ++ return NULL; ++ } ++ ++ /* Iterate over interfaces */ ++ list_for_each_entry(iface_entry, &iface_stat_list, list) { ++ if (!strcmp(ifname, iface_entry->ifname)) ++ goto done; ++ } ++ iface_entry = NULL; ++done: ++ return iface_entry; ++} ++ ++/* This is for fmt2 only */ ++static int pp_iface_stat_line(bool header, char *outp, ++ int char_count, struct iface_stat *iface_entry) ++{ ++ int len; ++ if (header) { ++ len = snprintf(outp, char_count, ++ "ifname " ++ "total_skb_rx_bytes total_skb_rx_packets " ++ "total_skb_tx_bytes total_skb_tx_packets " ++ "rx_tcp_bytes rx_tcp_packets " ++ "rx_udp_bytes rx_udp_packets " ++ "rx_other_bytes rx_other_packets " ++ "tx_tcp_bytes tx_tcp_packets " ++ "tx_udp_bytes tx_udp_packets " ++ "tx_other_bytes tx_other_packets\n" ++ ); ++ } else { ++ struct data_counters *cnts; ++ int cnt_set = 0; /* We only use one set for the device */ ++ cnts = &iface_entry->totals_via_skb; ++ len = snprintf( ++ outp, char_count, ++ "%s " ++ "%llu %llu %llu %llu %llu %llu %llu %llu " ++ "%llu %llu %llu %llu %llu %llu %llu %llu\n", ++ iface_entry->ifname, ++ dc_sum_bytes(cnts, cnt_set, IFS_RX), ++ dc_sum_packets(cnts, cnt_set, IFS_RX), ++ dc_sum_bytes(cnts, cnt_set, IFS_TX), ++ dc_sum_packets(cnts, cnt_set, IFS_TX), ++ cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, ++ cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, ++ cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, ++ cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, ++ cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, ++ cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, ++ cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, ++ cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, ++ cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, ++ cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, ++ cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, ++ cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); ++ } ++ return len; ++} ++ ++static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, ++ off_t items_to_skip, int char_count, ++ int *eof, void *data) ++{ ++ char *outp = page; ++ int item_index = 0; ++ int len; ++ int fmt = (int)data; /* The data is just 1 (old) or 2 (uses fmt) */ ++ struct iface_stat *iface_entry; ++ struct rtnl_link_stats64 dev_stats, *stats; ++ struct rtnl_link_stats64 no_dev_stats = {0}; ++ ++ if (unlikely(module_passive)) { ++ *eof = 1; ++ return 0; ++ } ++ ++ CT_DEBUG("qtaguid:proc iface_stat_fmt " ++ "pid=%u tgid=%u uid=%u " ++ "page=%p *num_items_returned=%p off=%ld " ++ "char_count=%d *eof=%d\n", ++ current->pid, current->tgid, current_fsuid(), ++ page, *num_items_returned, ++ items_to_skip, char_count, *eof); ++ ++ if (*eof) ++ return 0; ++ ++ if (fmt == 2 && item_index++ >= items_to_skip) { ++ len = pp_iface_stat_line(true, outp, char_count, NULL); ++ if (len >= char_count) { ++ *outp = '\0'; ++ return outp - page; ++ } ++ outp += len; ++ char_count -= len; ++ (*num_items_returned)++; ++ } ++ ++ /* ++ * This lock will prevent iface_stat_update() from changing active, ++ * and in turn prevent an interface from unregistering itself. ++ */ ++ spin_lock_bh(&iface_stat_list_lock); ++ list_for_each_entry(iface_entry, &iface_stat_list, list) { ++ if (item_index++ < items_to_skip) ++ continue; ++ ++ if (iface_entry->active) { ++ stats = dev_get_stats(iface_entry->net_dev, ++ &dev_stats); ++ } else { ++ stats = &no_dev_stats; ++ } ++ /* ++ * If the meaning of the data changes, then update the fmtX ++ * string. ++ */ ++ if (fmt == 1) { ++ len = snprintf( ++ outp, char_count, ++ "%s %d " ++ "%llu %llu %llu %llu " ++ "%llu %llu %llu %llu\n", ++ iface_entry->ifname, ++ iface_entry->active, ++ iface_entry->totals_via_dev[IFS_RX].bytes, ++ iface_entry->totals_via_dev[IFS_RX].packets, ++ iface_entry->totals_via_dev[IFS_TX].bytes, ++ iface_entry->totals_via_dev[IFS_TX].packets, ++ stats->rx_bytes, stats->rx_packets, ++ stats->tx_bytes, stats->tx_packets ++ ); ++ } else { ++ len = pp_iface_stat_line(false, outp, char_count, ++ iface_entry); ++ } ++ if (len >= char_count) { ++ spin_unlock_bh(&iface_stat_list_lock); ++ *outp = '\0'; ++ return outp - page; ++ } ++ outp += len; ++ char_count -= len; ++ (*num_items_returned)++; ++ } ++ spin_unlock_bh(&iface_stat_list_lock); ++ ++ *eof = 1; ++ return outp - page; ++} ++ ++static void iface_create_proc_worker(struct work_struct *work) ++{ ++ struct proc_dir_entry *proc_entry; ++ struct iface_stat_work *isw = container_of(work, struct iface_stat_work, ++ iface_work); ++ struct iface_stat *new_iface = isw->iface_entry; ++ ++ /* iface_entries are not deleted, so safe to manipulate. */ ++ proc_entry = proc_mkdir(new_iface->ifname, iface_stat_procdir); ++ if (IS_ERR_OR_NULL(proc_entry)) { ++ pr_err("qtaguid: iface_stat: create_proc(): alloc failed.\n"); ++ kfree(isw); ++ return; ++ } ++ ++ new_iface->proc_ptr = proc_entry; ++ ++ create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, ++ read_proc_u64, ++ &new_iface->totals_via_dev[IFS_TX].bytes); ++ create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, ++ read_proc_u64, ++ &new_iface->totals_via_dev[IFS_RX].bytes); ++ create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, ++ read_proc_u64, ++ &new_iface->totals_via_dev[IFS_TX].packets); ++ create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, ++ read_proc_u64, ++ &new_iface->totals_via_dev[IFS_RX].packets); ++ create_proc_read_entry("active", proc_iface_perms, proc_entry, ++ read_proc_bool, &new_iface->active); ++ ++ IF_DEBUG("qtaguid: iface_stat: create_proc(): done " ++ "entry=%p dev=%s\n", new_iface, new_iface->ifname); ++ kfree(isw); ++} ++ ++/* ++ * Will set the entry's active state, and ++ * update the net_dev accordingly also. ++ */ ++static void _iface_stat_set_active(struct iface_stat *entry, ++ struct net_device *net_dev, ++ bool activate) ++{ ++ if (activate) { ++ entry->net_dev = net_dev; ++ entry->active = true; ++ IF_DEBUG("qtaguid: %s(%s): " ++ "enable tracking. rfcnt=%d\n", __func__, ++ entry->ifname, ++ percpu_read(*net_dev->pcpu_refcnt)); ++ } else { ++ entry->active = false; ++ entry->net_dev = NULL; ++ IF_DEBUG("qtaguid: %s(%s): " ++ "disable tracking. rfcnt=%d\n", __func__, ++ entry->ifname, ++ percpu_read(*net_dev->pcpu_refcnt)); ++ ++ } ++} ++ ++/* Caller must hold iface_stat_list_lock */ ++static struct iface_stat *iface_alloc(struct net_device *net_dev) ++{ ++ struct iface_stat *new_iface; ++ struct iface_stat_work *isw; ++ ++ new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC); ++ if (new_iface == NULL) { ++ pr_err("qtaguid: iface_stat: create(%s): " ++ "iface_stat alloc failed\n", net_dev->name); ++ return NULL; ++ } ++ new_iface->ifname = kstrdup(net_dev->name, GFP_ATOMIC); ++ if (new_iface->ifname == NULL) { ++ pr_err("qtaguid: iface_stat: create(%s): " ++ "ifname alloc failed\n", net_dev->name); ++ kfree(new_iface); ++ return NULL; ++ } ++ spin_lock_init(&new_iface->tag_stat_list_lock); ++ new_iface->tag_stat_tree = RB_ROOT; ++ _iface_stat_set_active(new_iface, net_dev, true); ++ ++ /* ++ * ipv6 notifier chains are atomic :( ++ * No create_proc_read_entry() for you! ++ */ ++ isw = kmalloc(sizeof(*isw), GFP_ATOMIC); ++ if (!isw) { ++ pr_err("qtaguid: iface_stat: create(%s): " ++ "work alloc failed\n", new_iface->ifname); ++ _iface_stat_set_active(new_iface, net_dev, false); ++ kfree(new_iface->ifname); ++ kfree(new_iface); ++ return NULL; ++ } ++ isw->iface_entry = new_iface; ++ INIT_WORK(&isw->iface_work, iface_create_proc_worker); ++ schedule_work(&isw->iface_work); ++ list_add(&new_iface->list, &iface_stat_list); ++ return new_iface; ++} ++ ++static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, ++ struct iface_stat *iface) ++{ ++ struct rtnl_link_stats64 dev_stats, *stats; ++ bool stats_rewound; ++ ++ stats = dev_get_stats(net_dev, &dev_stats); ++ /* No empty packets */ ++ stats_rewound = ++ (stats->rx_bytes < iface->last_known[IFS_RX].bytes) ++ || (stats->tx_bytes < iface->last_known[IFS_TX].bytes); ++ ++ IF_DEBUG("qtaguid: %s(%s): iface=%p netdev=%p " ++ "bytes rx/tx=%llu/%llu " ++ "active=%d last_known=%d " ++ "stats_rewound=%d\n", __func__, ++ net_dev ? net_dev->name : "?", ++ iface, net_dev, ++ stats->rx_bytes, stats->tx_bytes, ++ iface->active, iface->last_known_valid, stats_rewound); ++ ++ if (iface->active && iface->last_known_valid && stats_rewound) { ++ pr_warn_once("qtaguid: iface_stat: %s(%s): " ++ "iface reset its stats unexpectedly\n", __func__, ++ net_dev->name); ++ ++ iface->totals_via_dev[IFS_TX].bytes += ++ iface->last_known[IFS_TX].bytes; ++ iface->totals_via_dev[IFS_TX].packets += ++ iface->last_known[IFS_TX].packets; ++ iface->totals_via_dev[IFS_RX].bytes += ++ iface->last_known[IFS_RX].bytes; ++ iface->totals_via_dev[IFS_RX].packets += ++ iface->last_known[IFS_RX].packets; ++ iface->last_known_valid = false; ++ IF_DEBUG("qtaguid: %s(%s): iface=%p " ++ "used last known bytes rx/tx=%llu/%llu\n", __func__, ++ iface->ifname, iface, iface->last_known[IFS_RX].bytes, ++ iface->last_known[IFS_TX].bytes); ++ } ++} ++ ++/* ++ * Create a new entry for tracking the specified interface. ++ * Do nothing if the entry already exists. ++ * Called when an interface is configured with a valid IP address. ++ */ ++static void iface_stat_create(struct net_device *net_dev, ++ struct in_ifaddr *ifa) ++{ ++ struct in_device *in_dev = NULL; ++ const char *ifname; ++ struct iface_stat *entry; ++ __be32 ipaddr = 0; ++ struct iface_stat *new_iface; ++ ++ IF_DEBUG("qtaguid: iface_stat: create(%s): ifa=%p netdev=%p\n", ++ net_dev ? net_dev->name : "?", ++ ifa, net_dev); ++ if (!net_dev) { ++ pr_err("qtaguid: iface_stat: create(): no net dev\n"); ++ return; ++ } ++ ++ ifname = net_dev->name; ++ if (!ifa) { ++ in_dev = in_dev_get(net_dev); ++ if (!in_dev) { ++ pr_err("qtaguid: iface_stat: create(%s): no inet dev\n", ++ ifname); ++ return; ++ } ++ IF_DEBUG("qtaguid: iface_stat: create(%s): in_dev=%p\n", ++ ifname, in_dev); ++ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { ++ IF_DEBUG("qtaguid: iface_stat: create(%s): " ++ "ifa=%p ifa_label=%s\n", ++ ifname, ifa, ++ ifa->ifa_label ? ifa->ifa_label : "(null)"); ++ if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) ++ break; ++ } ++ } ++ ++ if (!ifa) { ++ IF_DEBUG("qtaguid: iface_stat: create(%s): no matching IP\n", ++ ifname); ++ goto done_put; ++ } ++ ipaddr = ifa->ifa_local; ++ ++ spin_lock_bh(&iface_stat_list_lock); ++ entry = get_iface_entry(ifname); ++ if (entry != NULL) { ++ IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", ++ ifname, entry); ++ iface_check_stats_reset_and_adjust(net_dev, entry); ++ _iface_stat_set_active(entry, net_dev, true); ++ IF_DEBUG("qtaguid: %s(%s): " ++ "tracking now %d on ip=%pI4\n", __func__, ++ entry->ifname, true, &ipaddr); ++ goto done_unlock_put; ++ } ++ ++ new_iface = iface_alloc(net_dev); ++ IF_DEBUG("qtaguid: iface_stat: create(%s): done " ++ "entry=%p ip=%pI4\n", ifname, new_iface, &ipaddr); ++done_unlock_put: ++ spin_unlock_bh(&iface_stat_list_lock); ++done_put: ++ if (in_dev) ++ in_dev_put(in_dev); ++} ++ ++static void iface_stat_create_ipv6(struct net_device *net_dev, ++ struct inet6_ifaddr *ifa) ++{ ++ struct in_device *in_dev; ++ const char *ifname; ++ struct iface_stat *entry; ++ struct iface_stat *new_iface; ++ int addr_type; ++ ++ IF_DEBUG("qtaguid: iface_stat: create6(): ifa=%p netdev=%p->name=%s\n", ++ ifa, net_dev, net_dev ? net_dev->name : ""); ++ if (!net_dev) { ++ pr_err("qtaguid: iface_stat: create6(): no net dev!\n"); ++ return; ++ } ++ ifname = net_dev->name; ++ ++ in_dev = in_dev_get(net_dev); ++ if (!in_dev) { ++ pr_err("qtaguid: iface_stat: create6(%s): no inet dev\n", ++ ifname); ++ return; ++ } ++ ++ IF_DEBUG("qtaguid: iface_stat: create6(%s): in_dev=%p\n", ++ ifname, in_dev); ++ ++ if (!ifa) { ++ IF_DEBUG("qtaguid: iface_stat: create6(%s): no matching IP\n", ++ ifname); ++ goto done_put; ++ } ++ addr_type = ipv6_addr_type(&ifa->addr); ++ ++ spin_lock_bh(&iface_stat_list_lock); ++ entry = get_iface_entry(ifname); ++ if (entry != NULL) { ++ IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, ++ ifname, entry); ++ iface_check_stats_reset_and_adjust(net_dev, entry); ++ _iface_stat_set_active(entry, net_dev, true); ++ IF_DEBUG("qtaguid: %s(%s): " ++ "tracking now %d on ip=%pI6c\n", __func__, ++ entry->ifname, true, &ifa->addr); ++ goto done_unlock_put; ++ } ++ ++ new_iface = iface_alloc(net_dev); ++ IF_DEBUG("qtaguid: iface_stat: create6(%s): done " ++ "entry=%p ip=%pI6c\n", ifname, new_iface, &ifa->addr); ++ ++done_unlock_put: ++ spin_unlock_bh(&iface_stat_list_lock); ++done_put: ++ in_dev_put(in_dev); ++} ++ ++static struct sock_tag *get_sock_stat_nl(const struct sock *sk) ++{ ++ MT_DEBUG("qtaguid: get_sock_stat_nl(sk=%p)\n", sk); ++ return sock_tag_tree_search(&sock_tag_tree, sk); ++} ++ ++static struct sock_tag *get_sock_stat(const struct sock *sk) ++{ ++ struct sock_tag *sock_tag_entry; ++ MT_DEBUG("qtaguid: get_sock_stat(sk=%p)\n", sk); ++ if (!sk) ++ return NULL; ++ spin_lock_bh(&sock_tag_list_lock); ++ sock_tag_entry = get_sock_stat_nl(sk); ++ spin_unlock_bh(&sock_tag_list_lock); ++ return sock_tag_entry; ++} ++ ++static int ipx_proto(const struct sk_buff *skb, ++ struct xt_action_param *par) ++{ ++ int thoff, tproto; ++ ++ switch (par->family) { ++ case NFPROTO_IPV6: ++ tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); ++ if (tproto < 0) ++ MT_DEBUG("%s(): transport header not found in ipv6" ++ " skb=%p\n", __func__, skb); ++ break; ++ case NFPROTO_IPV4: ++ tproto = ip_hdr(skb)->protocol; ++ break; ++ default: ++ tproto = IPPROTO_RAW; ++ } ++ return tproto; ++} ++ ++static void ++data_counters_update(struct data_counters *dc, int set, ++ enum ifs_tx_rx direction, int proto, int bytes) ++{ ++ switch (proto) { ++ case IPPROTO_TCP: ++ dc_add_byte_packets(dc, set, direction, IFS_TCP, bytes, 1); ++ break; ++ case IPPROTO_UDP: ++ dc_add_byte_packets(dc, set, direction, IFS_UDP, bytes, 1); ++ break; ++ case IPPROTO_IP: ++ default: ++ dc_add_byte_packets(dc, set, direction, IFS_PROTO_OTHER, bytes, ++ 1); ++ break; ++ } ++} ++ ++/* ++ * Update stats for the specified interface. Do nothing if the entry ++ * does not exist (when a device was never configured with an IP address). ++ * Called when an device is being unregistered. ++ */ ++static void iface_stat_update(struct net_device *net_dev, bool stash_only) ++{ ++ struct rtnl_link_stats64 dev_stats, *stats; ++ struct iface_stat *entry; ++ ++ stats = dev_get_stats(net_dev, &dev_stats); ++ spin_lock_bh(&iface_stat_list_lock); ++ entry = get_iface_entry(net_dev->name); ++ if (entry == NULL) { ++ IF_DEBUG("qtaguid: iface_stat: update(%s): not tracked\n", ++ net_dev->name); ++ spin_unlock_bh(&iface_stat_list_lock); ++ return; ++ } ++ ++ IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, ++ net_dev->name, entry); ++ if (!entry->active) { ++ IF_DEBUG("qtaguid: %s(%s): already disabled\n", __func__, ++ net_dev->name); ++ spin_unlock_bh(&iface_stat_list_lock); ++ return; ++ } ++ ++ if (stash_only) { ++ entry->last_known[IFS_TX].bytes = stats->tx_bytes; ++ entry->last_known[IFS_TX].packets = stats->tx_packets; ++ entry->last_known[IFS_RX].bytes = stats->rx_bytes; ++ entry->last_known[IFS_RX].packets = stats->rx_packets; ++ entry->last_known_valid = true; ++ IF_DEBUG("qtaguid: %s(%s): " ++ "dev stats stashed rx/tx=%llu/%llu\n", __func__, ++ net_dev->name, stats->rx_bytes, stats->tx_bytes); ++ spin_unlock_bh(&iface_stat_list_lock); ++ return; ++ } ++ entry->totals_via_dev[IFS_TX].bytes += stats->tx_bytes; ++ entry->totals_via_dev[IFS_TX].packets += stats->tx_packets; ++ entry->totals_via_dev[IFS_RX].bytes += stats->rx_bytes; ++ entry->totals_via_dev[IFS_RX].packets += stats->rx_packets; ++ /* We don't need the last_known[] anymore */ ++ entry->last_known_valid = false; ++ _iface_stat_set_active(entry, net_dev, false); ++ IF_DEBUG("qtaguid: %s(%s): " ++ "disable tracking. rx/tx=%llu/%llu\n", __func__, ++ net_dev->name, stats->rx_bytes, stats->tx_bytes); ++ spin_unlock_bh(&iface_stat_list_lock); ++} ++ ++/* ++ * Update stats for the specified interface from the skb. ++ * Do nothing if the entry ++ * does not exist (when a device was never configured with an IP address). ++ * Called on each sk. ++ */ ++static void iface_stat_update_from_skb(const struct sk_buff *skb, ++ struct xt_action_param *par) ++{ ++ struct iface_stat *entry; ++ const struct net_device *el_dev; ++ enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; ++ int bytes = skb->len; ++ int proto; ++ ++ if (!skb->dev) { ++ MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); ++ el_dev = par->in ? : par->out; ++ } else { ++ const struct net_device *other_dev; ++ el_dev = skb->dev; ++ other_dev = par->in ? : par->out; ++ if (el_dev != other_dev) { ++ MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " ++ "par->(in/out)=%p %s\n", ++ par->hooknum, el_dev, el_dev->name, other_dev, ++ other_dev->name); ++ } ++ } ++ ++ if (unlikely(!el_dev)) { ++ pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", ++ par->hooknum, __func__); ++ BUG(); ++ } else if (unlikely(!el_dev->name)) { ++ pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", ++ par->hooknum, __func__); ++ BUG(); ++ } else { ++ proto = ipx_proto(skb, par); ++ MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", ++ par->hooknum, el_dev->name, el_dev->type, ++ par->family, proto); ++ } ++ ++ spin_lock_bh(&iface_stat_list_lock); ++ entry = get_iface_entry(el_dev->name); ++ if (entry == NULL) { ++ IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n", ++ __func__, el_dev->name); ++ spin_unlock_bh(&iface_stat_list_lock); ++ return; ++ } ++ ++ IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, ++ el_dev->name, entry); ++ ++ data_counters_update(&entry->totals_via_skb, 0, direction, proto, ++ bytes); ++ spin_unlock_bh(&iface_stat_list_lock); ++} ++ ++static void tag_stat_update(struct tag_stat *tag_entry, ++ enum ifs_tx_rx direction, int proto, int bytes) ++{ ++ int active_set; ++ active_set = get_active_counter_set(tag_entry->tn.tag); ++ MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%u) set=%d " ++ "dir=%d proto=%d bytes=%d)\n", ++ tag_entry->tn.tag, get_uid_from_tag(tag_entry->tn.tag), ++ active_set, direction, proto, bytes); ++ data_counters_update(&tag_entry->counters, active_set, direction, ++ proto, bytes); ++ if (tag_entry->parent_counters) ++ data_counters_update(tag_entry->parent_counters, active_set, ++ direction, proto, bytes); ++} ++ ++/* ++ * Create a new entry for tracking the specified {acct_tag,uid_tag} within ++ * the interface. ++ * iface_entry->tag_stat_list_lock should be held. ++ */ ++static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, ++ tag_t tag) ++{ ++ struct tag_stat *new_tag_stat_entry = NULL; ++ IF_DEBUG("qtaguid: iface_stat: %s(): ife=%p tag=0x%llx" ++ " (uid=%u)\n", __func__, ++ iface_entry, tag, get_uid_from_tag(tag)); ++ new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); ++ if (!new_tag_stat_entry) { ++ pr_err("qtaguid: iface_stat: tag stat alloc failed\n"); ++ goto done; ++ } ++ new_tag_stat_entry->tn.tag = tag; ++ tag_stat_tree_insert(new_tag_stat_entry, &iface_entry->tag_stat_tree); ++done: ++ return new_tag_stat_entry; ++} ++ ++static void if_tag_stat_update(const char *ifname, uid_t uid, ++ const struct sock *sk, enum ifs_tx_rx direction, ++ int proto, int bytes) ++{ ++ struct tag_stat *tag_stat_entry; ++ tag_t tag, acct_tag; ++ tag_t uid_tag; ++ struct data_counters *uid_tag_counters; ++ struct sock_tag *sock_tag_entry; ++ struct iface_stat *iface_entry; ++ struct tag_stat *new_tag_stat = NULL; ++ MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " ++ "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", ++ ifname, uid, sk, direction, proto, bytes); ++ ++ ++ iface_entry = get_iface_entry(ifname); ++ if (!iface_entry) { ++ pr_err("qtaguid: iface_stat: stat_update() %s not found\n", ++ ifname); ++ return; ++ } ++ /* It is ok to process data when an iface_entry is inactive */ ++ ++ MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n", ++ ifname, iface_entry); ++ ++ /* ++ * Look for a tagged sock. ++ * It will have an acct_uid. ++ */ ++ sock_tag_entry = get_sock_stat(sk); ++ if (sock_tag_entry) { ++ tag = sock_tag_entry->tag; ++ acct_tag = get_atag_from_tag(tag); ++ uid_tag = get_utag_from_tag(tag); ++ } else { ++ acct_tag = make_atag_from_value(0); ++ tag = combine_atag_with_uid(acct_tag, uid); ++ uid_tag = make_tag_from_uid(uid); ++ } ++ MT_DEBUG("qtaguid: iface_stat: stat_update(): " ++ " looking for tag=0x%llx (uid=%u) in ife=%p\n", ++ tag, get_uid_from_tag(tag), iface_entry); ++ /* Loop over tag list under this interface for {acct_tag,uid_tag} */ ++ spin_lock_bh(&iface_entry->tag_stat_list_lock); ++ ++ tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, ++ tag); ++ if (tag_stat_entry) { ++ /* ++ * Updating the {acct_tag, uid_tag} entry handles both stats: ++ * {0, uid_tag} will also get updated. ++ */ ++ tag_stat_update(tag_stat_entry, direction, proto, bytes); ++ spin_unlock_bh(&iface_entry->tag_stat_list_lock); ++ return; ++ } ++ ++ /* Loop over tag list under this interface for {0,uid_tag} */ ++ tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, ++ uid_tag); ++ if (!tag_stat_entry) { ++ /* Here: the base uid_tag did not exist */ ++ /* ++ * No parent counters. So ++ * - No {0, uid_tag} stats and no {acc_tag, uid_tag} stats. ++ */ ++ new_tag_stat = create_if_tag_stat(iface_entry, uid_tag); ++ if (!new_tag_stat) ++ goto unlock; ++ uid_tag_counters = &new_tag_stat->counters; ++ } else { ++ uid_tag_counters = &tag_stat_entry->counters; ++ } ++ ++ if (acct_tag) { ++ /* Create the child {acct_tag, uid_tag} and hook up parent. */ ++ new_tag_stat = create_if_tag_stat(iface_entry, tag); ++ if (!new_tag_stat) ++ goto unlock; ++ new_tag_stat->parent_counters = uid_tag_counters; ++ } else { ++ /* ++ * For new_tag_stat to be still NULL here would require: ++ * {0, uid_tag} exists ++ * and {acct_tag, uid_tag} doesn't exist ++ * AND acct_tag == 0. ++ * Impossible. This reassures us that new_tag_stat ++ * below will always be assigned. ++ */ ++ BUG_ON(!new_tag_stat); ++ } ++ tag_stat_update(new_tag_stat, direction, proto, bytes); ++unlock: ++ spin_unlock_bh(&iface_entry->tag_stat_list_lock); ++} ++ ++static int iface_netdev_event_handler(struct notifier_block *nb, ++ unsigned long event, void *ptr) { ++ struct net_device *dev = ptr; ++ ++ if (unlikely(module_passive)) ++ return NOTIFY_DONE; ++ ++ IF_DEBUG("qtaguid: iface_stat: netdev_event(): " ++ "ev=0x%lx/%s netdev=%p->name=%s\n", ++ event, netdev_evt_str(event), dev, dev ? dev->name : ""); ++ ++ switch (event) { ++ case NETDEV_UP: ++ iface_stat_create(dev, NULL); ++ atomic64_inc(&qtu_events.iface_events); ++ break; ++ case NETDEV_DOWN: ++ case NETDEV_UNREGISTER: ++ iface_stat_update(dev, event == NETDEV_DOWN); ++ atomic64_inc(&qtu_events.iface_events); ++ break; ++ } ++ return NOTIFY_DONE; ++} ++ ++static int iface_inet6addr_event_handler(struct notifier_block *nb, ++ unsigned long event, void *ptr) ++{ ++ struct inet6_ifaddr *ifa = ptr; ++ struct net_device *dev; ++ ++ if (unlikely(module_passive)) ++ return NOTIFY_DONE; ++ ++ IF_DEBUG("qtaguid: iface_stat: inet6addr_event(): " ++ "ev=0x%lx/%s ifa=%p\n", ++ event, netdev_evt_str(event), ifa); ++ ++ switch (event) { ++ case NETDEV_UP: ++ BUG_ON(!ifa || !ifa->idev); ++ dev = (struct net_device *)ifa->idev->dev; ++ iface_stat_create_ipv6(dev, ifa); ++ atomic64_inc(&qtu_events.iface_events); ++ break; ++ case NETDEV_DOWN: ++ case NETDEV_UNREGISTER: ++ BUG_ON(!ifa || !ifa->idev); ++ dev = (struct net_device *)ifa->idev->dev; ++ iface_stat_update(dev, event == NETDEV_DOWN); ++ atomic64_inc(&qtu_events.iface_events); ++ break; ++ } ++ return NOTIFY_DONE; ++} ++ ++static int iface_inetaddr_event_handler(struct notifier_block *nb, ++ unsigned long event, void *ptr) ++{ ++ struct in_ifaddr *ifa = ptr; ++ struct net_device *dev; ++ ++ if (unlikely(module_passive)) ++ return NOTIFY_DONE; ++ ++ IF_DEBUG("qtaguid: iface_stat: inetaddr_event(): " ++ "ev=0x%lx/%s ifa=%p\n", ++ event, netdev_evt_str(event), ifa); ++ ++ switch (event) { ++ case NETDEV_UP: ++ BUG_ON(!ifa || !ifa->ifa_dev); ++ dev = ifa->ifa_dev->dev; ++ iface_stat_create(dev, ifa); ++ atomic64_inc(&qtu_events.iface_events); ++ break; ++ case NETDEV_DOWN: ++ case NETDEV_UNREGISTER: ++ BUG_ON(!ifa || !ifa->ifa_dev); ++ dev = ifa->ifa_dev->dev; ++ iface_stat_update(dev, event == NETDEV_DOWN); ++ atomic64_inc(&qtu_events.iface_events); ++ break; ++ } ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block iface_netdev_notifier_blk = { ++ .notifier_call = iface_netdev_event_handler, ++}; ++ ++static struct notifier_block iface_inetaddr_notifier_blk = { ++ .notifier_call = iface_inetaddr_event_handler, ++}; ++ ++static struct notifier_block iface_inet6addr_notifier_blk = { ++ .notifier_call = iface_inet6addr_event_handler, ++}; ++ ++static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) ++{ ++ int err; ++ ++ iface_stat_procdir = proc_mkdir(iface_stat_procdirname, parent_procdir); ++ if (!iface_stat_procdir) { ++ pr_err("qtaguid: iface_stat: init failed to create proc entry\n"); ++ err = -1; ++ goto err; ++ } ++ ++ iface_stat_all_procfile = create_proc_entry(iface_stat_all_procfilename, ++ proc_iface_perms, ++ parent_procdir); ++ if (!iface_stat_all_procfile) { ++ pr_err("qtaguid: iface_stat: init " ++ " failed to create stat_old proc entry\n"); ++ err = -1; ++ goto err_zap_entry; ++ } ++ iface_stat_all_procfile->read_proc = iface_stat_fmt_proc_read; ++ iface_stat_all_procfile->data = (void *)1; /* fmt1 */ ++ ++ iface_stat_fmt_procfile = create_proc_entry(iface_stat_fmt_procfilename, ++ proc_iface_perms, ++ parent_procdir); ++ if (!iface_stat_fmt_procfile) { ++ pr_err("qtaguid: iface_stat: init " ++ " failed to create stat_all proc entry\n"); ++ err = -1; ++ goto err_zap_all_stats_entry; ++ } ++ iface_stat_fmt_procfile->read_proc = iface_stat_fmt_proc_read; ++ iface_stat_fmt_procfile->data = (void *)2; /* fmt2 */ ++ ++ ++ err = register_netdevice_notifier(&iface_netdev_notifier_blk); ++ if (err) { ++ pr_err("qtaguid: iface_stat: init " ++ "failed to register dev event handler\n"); ++ goto err_zap_all_stats_entries; ++ } ++ err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); ++ if (err) { ++ pr_err("qtaguid: iface_stat: init " ++ "failed to register ipv4 dev event handler\n"); ++ goto err_unreg_nd; ++ } ++ ++ err = register_inet6addr_notifier(&iface_inet6addr_notifier_blk); ++ if (err) { ++ pr_err("qtaguid: iface_stat: init " ++ "failed to register ipv6 dev event handler\n"); ++ goto err_unreg_ip4_addr; ++ } ++ return 0; ++ ++err_unreg_ip4_addr: ++ unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); ++err_unreg_nd: ++ unregister_netdevice_notifier(&iface_netdev_notifier_blk); ++err_zap_all_stats_entries: ++ remove_proc_entry(iface_stat_fmt_procfilename, parent_procdir); ++err_zap_all_stats_entry: ++ remove_proc_entry(iface_stat_all_procfilename, parent_procdir); ++err_zap_entry: ++ remove_proc_entry(iface_stat_procdirname, parent_procdir); ++err: ++ return err; ++} ++ ++static struct sock *qtaguid_find_sk(const struct sk_buff *skb, ++ struct xt_action_param *par) ++{ ++ struct sock *sk; ++ unsigned int hook_mask = (1 << par->hooknum); ++ ++ MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, ++ par->hooknum, par->family); ++ ++ /* ++ * Let's not abuse the the xt_socket_get*_sk(), or else it will ++ * return garbage SKs. ++ */ ++ if (!(hook_mask & XT_SOCKET_SUPPORTED_HOOKS)) ++ return NULL; ++ ++ switch (par->family) { ++ case NFPROTO_IPV6: ++ sk = xt_socket_get6_sk(skb, par); ++ break; ++ case NFPROTO_IPV4: ++ sk = xt_socket_get4_sk(skb, par); ++ break; ++ default: ++ return NULL; ++ } ++ ++ /* ++ * Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. ++ * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959 ++ * Not fixed in 3.0-r3 :( ++ */ ++ if (sk) { ++ MT_DEBUG("qtaguid: %p->sk_proto=%u " ++ "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); ++ if (sk->sk_state == TCP_TIME_WAIT) { ++ xt_socket_put_sk(sk); ++ sk = NULL; ++ } ++ } ++ return sk; ++} ++ ++static void account_for_uid(const struct sk_buff *skb, ++ const struct sock *alternate_sk, uid_t uid, ++ struct xt_action_param *par) ++{ ++ const struct net_device *el_dev; ++ ++ if (!skb->dev) { ++ MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); ++ el_dev = par->in ? : par->out; ++ } else { ++ const struct net_device *other_dev; ++ el_dev = skb->dev; ++ other_dev = par->in ? : par->out; ++ if (el_dev != other_dev) { ++ MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " ++ "par->(in/out)=%p %s\n", ++ par->hooknum, el_dev, el_dev->name, other_dev, ++ other_dev->name); ++ } ++ } ++ ++ if (unlikely(!el_dev)) { ++ pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum); ++ } else if (unlikely(!el_dev->name)) { ++ pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); ++ } else { ++ int proto = ipx_proto(skb, par); ++ MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", ++ par->hooknum, el_dev->name, el_dev->type, ++ par->family, proto); ++ ++ if_tag_stat_update(el_dev->name, uid, ++ skb->sk ? skb->sk : alternate_sk, ++ par->in ? IFS_RX : IFS_TX, ++ proto, skb->len); ++ } ++} ++ ++static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ const struct xt_qtaguid_match_info *info = par->matchinfo; ++ const struct file *filp; ++ bool got_sock = false; ++ struct sock *sk; ++ uid_t sock_uid; ++ bool res; ++ ++ if (unlikely(module_passive)) ++ return (info->match ^ info->invert) == 0; ++ ++ MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", ++ par->hooknum, skb, par->in, par->out, par->family); ++ ++ atomic64_inc(&qtu_events.match_calls); ++ if (skb == NULL) { ++ res = (info->match ^ info->invert) == 0; ++ goto ret_res; ++ } ++ ++ switch (par->hooknum) { ++ case NF_INET_PRE_ROUTING: ++ case NF_INET_POST_ROUTING: ++ atomic64_inc(&qtu_events.match_calls_prepost); ++ iface_stat_update_from_skb(skb, par); ++ /* ++ * We are done in pre/post. The skb will get processed ++ * further alter. ++ */ ++ res = (info->match ^ info->invert); ++ goto ret_res; ++ break; ++ /* default: Fall through and do UID releated work */ ++ } ++ ++ sk = skb->sk; ++ if (sk == NULL) { ++ /* ++ * A missing sk->sk_socket happens when packets are in-flight ++ * and the matching socket is already closed and gone. ++ */ ++ sk = qtaguid_find_sk(skb, par); ++ /* ++ * If we got the socket from the find_sk(), we will need to put ++ * it back, as nf_tproxy_get_sock_v4() got it. ++ */ ++ got_sock = sk; ++ if (sk) ++ atomic64_inc(&qtu_events.match_found_sk_in_ct); ++ else ++ atomic64_inc(&qtu_events.match_found_no_sk_in_ct); ++ } else { ++ atomic64_inc(&qtu_events.match_found_sk); ++ } ++ MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", ++ par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); ++ if (sk != NULL) { ++ MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", ++ par->hooknum, sk, sk->sk_socket, ++ sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); ++ filp = sk->sk_socket ? sk->sk_socket->file : NULL; ++ MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", ++ par->hooknum, filp ? filp->f_cred->fsuid : -1); ++ } ++ ++ if (sk == NULL || sk->sk_socket == NULL) { ++ /* ++ * Here, the qtaguid_find_sk() using connection tracking ++ * couldn't find the owner, so for now we just count them ++ * against the system. ++ */ ++ /* ++ * TODO: unhack how to force just accounting. ++ * For now we only do iface stats when the uid-owner is not ++ * requested. ++ */ ++ if (!(info->match & XT_QTAGUID_UID)) ++ account_for_uid(skb, sk, 0, par); ++ MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", ++ par->hooknum, ++ sk ? sk->sk_socket : NULL); ++ res = (info->match ^ info->invert) == 0; ++ atomic64_inc(&qtu_events.match_no_sk); ++ goto put_sock_ret_res; ++ } else if (info->match & info->invert & XT_QTAGUID_SOCKET) { ++ res = false; ++ goto put_sock_ret_res; ++ } ++ filp = sk->sk_socket->file; ++ if (filp == NULL) { ++ MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); ++ account_for_uid(skb, sk, 0, par); ++ res = ((info->match ^ info->invert) & ++ (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; ++ atomic64_inc(&qtu_events.match_no_sk_file); ++ goto put_sock_ret_res; ++ } ++ sock_uid = filp->f_cred->fsuid; ++ /* ++ * TODO: unhack how to force just accounting. ++ * For now we only do iface stats when the uid-owner is not requested ++ */ ++ if (!(info->match & XT_QTAGUID_UID)) ++ account_for_uid(skb, sk, sock_uid, par); ++ ++ /* ++ * The following two tests fail the match when: ++ * id not in range AND no inverted condition requested ++ * or id in range AND inverted condition requested ++ * Thus (!a && b) || (a && !b) == a ^ b ++ */ ++ if (info->match & XT_QTAGUID_UID) ++ if ((filp->f_cred->fsuid >= info->uid_min && ++ filp->f_cred->fsuid <= info->uid_max) ^ ++ !(info->invert & XT_QTAGUID_UID)) { ++ MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", ++ par->hooknum); ++ res = false; ++ goto put_sock_ret_res; ++ } ++ if (info->match & XT_QTAGUID_GID) ++ if ((filp->f_cred->fsgid >= info->gid_min && ++ filp->f_cred->fsgid <= info->gid_max) ^ ++ !(info->invert & XT_QTAGUID_GID)) { ++ MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", ++ par->hooknum); ++ res = false; ++ goto put_sock_ret_res; ++ } ++ ++ MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum); ++ res = true; ++ ++put_sock_ret_res: ++ if (got_sock) ++ xt_socket_put_sk(sk); ++ret_res: ++ MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); ++ return res; ++} ++ ++#ifdef DDEBUG ++/* This function is not in xt_qtaguid_print.c because of locks visibility */ ++static void prdebug_full_state(int indent_level, const char *fmt, ...) ++{ ++ va_list args; ++ char *fmt_buff; ++ char *buff; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ fmt_buff = kasprintf(GFP_ATOMIC, ++ "qtaguid: %s(): %s {\n", __func__, fmt); ++ BUG_ON(!fmt_buff); ++ va_start(args, fmt); ++ buff = kvasprintf(GFP_ATOMIC, ++ fmt_buff, args); ++ BUG_ON(!buff); ++ pr_debug("%s", buff); ++ kfree(fmt_buff); ++ kfree(buff); ++ va_end(args); ++ ++ spin_lock_bh(&sock_tag_list_lock); ++ prdebug_sock_tag_tree(indent_level, &sock_tag_tree); ++ spin_unlock_bh(&sock_tag_list_lock); ++ ++ spin_lock_bh(&sock_tag_list_lock); ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); ++ prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ spin_unlock_bh(&sock_tag_list_lock); ++ ++ spin_lock_bh(&iface_stat_list_lock); ++ prdebug_iface_stat_list(indent_level, &iface_stat_list); ++ spin_unlock_bh(&iface_stat_list_lock); ++ ++ pr_debug("qtaguid: %s(): }\n", __func__); ++} ++#else ++static void prdebug_full_state(int indent_level, const char *fmt, ...) {} ++#endif ++ ++/* ++ * Procfs reader to get all active socket tags using style "1)" as described in ++ * fs/proc/generic.c ++ */ ++static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, ++ off_t items_to_skip, int char_count, int *eof, ++ void *data) ++{ ++ char *outp = page; ++ int len; ++ uid_t uid; ++ struct rb_node *node; ++ struct sock_tag *sock_tag_entry; ++ int item_index = 0; ++ int indent_level = 0; ++ long f_count; ++ ++ if (unlikely(module_passive)) { ++ *eof = 1; ++ return 0; ++ } ++ ++ if (*eof) ++ return 0; ++ ++ CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u " ++ "page=%p off=%ld char_count=%d *eof=%d\n", ++ current->pid, current->tgid, current_fsuid(), ++ page, items_to_skip, char_count, *eof); ++ ++ spin_lock_bh(&sock_tag_list_lock); ++ for (node = rb_first(&sock_tag_tree); ++ node; ++ node = rb_next(node)) { ++ if (item_index++ < items_to_skip) ++ continue; ++ sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); ++ uid = get_uid_from_tag(sock_tag_entry->tag); ++ CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) " ++ "pid=%u\n", ++ sock_tag_entry->sk, ++ sock_tag_entry->tag, ++ uid, ++ sock_tag_entry->pid ++ ); ++ f_count = atomic_long_read( ++ &sock_tag_entry->socket->file->f_count); ++ len = snprintf(outp, char_count, ++ "sock=%p tag=0x%llx (uid=%u) pid=%u " ++ "f_count=%lu\n", ++ sock_tag_entry->sk, ++ sock_tag_entry->tag, uid, ++ sock_tag_entry->pid, f_count); ++ if (len >= char_count) { ++ spin_unlock_bh(&sock_tag_list_lock); ++ *outp = '\0'; ++ return outp - page; ++ } ++ outp += len; ++ char_count -= len; ++ (*num_items_returned)++; ++ } ++ spin_unlock_bh(&sock_tag_list_lock); ++ ++ if (item_index++ >= items_to_skip) { ++ len = snprintf(outp, char_count, ++ "events: sockets_tagged=%llu " ++ "sockets_untagged=%llu " ++ "counter_set_changes=%llu " ++ "delete_cmds=%llu " ++ "iface_events=%llu " ++ "match_calls=%llu " ++ "match_calls_prepost=%llu " ++ "match_found_sk=%llu " ++ "match_found_sk_in_ct=%llu " ++ "match_found_no_sk_in_ct=%llu " ++ "match_no_sk=%llu " ++ "match_no_sk_file=%llu\n", ++ atomic64_read(&qtu_events.sockets_tagged), ++ atomic64_read(&qtu_events.sockets_untagged), ++ atomic64_read(&qtu_events.counter_set_changes), ++ atomic64_read(&qtu_events.delete_cmds), ++ atomic64_read(&qtu_events.iface_events), ++ atomic64_read(&qtu_events.match_calls), ++ atomic64_read(&qtu_events.match_calls_prepost), ++ atomic64_read(&qtu_events.match_found_sk), ++ atomic64_read(&qtu_events.match_found_sk_in_ct), ++ atomic64_read( ++ &qtu_events.match_found_no_sk_in_ct), ++ atomic64_read(&qtu_events.match_no_sk), ++ atomic64_read(&qtu_events.match_no_sk_file)); ++ if (len >= char_count) { ++ *outp = '\0'; ++ return outp - page; ++ } ++ outp += len; ++ char_count -= len; ++ (*num_items_returned)++; ++ } ++ ++ /* Count the following as part of the last item_index */ ++ if (item_index > items_to_skip) { ++ prdebug_full_state(indent_level, "proc ctrl"); ++ } ++ ++ *eof = 1; ++ return outp - page; ++} ++ ++/* ++ * Delete socket tags, and stat tags associated with a given ++ * accouting tag and uid. ++ */ ++static int ctrl_cmd_delete(const char *input) ++{ ++ char cmd; ++ uid_t uid; ++ uid_t entry_uid; ++ tag_t acct_tag; ++ tag_t tag; ++ int res, argc; ++ struct iface_stat *iface_entry; ++ struct rb_node *node; ++ struct sock_tag *st_entry; ++ struct rb_root st_to_free_tree = RB_ROOT; ++ struct tag_stat *ts_entry; ++ struct tag_counter_set *tcs_entry; ++ struct tag_ref *tr_entry; ++ struct uid_tag_data *utd_entry; ++ ++ argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); ++ CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " ++ "user_tag=0x%llx uid=%u\n", input, argc, cmd, ++ acct_tag, uid); ++ if (argc < 2) { ++ res = -EINVAL; ++ goto err; ++ } ++ if (!valid_atag(acct_tag)) { ++ pr_info("qtaguid: ctrl_delete(%s): invalid tag\n", input); ++ res = -EINVAL; ++ goto err; ++ } ++ if (argc < 3) { ++ uid = current_fsuid(); ++ } else if (!can_impersonate_uid(uid)) { ++ pr_info("qtaguid: ctrl_delete(%s): " ++ "insufficient priv from pid=%u tgid=%u uid=%u\n", ++ input, current->pid, current->tgid, current_fsuid()); ++ res = -EPERM; ++ goto err; ++ } ++ ++ tag = combine_atag_with_uid(acct_tag, uid); ++ CT_DEBUG("qtaguid: ctrl_delete(%s): " ++ "looking for tag=0x%llx (uid=%u)\n", ++ input, tag, uid); ++ ++ /* Delete socket tags */ ++ spin_lock_bh(&sock_tag_list_lock); ++ node = rb_first(&sock_tag_tree); ++ while (node) { ++ st_entry = rb_entry(node, struct sock_tag, sock_node); ++ entry_uid = get_uid_from_tag(st_entry->tag); ++ node = rb_next(node); ++ if (entry_uid != uid) ++ continue; ++ ++ CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n", ++ input, st_entry->tag, entry_uid); ++ ++ if (!acct_tag || st_entry->tag == tag) { ++ rb_erase(&st_entry->sock_node, &sock_tag_tree); ++ /* Can't sockfd_put() within spinlock, do it later. */ ++ sock_tag_tree_insert(st_entry, &st_to_free_tree); ++ tr_entry = lookup_tag_ref(st_entry->tag, NULL); ++ BUG_ON(tr_entry->num_sock_tags <= 0); ++ tr_entry->num_sock_tags--; ++ /* ++ * TODO: remove if, and start failing. ++ * This is a hack to work around the fact that in some ++ * places we have "if (IS_ERR_OR_NULL(pqd_entry))" ++ * and are trying to work around apps ++ * that didn't open the /dev/xt_qtaguid. ++ */ ++ if (st_entry->list.next && st_entry->list.prev) ++ list_del(&st_entry->list); ++ } ++ } ++ spin_unlock_bh(&sock_tag_list_lock); ++ ++ sock_tag_tree_erase(&st_to_free_tree); ++ ++ /* Delete tag counter-sets */ ++ spin_lock_bh(&tag_counter_set_list_lock); ++ /* Counter sets are only on the uid tag, not full tag */ ++ tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); ++ if (tcs_entry) { ++ CT_DEBUG("qtaguid: ctrl_delete(%s): " ++ "erase tcs: tag=0x%llx (uid=%u) set=%d\n", ++ input, ++ tcs_entry->tn.tag, ++ get_uid_from_tag(tcs_entry->tn.tag), ++ tcs_entry->active_set); ++ rb_erase(&tcs_entry->tn.node, &tag_counter_set_tree); ++ kfree(tcs_entry); ++ } ++ spin_unlock_bh(&tag_counter_set_list_lock); ++ ++ /* ++ * If acct_tag is 0, then all entries belonging to uid are ++ * erased. ++ */ ++ spin_lock_bh(&iface_stat_list_lock); ++ list_for_each_entry(iface_entry, &iface_stat_list, list) { ++ spin_lock_bh(&iface_entry->tag_stat_list_lock); ++ node = rb_first(&iface_entry->tag_stat_tree); ++ while (node) { ++ ts_entry = rb_entry(node, struct tag_stat, tn.node); ++ entry_uid = get_uid_from_tag(ts_entry->tn.tag); ++ node = rb_next(node); ++ ++ CT_DEBUG("qtaguid: ctrl_delete(%s): " ++ "ts tag=0x%llx (uid=%u)\n", ++ input, ts_entry->tn.tag, entry_uid); ++ ++ if (entry_uid != uid) ++ continue; ++ if (!acct_tag || ts_entry->tn.tag == tag) { ++ CT_DEBUG("qtaguid: ctrl_delete(%s): " ++ "erase ts: %s 0x%llx %u\n", ++ input, iface_entry->ifname, ++ get_atag_from_tag(ts_entry->tn.tag), ++ entry_uid); ++ rb_erase(&ts_entry->tn.node, ++ &iface_entry->tag_stat_tree); ++ kfree(ts_entry); ++ } ++ } ++ spin_unlock_bh(&iface_entry->tag_stat_list_lock); ++ } ++ spin_unlock_bh(&iface_stat_list_lock); ++ ++ /* Cleanup the uid_tag_data */ ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ node = rb_first(&uid_tag_data_tree); ++ while (node) { ++ utd_entry = rb_entry(node, struct uid_tag_data, node); ++ entry_uid = utd_entry->uid; ++ node = rb_next(node); ++ ++ CT_DEBUG("qtaguid: ctrl_delete(%s): " ++ "utd uid=%u\n", ++ input, entry_uid); ++ ++ if (entry_uid != uid) ++ continue; ++ /* ++ * Go over the tag_refs, and those that don't have ++ * sock_tags using them are freed. ++ */ ++ put_tag_ref_tree(tag, utd_entry); ++ put_utd_entry(utd_entry); ++ } ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ ++ atomic64_inc(&qtu_events.delete_cmds); ++ res = 0; ++ ++err: ++ return res; ++} ++ ++static int ctrl_cmd_counter_set(const char *input) ++{ ++ char cmd; ++ uid_t uid = 0; ++ tag_t tag; ++ int res, argc; ++ struct tag_counter_set *tcs; ++ int counter_set; ++ ++ argc = sscanf(input, "%c %d %u", &cmd, &counter_set, &uid); ++ CT_DEBUG("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c " ++ "set=%d uid=%u\n", input, argc, cmd, ++ counter_set, uid); ++ if (argc != 3) { ++ res = -EINVAL; ++ goto err; ++ } ++ if (counter_set < 0 || counter_set >= IFS_MAX_COUNTER_SETS) { ++ pr_info("qtaguid: ctrl_counterset(%s): invalid counter_set range\n", ++ input); ++ res = -EINVAL; ++ goto err; ++ } ++ if (!can_manipulate_uids()) { ++ pr_info("qtaguid: ctrl_counterset(%s): " ++ "insufficient priv from pid=%u tgid=%u uid=%u\n", ++ input, current->pid, current->tgid, current_fsuid()); ++ res = -EPERM; ++ goto err; ++ } ++ ++ tag = make_tag_from_uid(uid); ++ spin_lock_bh(&tag_counter_set_list_lock); ++ tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); ++ if (!tcs) { ++ tcs = kzalloc(sizeof(*tcs), GFP_ATOMIC); ++ if (!tcs) { ++ spin_unlock_bh(&tag_counter_set_list_lock); ++ pr_err("qtaguid: ctrl_counterset(%s): " ++ "failed to alloc counter set\n", ++ input); ++ res = -ENOMEM; ++ goto err; ++ } ++ tcs->tn.tag = tag; ++ tag_counter_set_tree_insert(tcs, &tag_counter_set_tree); ++ CT_DEBUG("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx " ++ "(uid=%u) set=%d\n", ++ input, tag, get_uid_from_tag(tag), counter_set); ++ } ++ tcs->active_set = counter_set; ++ spin_unlock_bh(&tag_counter_set_list_lock); ++ atomic64_inc(&qtu_events.counter_set_changes); ++ res = 0; ++ ++err: ++ return res; ++} ++ ++static int ctrl_cmd_tag(const char *input) ++{ ++ char cmd; ++ int sock_fd = 0; ++ uid_t uid = 0; ++ tag_t acct_tag = make_atag_from_value(0); ++ tag_t full_tag; ++ struct socket *el_socket; ++ int res, argc; ++ struct sock_tag *sock_tag_entry; ++ struct tag_ref *tag_ref_entry; ++ struct uid_tag_data *uid_tag_data_entry; ++ struct proc_qtu_data *pqd_entry; ++ ++ /* Unassigned args will get defaulted later. */ ++ argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); ++ CT_DEBUG("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " ++ "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, ++ acct_tag, uid); ++ if (argc < 2) { ++ res = -EINVAL; ++ goto err; ++ } ++ el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ ++ if (!el_socket) { ++ pr_info("qtaguid: ctrl_tag(%s): failed to lookup" ++ " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", ++ input, sock_fd, res, current->pid, current->tgid, ++ current_fsuid()); ++ goto err; ++ } ++ CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", ++ input, atomic_long_read(&el_socket->file->f_count), ++ el_socket->sk); ++ if (argc < 3) { ++ acct_tag = make_atag_from_value(0); ++ } else if (!valid_atag(acct_tag)) { ++ pr_info("qtaguid: ctrl_tag(%s): invalid tag\n", input); ++ res = -EINVAL; ++ goto err_put; ++ } ++ CT_DEBUG("qtaguid: ctrl_tag(%s): " ++ "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " ++ "ctrl.gid=%u in_group()=%d in_egroup()=%d\n", ++ input, current->pid, current->tgid, current_uid(), ++ current_euid(), current_fsuid(), ++ xt_qtaguid_ctrl_file->gid, ++ in_group_p(xt_qtaguid_ctrl_file->gid), ++ in_egroup_p(xt_qtaguid_ctrl_file->gid)); ++ if (argc < 4) { ++ uid = current_fsuid(); ++ } else if (!can_impersonate_uid(uid)) { ++ pr_info("qtaguid: ctrl_tag(%s): " ++ "insufficient priv from pid=%u tgid=%u uid=%u\n", ++ input, current->pid, current->tgid, current_fsuid()); ++ res = -EPERM; ++ goto err_put; ++ } ++ full_tag = combine_atag_with_uid(acct_tag, uid); ++ ++ spin_lock_bh(&sock_tag_list_lock); ++ sock_tag_entry = get_sock_stat_nl(el_socket->sk); ++ tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry); ++ if (IS_ERR(tag_ref_entry)) { ++ res = PTR_ERR(tag_ref_entry); ++ spin_unlock_bh(&sock_tag_list_lock); ++ goto err_put; ++ } ++ tag_ref_entry->num_sock_tags++; ++ if (sock_tag_entry) { ++ struct tag_ref *prev_tag_ref_entry; ++ ++ CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p " ++ "st@%p ...->f_count=%ld\n", ++ input, el_socket->sk, sock_tag_entry, ++ atomic_long_read(&el_socket->file->f_count)); ++ /* ++ * This is a re-tagging, so release the sock_fd that was ++ * locked at the time of the 1st tagging. ++ * There is still the ref from this call's sockfd_lookup() so ++ * it can be done within the spinlock. ++ */ ++ sockfd_put(sock_tag_entry->socket); ++ prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, ++ &uid_tag_data_entry); ++ BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry)); ++ BUG_ON(prev_tag_ref_entry->num_sock_tags <= 0); ++ prev_tag_ref_entry->num_sock_tags--; ++ sock_tag_entry->tag = full_tag; ++ } else { ++ CT_DEBUG("qtaguid: ctrl_tag(%s): newtag for sk=%p\n", ++ input, el_socket->sk); ++ sock_tag_entry = kzalloc(sizeof(*sock_tag_entry), ++ GFP_ATOMIC); ++ if (!sock_tag_entry) { ++ pr_err("qtaguid: ctrl_tag(%s): " ++ "socket tag alloc failed\n", ++ input); ++ spin_unlock_bh(&sock_tag_list_lock); ++ res = -ENOMEM; ++ goto err_tag_unref_put; ++ } ++ sock_tag_entry->sk = el_socket->sk; ++ sock_tag_entry->socket = el_socket; ++ sock_tag_entry->pid = current->tgid; ++ sock_tag_entry->tag = combine_atag_with_uid(acct_tag, ++ uid); ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ pqd_entry = proc_qtu_data_tree_search( ++ &proc_qtu_data_tree, current->tgid); ++ /* ++ * TODO: remove if, and start failing. ++ * At first, we want to catch user-space code that is not ++ * opening the /dev/xt_qtaguid. ++ */ ++ if (IS_ERR_OR_NULL(pqd_entry)) ++ pr_warn_once( ++ "qtaguid: %s(): " ++ "User space forgot to open /dev/xt_qtaguid? " ++ "pid=%u tgid=%u uid=%u\n", __func__, ++ current->pid, current->tgid, ++ current_fsuid()); ++ else ++ list_add(&sock_tag_entry->list, ++ &pqd_entry->sock_tag_list); ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ ++ sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); ++ atomic64_inc(&qtu_events.sockets_tagged); ++ } ++ spin_unlock_bh(&sock_tag_list_lock); ++ /* We keep the ref to the socket (file) until it is untagged */ ++ CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n", ++ input, sock_tag_entry, ++ atomic_long_read(&el_socket->file->f_count)); ++ return 0; ++ ++err_tag_unref_put: ++ BUG_ON(tag_ref_entry->num_sock_tags <= 0); ++ tag_ref_entry->num_sock_tags--; ++ free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); ++err_put: ++ CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n", ++ input, atomic_long_read(&el_socket->file->f_count) - 1); ++ /* Release the sock_fd that was grabbed by sockfd_lookup(). */ ++ sockfd_put(el_socket); ++ return res; ++ ++err: ++ CT_DEBUG("qtaguid: ctrl_tag(%s): done.\n", input); ++ return res; ++} ++ ++static int ctrl_cmd_untag(const char *input) ++{ ++ char cmd; ++ int sock_fd = 0; ++ struct socket *el_socket; ++ int res, argc; ++ struct sock_tag *sock_tag_entry; ++ struct tag_ref *tag_ref_entry; ++ struct uid_tag_data *utd_entry; ++ struct proc_qtu_data *pqd_entry; ++ ++ argc = sscanf(input, "%c %d", &cmd, &sock_fd); ++ CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", ++ input, argc, cmd, sock_fd); ++ if (argc < 2) { ++ res = -EINVAL; ++ goto err; ++ } ++ el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ ++ if (!el_socket) { ++ pr_info("qtaguid: ctrl_untag(%s): failed to lookup" ++ " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", ++ input, sock_fd, res, current->pid, current->tgid, ++ current_fsuid()); ++ goto err; ++ } ++ CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", ++ input, atomic_long_read(&el_socket->file->f_count), ++ el_socket->sk); ++ spin_lock_bh(&sock_tag_list_lock); ++ sock_tag_entry = get_sock_stat_nl(el_socket->sk); ++ if (!sock_tag_entry) { ++ spin_unlock_bh(&sock_tag_list_lock); ++ res = -EINVAL; ++ goto err_put; ++ } ++ /* ++ * The socket already belongs to the current process ++ * so it can do whatever it wants to it. ++ */ ++ rb_erase(&sock_tag_entry->sock_node, &sock_tag_tree); ++ ++ tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, &utd_entry); ++ BUG_ON(!tag_ref_entry); ++ BUG_ON(tag_ref_entry->num_sock_tags <= 0); ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ pqd_entry = proc_qtu_data_tree_search( ++ &proc_qtu_data_tree, current->tgid); ++ /* ++ * TODO: remove if, and start failing. ++ * At first, we want to catch user-space code that is not ++ * opening the /dev/xt_qtaguid. ++ */ ++ if (IS_ERR_OR_NULL(pqd_entry)) ++ pr_warn_once("qtaguid: %s(): " ++ "User space forgot to open /dev/xt_qtaguid? " ++ "pid=%u tgid=%u uid=%u\n", __func__, ++ current->pid, current->tgid, current_fsuid()); ++ else ++ list_del(&sock_tag_entry->list); ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ /* ++ * We don't free tag_ref from the utd_entry here, ++ * only during a cmd_delete(). ++ */ ++ tag_ref_entry->num_sock_tags--; ++ spin_unlock_bh(&sock_tag_list_lock); ++ /* ++ * Release the sock_fd that was grabbed at tag time, ++ * and once more for the sockfd_lookup() here. ++ */ ++ sockfd_put(sock_tag_entry->socket); ++ CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n", ++ input, sock_tag_entry, ++ atomic_long_read(&el_socket->file->f_count) - 1); ++ sockfd_put(el_socket); ++ ++ kfree(sock_tag_entry); ++ atomic64_inc(&qtu_events.sockets_untagged); ++ ++ return 0; ++ ++err_put: ++ CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n", ++ input, atomic_long_read(&el_socket->file->f_count) - 1); ++ /* Release the sock_fd that was grabbed by sockfd_lookup(). */ ++ sockfd_put(el_socket); ++ return res; ++ ++err: ++ CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input); ++ return res; ++} ++ ++static int qtaguid_ctrl_parse(const char *input, int count) ++{ ++ char cmd; ++ int res; ++ ++ CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", ++ input, current->pid, current->tgid, current_fsuid()); ++ ++ cmd = input[0]; ++ /* Collect params for commands */ ++ switch (cmd) { ++ case 'd': ++ res = ctrl_cmd_delete(input); ++ break; ++ ++ case 's': ++ res = ctrl_cmd_counter_set(input); ++ break; ++ ++ case 't': ++ res = ctrl_cmd_tag(input); ++ break; ++ ++ case 'u': ++ res = ctrl_cmd_untag(input); ++ break; ++ ++ default: ++ res = -EINVAL; ++ goto err; ++ } ++ if (!res) ++ res = count; ++err: ++ CT_DEBUG("qtaguid: ctrl(%s): res=%d\n", input, res); ++ return res; ++} ++ ++#define MAX_QTAGUID_CTRL_INPUT_LEN 255 ++static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, ++ unsigned long count, void *data) ++{ ++ char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN]; ++ ++ if (unlikely(module_passive)) ++ return count; ++ ++ if (count >= MAX_QTAGUID_CTRL_INPUT_LEN) ++ return -EINVAL; ++ ++ if (copy_from_user(input_buf, buffer, count)) ++ return -EFAULT; ++ ++ input_buf[count] = '\0'; ++ return qtaguid_ctrl_parse(input_buf, count); ++} ++ ++struct proc_print_info { ++ char *outp; ++ char **num_items_returned; ++ struct iface_stat *iface_entry; ++ struct tag_stat *ts_entry; ++ int item_index; ++ int items_to_skip; ++ int char_count; ++}; ++ ++static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) ++{ ++ int len; ++ struct data_counters *cnts; ++ ++ if (!ppi->item_index) { ++ if (ppi->item_index++ < ppi->items_to_skip) ++ return 0; ++ len = snprintf(ppi->outp, ppi->char_count, ++ "idx iface acct_tag_hex uid_tag_int cnt_set " ++ "rx_bytes rx_packets " ++ "tx_bytes tx_packets " ++ "rx_tcp_bytes rx_tcp_packets " ++ "rx_udp_bytes rx_udp_packets " ++ "rx_other_bytes rx_other_packets " ++ "tx_tcp_bytes tx_tcp_packets " ++ "tx_udp_bytes tx_udp_packets " ++ "tx_other_bytes tx_other_packets\n"); ++ } else { ++ tag_t tag = ppi->ts_entry->tn.tag; ++ uid_t stat_uid = get_uid_from_tag(tag); ++ /* Detailed tags are not available to everybody */ ++ if (get_atag_from_tag(tag) ++ && !can_read_other_uid_stats(stat_uid)) { ++ CT_DEBUG("qtaguid: stats line: " ++ "%s 0x%llx %u: insufficient priv " ++ "from pid=%u tgid=%u uid=%u stats.gid=%u\n", ++ ppi->iface_entry->ifname, ++ get_atag_from_tag(tag), stat_uid, ++ current->pid, current->tgid, current_fsuid(), ++ xt_qtaguid_stats_file->gid); ++ return 0; ++ } ++ if (ppi->item_index++ < ppi->items_to_skip) ++ return 0; ++ cnts = &ppi->ts_entry->counters; ++ len = snprintf( ++ ppi->outp, ppi->char_count, ++ "%d %s 0x%llx %u %u " ++ "%llu %llu " ++ "%llu %llu " ++ "%llu %llu " ++ "%llu %llu " ++ "%llu %llu " ++ "%llu %llu " ++ "%llu %llu " ++ "%llu %llu\n", ++ ppi->item_index, ++ ppi->iface_entry->ifname, ++ get_atag_from_tag(tag), ++ stat_uid, ++ cnt_set, ++ dc_sum_bytes(cnts, cnt_set, IFS_RX), ++ dc_sum_packets(cnts, cnt_set, IFS_RX), ++ dc_sum_bytes(cnts, cnt_set, IFS_TX), ++ dc_sum_packets(cnts, cnt_set, IFS_TX), ++ cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, ++ cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, ++ cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, ++ cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, ++ cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, ++ cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, ++ cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, ++ cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, ++ cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, ++ cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, ++ cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, ++ cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); ++ } ++ return len; ++} ++ ++static bool pp_sets(struct proc_print_info *ppi) ++{ ++ int len; ++ int counter_set; ++ for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS; ++ counter_set++) { ++ len = pp_stats_line(ppi, counter_set); ++ if (len >= ppi->char_count) { ++ *ppi->outp = '\0'; ++ return false; ++ } ++ if (len) { ++ ppi->outp += len; ++ ppi->char_count -= len; ++ (*ppi->num_items_returned)++; ++ } ++ } ++ return true; ++} ++ ++/* ++ * Procfs reader to get all tag stats using style "1)" as described in ++ * fs/proc/generic.c ++ * Groups all protocols tx/rx bytes. ++ */ ++static int qtaguid_stats_proc_read(char *page, char **num_items_returned, ++ off_t items_to_skip, int char_count, int *eof, ++ void *data) ++{ ++ struct proc_print_info ppi; ++ int len; ++ ++ ppi.outp = page; ++ ppi.item_index = 0; ++ ppi.char_count = char_count; ++ ppi.num_items_returned = num_items_returned; ++ ppi.items_to_skip = items_to_skip; ++ ++ if (unlikely(module_passive)) { ++ len = pp_stats_line(&ppi, 0); ++ /* The header should always be shorter than the buffer. */ ++ BUG_ON(len >= ppi.char_count); ++ (*num_items_returned)++; ++ *eof = 1; ++ return len; ++ } ++ ++ CT_DEBUG("qtaguid:proc stats pid=%u tgid=%u uid=%u " ++ "page=%p *num_items_returned=%p off=%ld " ++ "char_count=%d *eof=%d\n", ++ current->pid, current->tgid, current_fsuid(), ++ page, *num_items_returned, ++ items_to_skip, char_count, *eof); ++ ++ if (*eof) ++ return 0; ++ ++ /* The idx is there to help debug when things go belly up. */ ++ len = pp_stats_line(&ppi, 0); ++ /* Don't advance the outp unless the whole line was printed */ ++ if (len >= ppi.char_count) { ++ *ppi.outp = '\0'; ++ return ppi.outp - page; ++ } ++ if (len) { ++ ppi.outp += len; ++ ppi.char_count -= len; ++ (*num_items_returned)++; ++ } ++ ++ spin_lock_bh(&iface_stat_list_lock); ++ list_for_each_entry(ppi.iface_entry, &iface_stat_list, list) { ++ struct rb_node *node; ++ spin_lock_bh(&ppi.iface_entry->tag_stat_list_lock); ++ for (node = rb_first(&ppi.iface_entry->tag_stat_tree); ++ node; ++ node = rb_next(node)) { ++ ppi.ts_entry = rb_entry(node, struct tag_stat, tn.node); ++ if (!pp_sets(&ppi)) { ++ spin_unlock_bh( ++ &ppi.iface_entry->tag_stat_list_lock); ++ spin_unlock_bh(&iface_stat_list_lock); ++ return ppi.outp - page; ++ } ++ } ++ spin_unlock_bh(&ppi.iface_entry->tag_stat_list_lock); ++ } ++ spin_unlock_bh(&iface_stat_list_lock); ++ ++ *eof = 1; ++ return ppi.outp - page; ++} ++ ++/*------------------------------------------*/ ++static int qtudev_open(struct inode *inode, struct file *file) ++{ ++ struct uid_tag_data *utd_entry; ++ struct proc_qtu_data *pqd_entry; ++ struct proc_qtu_data *new_pqd_entry; ++ int res; ++ bool utd_entry_found; ++ ++ if (unlikely(qtu_proc_handling_passive)) ++ return 0; ++ ++ DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n", ++ current->pid, current->tgid, current_fsuid()); ++ ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ ++ /* Look for existing uid data, or alloc one. */ ++ utd_entry = get_uid_data(current_fsuid(), &utd_entry_found); ++ if (IS_ERR_OR_NULL(utd_entry)) { ++ res = PTR_ERR(utd_entry); ++ goto err_unlock; ++ } ++ ++ /* Look for existing PID based proc_data */ ++ pqd_entry = proc_qtu_data_tree_search(&proc_qtu_data_tree, ++ current->tgid); ++ if (pqd_entry) { ++ pr_err("qtaguid: qtudev_open(): %u/%u %u " ++ "%s already opened\n", ++ current->pid, current->tgid, current_fsuid(), ++ QTU_DEV_NAME); ++ res = -EBUSY; ++ goto err_unlock_free_utd; ++ } ++ ++ new_pqd_entry = kzalloc(sizeof(*new_pqd_entry), GFP_ATOMIC); ++ if (!new_pqd_entry) { ++ pr_err("qtaguid: qtudev_open(): %u/%u %u: " ++ "proc data alloc failed\n", ++ current->pid, current->tgid, current_fsuid()); ++ res = -ENOMEM; ++ goto err_unlock_free_utd; ++ } ++ new_pqd_entry->pid = current->tgid; ++ INIT_LIST_HEAD(&new_pqd_entry->sock_tag_list); ++ new_pqd_entry->parent_tag_data = utd_entry; ++ utd_entry->num_pqd++; ++ ++ proc_qtu_data_tree_insert(new_pqd_entry, ++ &proc_qtu_data_tree); ++ ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n", ++ current_fsuid(), new_pqd_entry); ++ file->private_data = new_pqd_entry; ++ return 0; ++ ++err_unlock_free_utd: ++ if (!utd_entry_found) { ++ rb_erase(&utd_entry->node, &uid_tag_data_tree); ++ kfree(utd_entry); ++ } ++err_unlock: ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ return res; ++} ++ ++static int qtudev_release(struct inode *inode, struct file *file) ++{ ++ struct proc_qtu_data *pqd_entry = file->private_data; ++ struct uid_tag_data *utd_entry = pqd_entry->parent_tag_data; ++ struct sock_tag *st_entry; ++ struct rb_root st_to_free_tree = RB_ROOT; ++ struct list_head *entry, *next; ++ struct tag_ref *tr; ++ ++ if (unlikely(qtu_proc_handling_passive)) ++ return 0; ++ ++ /* ++ * Do not trust the current->pid, it might just be a kworker cleaning ++ * up after a dead proc. ++ */ ++ DR_DEBUG("qtaguid: qtudev_release(): " ++ "pid=%u tgid=%u uid=%u " ++ "pqd_entry=%p->pid=%u utd_entry=%p->active_tags=%d\n", ++ current->pid, current->tgid, pqd_entry->parent_tag_data->uid, ++ pqd_entry, pqd_entry->pid, utd_entry, ++ utd_entry->num_active_tags); ++ ++ spin_lock_bh(&sock_tag_list_lock); ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ ++ list_for_each_safe(entry, next, &pqd_entry->sock_tag_list) { ++ st_entry = list_entry(entry, struct sock_tag, list); ++ DR_DEBUG("qtaguid: %s(): " ++ "erase sock_tag=%p->sk=%p pid=%u tgid=%u uid=%u\n", ++ __func__, ++ st_entry, st_entry->sk, ++ current->pid, current->tgid, ++ pqd_entry->parent_tag_data->uid); ++ ++ utd_entry = uid_tag_data_tree_search( ++ &uid_tag_data_tree, ++ get_uid_from_tag(st_entry->tag)); ++ BUG_ON(IS_ERR_OR_NULL(utd_entry)); ++ DR_DEBUG("qtaguid: %s(): " ++ "looking for tag=0x%llx in utd_entry=%p\n", __func__, ++ st_entry->tag, utd_entry); ++ tr = tag_ref_tree_search(&utd_entry->tag_ref_tree, ++ st_entry->tag); ++ BUG_ON(!tr); ++ BUG_ON(tr->num_sock_tags <= 0); ++ tr->num_sock_tags--; ++ free_tag_ref_from_utd_entry(tr, utd_entry); ++ ++ rb_erase(&st_entry->sock_node, &sock_tag_tree); ++ list_del(&st_entry->list); ++ /* Can't sockfd_put() within spinlock, do it later. */ ++ sock_tag_tree_insert(st_entry, &st_to_free_tree); ++ ++ /* ++ * Try to free the utd_entry if no other proc_qtu_data is ++ * using it (num_pqd is 0) and it doesn't have active tags ++ * (num_active_tags is 0). ++ */ ++ put_utd_entry(utd_entry); ++ } ++ ++ rb_erase(&pqd_entry->node, &proc_qtu_data_tree); ++ BUG_ON(pqd_entry->parent_tag_data->num_pqd < 1); ++ pqd_entry->parent_tag_data->num_pqd--; ++ put_utd_entry(pqd_entry->parent_tag_data); ++ kfree(pqd_entry); ++ file->private_data = NULL; ++ ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ spin_unlock_bh(&sock_tag_list_lock); ++ ++ ++ sock_tag_tree_erase(&st_to_free_tree); ++ ++ prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__, ++ current->pid, current->tgid); ++ return 0; ++} ++ ++/*------------------------------------------*/ ++static const struct file_operations qtudev_fops = { ++ .owner = THIS_MODULE, ++ .open = qtudev_open, ++ .release = qtudev_release, ++}; ++ ++static struct miscdevice qtu_device = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = QTU_DEV_NAME, ++ .fops = &qtudev_fops, ++ /* How sad it doesn't allow for defaults: .mode = S_IRUGO | S_IWUSR */ ++}; ++ ++/*------------------------------------------*/ ++static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) ++{ ++ int ret; ++ *res_procdir = proc_mkdir(module_procdirname, init_net.proc_net); ++ if (!*res_procdir) { ++ pr_err("qtaguid: failed to create proc/.../xt_qtaguid\n"); ++ ret = -ENOMEM; ++ goto no_dir; ++ } ++ ++ xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms, ++ *res_procdir); ++ if (!xt_qtaguid_ctrl_file) { ++ pr_err("qtaguid: failed to create xt_qtaguid/ctrl " ++ " file\n"); ++ ret = -ENOMEM; ++ goto no_ctrl_entry; ++ } ++ xt_qtaguid_ctrl_file->read_proc = qtaguid_ctrl_proc_read; ++ xt_qtaguid_ctrl_file->write_proc = qtaguid_ctrl_proc_write; ++ ++ xt_qtaguid_stats_file = create_proc_entry("stats", proc_stats_perms, ++ *res_procdir); ++ if (!xt_qtaguid_stats_file) { ++ pr_err("qtaguid: failed to create xt_qtaguid/stats " ++ "file\n"); ++ ret = -ENOMEM; ++ goto no_stats_entry; ++ } ++ xt_qtaguid_stats_file->read_proc = qtaguid_stats_proc_read; ++ /* ++ * TODO: add support counter hacking ++ * xt_qtaguid_stats_file->write_proc = qtaguid_stats_proc_write; ++ */ ++ return 0; ++ ++no_stats_entry: ++ remove_proc_entry("ctrl", *res_procdir); ++no_ctrl_entry: ++ remove_proc_entry("xt_qtaguid", NULL); ++no_dir: ++ return ret; ++} ++ ++static struct xt_match qtaguid_mt_reg __read_mostly = { ++ /* ++ * This module masquerades as the "owner" module so that iptables ++ * tools can deal with it. ++ */ ++ .name = "owner", ++ .revision = 1, ++ .family = NFPROTO_UNSPEC, ++ .match = qtaguid_mt, ++ .matchsize = sizeof(struct xt_qtaguid_match_info), ++ .me = THIS_MODULE, ++}; ++ ++static int __init qtaguid_mt_init(void) ++{ ++ if (qtaguid_proc_register(&xt_qtaguid_procdir) ++ || iface_stat_init(xt_qtaguid_procdir) ++ || xt_register_match(&qtaguid_mt_reg) ++ || misc_register(&qtu_device)) ++ return -1; ++ return 0; ++} ++ ++/* ++ * TODO: allow unloading of the module. ++ * For now stats are permanent. ++ * Kconfig forces'y/n' and never an 'm'. ++ */ ++ ++module_init(qtaguid_mt_init); ++MODULE_AUTHOR("jpa "); ++MODULE_DESCRIPTION("Xtables: socket owner+tag matching and associated stats"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_owner"); ++MODULE_ALIAS("ip6t_owner"); ++MODULE_ALIAS("ipt_qtaguid"); ++MODULE_ALIAS("ip6t_qtaguid"); +diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h +new file mode 100644 +index 00000000..6dc14a9c +--- /dev/null ++++ b/net/netfilter/xt_qtaguid_internal.h +@@ -0,0 +1,352 @@ ++/* ++ * Kernel iptables module to track stats for packets based on user tags. ++ * ++ * (C) 2011 Google, Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef __XT_QTAGUID_INTERNAL_H__ ++#define __XT_QTAGUID_INTERNAL_H__ ++ ++#include ++#include ++#include ++#include ++ ++/* Iface handling */ ++#define IDEBUG_MASK (1<<0) ++/* Iptable Matching. Per packet. */ ++#define MDEBUG_MASK (1<<1) ++/* Red-black tree handling. Per packet. */ ++#define RDEBUG_MASK (1<<2) ++/* procfs ctrl/stats handling */ ++#define CDEBUG_MASK (1<<3) ++/* dev and resource tracking */ ++#define DDEBUG_MASK (1<<4) ++ ++/* E.g (IDEBUG_MASK | CDEBUG_MASK | DDEBUG_MASK) */ ++#define DEFAULT_DEBUG_MASK 0 ++ ++/* ++ * (Un)Define these *DEBUG to compile out/in the pr_debug calls. ++ * All undef: text size ~ 0x3030; all def: ~ 0x4404. ++ */ ++#define IDEBUG ++#define MDEBUG ++#define RDEBUG ++#define CDEBUG ++#define DDEBUG ++ ++#define MSK_DEBUG(mask, ...) do { \ ++ if (unlikely(qtaguid_debug_mask & (mask))) \ ++ pr_debug(__VA_ARGS__); \ ++ } while (0) ++#ifdef IDEBUG ++#define IF_DEBUG(...) MSK_DEBUG(IDEBUG_MASK, __VA_ARGS__) ++#else ++#define IF_DEBUG(...) no_printk(__VA_ARGS__) ++#endif ++#ifdef MDEBUG ++#define MT_DEBUG(...) MSK_DEBUG(MDEBUG_MASK, __VA_ARGS__) ++#else ++#define MT_DEBUG(...) no_printk(__VA_ARGS__) ++#endif ++#ifdef RDEBUG ++#define RB_DEBUG(...) MSK_DEBUG(RDEBUG_MASK, __VA_ARGS__) ++#else ++#define RB_DEBUG(...) no_printk(__VA_ARGS__) ++#endif ++#ifdef CDEBUG ++#define CT_DEBUG(...) MSK_DEBUG(CDEBUG_MASK, __VA_ARGS__) ++#else ++#define CT_DEBUG(...) no_printk(__VA_ARGS__) ++#endif ++#ifdef DDEBUG ++#define DR_DEBUG(...) MSK_DEBUG(DDEBUG_MASK, __VA_ARGS__) ++#else ++#define DR_DEBUG(...) no_printk(__VA_ARGS__) ++#endif ++ ++extern uint qtaguid_debug_mask; ++ ++/*---------------------------------------------------------------------------*/ ++/* ++ * Tags: ++ * ++ * They represent what the data usage counters will be tracked against. ++ * By default a tag is just based on the UID. ++ * The UID is used as the base for policing, and can not be ignored. ++ * So a tag will always at least represent a UID (uid_tag). ++ * ++ * A tag can be augmented with an "accounting tag" which is associated ++ * with a UID. ++ * User space can set the acct_tag portion of the tag which is then used ++ * with sockets: all data belonging to that socket will be counted against the ++ * tag. The policing is then based on the tag's uid_tag portion, ++ * and stats are collected for the acct_tag portion separately. ++ * ++ * There could be ++ * a: {acct_tag=1, uid_tag=10003} ++ * b: {acct_tag=2, uid_tag=10003} ++ * c: {acct_tag=3, uid_tag=10003} ++ * d: {acct_tag=0, uid_tag=10003} ++ * a, b, and c represent tags associated with specific sockets. ++ * d is for the totals for that uid, including all untagged traffic. ++ * Typically d is used with policing/quota rules. ++ * ++ * We want tag_t big enough to distinguish uid_t and acct_tag. ++ * It might become a struct if needed. ++ * Nothing should be using it as an int. ++ */ ++typedef uint64_t tag_t; /* Only used via accessors */ ++ ++#define TAG_UID_MASK 0xFFFFFFFFULL ++#define TAG_ACCT_MASK (~0xFFFFFFFFULL) ++ ++static inline int tag_compare(tag_t t1, tag_t t2) ++{ ++ return t1 < t2 ? -1 : t1 == t2 ? 0 : 1; ++} ++ ++static inline tag_t combine_atag_with_uid(tag_t acct_tag, uid_t uid) ++{ ++ return acct_tag | uid; ++} ++static inline tag_t make_tag_from_uid(uid_t uid) ++{ ++ return uid; ++} ++static inline uid_t get_uid_from_tag(tag_t tag) ++{ ++ return tag & TAG_UID_MASK; ++} ++static inline tag_t get_utag_from_tag(tag_t tag) ++{ ++ return tag & TAG_UID_MASK; ++} ++static inline tag_t get_atag_from_tag(tag_t tag) ++{ ++ return tag & TAG_ACCT_MASK; ++} ++ ++static inline bool valid_atag(tag_t tag) ++{ ++ return !(tag & TAG_UID_MASK); ++} ++static inline tag_t make_atag_from_value(uint32_t value) ++{ ++ return (uint64_t)value << 32; ++} ++/*---------------------------------------------------------------------------*/ ++ ++/* ++ * Maximum number of socket tags that a UID is allowed to have active. ++ * Multiple processes belonging to the same UID contribute towards this limit. ++ * Special UIDs that can impersonate a UID also contribute (e.g. download ++ * manager, ...) ++ */ ++#define DEFAULT_MAX_SOCK_TAGS 1024 ++ ++/* ++ * For now we only track 2 sets of counters. ++ * The default set is 0. ++ * Userspace can activate another set for a given uid being tracked. ++ */ ++#define IFS_MAX_COUNTER_SETS 2 ++ ++enum ifs_tx_rx { ++ IFS_TX, ++ IFS_RX, ++ IFS_MAX_DIRECTIONS ++}; ++ ++/* For now, TCP, UDP, the rest */ ++enum ifs_proto { ++ IFS_TCP, ++ IFS_UDP, ++ IFS_PROTO_OTHER, ++ IFS_MAX_PROTOS ++}; ++ ++struct byte_packet_counters { ++ uint64_t bytes; ++ uint64_t packets; ++}; ++ ++struct data_counters { ++ struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; ++}; ++ ++static inline uint64_t dc_sum_bytes(struct data_counters *counters, ++ int set, ++ enum ifs_tx_rx direction) ++{ ++ return counters->bpc[set][direction][IFS_TCP].bytes ++ + counters->bpc[set][direction][IFS_UDP].bytes ++ + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; ++} ++ ++static inline uint64_t dc_sum_packets(struct data_counters *counters, ++ int set, ++ enum ifs_tx_rx direction) ++{ ++ return counters->bpc[set][direction][IFS_TCP].packets ++ + counters->bpc[set][direction][IFS_UDP].packets ++ + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; ++} ++ ++ ++/* Generic X based nodes used as a base for rb_tree ops */ ++struct tag_node { ++ struct rb_node node; ++ tag_t tag; ++}; ++ ++struct tag_stat { ++ struct tag_node tn; ++ struct data_counters counters; ++ /* ++ * If this tag is acct_tag based, we need to count against the ++ * matching parent uid_tag. ++ */ ++ struct data_counters *parent_counters; ++}; ++ ++struct iface_stat { ++ struct list_head list; /* in iface_stat_list */ ++ char *ifname; ++ bool active; ++ /* net_dev is only valid for active iface_stat */ ++ struct net_device *net_dev; ++ ++ struct byte_packet_counters totals_via_dev[IFS_MAX_DIRECTIONS]; ++ struct data_counters totals_via_skb; ++ /* ++ * We keep the last_known, because some devices reset their counters ++ * just before NETDEV_UP, while some will reset just before ++ * NETDEV_REGISTER (which is more normal). ++ * So now, if the device didn't do a NETDEV_UNREGISTER and we see ++ * its current dev stats smaller that what was previously known, we ++ * assume an UNREGISTER and just use the last_known. ++ */ ++ struct byte_packet_counters last_known[IFS_MAX_DIRECTIONS]; ++ /* last_known is usable when last_known_valid is true */ ++ bool last_known_valid; ++ ++ struct proc_dir_entry *proc_ptr; ++ ++ struct rb_root tag_stat_tree; ++ spinlock_t tag_stat_list_lock; ++}; ++ ++/* This is needed to create proc_dir_entries from atomic context. */ ++struct iface_stat_work { ++ struct work_struct iface_work; ++ struct iface_stat *iface_entry; ++}; ++ ++/* ++ * Track tag that this socket is transferring data for, and not necessarily ++ * the uid that owns the socket. ++ * This is the tag against which tag_stat.counters will be billed. ++ * These structs need to be looked up by sock and pid. ++ */ ++struct sock_tag { ++ struct rb_node sock_node; ++ struct sock *sk; /* Only used as a number, never dereferenced */ ++ /* The socket is needed for sockfd_put() */ ++ struct socket *socket; ++ /* Used to associate with a given pid */ ++ struct list_head list; /* in proc_qtu_data.sock_tag_list */ ++ pid_t pid; ++ ++ tag_t tag; ++}; ++ ++struct qtaguid_event_counts { ++ /* Various successful events */ ++ atomic64_t sockets_tagged; ++ atomic64_t sockets_untagged; ++ atomic64_t counter_set_changes; ++ atomic64_t delete_cmds; ++ atomic64_t iface_events; /* Number of NETDEV_* events handled */ ++ ++ atomic64_t match_calls; /* Number of times iptables called mt */ ++ /* Number of times iptables called mt from pre or post routing hooks */ ++ atomic64_t match_calls_prepost; ++ /* ++ * match_found_sk_*: numbers related to the netfilter matching ++ * function finding a sock for the sk_buff. ++ * Total skbs processed is sum(match_found*). ++ */ ++ atomic64_t match_found_sk; /* An sk was already in the sk_buff. */ ++ /* The connection tracker had or didn't have the sk. */ ++ atomic64_t match_found_sk_in_ct; ++ atomic64_t match_found_no_sk_in_ct; ++ /* ++ * No sk could be found. No apparent owner. Could happen with ++ * unsolicited traffic. ++ */ ++ atomic64_t match_no_sk; ++ /* ++ * The file ptr in the sk_socket wasn't there. ++ * This might happen for traffic while the socket is being closed. ++ */ ++ atomic64_t match_no_sk_file; ++}; ++ ++/* Track the set active_set for the given tag. */ ++struct tag_counter_set { ++ struct tag_node tn; ++ int active_set; ++}; ++ ++/*----------------------------------------------*/ ++/* ++ * The qtu uid data is used to track resources that are created directly or ++ * indirectly by processes (uid tracked). ++ * It is shared by the processes with the same uid. ++ * Some of the resource will be counted to prevent further rogue allocations, ++ * some will need freeing once the owner process (uid) exits. ++ */ ++struct uid_tag_data { ++ struct rb_node node; ++ uid_t uid; ++ ++ /* ++ * For the uid, how many accounting tags have been set. ++ */ ++ int num_active_tags; ++ /* Track the number of proc_qtu_data that reference it */ ++ int num_pqd; ++ struct rb_root tag_ref_tree; ++ /* No tag_node_tree_lock; use uid_tag_data_tree_lock */ ++}; ++ ++struct tag_ref { ++ struct tag_node tn; ++ ++ /* ++ * This tracks the number of active sockets that have a tag on them ++ * which matches this tag_ref.tn.tag. ++ * A tag ref can live on after the sockets are untagged. ++ * A tag ref can only be removed during a tag delete command. ++ */ ++ int num_sock_tags; ++}; ++ ++struct proc_qtu_data { ++ struct rb_node node; ++ pid_t pid; ++ ++ struct uid_tag_data *parent_tag_data; ++ ++ /* Tracks the sock_tags that need freeing upon this proc's death */ ++ struct list_head sock_tag_list; ++ /* No spinlock_t sock_tag_list_lock; use the global one. */ ++}; ++ ++/*----------------------------------------------*/ ++#endif /* ifndef __XT_QTAGUID_INTERNAL_H__ */ +diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c +new file mode 100644 +index 00000000..f6a00a35 +--- /dev/null ++++ b/net/netfilter/xt_qtaguid_print.c +@@ -0,0 +1,566 @@ ++/* ++ * Pretty printing Support for iptables xt_qtaguid module. ++ * ++ * (C) 2011 Google, Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * Most of the functions in this file just waste time if DEBUG is not defined. ++ * The matching xt_qtaguid_print.h will static inline empty funcs if the needed ++ * debug flags ore not defined. ++ * Those funcs that fail to allocate memory will panic as there is no need to ++ * hobble allong just pretending to do the requested work. ++ */ ++ ++#define DEBUG ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include "xt_qtaguid_internal.h" ++#include "xt_qtaguid_print.h" ++ ++#ifdef DDEBUG ++ ++static void _bug_on_err_or_null(void *ptr) ++{ ++ if (IS_ERR_OR_NULL(ptr)) { ++ pr_err("qtaguid: kmalloc failed\n"); ++ BUG(); ++ } ++} ++ ++char *pp_tag_t(tag_t *tag) ++{ ++ char *res; ++ ++ if (!tag) ++ res = kasprintf(GFP_ATOMIC, "tag_t@null{}"); ++ else ++ res = kasprintf(GFP_ATOMIC, ++ "tag_t@%p{tag=0x%llx, uid=%u}", ++ tag, *tag, get_uid_from_tag(*tag)); ++ _bug_on_err_or_null(res); ++ return res; ++} ++ ++char *pp_data_counters(struct data_counters *dc, bool showValues) ++{ ++ char *res; ++ ++ if (!dc) ++ res = kasprintf(GFP_ATOMIC, "data_counters@null{}"); ++ else if (showValues) ++ res = kasprintf( ++ GFP_ATOMIC, "data_counters@%p{" ++ "set0{" ++ "rx{" ++ "tcp{b=%llu, p=%llu}, " ++ "udp{b=%llu, p=%llu}," ++ "other{b=%llu, p=%llu}}, " ++ "tx{" ++ "tcp{b=%llu, p=%llu}, " ++ "udp{b=%llu, p=%llu}," ++ "other{b=%llu, p=%llu}}}, " ++ "set1{" ++ "rx{" ++ "tcp{b=%llu, p=%llu}, " ++ "udp{b=%llu, p=%llu}," ++ "other{b=%llu, p=%llu}}, " ++ "tx{" ++ "tcp{b=%llu, p=%llu}, " ++ "udp{b=%llu, p=%llu}," ++ "other{b=%llu, p=%llu}}}}", ++ dc, ++ dc->bpc[0][IFS_RX][IFS_TCP].bytes, ++ dc->bpc[0][IFS_RX][IFS_TCP].packets, ++ dc->bpc[0][IFS_RX][IFS_UDP].bytes, ++ dc->bpc[0][IFS_RX][IFS_UDP].packets, ++ dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].bytes, ++ dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].packets, ++ dc->bpc[0][IFS_TX][IFS_TCP].bytes, ++ dc->bpc[0][IFS_TX][IFS_TCP].packets, ++ dc->bpc[0][IFS_TX][IFS_UDP].bytes, ++ dc->bpc[0][IFS_TX][IFS_UDP].packets, ++ dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].bytes, ++ dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].packets, ++ dc->bpc[1][IFS_RX][IFS_TCP].bytes, ++ dc->bpc[1][IFS_RX][IFS_TCP].packets, ++ dc->bpc[1][IFS_RX][IFS_UDP].bytes, ++ dc->bpc[1][IFS_RX][IFS_UDP].packets, ++ dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].bytes, ++ dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].packets, ++ dc->bpc[1][IFS_TX][IFS_TCP].bytes, ++ dc->bpc[1][IFS_TX][IFS_TCP].packets, ++ dc->bpc[1][IFS_TX][IFS_UDP].bytes, ++ dc->bpc[1][IFS_TX][IFS_UDP].packets, ++ dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].bytes, ++ dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].packets); ++ else ++ res = kasprintf(GFP_ATOMIC, "data_counters@%p{...}", dc); ++ _bug_on_err_or_null(res); ++ return res; ++} ++ ++char *pp_tag_node(struct tag_node *tn) ++{ ++ char *tag_str; ++ char *res; ++ ++ if (!tn) { ++ res = kasprintf(GFP_ATOMIC, "tag_node@null{}"); ++ _bug_on_err_or_null(res); ++ return res; ++ } ++ tag_str = pp_tag_t(&tn->tag); ++ res = kasprintf(GFP_ATOMIC, ++ "tag_node@%p{tag=%s}", ++ tn, tag_str); ++ _bug_on_err_or_null(res); ++ kfree(tag_str); ++ return res; ++} ++ ++char *pp_tag_ref(struct tag_ref *tr) ++{ ++ char *tn_str; ++ char *res; ++ ++ if (!tr) { ++ res = kasprintf(GFP_ATOMIC, "tag_ref@null{}"); ++ _bug_on_err_or_null(res); ++ return res; ++ } ++ tn_str = pp_tag_node(&tr->tn); ++ res = kasprintf(GFP_ATOMIC, ++ "tag_ref@%p{%s, num_sock_tags=%d}", ++ tr, tn_str, tr->num_sock_tags); ++ _bug_on_err_or_null(res); ++ kfree(tn_str); ++ return res; ++} ++ ++char *pp_tag_stat(struct tag_stat *ts) ++{ ++ char *tn_str; ++ char *counters_str; ++ char *parent_counters_str; ++ char *res; ++ ++ if (!ts) { ++ res = kasprintf(GFP_ATOMIC, "tag_stat@null{}"); ++ _bug_on_err_or_null(res); ++ return res; ++ } ++ tn_str = pp_tag_node(&ts->tn); ++ counters_str = pp_data_counters(&ts->counters, true); ++ parent_counters_str = pp_data_counters(ts->parent_counters, false); ++ res = kasprintf(GFP_ATOMIC, ++ "tag_stat@%p{%s, counters=%s, parent_counters=%s}", ++ ts, tn_str, counters_str, parent_counters_str); ++ _bug_on_err_or_null(res); ++ kfree(tn_str); ++ kfree(counters_str); ++ kfree(parent_counters_str); ++ return res; ++} ++ ++char *pp_iface_stat(struct iface_stat *is) ++{ ++ char *res; ++ if (!is) { ++ res = kasprintf(GFP_ATOMIC, "iface_stat@null{}"); ++ } else { ++ struct data_counters *cnts = &is->totals_via_skb; ++ res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" ++ "list=list_head{...}, " ++ "ifname=%s, " ++ "total_dev={rx={bytes=%llu, " ++ "packets=%llu}, " ++ "tx={bytes=%llu, " ++ "packets=%llu}}, " ++ "total_skb={rx={bytes=%llu, " ++ "packets=%llu}, " ++ "tx={bytes=%llu, " ++ "packets=%llu}}, " ++ "last_known_valid=%d, " ++ "last_known={rx={bytes=%llu, " ++ "packets=%llu}, " ++ "tx={bytes=%llu, " ++ "packets=%llu}}, " ++ "active=%d, " ++ "net_dev=%p, " ++ "proc_ptr=%p, " ++ "tag_stat_tree=rb_root{...}}", ++ is, ++ is->ifname, ++ is->totals_via_dev[IFS_RX].bytes, ++ is->totals_via_dev[IFS_RX].packets, ++ is->totals_via_dev[IFS_TX].bytes, ++ is->totals_via_dev[IFS_TX].packets, ++ dc_sum_bytes(cnts, 0, IFS_RX), ++ dc_sum_packets(cnts, 0, IFS_RX), ++ dc_sum_bytes(cnts, 0, IFS_TX), ++ dc_sum_packets(cnts, 0, IFS_TX), ++ is->last_known_valid, ++ is->last_known[IFS_RX].bytes, ++ is->last_known[IFS_RX].packets, ++ is->last_known[IFS_TX].bytes, ++ is->last_known[IFS_TX].packets, ++ is->active, ++ is->net_dev, ++ is->proc_ptr); ++ } ++ _bug_on_err_or_null(res); ++ return res; ++} ++ ++char *pp_sock_tag(struct sock_tag *st) ++{ ++ char *tag_str; ++ char *res; ++ ++ if (!st) { ++ res = kasprintf(GFP_ATOMIC, "sock_tag@null{}"); ++ _bug_on_err_or_null(res); ++ return res; ++ } ++ tag_str = pp_tag_t(&st->tag); ++ res = kasprintf(GFP_ATOMIC, "sock_tag@%p{" ++ "sock_node=rb_node{...}, " ++ "sk=%p socket=%p (f_count=%lu), list=list_head{...}, " ++ "pid=%u, tag=%s}", ++ st, st->sk, st->socket, atomic_long_read( ++ &st->socket->file->f_count), ++ st->pid, tag_str); ++ _bug_on_err_or_null(res); ++ kfree(tag_str); ++ return res; ++} ++ ++char *pp_uid_tag_data(struct uid_tag_data *utd) ++{ ++ char *res; ++ ++ if (!utd) ++ res = kasprintf(GFP_ATOMIC, "uid_tag_data@null{}"); ++ else ++ res = kasprintf(GFP_ATOMIC, "uid_tag_data@%p{" ++ "uid=%u, num_active_acct_tags=%d, " ++ "num_pqd=%d, " ++ "tag_node_tree=rb_root{...}, " ++ "proc_qtu_data_tree=rb_root{...}}", ++ utd, utd->uid, ++ utd->num_active_tags, utd->num_pqd); ++ _bug_on_err_or_null(res); ++ return res; ++} ++ ++char *pp_proc_qtu_data(struct proc_qtu_data *pqd) ++{ ++ char *parent_tag_data_str; ++ char *res; ++ ++ if (!pqd) { ++ res = kasprintf(GFP_ATOMIC, "proc_qtu_data@null{}"); ++ _bug_on_err_or_null(res); ++ return res; ++ } ++ parent_tag_data_str = pp_uid_tag_data(pqd->parent_tag_data); ++ res = kasprintf(GFP_ATOMIC, "proc_qtu_data@%p{" ++ "node=rb_node{...}, pid=%u, " ++ "parent_tag_data=%s, " ++ "sock_tag_list=list_head{...}}", ++ pqd, pqd->pid, parent_tag_data_str ++ ); ++ _bug_on_err_or_null(res); ++ kfree(parent_tag_data_str); ++ return res; ++} ++ ++/*------------------------------------------*/ ++void prdebug_sock_tag_tree(int indent_level, ++ struct rb_root *sock_tag_tree) ++{ ++ struct rb_node *node; ++ struct sock_tag *sock_tag_entry; ++ char *str; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (RB_EMPTY_ROOT(sock_tag_tree)) { ++ str = "sock_tag_tree=rb_root{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "sock_tag_tree=rb_root{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ for (node = rb_first(sock_tag_tree); ++ node; ++ node = rb_next(node)) { ++ sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); ++ str = pp_sock_tag(sock_tag_entry); ++ pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); ++ kfree(str); ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++void prdebug_sock_tag_list(int indent_level, ++ struct list_head *sock_tag_list) ++{ ++ struct sock_tag *sock_tag_entry; ++ char *str; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (list_empty(sock_tag_list)) { ++ str = "sock_tag_list=list_head{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "sock_tag_list=list_head{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ list_for_each_entry(sock_tag_entry, sock_tag_list, list) { ++ str = pp_sock_tag(sock_tag_entry); ++ pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); ++ kfree(str); ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++void prdebug_proc_qtu_data_tree(int indent_level, ++ struct rb_root *proc_qtu_data_tree) ++{ ++ char *str; ++ struct rb_node *node; ++ struct proc_qtu_data *proc_qtu_data_entry; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (RB_EMPTY_ROOT(proc_qtu_data_tree)) { ++ str = "proc_qtu_data_tree=rb_root{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "proc_qtu_data_tree=rb_root{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ for (node = rb_first(proc_qtu_data_tree); ++ node; ++ node = rb_next(node)) { ++ proc_qtu_data_entry = rb_entry(node, ++ struct proc_qtu_data, ++ node); ++ str = pp_proc_qtu_data(proc_qtu_data_entry); ++ pr_debug("%*d: %s,\n", indent_level*2, indent_level, ++ str); ++ kfree(str); ++ indent_level++; ++ prdebug_sock_tag_list(indent_level, ++ &proc_qtu_data_entry->sock_tag_list); ++ indent_level--; ++ ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) ++{ ++ char *str; ++ struct rb_node *node; ++ struct tag_ref *tag_ref_entry; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (RB_EMPTY_ROOT(tag_ref_tree)) { ++ str = "tag_ref_tree{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "tag_ref_tree{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ for (node = rb_first(tag_ref_tree); ++ node; ++ node = rb_next(node)) { ++ tag_ref_entry = rb_entry(node, ++ struct tag_ref, ++ tn.node); ++ str = pp_tag_ref(tag_ref_entry); ++ pr_debug("%*d: %s,\n", indent_level*2, indent_level, ++ str); ++ kfree(str); ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++void prdebug_uid_tag_data_tree(int indent_level, ++ struct rb_root *uid_tag_data_tree) ++{ ++ char *str; ++ struct rb_node *node; ++ struct uid_tag_data *uid_tag_data_entry; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (RB_EMPTY_ROOT(uid_tag_data_tree)) { ++ str = "uid_tag_data_tree=rb_root{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "uid_tag_data_tree=rb_root{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ for (node = rb_first(uid_tag_data_tree); ++ node; ++ node = rb_next(node)) { ++ uid_tag_data_entry = rb_entry(node, struct uid_tag_data, ++ node); ++ str = pp_uid_tag_data(uid_tag_data_entry); ++ pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); ++ kfree(str); ++ if (!RB_EMPTY_ROOT(&uid_tag_data_entry->tag_ref_tree)) { ++ indent_level++; ++ prdebug_tag_ref_tree(indent_level, ++ &uid_tag_data_entry->tag_ref_tree); ++ indent_level--; ++ } ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++void prdebug_tag_stat_tree(int indent_level, ++ struct rb_root *tag_stat_tree) ++{ ++ char *str; ++ struct rb_node *node; ++ struct tag_stat *ts_entry; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (RB_EMPTY_ROOT(tag_stat_tree)) { ++ str = "tag_stat_tree{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "tag_stat_tree{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ for (node = rb_first(tag_stat_tree); ++ node; ++ node = rb_next(node)) { ++ ts_entry = rb_entry(node, struct tag_stat, tn.node); ++ str = pp_tag_stat(ts_entry); ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, ++ str); ++ kfree(str); ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++void prdebug_iface_stat_list(int indent_level, ++ struct list_head *iface_stat_list) ++{ ++ char *str; ++ struct iface_stat *iface_entry; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (list_empty(iface_stat_list)) { ++ str = "iface_stat_list=list_head{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "iface_stat_list=list_head{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ list_for_each_entry(iface_entry, iface_stat_list, list) { ++ str = pp_iface_stat(iface_entry); ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ kfree(str); ++ ++ spin_lock_bh(&iface_entry->tag_stat_list_lock); ++ if (!RB_EMPTY_ROOT(&iface_entry->tag_stat_tree)) { ++ indent_level++; ++ prdebug_tag_stat_tree(indent_level, ++ &iface_entry->tag_stat_tree); ++ indent_level--; ++ } ++ spin_unlock_bh(&iface_entry->tag_stat_list_lock); ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++#endif /* ifdef DDEBUG */ ++/*------------------------------------------*/ ++static const char * const netdev_event_strings[] = { ++ "netdev_unknown", ++ "NETDEV_UP", ++ "NETDEV_DOWN", ++ "NETDEV_REBOOT", ++ "NETDEV_CHANGE", ++ "NETDEV_REGISTER", ++ "NETDEV_UNREGISTER", ++ "NETDEV_CHANGEMTU", ++ "NETDEV_CHANGEADDR", ++ "NETDEV_GOING_DOWN", ++ "NETDEV_CHANGENAME", ++ "NETDEV_FEAT_CHANGE", ++ "NETDEV_BONDING_FAILOVER", ++ "NETDEV_PRE_UP", ++ "NETDEV_PRE_TYPE_CHANGE", ++ "NETDEV_POST_TYPE_CHANGE", ++ "NETDEV_POST_INIT", ++ "NETDEV_UNREGISTER_BATCH", ++ "NETDEV_RELEASE", ++ "NETDEV_NOTIFY_PEERS", ++ "NETDEV_JOIN", ++}; ++ ++const char *netdev_evt_str(int netdev_event) ++{ ++ if (netdev_event < 0 ++ || netdev_event >= ARRAY_SIZE(netdev_event_strings)) ++ return "bad event num"; ++ return netdev_event_strings[netdev_event]; ++} +diff --git a/net/netfilter/xt_qtaguid_print.h b/net/netfilter/xt_qtaguid_print.h +new file mode 100644 +index 00000000..b63871a0 +--- /dev/null ++++ b/net/netfilter/xt_qtaguid_print.h +@@ -0,0 +1,120 @@ ++/* ++ * Pretty printing Support for iptables xt_qtaguid module. ++ * ++ * (C) 2011 Google, Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef __XT_QTAGUID_PRINT_H__ ++#define __XT_QTAGUID_PRINT_H__ ++ ++#include "xt_qtaguid_internal.h" ++ ++#ifdef DDEBUG ++ ++char *pp_tag_t(tag_t *tag); ++char *pp_data_counters(struct data_counters *dc, bool showValues); ++char *pp_tag_node(struct tag_node *tn); ++char *pp_tag_ref(struct tag_ref *tr); ++char *pp_tag_stat(struct tag_stat *ts); ++char *pp_iface_stat(struct iface_stat *is); ++char *pp_sock_tag(struct sock_tag *st); ++char *pp_uid_tag_data(struct uid_tag_data *qtd); ++char *pp_proc_qtu_data(struct proc_qtu_data *pqd); ++ ++/*------------------------------------------*/ ++void prdebug_sock_tag_list(int indent_level, ++ struct list_head *sock_tag_list); ++void prdebug_sock_tag_tree(int indent_level, ++ struct rb_root *sock_tag_tree); ++void prdebug_proc_qtu_data_tree(int indent_level, ++ struct rb_root *proc_qtu_data_tree); ++void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree); ++void prdebug_uid_tag_data_tree(int indent_level, ++ struct rb_root *uid_tag_data_tree); ++void prdebug_tag_stat_tree(int indent_level, ++ struct rb_root *tag_stat_tree); ++void prdebug_iface_stat_list(int indent_level, ++ struct list_head *iface_stat_list); ++ ++#else ++ ++/*------------------------------------------*/ ++static inline char *pp_tag_t(tag_t *tag) ++{ ++ return NULL; ++} ++static inline char *pp_data_counters(struct data_counters *dc, bool showValues) ++{ ++ return NULL; ++} ++static inline char *pp_tag_node(struct tag_node *tn) ++{ ++ return NULL; ++} ++static inline char *pp_tag_ref(struct tag_ref *tr) ++{ ++ return NULL; ++} ++static inline char *pp_tag_stat(struct tag_stat *ts) ++{ ++ return NULL; ++} ++static inline char *pp_iface_stat(struct iface_stat *is) ++{ ++ return NULL; ++} ++static inline char *pp_sock_tag(struct sock_tag *st) ++{ ++ return NULL; ++} ++static inline char *pp_uid_tag_data(struct uid_tag_data *qtd) ++{ ++ return NULL; ++} ++static inline char *pp_proc_qtu_data(struct proc_qtu_data *pqd) ++{ ++ return NULL; ++} ++ ++/*------------------------------------------*/ ++static inline ++void prdebug_sock_tag_list(int indent_level, ++ struct list_head *sock_tag_list) ++{ ++} ++static inline ++void prdebug_sock_tag_tree(int indent_level, ++ struct rb_root *sock_tag_tree) ++{ ++} ++static inline ++void prdebug_proc_qtu_data_tree(int indent_level, ++ struct rb_root *proc_qtu_data_tree) ++{ ++} ++static inline ++void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) ++{ ++} ++static inline ++void prdebug_uid_tag_data_tree(int indent_level, ++ struct rb_root *uid_tag_data_tree) ++{ ++} ++static inline ++void prdebug_tag_stat_tree(int indent_level, ++ struct rb_root *tag_stat_tree) ++{ ++} ++static inline ++void prdebug_iface_stat_list(int indent_level, ++ struct list_head *iface_stat_list) ++{ ++} ++#endif ++/*------------------------------------------*/ ++const char *netdev_evt_str(int netdev_event); ++#endif /* ifndef __XT_QTAGUID_PRINT_H__ */ +diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c +new file mode 100644 +index 00000000..fb2ef46b +--- /dev/null ++++ b/net/netfilter/xt_quota2.c +@@ -0,0 +1,382 @@ ++/* ++ * xt_quota2 - enhanced xt_quota that can count upwards and in packets ++ * as a minimal accounting match. ++ * by Jan Engelhardt , 2008 ++ * ++ * Originally based on xt_quota.c: ++ * netfilter module to enforce network quotas ++ * Sam Johnston ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License; either ++ * version 2 of the License, as published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG ++#include ++#endif ++ ++/** ++ * @lock: lock to protect quota writers from each other ++ */ ++struct xt_quota_counter { ++ u_int64_t quota; ++ spinlock_t lock; ++ struct list_head list; ++ atomic_t ref; ++ char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)]; ++ struct proc_dir_entry *procfs_entry; ++}; ++ ++#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG ++/* Harald's favorite number +1 :D From ipt_ULOG.C */ ++static int qlog_nl_event = 112; ++module_param_named(event_num, qlog_nl_event, uint, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(event_num, ++ "Event number for NETLINK_NFLOG message. 0 disables log." ++ "111 is what ipt_ULOG uses."); ++static struct sock *nflognl; ++#endif ++ ++static LIST_HEAD(counter_list); ++static DEFINE_SPINLOCK(counter_list_lock); ++ ++static struct proc_dir_entry *proc_xt_quota; ++static unsigned int quota_list_perms = S_IRUGO | S_IWUSR; ++static unsigned int quota_list_uid = 0; ++static unsigned int quota_list_gid = 0; ++module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); ++module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); ++module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); ++ ++ ++#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG ++static void quota2_log(unsigned int hooknum, ++ const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const char *prefix) ++{ ++ ulog_packet_msg_t *pm; ++ struct sk_buff *log_skb; ++ size_t size; ++ struct nlmsghdr *nlh; ++ ++ if (!qlog_nl_event) ++ return; ++ ++ size = NLMSG_SPACE(sizeof(*pm)); ++ size = max(size, (size_t)NLMSG_GOODSIZE); ++ log_skb = alloc_skb(size, GFP_ATOMIC); ++ if (!log_skb) { ++ pr_err("xt_quota2: cannot alloc skb for logging\n"); ++ return; ++ } ++ ++ /* NLMSG_PUT() uses "goto nlmsg_failure" */ ++ nlh = NLMSG_PUT(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event, ++ sizeof(*pm)); ++ pm = NLMSG_DATA(nlh); ++ if (skb->tstamp.tv64 == 0) ++ __net_timestamp((struct sk_buff *)skb); ++ pm->data_len = 0; ++ pm->hook = hooknum; ++ if (prefix != NULL) ++ strlcpy(pm->prefix, prefix, sizeof(pm->prefix)); ++ else ++ *(pm->prefix) = '\0'; ++ if (in) ++ strlcpy(pm->indev_name, in->name, sizeof(pm->indev_name)); ++ else ++ pm->indev_name[0] = '\0'; ++ ++ if (out) ++ strlcpy(pm->outdev_name, out->name, sizeof(pm->outdev_name)); ++ else ++ pm->outdev_name[0] = '\0'; ++ ++ NETLINK_CB(log_skb).dst_group = 1; ++ pr_debug("throwing 1 packets to netlink group 1\n"); ++ netlink_broadcast(nflognl, log_skb, 0, 1, GFP_ATOMIC); ++ ++nlmsg_failure: /* Used within NLMSG_PUT() */ ++ pr_debug("xt_quota2: error during NLMSG_PUT\n"); ++} ++#else ++static void quota2_log(unsigned int hooknum, ++ const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const char *prefix) ++{ ++} ++#endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */ ++ ++static int quota_proc_read(char *page, char **start, off_t offset, ++ int count, int *eof, void *data) ++{ ++ struct xt_quota_counter *e = data; ++ int ret; ++ ++ spin_lock_bh(&e->lock); ++ ret = snprintf(page, PAGE_SIZE, "%llu\n", e->quota); ++ spin_unlock_bh(&e->lock); ++ return ret; ++} ++ ++static int quota_proc_write(struct file *file, const char __user *input, ++ unsigned long size, void *data) ++{ ++ struct xt_quota_counter *e = data; ++ char buf[sizeof("18446744073709551616")]; ++ ++ if (size > sizeof(buf)) ++ size = sizeof(buf); ++ if (copy_from_user(buf, input, size) != 0) ++ return -EFAULT; ++ buf[sizeof(buf)-1] = '\0'; ++ ++ spin_lock_bh(&e->lock); ++ e->quota = simple_strtoull(buf, NULL, 0); ++ spin_unlock_bh(&e->lock); ++ return size; ++} ++ ++static struct xt_quota_counter * ++q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) ++{ ++ struct xt_quota_counter *e; ++ unsigned int size; ++ ++ /* Do not need all the procfs things for anonymous counters. */ ++ size = anon ? offsetof(typeof(*e), list) : sizeof(*e); ++ e = kmalloc(size, GFP_KERNEL); ++ if (e == NULL) ++ return NULL; ++ ++ e->quota = q->quota; ++ spin_lock_init(&e->lock); ++ if (!anon) { ++ INIT_LIST_HEAD(&e->list); ++ atomic_set(&e->ref, 1); ++ strlcpy(e->name, q->name, sizeof(e->name)); ++ } ++ return e; ++} ++ ++/** ++ * q2_get_counter - get ref to counter or create new ++ * @name: name of counter ++ */ ++static struct xt_quota_counter * ++q2_get_counter(const struct xt_quota_mtinfo2 *q) ++{ ++ struct proc_dir_entry *p; ++ struct xt_quota_counter *e = NULL; ++ struct xt_quota_counter *new_e; ++ ++ if (*q->name == '\0') ++ return q2_new_counter(q, true); ++ ++ /* No need to hold a lock while getting a new counter */ ++ new_e = q2_new_counter(q, false); ++ if (new_e == NULL) ++ goto out; ++ ++ spin_lock_bh(&counter_list_lock); ++ list_for_each_entry(e, &counter_list, list) ++ if (strcmp(e->name, q->name) == 0) { ++ atomic_inc(&e->ref); ++ spin_unlock_bh(&counter_list_lock); ++ kfree(new_e); ++ pr_debug("xt_quota2: old counter name=%s", e->name); ++ return e; ++ } ++ e = new_e; ++ pr_debug("xt_quota2: new_counter name=%s", e->name); ++ list_add_tail(&e->list, &counter_list); ++ /* The entry having a refcount of 1 is not directly destructible. ++ * This func has not yet returned the new entry, thus iptables ++ * has not references for destroying this entry. ++ * For another rule to try to destroy it, it would 1st need for this ++ * func* to be re-invoked, acquire a new ref for the same named quota. ++ * Nobody will access the e->procfs_entry either. ++ * So release the lock. */ ++ spin_unlock_bh(&counter_list_lock); ++ ++ /* create_proc_entry() is not spin_lock happy */ ++ p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, ++ proc_xt_quota); ++ ++ if (IS_ERR_OR_NULL(p)) { ++ spin_lock_bh(&counter_list_lock); ++ list_del(&e->list); ++ spin_unlock_bh(&counter_list_lock); ++ goto out; ++ } ++ p->data = e; ++ p->read_proc = quota_proc_read; ++ p->write_proc = quota_proc_write; ++ p->uid = quota_list_uid; ++ p->gid = quota_list_gid; ++ return e; ++ ++ out: ++ kfree(e); ++ return NULL; ++} ++ ++static int quota_mt2_check(const struct xt_mtchk_param *par) ++{ ++ struct xt_quota_mtinfo2 *q = par->matchinfo; ++ ++ pr_debug("xt_quota2: check() flags=0x%04x", q->flags); ++ ++ if (q->flags & ~XT_QUOTA_MASK) ++ return -EINVAL; ++ ++ q->name[sizeof(q->name)-1] = '\0'; ++ if (*q->name == '.' || strchr(q->name, '/') != NULL) { ++ printk(KERN_ERR "xt_quota.3: illegal name\n"); ++ return -EINVAL; ++ } ++ ++ q->master = q2_get_counter(q); ++ if (q->master == NULL) { ++ printk(KERN_ERR "xt_quota.3: memory alloc failure\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void quota_mt2_destroy(const struct xt_mtdtor_param *par) ++{ ++ struct xt_quota_mtinfo2 *q = par->matchinfo; ++ struct xt_quota_counter *e = q->master; ++ ++ if (*q->name == '\0') { ++ kfree(e); ++ return; ++ } ++ ++ spin_lock_bh(&counter_list_lock); ++ if (!atomic_dec_and_test(&e->ref)) { ++ spin_unlock_bh(&counter_list_lock); ++ return; ++ } ++ ++ list_del(&e->list); ++ remove_proc_entry(e->name, proc_xt_quota); ++ spin_unlock_bh(&counter_list_lock); ++ kfree(e); ++} ++ ++static bool ++quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ struct xt_quota_mtinfo2 *q = (void *)par->matchinfo; ++ struct xt_quota_counter *e = q->master; ++ bool ret = q->flags & XT_QUOTA_INVERT; ++ ++ spin_lock_bh(&e->lock); ++ if (q->flags & XT_QUOTA_GROW) { ++ /* ++ * While no_change is pointless in "grow" mode, we will ++ * implement it here simply to have a consistent behavior. ++ */ ++ if (!(q->flags & XT_QUOTA_NO_CHANGE)) { ++ e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; ++ } ++ ret = true; ++ } else { ++ if (e->quota >= skb->len) { ++ if (!(q->flags & XT_QUOTA_NO_CHANGE)) ++ e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; ++ ret = !ret; ++ } else { ++ /* We are transitioning, log that fact. */ ++ if (e->quota) { ++ quota2_log(par->hooknum, ++ skb, ++ par->in, ++ par->out, ++ q->name); ++ } ++ /* we do not allow even small packets from now on */ ++ e->quota = 0; ++ } ++ } ++ spin_unlock_bh(&e->lock); ++ return ret; ++} ++ ++static struct xt_match quota_mt2_reg[] __read_mostly = { ++ { ++ .name = "quota2", ++ .revision = 3, ++ .family = NFPROTO_IPV4, ++ .checkentry = quota_mt2_check, ++ .match = quota_mt2, ++ .destroy = quota_mt2_destroy, ++ .matchsize = sizeof(struct xt_quota_mtinfo2), ++ .me = THIS_MODULE, ++ }, ++ { ++ .name = "quota2", ++ .revision = 3, ++ .family = NFPROTO_IPV6, ++ .checkentry = quota_mt2_check, ++ .match = quota_mt2, ++ .destroy = quota_mt2_destroy, ++ .matchsize = sizeof(struct xt_quota_mtinfo2), ++ .me = THIS_MODULE, ++ }, ++}; ++ ++static int __init quota_mt2_init(void) ++{ ++ int ret; ++ pr_debug("xt_quota2: init()"); ++ ++#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG ++ nflognl = netlink_kernel_create(&init_net, ++ NETLINK_NFLOG, 1, NULL, ++ NULL, THIS_MODULE); ++ if (!nflognl) ++ return -ENOMEM; ++#endif ++ ++ proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net); ++ if (proc_xt_quota == NULL) ++ return -EACCES; ++ ++ ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); ++ if (ret < 0) ++ remove_proc_entry("xt_quota", init_net.proc_net); ++ pr_debug("xt_quota2: init() %d", ret); ++ return ret; ++} ++ ++static void __exit quota_mt2_exit(void) ++{ ++ xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); ++ remove_proc_entry("xt_quota", init_net.proc_net); ++} ++ ++module_init(quota_mt2_init); ++module_exit(quota_mt2_exit); ++MODULE_DESCRIPTION("Xtables: countdown quota match; up counter"); ++MODULE_AUTHOR("Sam Johnston "); ++MODULE_AUTHOR("Jan Engelhardt "); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_quota2"); ++MODULE_ALIAS("ip6t_quota2"); +diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c +index 72bb07f5..1e48fcf2 100644 +--- a/net/netfilter/xt_socket.c ++++ b/net/netfilter/xt_socket.c +@@ -35,7 +35,7 @@ + #include + #endif + +-static void ++void + xt_socket_put_sk(struct sock *sk) + { + if (sk->sk_state == TCP_TIME_WAIT) +@@ -43,6 +43,7 @@ xt_socket_put_sk(struct sock *sk) + else + sock_put(sk); + } ++EXPORT_SYMBOL(xt_socket_put_sk); + + static int + extract_icmp4_fields(const struct sk_buff *skb, +@@ -101,9 +102,8 @@ extract_icmp4_fields(const struct sk_buff *skb, + return 0; + } + +-static bool +-socket_match(const struct sk_buff *skb, struct xt_action_param *par, +- const struct xt_socket_mtinfo1 *info) ++struct sock* ++xt_socket_get4_sk(const struct sk_buff *skb, struct xt_action_param *par) + { + const struct iphdr *iph = ip_hdr(skb); + struct udphdr _hdr, *hp = NULL; +@@ -120,7 +120,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, + hp = skb_header_pointer(skb, ip_hdrlen(skb), + sizeof(_hdr), &_hdr); + if (hp == NULL) +- return false; ++ return NULL; + + protocol = iph->protocol; + saddr = iph->saddr; +@@ -131,9 +131,9 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, + } else if (iph->protocol == IPPROTO_ICMP) { + if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr, + &sport, &dport)) +- return false; ++ return NULL; + } else { +- return false; ++ return NULL; + } + + #ifdef XT_SOCKET_HAVE_CONNTRACK +@@ -157,6 +157,23 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, + + sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, + saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); ++ ++ pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n", ++ protocol, &saddr, ntohs(sport), ++ &daddr, ntohs(dport), ++ &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); ++ ++ return sk; ++} ++EXPORT_SYMBOL(xt_socket_get4_sk); ++ ++static bool ++socket_match(const struct sk_buff *skb, struct xt_action_param *par, ++ const struct xt_socket_mtinfo1 *info) ++{ ++ struct sock *sk; ++ ++ sk = xt_socket_get4_sk(skb, par); + if (sk != NULL) { + bool wildcard; + bool transparent = true; +@@ -179,11 +196,6 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, + sk = NULL; + } + +- pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n", +- protocol, &saddr, ntohs(sport), +- &daddr, ntohs(dport), +- &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); +- + return (sk != NULL); + } + +@@ -255,8 +267,8 @@ extract_icmp6_fields(const struct sk_buff *skb, + return 0; + } + +-static bool +-socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) ++struct sock* ++xt_socket_get6_sk(const struct sk_buff *skb, struct xt_action_param *par) + { + struct ipv6hdr *iph = ipv6_hdr(skb); + struct udphdr _hdr, *hp = NULL; +@@ -264,7 +276,6 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) + struct in6_addr *daddr, *saddr; + __be16 dport, sport; + int thoff, tproto; +- const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; + + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); + if (tproto < 0) { +@@ -276,7 +287,7 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) + hp = skb_header_pointer(skb, thoff, + sizeof(_hdr), &_hdr); + if (hp == NULL) +- return false; ++ return NULL; + + saddr = &iph->saddr; + sport = hp->source; +@@ -286,13 +297,30 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) + } else if (tproto == IPPROTO_ICMPV6) { + if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr, + &sport, &dport)) +- return false; ++ return NULL; + } else { +- return false; ++ return NULL; + } + + sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, + saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); ++ pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu " ++ "(orig %pI6:%hu) sock %p\n", ++ tproto, saddr, ntohs(sport), ++ daddr, ntohs(dport), ++ &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); ++ return sk; ++} ++EXPORT_SYMBOL(xt_socket_get6_sk); ++ ++static bool ++socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ struct sock *sk; ++ const struct xt_socket_mtinfo1 *info; ++ ++ info = (struct xt_socket_mtinfo1 *) par->matchinfo; ++ sk = xt_socket_get6_sk(skb, par); + if (sk != NULL) { + bool wildcard; + bool transparent = true; +@@ -315,12 +343,6 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) + sk = NULL; + } + +- pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu " +- "(orig %pI6:%hu) sock %p\n", +- tproto, saddr, ntohs(sport), +- daddr, ntohs(dport), +- &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); +- + return (sk != NULL); + } + #endif +diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig +index 78efe895..8e12c8a2 100644 +--- a/net/rfkill/Kconfig ++++ b/net/rfkill/Kconfig +@@ -10,6 +10,11 @@ menuconfig RFKILL + To compile this driver as a module, choose M here: the + module will be called rfkill. + ++config RFKILL_PM ++ bool "Power off on suspend" ++ depends on RFKILL && PM ++ default y ++ + # LED trigger support + config RFKILL_LEDS + bool +diff --git a/net/rfkill/core.c b/net/rfkill/core.c +index f9749617..04afd89a 100644 +--- a/net/rfkill/core.c ++++ b/net/rfkill/core.c +@@ -770,6 +770,7 @@ void rfkill_pause_polling(struct rfkill *rfkill) + } + EXPORT_SYMBOL(rfkill_pause_polling); + ++#ifdef CONFIG_RFKILL_PM + void rfkill_resume_polling(struct rfkill *rfkill) + { + BUG_ON(!rfkill); +@@ -804,14 +805,17 @@ static int rfkill_resume(struct device *dev) + + return 0; + } ++#endif + + static struct class rfkill_class = { + .name = "rfkill", + .dev_release = rfkill_release, + .dev_attrs = rfkill_dev_attrs, + .dev_uevent = rfkill_dev_uevent, ++#ifdef CONFIG_RFKILL_PM + .suspend = rfkill_suspend, + .resume = rfkill_resume, ++#endif + }; + + bool rfkill_blocked(struct rfkill *rfkill) +diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig +index 2e4444fe..8db24da2 100644 +--- a/net/wireless/Kconfig ++++ b/net/wireless/Kconfig +@@ -160,3 +160,14 @@ config LIB80211_DEBUG + from lib80211. + + If unsure, say N. ++ ++config CFG80211_ALLOW_RECONNECT ++ bool "Allow reconnect while already connected" ++ depends on CFG80211 ++ default n ++ help ++ cfg80211 stack doesn't allow to connect if you are already ++ connected. This option allows to make a connection in this case. ++ ++ Select this option ONLY for wlan drivers that are specifically ++ built for such purposes. +diff --git a/net/wireless/scan.c b/net/wireless/scan.c +index 70faadf1..e40104f5 100644 +--- a/net/wireless/scan.c ++++ b/net/wireless/scan.c +@@ -18,7 +18,7 @@ + #include "nl80211.h" + #include "wext-compat.h" + +-#define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) ++#define IEEE80211_SCAN_RESULT_EXPIRE (3 * HZ) + + void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) + { +diff --git a/net/wireless/sme.c b/net/wireless/sme.c +index f7e937ff..bbbed736 100644 +--- a/net/wireless/sme.c ++++ b/net/wireless/sme.c +@@ -689,8 +689,10 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) + return; + ++#ifndef CONFIG_CFG80211_ALLOW_RECONNECT + if (wdev->sme_state != CFG80211_SME_CONNECTED) + return; ++#endif + + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); +@@ -767,10 +769,14 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, + + ASSERT_WDEV_LOCK(wdev); + ++#ifndef CONFIG_CFG80211_ALLOW_RECONNECT + if (wdev->sme_state != CFG80211_SME_IDLE) + return -EALREADY; + + if (WARN_ON(wdev->connect_keys)) { ++#else ++ if (wdev->connect_keys) { ++#endif + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + } +diff --git a/samples/uhid/Makefile b/samples/uhid/Makefile +new file mode 100644 +index 00000000..c95a6965 +--- /dev/null ++++ b/samples/uhid/Makefile +@@ -0,0 +1,10 @@ ++# kbuild trick to avoid linker error. Can be omitted if a module is built. ++obj- := dummy.o ++ ++# List of programs to build ++hostprogs-y := uhid-example ++ ++# Tell kbuild to always build the programs ++always := $(hostprogs-y) ++ ++HOSTCFLAGS_uhid-example.o += -I$(objtree)/usr/include +diff --git a/samples/uhid/uhid-example.c b/samples/uhid/uhid-example.c +new file mode 100644 +index 00000000..03ce3c05 +--- /dev/null ++++ b/samples/uhid/uhid-example.c +@@ -0,0 +1,381 @@ ++/* ++ * UHID Example ++ * ++ * Copyright (c) 2012 David Herrmann ++ * ++ * The code may be used by anyone for any purpose, ++ * and can serve as a starting point for developing ++ * applications using uhid. ++ */ ++ ++/* UHID Example ++ * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this ++ * program as root and then use the following keys to control the mouse: ++ * q: Quit the application ++ * 1: Toggle left button (down, up, ...) ++ * 2: Toggle right button ++ * 3: Toggle middle button ++ * a: Move mouse left ++ * d: Move mouse right ++ * w: Move mouse up ++ * s: Move mouse down ++ * r: Move wheel up ++ * f: Move wheel down ++ * ++ * If uhid is not available as /dev/uhid, then you can pass a different path as ++ * first argument. ++ * If is not installed in /usr, then compile this with: ++ * gcc -o ./uhid_test -Wall -I./include ./samples/uhid/uhid-example.c ++ * And ignore the warning about kernel headers. However, it is recommended to ++ * use the installed uhid.h if available. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* HID Report Desciptor ++ * We emulate a basic 3 button mouse with wheel. This is the report-descriptor ++ * as the kernel will parse it: ++ * ++ * INPUT[INPUT] ++ * Field(0) ++ * Physical(GenericDesktop.Pointer) ++ * Application(GenericDesktop.Mouse) ++ * Usage(3) ++ * Button.0001 ++ * Button.0002 ++ * Button.0003 ++ * Logical Minimum(0) ++ * Logical Maximum(1) ++ * Report Size(1) ++ * Report Count(3) ++ * Report Offset(0) ++ * Flags( Variable Absolute ) ++ * Field(1) ++ * Physical(GenericDesktop.Pointer) ++ * Application(GenericDesktop.Mouse) ++ * Usage(3) ++ * GenericDesktop.X ++ * GenericDesktop.Y ++ * GenericDesktop.Wheel ++ * Logical Minimum(-128) ++ * Logical Maximum(127) ++ * Report Size(8) ++ * Report Count(3) ++ * Report Offset(8) ++ * Flags( Variable Relative ) ++ * ++ * This is the mapping that we expect: ++ * Button.0001 ---> Key.LeftBtn ++ * Button.0002 ---> Key.RightBtn ++ * Button.0003 ---> Key.MiddleBtn ++ * GenericDesktop.X ---> Relative.X ++ * GenericDesktop.Y ---> Relative.Y ++ * GenericDesktop.Wheel ---> Relative.Wheel ++ * ++ * This information can be verified by reading /sys/kernel/debug/hid//rdesc ++ * This file should print the same information as showed above. ++ */ ++ ++static unsigned char rdesc[] = { ++ 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, ++ 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, ++ 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, ++ 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, ++ 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, ++ 0x15, 0x80, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, ++ 0x81, 0x06, 0xc0, 0xc0, ++}; ++ ++static int uhid_write(int fd, const struct uhid_event *ev) ++{ ++ ssize_t ret; ++ ++ ret = write(fd, ev, sizeof(*ev)); ++ if (ret < 0) { ++ fprintf(stderr, "Cannot write to uhid: %m\n"); ++ return -errno; ++ } else if (ret != sizeof(*ev)) { ++ fprintf(stderr, "Wrong size written to uhid: %ld != %lu\n", ++ ret, sizeof(ev)); ++ return -EFAULT; ++ } else { ++ return 0; ++ } ++} ++ ++static int create(int fd) ++{ ++ struct uhid_event ev; ++ ++ memset(&ev, 0, sizeof(ev)); ++ ev.type = UHID_CREATE; ++ strcpy((char*)ev.u.create.name, "test-uhid-device"); ++ ev.u.create.rd_data = rdesc; ++ ev.u.create.rd_size = sizeof(rdesc); ++ ev.u.create.bus = BUS_USB; ++ ev.u.create.vendor = 0x15d9; ++ ev.u.create.product = 0x0a37; ++ ev.u.create.version = 0; ++ ev.u.create.country = 0; ++ ++ return uhid_write(fd, &ev); ++} ++ ++static void destroy(int fd) ++{ ++ struct uhid_event ev; ++ ++ memset(&ev, 0, sizeof(ev)); ++ ev.type = UHID_DESTROY; ++ ++ uhid_write(fd, &ev); ++} ++ ++static int event(int fd) ++{ ++ struct uhid_event ev; ++ ssize_t ret; ++ ++ memset(&ev, 0, sizeof(ev)); ++ ret = read(fd, &ev, sizeof(ev)); ++ if (ret == 0) { ++ fprintf(stderr, "Read HUP on uhid-cdev\n"); ++ return -EFAULT; ++ } else if (ret < 0) { ++ fprintf(stderr, "Cannot read uhid-cdev: %m\n"); ++ return -errno; ++ } else if (ret != sizeof(ev)) { ++ fprintf(stderr, "Invalid size read from uhid-dev: %ld != %lu\n", ++ ret, sizeof(ev)); ++ return -EFAULT; ++ } ++ ++ switch (ev.type) { ++ case UHID_START: ++ fprintf(stderr, "UHID_START from uhid-dev\n"); ++ break; ++ case UHID_STOP: ++ fprintf(stderr, "UHID_STOP from uhid-dev\n"); ++ break; ++ case UHID_OPEN: ++ fprintf(stderr, "UHID_OPEN from uhid-dev\n"); ++ break; ++ case UHID_CLOSE: ++ fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); ++ break; ++ case UHID_OUTPUT: ++ fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); ++ break; ++ case UHID_OUTPUT_EV: ++ fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); ++ break; ++ default: ++ fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); ++ } ++ ++ return 0; ++} ++ ++static bool btn1_down; ++static bool btn2_down; ++static bool btn3_down; ++static signed char abs_hor; ++static signed char abs_ver; ++static signed char wheel; ++ ++static int send_event(int fd) ++{ ++ struct uhid_event ev; ++ ++ memset(&ev, 0, sizeof(ev)); ++ ev.type = UHID_INPUT; ++ ev.u.input.size = 4; ++ ++ if (btn1_down) ++ ev.u.input.data[0] |= 0x1; ++ if (btn2_down) ++ ev.u.input.data[0] |= 0x2; ++ if (btn3_down) ++ ev.u.input.data[0] |= 0x4; ++ ++ ev.u.input.data[1] = abs_hor; ++ ev.u.input.data[2] = abs_ver; ++ ev.u.input.data[3] = wheel; ++ ++ return uhid_write(fd, &ev); ++} ++ ++static int keyboard(int fd) ++{ ++ char buf[128]; ++ ssize_t ret, i; ++ ++ ret = read(STDIN_FILENO, buf, sizeof(buf)); ++ if (ret == 0) { ++ fprintf(stderr, "Read HUP on stdin\n"); ++ return -EFAULT; ++ } else if (ret < 0) { ++ fprintf(stderr, "Cannot read stdin: %m\n"); ++ return -errno; ++ } ++ ++ for (i = 0; i < ret; ++i) { ++ switch (buf[i]) { ++ case '1': ++ btn1_down = !btn1_down; ++ ret = send_event(fd); ++ if (ret) ++ return ret; ++ break; ++ case '2': ++ btn2_down = !btn2_down; ++ ret = send_event(fd); ++ if (ret) ++ return ret; ++ break; ++ case '3': ++ btn3_down = !btn3_down; ++ ret = send_event(fd); ++ if (ret) ++ return ret; ++ break; ++ case 'a': ++ abs_hor = -20; ++ ret = send_event(fd); ++ abs_hor = 0; ++ if (ret) ++ return ret; ++ break; ++ case 'd': ++ abs_hor = 20; ++ ret = send_event(fd); ++ abs_hor = 0; ++ if (ret) ++ return ret; ++ break; ++ case 'w': ++ abs_ver = -20; ++ ret = send_event(fd); ++ abs_ver = 0; ++ if (ret) ++ return ret; ++ break; ++ case 's': ++ abs_ver = 20; ++ ret = send_event(fd); ++ abs_ver = 0; ++ if (ret) ++ return ret; ++ break; ++ case 'r': ++ wheel = 1; ++ ret = send_event(fd); ++ wheel = 0; ++ if (ret) ++ return ret; ++ break; ++ case 'f': ++ wheel = -1; ++ ret = send_event(fd); ++ wheel = 0; ++ if (ret) ++ return ret; ++ break; ++ case 'q': ++ return -ECANCELED; ++ default: ++ fprintf(stderr, "Invalid input: %c\n", buf[i]); ++ } ++ } ++ ++ return 0; ++} ++ ++int main(int argc, char **argv) ++{ ++ int fd; ++ const char *path = "/dev/uhid"; ++ struct pollfd pfds[2]; ++ int ret; ++ struct termios state; ++ ++ ret = tcgetattr(STDIN_FILENO, &state); ++ if (ret) { ++ fprintf(stderr, "Cannot get tty state\n"); ++ } else { ++ state.c_lflag &= ~ICANON; ++ state.c_cc[VMIN] = 1; ++ ret = tcsetattr(STDIN_FILENO, TCSANOW, &state); ++ if (ret) ++ fprintf(stderr, "Cannot set tty state\n"); ++ } ++ ++ if (argc >= 2) { ++ if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { ++ fprintf(stderr, "Usage: %s [%s]\n", argv[0], path); ++ return EXIT_SUCCESS; ++ } else { ++ path = argv[1]; ++ } ++ } ++ ++ fprintf(stderr, "Open uhid-cdev %s\n", path); ++ fd = open(path, O_RDWR | O_CLOEXEC); ++ if (fd < 0) { ++ fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path); ++ return EXIT_FAILURE; ++ } ++ ++ fprintf(stderr, "Create uhid device\n"); ++ ret = create(fd); ++ if (ret) { ++ close(fd); ++ return EXIT_FAILURE; ++ } ++ ++ pfds[0].fd = STDIN_FILENO; ++ pfds[0].events = POLLIN; ++ pfds[1].fd = fd; ++ pfds[1].events = POLLIN; ++ ++ fprintf(stderr, "Press 'q' to quit...\n"); ++ while (1) { ++ ret = poll(pfds, 2, -1); ++ if (ret < 0) { ++ fprintf(stderr, "Cannot poll for fds: %m\n"); ++ break; ++ } ++ if (pfds[0].revents & POLLHUP) { ++ fprintf(stderr, "Received HUP on stdin\n"); ++ break; ++ } ++ if (pfds[1].revents & POLLHUP) { ++ fprintf(stderr, "Received HUP on uhid-cdev\n"); ++ break; ++ } ++ ++ if (pfds[0].revents & POLLIN) { ++ ret = keyboard(fd); ++ if (ret) ++ break; ++ } ++ if (pfds[1].revents & POLLIN) { ++ ret = event(fd); ++ if (ret) ++ break; ++ } ++ } ++ ++ fprintf(stderr, "Destroy uhid device\n"); ++ destroy(fd); ++ return EXIT_SUCCESS; ++} +diff --git a/security/capability.c b/security/capability.c +index 5bb21b1c..fc991bca 100644 +--- a/security/capability.c ++++ b/security/capability.c +@@ -12,6 +12,26 @@ + + #include + ++static int cap_binder_set_context_mgr(struct task_struct *mgr) ++{ ++ return 0; ++} ++ ++static int cap_binder_transaction(struct task_struct *from, struct task_struct *to) ++{ ++ return 0; ++} ++ ++static int cap_binder_transfer_binder(struct task_struct *from, struct task_struct *to) ++{ ++ return 0; ++} ++ ++static int cap_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) ++{ ++ return 0; ++} ++ + static int cap_syslog(int type) + { + return 0; +@@ -877,6 +897,10 @@ static void cap_audit_rule_free(void *lsmrule) + + void __init security_fixup_ops(struct security_operations *ops) + { ++ set_to_cap_if_null(ops, binder_set_context_mgr); ++ set_to_cap_if_null(ops, binder_transaction); ++ set_to_cap_if_null(ops, binder_transfer_binder); ++ set_to_cap_if_null(ops, binder_transfer_file); + set_to_cap_if_null(ops, ptrace_access_check); + set_to_cap_if_null(ops, ptrace_traceme); + set_to_cap_if_null(ops, capget); +diff --git a/security/commoncap.c b/security/commoncap.c +index 71a166a0..0051ac2d 100644 +--- a/security/commoncap.c ++++ b/security/commoncap.c +@@ -31,6 +31,10 @@ + #include + #include + ++#ifdef CONFIG_ANDROID_PARANOID_NETWORK ++#include ++#endif ++ + /* + * If a non-root user executes a setuid-root binary in + * !secure(SECURE_NOROOT) mode, then we raise capabilities. +@@ -76,6 +80,13 @@ int cap_netlink_send(struct sock *sk, struct sk_buff *skb) + int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, + int cap, int audit) + { ++#ifdef CONFIG_ANDROID_PARANOID_NETWORK ++ if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) ++ return 0; ++ if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) ++ return 0; ++#endif ++ + for (;;) { + /* The creator of the user namespace has all caps. */ + if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) +diff --git a/security/security.c b/security/security.c +index bf619ffc..cecd55e5 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -130,6 +130,26 @@ int __init register_security(struct security_operations *ops) + + /* Security operations */ + ++int security_binder_set_context_mgr(struct task_struct *mgr) ++{ ++ return security_ops->binder_set_context_mgr(mgr); ++} ++ ++int security_binder_transaction(struct task_struct *from, struct task_struct *to) ++{ ++ return security_ops->binder_transaction(from, to); ++} ++ ++int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to) ++{ ++ return security_ops->binder_transfer_binder(from, to); ++} ++ ++int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) ++{ ++ return security_ops->binder_transfer_file(from, to, file); ++} ++ + int security_ptrace_access_check(struct task_struct *child, unsigned int mode) + { + return security_ops->ptrace_access_check(child, mode); +diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c +index 56262223..1ae096ed 100644 +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -1818,6 +1818,67 @@ static inline u32 open_file_to_av(struct file *file) + + /* Hook functions begin here. */ + ++static int selinux_binder_set_context_mgr(struct task_struct *mgr) ++{ ++ u32 mysid = current_sid(); ++ u32 mgrsid = task_sid(mgr); ++ ++ return avc_has_perm(mysid, mgrsid, SECCLASS_BINDER, BINDER__SET_CONTEXT_MGR, NULL); ++} ++ ++static int selinux_binder_transaction(struct task_struct *from, struct task_struct *to) ++{ ++ u32 mysid = current_sid(); ++ u32 fromsid = task_sid(from); ++ u32 tosid = task_sid(to); ++ int rc; ++ ++ if (mysid != fromsid) { ++ rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER, BINDER__IMPERSONATE, NULL); ++ if (rc) ++ return rc; ++ } ++ ++ return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__CALL, NULL); ++} ++ ++static int selinux_binder_transfer_binder(struct task_struct *from, struct task_struct *to) ++{ ++ u32 fromsid = task_sid(from); ++ u32 tosid = task_sid(to); ++ return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER, NULL); ++} ++ ++static int selinux_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) ++{ ++ u32 sid = task_sid(to); ++ struct file_security_struct *fsec = file->f_security; ++ struct inode *inode = file->f_path.dentry->d_inode; ++ struct inode_security_struct *isec = inode->i_security; ++ struct common_audit_data ad; ++ struct selinux_audit_data sad = {0,}; ++ int rc; ++ ++ COMMON_AUDIT_DATA_INIT(&ad, PATH); ++ ad.u.path = file->f_path; ++ ad.selinux_audit_data = &sad; ++ ++ if (sid != fsec->sid) { ++ rc = avc_has_perm(sid, fsec->sid, ++ SECCLASS_FD, ++ FD__USE, ++ &ad); ++ if (rc) ++ return rc; ++ } ++ ++ if (unlikely(IS_PRIVATE(inode))) ++ return 0; ++ ++ return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file), ++ &ad); ++} ++ + static int selinux_ptrace_access_check(struct task_struct *child, + unsigned int mode) + { +@@ -5523,6 +5584,11 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) + static struct security_operations selinux_ops = { + .name = "selinux", + ++ .binder_set_context_mgr = selinux_binder_set_context_mgr, ++ .binder_transaction = selinux_binder_transaction, ++ .binder_transfer_binder = selinux_binder_transfer_binder, ++ .binder_transfer_file = selinux_binder_transfer_file, ++ + .ptrace_access_check = selinux_ptrace_access_check, + .ptrace_traceme = selinux_ptrace_traceme, + .capget = selinux_capget, +diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h +index b8c53723..4a4a9aeb 100644 +--- a/security/selinux/include/classmap.h ++++ b/security/selinux/include/classmap.h +@@ -149,5 +149,6 @@ struct security_class_mapping secclass_map[] = { + { "kernel_service", { "use_as_override", "create_files_as", NULL } }, + { "tun_socket", + { COMMON_SOCK_PERMS, NULL } }, ++ { "binder", { "impersonate", "call", "set_context_mgr", "transfer", NULL } }, + { NULL } + }; +diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig +index 885683a3..46caf837 100644 +--- a/sound/arm/Kconfig ++++ b/sound/arm/Kconfig +@@ -11,6 +11,45 @@ menuconfig SND_ARM + + if SND_ARM + ++config SND_AK_PCM ++ tristate "Anyka ADC/DAC sound driver" ++ select SND_PCM ++ help ++ ALSA sound driver fo the AK ADC/DAC. ++ ++choice ++ prompt "Select codec" ++ depends on SND_AK_PCM ++ ++config CODEC_AK39 ++ bool "AK39 internal codec" ++ depends on SND_AK_PCM ++ help ++ ak39 SoC audio codec. ++ ++endchoice ++ ++choice ++ prompt "HP/Speaker switch mode" ++ depends on SND_AK_PCM ++ ++config SPKHP_SWITCH_AUTO ++ bool "auto switch in audio driver(default, for both linux and android)" ++ ++config SPKHP_SWITCH_MIXER ++ bool "notify,but not switch in audio driver(with ALSA lib amixer)" ++ ++config SPKHP_SWITCH_UEVENT ++ bool "detect HP by UEVENT(only for android)" ++ ++endchoice ++ ++config SUPPORT_AEC ++ bool "Support Accoustic Echo Cancllation" ++ depends on SND_AK_PCM ++ help ++ Support Accoustic Echo Cancllation. ++ + config SND_ARMAACI + tristate "ARM PrimeCell PL041 AC Link support" + depends on ARM_AMBA +diff --git a/sound/arm/Makefile b/sound/arm/Makefile +index 8c0c851d..cb258247 100644 +--- a/sound/arm/Makefile ++++ b/sound/arm/Makefile +@@ -14,3 +14,9 @@ snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o + + obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o + snd-pxa2xx-ac97-objs := pxa2xx-ac97.o ++ ++obj-$(CONFIG_SND_AK_PCM) += snd-akpcm.o ++snd-akpcm-objs := ak_pcm.o ++ ++obj-$(CONFIG_CODEC_AK39) += snd-codec-ak39.o ++snd-codec-ak39-objs := ak39_codec.o +diff --git a/sound/arm/ak39_codec.c b/sound/arm/ak39_codec.c +new file mode 100644 +index 00000000..cea436db +--- /dev/null ++++ b/sound/arm/ak39_codec.c +@@ -0,0 +1,2219 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++//#define SND_CODEC_DBG ++ ++#ifdef SND_CODEC_DBG ++#ifdef __KERNEL__ ++#define PDEBUG(fmt, args...) printk(KERN_INFO "ak39 codec:" fmt, ##args) ++#else ++#define PDEBUG(fmt, args...) fprintf(stderr, "%s %d:" fmt,__FILE__, __LINE__, ## args) ++#endif ++#else ++#define PDEBUG(fmt, args...) ++#endif ++ ++ ++enum mixer_vol_port { ++ MIXER_PORT_HP_VOL = 0, ++ MIXER_PORT_LINEIN_VOL, ++ MIXER_PORT_MIC_VOL, ++ MIXER_PORT_VOL_COUNT, ++}; ++ ++ ++enum mixer_dst_port { ++ MIXER_ADDR_DST_HP = 0, ++ MIXER_ADDR_DST_ADC2, ++ MIXER_DST_COUNT, ++}; ++ ++enum mixer_detect_port { ++ MIXER_ADDR_HPDET = 0, ++ MIXER_ADDR_MICDET, ++ MIXER_ADDR_LINEINDET, ++ MIXER_SWITCH_COUNT, ++}; ++ ++ ++enum mixer_outmode { ++ MIXER_ADDR_OUTMODE_DAC = 0, ++ MIXER_OUTMODE_COUNT, ++}; ++ ++enum mixer_chnl_duration { ++ MIXER_ADDR_PLAY_DURATION = 0, ++ MIXER_DURATION_COUNT, ++}; ++ ++#define MAX_DACCLK 24000000 ++ ++#define HEADPHONE_DEFAULT_VOL (HEADPHONE_GAIN_MAX - 2) ++#define LINEIN_DEFAULT_VOL 10 ++#define MIC_DEFAULT_VOL 6 ++ ++#define PLAYMODE_STATUS_HP (1) ++#define PLAYMODE_STATUS_SPEAKER (2) ++ ++#define OUTMODE_AUTO (0) ++#define OUTMODE_HP (1) ++#define OUTMODE_LINEOUT (2) ++#define OUTMODE_MIN OUTMODE_AUTO ++#define OUTMODE_MAX OUTMODE_HP ++ ++#define CHNLDURATION_CONSTANT 0 ++#define CHNLDURATION_EVEROPEN 1 ++#define CHNLDURATION_MIN CHNLDURATION_CONSTANT ++#define CHNLDURATION_MAX CHNLDURATION_EVEROPEN ++ ++struct ak39_codec { ++ struct ak_codec_dai dai; ++ void __iomem *analog_ctrl_base; ++ void __iomem *adda_cfg_base; ++ struct delayed_work d_work; ++ ++ int mixer_volume[MIXER_PORT_VOL_COUNT]; ++ int mixer_route[MIXER_DST_COUNT]; ++ int mixer_switch[MIXER_SWITCH_COUNT]; ++ int mixer_outmode[MIXER_OUTMODE_COUNT]; ++ int mixer_ch_duration[MIXER_DURATION_COUNT]; ++ ++ struct gpio_info hpdet_gpio; ++ struct gpio_info spkrshdn_gpio; ++ struct gpio_info hpmute_gpio; ++ ++ int hp_det_irq; ++ int irq_hp_on_type; ++ int hp_on_value; ++ int playmode; ++ int hpmute_en_val; ++ ++ unsigned used_hp_mute:1; // whether to use hardware de-pipa or not ++ unsigned outputing:1; ++ unsigned dac_state:1; ++ unsigned adc2_state:1; ++}; ++ ++struct ak39_volume_info ++{ ++ int vol_default; ++ int vol_min; ++ int vol_max; ++}; ++ ++ ++#define VOL_INFO_DEFAULT 0 ++#define VOL_INFO_MIN 1 ++#define VOL_INFO_MAX 2 ++ ++static struct ak39_volume_info vol_info[MIXER_PORT_VOL_COUNT] = { ++ [MIXER_PORT_HP_VOL] = { ++ .vol_default = HEADPHONE_DEFAULT_VOL, ++ .vol_min = HEADPHONE_GAIN_MIN, ++ .vol_max = HEADPHONE_GAIN_MAX, ++ }, ++ [MIXER_PORT_LINEIN_VOL] = { ++ .vol_default = LINEIN_DEFAULT_VOL, ++ .vol_min = LINEIN_GAIN_MIN, ++ .vol_max = LINEIN_GAIN_MAX, ++ }, ++ [MIXER_PORT_MIC_VOL] = { ++ .vol_default = MIC_DEFAULT_VOL, ++ .vol_min = MIC_GAIN_MIN, ++ .vol_max = MIC_GAIN_MAX, ++ } ++ ++}; ++ ++static int default_route[MIXER_DST_COUNT] = { ++ [MIXER_ADDR_DST_HP] = SOURCE_DAC, ++ [MIXER_ADDR_DST_ADC2] = SOURCE_MIC, ++}; ++ ++extern unsigned long playback_statu; ++ ++#if 0 ++static void dump_codec_reg(struct ak39_codec *codec) ++{ ++ u32 reg_val; ++ ++ reg_val = REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG); ++ printk("\nCLOCK_CTRL_REG(0x0800%04x):%08x.\n", CLOCK_CTRL_REG, reg_val); ++ ++ reg_val = REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG); ++ printk("HIGHSPEED_CLOCK_CTRL_REG(0x0800%04x):%08x.\n", HIGHSPEED_CLOCK_CTRL_REG, reg_val); ++ ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ printk("\nANALOG_CTRL_REG1(0x0800%04x):%08x.\n", ANALOG_CTRL_REG1, reg_val); ++ ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); ++ printk("ANALOG_CTRL_REG2(0x0800%04x):%08x.\n", ANALOG_CTRL_REG2, reg_val); ++ ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG3); ++ printk("ANALOG_CTRL_REG3(0x0800%04x):%08x.\n", ANALOG_CTRL_REG3, reg_val); ++ ++ reg_val = REG32(codec->adda_cfg_base + ADC2_CONFIG_REG); ++ printk("ADC2_CONFIG_REG(0x2011%04x):%08x.\n", ADC2_CONFIG_REG, reg_val); ++ ++ reg_val = REG32(codec->adda_cfg_base + ADC2_DATA_REG); ++ printk("ADC2_DATA_REG(0x2011%04x):%08x.\n", ADC2_DATA_REG, reg_val); ++ ++ reg_val = REG32(codec->adda_cfg_base + DAC_CONFIG_REG); ++ printk("DAC_CONFIG_REG(0x2011%04x):%08x.\n", DAC_CONFIG_REG, reg_val); ++ ++ reg_val = REG32(codec->analog_ctrl_base + ADC1_CONF_REG1); ++ printk("ADC1_CONF_REG1:(0x0800%04x):%08x.\n", ADC1_CONF_REG1, reg_val); ++} ++#endif ++static inline struct ak39_codec *to_ak39_codec(struct ak_codec_dai *dai) ++{ ++ return dai ? container_of(dai, struct ak39_codec, dai): NULL; ++} ++ ++ ++static inline void pd_ref_enable(struct ak39_codec *codec) ++{ ++ u32 reg_val; ++ ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_val &= ~PD_REF; //power on codec ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++} ++ ++static inline void pd_ref_disable(struct ak39_codec *codec) ++{ ++ u32 reg_val; ++ ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_val |= PD_REF; //power off codec ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++} ++ ++#if 0 ++static void SetHpDisChgCur(unsigned long value) ++{ ++ int reg_val; ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_val &= ~(0x7 << 29); ++ reg_val |= (value << 29); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++} ++ ++ ++static void set_cur_pmos(struct ak39_codec *codec, unsigned long value) ++{ ++ int reg_val; ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_val &= ~(0xf << 25); ++ reg_val |= (value << 25); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++} ++#endif ++ ++static void set_cur_vcm2_dischg(struct ak39_codec *codec, unsigned long value) ++{ ++ int reg_val; ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_val &= ~(0x1f << 4); ++ reg_val |= (value << 4); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++} ++ ++static void set_cur_vcm2(struct ak39_codec *codec, unsigned long value) ++{ ++ int reg_val; ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); ++ reg_val &= ~(0x1F << 16); ++ reg_val |= (value << 16); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; ++} ++ ++ ++/** ++ * @brief select HP in signal ++ * @author ++ * @date ++ * @param[in] (HP_In_Signal)signal: signal desired ++ * @return void ++ */ ++void ak39_set_hp_in(struct ak39_codec *codec, unsigned long signal) ++{ ++ unsigned long reg_val; ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_val &= ~(0x7 << 12); ++ reg_val |= ((signal&0x7) << 12); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++} ++ ++ ++/** ++ * @brief set ADC2 source ++ * @author ++ * @date ++ * @param[in] signal: DAC|LINEIN|MIC ++ * @return void ++ */ ++void ak39_set_adc2_in(struct ak39_codec *codec, unsigned long signal) ++{ ++ unsigned long reg_val; ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); ++ reg_val &= ~(0x7 << 2); ++ reg_val |= ((signal&0x7) << 2); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; ++} ++ ++ ++/** ++ * @brief set HP gain ++ * @author ++ * @date ++ * @param[in] x: (0.x)times ++ * @return void ++ */ ++void ak39_set_hp_gain(struct ak39_codec *codec, unsigned long gain) ++{ ++ unsigned long reg_value; ++ unsigned long gain_table[6] = {0x0, 0x18, 0x14, 0x12, 0x11, 0x10}; ++ ++ reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_value &= ~MASK_HP_GAIN; ++ reg_value |= HP_GAIN(gain_table[gain]); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; ++} ++ ++/** ++ * @brief set mic gain ++ * @author ++ * @date ++ * @param[in] gain: 0~7 ++ * @return void ++ */ ++void ak39_set_mic_gain(struct ak39_codec *codec, unsigned long gain) ++{ ++ unsigned long reg_val = 0; ++ ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); ++ reg_val &= ~(0x7<<10); ++ reg_val |= ((gain&0x7)<<10); ++ reg_val |= (1<<15); //double Mic gain ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; ++} ++ ++/** ++ * @brief set linein gain ++ * @author ++ * @date ++ * @param[in] gain: 0~15 ++ * @return void ++ */ ++void ak39_set_linein_gain(struct ak39_codec *codec, unsigned long gain) ++{ ++ unsigned long reg_val = 0; ++ ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); ++ reg_val &= ~(0xF<<6); ++ reg_val |= ((gain&0xF)<<6); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; ++} ++ ++enum depipa_noise_ctrl { ++ DEPIPA_NOISE_NOT_USE, ++ DEPIPA_NOISE_USE_3KOHM_RESISTOR, ++ DEPIPA_NOISE_USE_1KOHM_RESISTOR, ++ DEPIPA_NOISE_USE_MAX_RESISTOR, ++}; ++ ++static void set_bit_depipa_noise_ctrl(struct ak39_codec *codec, int mode) ++{ ++ unsigned long reg_val = 0; ++ ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ ++ if(mode == DEPIPA_NOISE_USE_MAX_RESISTOR) { ++ reg_val |= (PRE_EN1); ++ reg_val |= (PRE_EN2); ++ } else if(mode == DEPIPA_NOISE_USE_3KOHM_RESISTOR) { ++ reg_val |= (PRE_EN1); ++ reg_val &= ~(PRE_EN2); ++ } else if(mode == DEPIPA_NOISE_USE_1KOHM_RESISTOR) { ++ reg_val &= ~(PRE_EN1); ++ reg_val |= (PRE_EN2); ++ } else { ++ reg_val &= ~(PRE_EN1); ++ reg_val &= ~(PRE_EN2); ++ } ++ ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++} ++ ++ ++static void ak39_set_vcm_ref_power(struct ak39_codec *codec, bool bOn) ++{ ++ unsigned long reg_val; ++ if(bOn) ++ { ++ //add power control here for MIC-to-Lineout channel ++ //power on REF ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_val &= ~(PD_REF | PL_VCM2 ); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++ ++ //power on vcm2/vcm3 ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_val &= ~(PD_VCM2 | PD_VCM3); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++ } ++ else ++ { ++ //power off vcm2/vcm3 ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_val |= (PD_VCM2); ++ reg_val |= (PD_VCM3); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++ ++ //power off codec ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_val |= (PD_REF | PL_VCM2); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++ } ++} ++ ++ ++/** ++ * @brief power on HP ++ * @author ++ * @date ++ * @param[in] bool poweron or poweroff ++ * @ [in] bool use delay to de-pipa or not ++ * @return void ++ */ ++void ak39_set_hp_power(struct ak39_codec *codec, bool bOn, bool soft_de_pipa) ++{ ++ int reg_value; ++ ++ printk("ak39_set_hp_power %d,soft_de_pipa=%d\n",bOn,soft_de_pipa); ++ if(bOn) ++ { ++// REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= (PL_VCM2); ++ set_bit_depipa_noise_ctrl(codec, DEPIPA_NOISE_USE_MAX_RESISTOR); ++ ++ mdelay(10); ++ ++ pd_ref_enable(codec); ++ ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= (0x7UL << 29); ++ ++ mdelay(20); ++ reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_value &= ~(PD1_HP); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; ++ ++ mdelay(10); ++ ++ reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_value &= ~(PD2_HP); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; ++ ++ ak39_set_vcm_ref_power(codec, 1); ++ ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(0x1f << 4); ++ set_cur_vcm2(codec, 0x1e); ++ mdelay(100); ++ ++ set_bit_depipa_noise_ctrl(codec, DEPIPA_NOISE_NOT_USE); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(RST_DAC); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(0x7UL << 29); ++ } ++ else ++ { ++ set_bit_depipa_noise_ctrl(codec, DEPIPA_NOISE_USE_3KOHM_RESISTOR); ++ ++ if (!codec->adc2_state) { ++ //off mute ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(0x7 << 12); ++ // REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= (0x1f << 4); ++ ++ ak39_set_vcm_ref_power(codec, 0); ++ set_cur_vcm2_dischg(codec, 0x1f); ++ mdelay(50); ++ } ++ ++ //power off hp ++ reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_value |= ((PD2_HP)); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; ++ ++ //power off hp ++ reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_value |= (PD1_HP); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; ++ } ++} ++ ++ ++/** ++ * @brief set linein interface power ++ * @author ++ * @date ++ * @param[in] bTrue: 1-power on; 0-power off ++ * @return void ++ */ ++static void ak39_set_linein_power(struct ak39_codec *codec, bool bOn) ++{ ++ unsigned long reg_val = 0; ++ if(bOn) ++ { ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); ++ reg_val &= ~(1 << 14); //power on the channel ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; ++ } ++ else ++ { ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); ++ reg_val |= (1 << 14); //power off the channel ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; ++ } ++} ++ ++/** ++ * @brief set DAC to lineout ++ * @author ++ * @date ++ * @param[in] bTrue: 1-connet DACto lineout; 0-disconnect ++ * @return void ++ */ ++static void ak39_set_mic_power(struct ak39_codec *codec, bool bOn) ++{ ++ unsigned long reg_val = 0; ++ if(bOn) ++ { ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); ++ reg_val |= VDD_MIC_SEL; ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; ++ ++ //power on mic interface ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); ++ reg_val &= ~(0x1 << 23); //power on differential mic ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; ++ } ++ else ++ { ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2); ++ reg_val |= (0x1 << 23); //power off differential mic ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) = reg_val; ++ } ++} ++ ++static void ak39_set_sel_vref(struct ak39_codec *codec, bool bOn) ++{ ++ unsigned long reg_val; ++ reg_val = REG32(codec->analog_ctrl_base + ADC1_CONF_REG1); ++ if(bOn) ++ reg_val |= SEL_VREF; ++ else ++ reg_val &= ~SEL_VREF; ++ ++ REG32(codec->analog_ctrl_base + ADC1_CONF_REG1) = reg_val; ++} ++ ++ ++static void set_dac_highspeed(struct ak39_codec *codec, ++ unsigned long sample_rate) ++{ ++ static unsigned long rate_list[] = ++ {8000, 16000, 24000, 48000, 96000}; ++ static unsigned long dac_hclk[sizeof(rate_list)/sizeof(rate_list[0])] = ++ {6, 12, 18, 36, 72}; ++ ++ unsigned long reg_value; ++ unsigned long pllclk; ++ unsigned long hclk_div; ++ unsigned long hclk; ++ unsigned long i; ++ ++ pllclk = ak_get_asic_pll_clk(); ++ pllclk /= 1000000; ++ ++ printk("seths sr %lu,pll %lu\n",sample_rate,pllclk); ++ for(i=0; i < sizeof(rate_list)/sizeof(rate_list[0]); ++i) ++ { ++ if(sample_rate <= rate_list[i]) ++ { ++ break; ++ } ++ } ++ ++ if(sizeof(rate_list)/sizeof(rate_list[0]) == i) ++ { ++ hclk = dac_hclk[i-1]; ++ } ++ else ++ { ++ hclk = dac_hclk[i]; ++ } ++ ++ hclk_div = pllclk / hclk; ++ //04V 3771 chip: when the divider of the DAC CLK is even number, ++ //the DAC MODULE is more steady ++ hclk_div &= ~1; ++ ++ if(0 != hclk_div) ++ { ++ hclk_div = hclk_div - 1; ++ } ++ ++ reg_value = REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG); ++ reg_value &= ~(MASK_DAC_HCLK_DIV); ++ reg_value |= DAC_HSDIV_VLD | DAC_HCLK_DIV(hclk_div); ++ REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) = reg_value; ++ ++ i = 0; ++ while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (DAC_HSDIV_VLD)) ++ { ++ ++i; ++ if(i > 100000) ++ { ++ printk("set high speed clk reg fail\n"); ++ return ; ++ } ++ } ++} ++ ++static void set_adc_highspeed(struct ak39_codec *codec, ++ unsigned long sample_rate) ++{ ++ unsigned long reg_value; ++ unsigned long pllclk; ++ unsigned long hclk_div; ++ unsigned long hclk; ++ unsigned long i; ++ ++ pllclk = ak_get_asic_pll_clk(); ++ for (i=1; i<=0x40; i++) ++ { ++ hclk = pllclk/i; ++ if ((hclk<=62000000)&&(hclk>=28000000)) ++ { ++ break; ++ } ++ } ++ hclk_div = i-1; ++ ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<29); ++ ++ i = 0; ++ while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_HSDIV_VLD)) ++ { ++ ++i; ++ if(i > 100000) ++ { ++ printk("adc set high speed clk reg fail\n"); ++ return ; ++ } ++ } ++ ++ reg_value = REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG); ++ reg_value &= ~(MASK_ADC2_HCLK_DIV); ++ reg_value |= ADC2_HSDIV_VLD | ADC2_HCLK_DIV(hclk_div); ++ REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) = reg_value; ++ ++ i = 0; ++ while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_HSDIV_VLD)) ++ { ++ ++i; ++ if(i > 100000) ++ { ++ printk("adc set high speed clk reg fail\n"); ++ return ; ++ } ++ } ++ ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (1<<29); ++} ++ ++/** ++ * @brief set ADC2 mode and clk_div ++ * @author ++ * @date ++ * @param[in] des_sr: desired rate to be set ++ * @param[out] mode_sel: reg(0x08000064) bit[12] ++ * @param[out] mclkdiv: reg(0x08000008) bit[11:4] ++ * @return void ++ */ ++static unsigned long get_adc2_osr_div(struct ak39_codec *codec, unsigned char *mode_sel, ++ unsigned char *mclkdiv, unsigned long des_sr) ++{ ++ unsigned short k, max_div; ++ unsigned short OSR_value=256; ++ unsigned long SR_save, out_sr=0; ++ signed long a, b; ++ unsigned long clk168m; ++ unsigned long perfect_pll; ++ ++ clk168m = ak_get_asic_pll_clk(); ++ max_div = 0x40; ++ SR_save = 0; ++ *mode_sel = 0; ++ *mclkdiv = 0; ++ ++ if (des_sr > 24000) ++ { ++ OSR_value = 256; ++ *mode_sel = 1; //48k mode ++ } ++ else ++ { ++ OSR_value = 512; ++ *mode_sel = 0; //16k mode ++ } ++ ++ for(k=0; k0)? a : (-a); ++ b = SR_save - des_sr; ++ b = (b>0)? b : (-b); ++ if (a=0; k--) //DAC_DIV value ++ { ++ out_sr = clk168m/(k+1); ++ if (out_sr > MAX_DACCLK) ++ break; ++ out_sr = out_sr/OSR_table[j]; ++ a = out_sr-des_sr; ++ a = (a>0)? a : (-a); ++ b = SR_save-des_sr; ++ b = (b>0)? b : (-b); ++ if (a vol_info[port].vol_max) ++ vol = vol_info[port].vol_max; ++ ++ return vol; ++} ++ ++static inline int get_volume_info(int port, int type) ++{ ++ int vol; ++ ++ switch(type){ ++ case VOL_INFO_DEFAULT: ++ vol = vol_info[port].vol_default; ++ break; ++ case VOL_INFO_MIN: ++ vol = vol_info[port].vol_min; ++ break; ++ case VOL_INFO_MAX: ++ vol = vol_info[port].vol_max; ++ break; ++ default: ++ vol = 0; ++ break; ++ } ++ return vol; ++} ++ ++ ++/* ++ *set mixer volume by port, ++ *return old volume; ++ * ++ * */ ++static inline int set_mixer_volume(struct ak39_codec *codec, ++ int port, int volume) ++{ ++ int old_vol; ++ ++ old_vol = codec->mixer_volume[port]; ++ codec->mixer_volume[port] = volume; ++ ++ return old_vol; ++} ++ ++static inline int get_mixer_volume(struct ak39_codec *codec, ++ int port) ++{ ++ return codec->mixer_volume[port]; ++} ++ ++/** ++ * @brief set hp/linein/mic gain ++ * @author lixinhai ++ * @date 2013-08-02 ++ * @input codec: handle. ++ * @input port: control port:hp, linein or mic. ++ * @input gain: gain value. ++ * @return void ++ */ ++static void ak39_set_gain(struct ak39_codec *codec, int port, int gain) ++{ ++ switch(port) { ++ case MIXER_PORT_HP_VOL: ++ ak39_set_hp_gain(codec, gain); ++ break; ++ case MIXER_PORT_LINEIN_VOL: ++ ak39_set_linein_gain(codec, gain); ++ break; ++ case MIXER_PORT_MIC_VOL: ++ ak39_set_mic_gain(codec, gain); ++ break; ++ } ++} ++ ++/** ++ * @brief set playback mode: headphone or speaker ++ * @author lixinhai ++ * @date 2013-08-02 ++ * @input codec: handle. ++ * @input plug_in: headphone is plug in. ++ * @return void ++ */ ++static inline void set_playmode_switch(struct ak39_codec *codec, ++ int plug_in) ++{ ++ if(codec->playmode != PLAYMODE_AUTO_SWITCH) ++ return; ++ ++ if(plug_in) ++ codec->mixer_switch[MIXER_ADDR_HPDET] = PLAYMODE_STATUS_HP; ++ else ++ codec->mixer_switch[MIXER_ADDR_HPDET] = PLAYMODE_STATUS_SPEAKER; ++} ++ ++/** ++ * @brief get playback mode: headphone or speaker ++ * @author lixinhai ++ * @date 2013-08-02 ++ * @input codec: handle. ++ * @return void ++ */ ++static int ak39_codec_get_playmode(struct ak39_codec *codec) ++{ ++ if(codec->playmode == PLAYMODE_HP) { ++ return PLAYMODE_STATUS_HP; ++ } else if(codec->playmode == PLAYMODE_SPEAKER) ++ return PLAYMODE_STATUS_SPEAKER; ++ else ++ return codec->mixer_switch[MIXER_ADDR_HPDET]; ++} ++ ++/** ++ * @brief set route source on destation. ++ * @author lixinhai ++ * @date 2013-08-02 ++ * @input codec: handle. ++ * @input dst: destation ++ * @input src: source ++ * @input src_mask: source mask. ++ * @return new destation of route. ++ */ ++static inline int set_route_src_on_dst(struct ak39_codec *codec, ++ int dst, int src, int src_mask) ++{ ++ codec->mixer_route[dst] &= ~src_mask; ++ codec->mixer_route[dst] |= src; ++ ++ PDEBUG("%s: destion:%d, src:%d, src_mask:%d, mixer source:%d.\n", ++ __func__, dst, src, src_mask, codec->mixer_route[dst]); ++ return codec->mixer_route[dst]; ++} ++ ++static inline int get_route_src_on_dst(struct ak39_codec *codec, ++ int dst, int src_mask) ++{ ++ return codec->mixer_route[dst] & src_mask; ++} ++ ++ ++static inline int check_route_src_on_dst(struct ak39_codec *codec, ++ int dst, int src) ++{ ++ return !!(codec->mixer_route[dst] & src); ++} ++ ++static int calc_route_src_status(struct ak39_codec *codec, int dst, int src, int src_mask) ++{ ++ int i; ++ int status = 0; ++ ++ for(i=0; imixer_route[i] & (src & src_mask); ++ else ++ status |= codec->mixer_route[i] & src_mask; ++ } ++ ++ return !!status; ++} ++ ++ ++/** ++ * @brief set input device power ++ * @author ++ * @date ++ * @param[in] src: DAC|LINEIN|MIC ++ * addr:route No. ++ * CurSrc:all three route's current src ++ * @return void ++ */ ++static void ak39_set_src_power(struct ak39_codec *codec, int dst, int src) ++{ ++ int s_dac, s_linein, s_mic, s_src; ++ ++ s_dac = calc_route_src_status(codec, dst, src, SOURCE_DAC); ++ s_linein = calc_route_src_status(codec, dst, src, SOURCE_LINEIN); ++ s_mic = calc_route_src_status(codec, dst, src, SOURCE_MIC); ++ ++ ++ s_src = s_dac|s_linein|s_mic; ++ ++ PDEBUG("%s: src:%d, s_dac:%d, s_linein:%d, s_mic:%d.\n", ++ __func__, src, s_dac, s_linein, s_mic); ++ ++ ak39_set_sel_vref(codec, s_src); ++ ak39_set_vcm_ref_power(codec, s_src); ++ ++ //set DAC power in PCM interface function: codec_playback_prepare() ++ ak39_set_linein_power(codec, s_linein); ++ ak39_set_mic_power(codec, s_mic); ++} ++ ++ ++static int ak39_codec_stream_control(struct ak39_codec *codec, ++ int dst, int running) ++{ ++ int src; ++ ++ src = get_route_src_on_dst(codec, dst, SOURCE_MIXED_ALL_MASK); ++ PDEBUG("%s: dst:%d, src:%d, status:%s.\n", ++ __func__, dst, src, running ? "running":"stop"); ++ ++ if(!running) ++ src = 0; ++ ++ switch(dst) { ++ case MIXER_ADDR_DST_HP: ++ if(codec->used_hp_mute) ++ ak_gpio_setpin(codec->hpmute_gpio.pin, codec->hpmute_en_val); ++ ++ ak39_set_hp_power(codec, !!src, !codec->used_hp_mute); ++ ++ if(codec->used_hp_mute) { ++ int dis_val; ++ ++ dis_val = (codec->hpmute_en_val == AK_GPIO_OUT_HIGH)? ++ AK_GPIO_OUT_LOW:AK_GPIO_OUT_HIGH; ++ ak_gpio_setpin(codec->hpmute_gpio.pin, dis_val); ++ } ++ ak39_set_hp_in(codec, src); ++ ak39_set_src_power(codec, dst, src); ++ ++ break; ++ case MIXER_ADDR_DST_ADC2: ++ ak39_set_adc2_in(codec, src); ++ ak39_set_src_power(codec, dst, src); ++ //dump_codec_reg(codec); ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++ ++/** ++ * @brief open a dac device ++ * @author ++ * @date ++ * @return void ++ */ ++void ak39_codec_dac_open(struct ak_codec_dai *dai) ++{ ++ int reg_value; ++ int i; ++ struct ak39_codec *codec = to_ak39_codec(dai); ++ ++ if(codec->dac_state) ++ return; ++ ++ codec->dac_state = 1; ++ ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<4); ++ REG32(codec->analog_ctrl_base + CLOCK_GATE_CTRL_REG) &= ~(1<<4); ++ ++ REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= MUTE; ++ ++ REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= ~DAC_CTRL_EN; ++ REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= ~DAC_CLK_EN; ++ ++ REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= DAC_DIV_VLD; ++ i = 0; ++ while(REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) & (DAC_DIV_VLD)) ++ { ++ ++i; ++ if(i > 100000) ++ { ++ printk("set da clk reg fail\n"); ++ return ; ++ } ++ } ++ ++ // to enable DAC CLK ++ REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= DAC_CLK_EN; ++ ++ //soft reset DAC ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(DAC_SOFT_RST); ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= DAC_SOFT_RST; ++ ++ //to enable internal dac/adc via i2s ++ REG32(codec->analog_ctrl_base + MULTIPLE_FUN_CTRL_REG1) |= IN_DAAD_EN; ++ ++ //set default dac div and osr, for de pipa noise ++ ++ //I2S config reg ++ REG32(codec->adda_cfg_base + I2S_CONFIG_REG) &= (~I2S_CONFIG_WORDLENGTH_MASK); ++ REG32(codec->adda_cfg_base + I2S_CONFIG_REG) |= (16<<0); ++ ++ //config I2S interface DAC ++ REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= DAC_CTRL_EN; ++ ++ //enable accept data from l2 ++ REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= L2_EN; ++ REG32(codec->adda_cfg_base + DAC_CONFIG_REG) |= FORMAT; ++ REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= ~ARM_INT; ++ REG32(codec->adda_cfg_base + I2S_CONFIG_REG) &= ~POLARITY_SEL; ++ ++ REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) |= DAC_FILTER_EN; ++ ++ REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= ~MUTE; ++ ++ ++ reg_value = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_value &= ~( PD_OP | PD_CK); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_value; ++} ++ ++ ++/** ++ * @brief Close a dac device ++ * @author ++ * @date ++ * @return void ++ */ ++void ak39_codec_dac_close(struct ak_codec_dai *dai) ++{ ++ int reg_val; ++ struct ak39_codec *codec = to_ak39_codec(dai); ++ ++ if(!codec->dac_state) ++ return; ++ ++ codec->dac_state = 0; ++ REG32(codec->adda_cfg_base + DAC_CONFIG_REG) &= (~L2_EN); ++ ++ // to power off DACs/DAC clock ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_val |= (PD_OP); ++ reg_val |= (PD_CK); ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++ ++ mdelay(5); ++ ++ REG32(codec->analog_ctrl_base + MULTIPLE_FUN_CTRL_REG1) &= (~(IN_DAAD_EN));// tdisable internal DAC/ADC ++ REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= (~(DAC_CLK_EN|DAC_DIV_VLD));// disable DAC CLK ++ REG32(codec->analog_ctrl_base + DAC_CONFIG_REG) &= (~DAC_CTRL_EN);// to disable DACs ++} ++ ++/** ++ * @brief Set sample rate ++ * @author ++ * @date ++ * @param[in] samplerate: desired sample rate ++ * @return void ++ */ ++void ak39_codec_set_dac_samplerate(struct ak_codec_dai *dai, unsigned int samplerate) ++{ ++ unsigned char osr, mclkdiv; ++ struct ak39_codec *codec = to_ak39_codec(dai); ++ ++ get_dac_osv_div(codec, &osr, &mclkdiv, samplerate); ++ ++ //disable HCLK, and disable DAC interface ++ REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(DAC_HCLK_EN); ++ REG32(codec->analog_ctrl_base + DAC_CONFIG_REG) &= ~(DAC_CTRL_EN); ++ ++ set_dac_highspeed(codec, samplerate); ++ ++ // NOTE: should reset DAC!!! ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= (~DAC_SOFT_RST); ++ mdelay(5); ++ ++ //set OSR ++ REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) &= (~OSR_MASK); ++ REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) |= (OSR(osr)); ++ mdelay(2); ++ ++ //set DIV ++ REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= (~(MASK_DAC_DIV_INT)); ++ REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= (DAC_DIV_INT(mclkdiv)); ++ ++ REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) &= (~(MASK_DAC_DIV_FRAC)); ++ REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= (DAC_DIV_VLD); ++ mdelay(2); ++ ++ //to reset dac ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= DAC_SOFT_RST; ++ ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= (~DAC_SOFT_RST); ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= DAC_SOFT_RST; ++ ++ //enable HCLK, and enable DAC interface ++ REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (DAC_HCLK_EN); ++ REG32(codec->analog_ctrl_base + DAC_CONFIG_REG) |= (DAC_CTRL_EN); ++} ++ ++/** ++ * @brief Set DAC channels: mono,stereo ++ * @author ++ * @date ++ * @param[in] bool mono: true-mono, false-stereo ++ * @return void ++ */ ++void ak39_codec_set_dac_channels(struct ak_codec_dai *dai, unsigned int chnl) ++{ ++ ++} ++ ++ ++/** ++ * @brief open ADC2 ++ * @author ++ * @date ++ * @param[in] void ++ * @return void ++ */ ++void ak39_codec_adc2_open(struct ak_codec_dai *dai) ++{ ++ struct ak39_codec *codec = to_ak39_codec(dai); ++ ++ //unsigned long i; ++ if(codec->adc2_state) ++ return; ++ ++ codec->adc2_state = 1; ++ ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<3); ++ REG32(codec->analog_ctrl_base + CLOCK_GATE_CTRL_REG) &= ~(1<<3); ++ ++ // soft reset ADC2 controller ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (ADC2_SOFT_RST); ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(ADC2_SOFT_RST); ++ ++// set_cur_pmos(codec, 0x0); //must be set to 0 when ADC2 is working ++ ++ REG32(codec->analog_ctrl_base + MULTIPLE_FUN_CTRL_REG1) |= IN_DAAD_EN; //enable internal ++ ++ //disable vcm2 discharge ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~(0x1f << 4); ++ ++ //SelVcm3 ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) |= VCM3_SEL; ++ ++ //PowerOn Vcm2/3 ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD_VCM3; ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PD_VCM2; ++ ++ //SetVcmNormal ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) &= ~PL_VCM2; ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~PL_VCM3; ++ ++ //EnableAdc2Limit ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) |= ADC_LIM; ++ ++ REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~HOST_RD_INT_EN; ++ REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= CH_POLARITY_SEL;//Receive the left channel data when the lrclk is high ++ REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~I2S_EN; //Internal ADC MODE ++ REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~WORD_LENGTH_MASK; ++ REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= (0xF << 8); //WORD LENGTH IS 16 BIT ++ ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (ADC2_SOFT_RST); ++ ++ //Enable adc controller ++ REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= ADC2_CTRL_EN; ++ ++ //Enable l2 ++ REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= ADC2MODE_L2_EN; ++ ++ //Power on adc2 conversion ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~PD_S2D; ++ ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) &= ~PD_ADC2; ++ ++ mdelay(1); ++ pd_ref_enable(codec); ++} ++ ++/** ++ * @brief close ADC2 ++ * @author ++ * @date ++ * @param[in] void ++ * @return void ++ */ ++void ak39_codec_adc2_close(struct ak_codec_dai *dai) ++{ ++ unsigned long reg_val; ++ struct ak39_codec *codec = to_ak39_codec(dai); ++ ++ if(!codec->adc2_state) ++ return; ++ ++ codec->adc2_state = 0; ++ ++ //disable l2 ++ REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~ADC2MODE_L2_EN; ++ ++ //disable adc2 clk ++ REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~ADC2_CLK_EN; ++ ++ //disable ADC2 interface ++ REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~ADC2_CTRL_EN; ++ ++ //Power off adc2 ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG2) |= PD_ADC2; ++ ++ if((2 << 12) != (REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) & (2 << 12))) ++ { ++ if(!(REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) & DAC_CLK_EN)) ++ { ++ reg_val = REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1); ++ reg_val |= PD_VCM3; ++ reg_val |= PD_VCM2; ++ reg_val |= PL_VCM2; ++ //reg_val |= PD_REF; ++ REG32(codec->analog_ctrl_base + ANALOG_CTRL_REG1) = reg_val; ++ pd_ref_disable(codec); ++ ++ } ++ } ++ REG32(codec->analog_ctrl_base + CLOCK_CTRL_REG) |= (DAC_DIV_VLD); //inhabit adc2 clock ++ ++} ++ ++/** ++ * @brief set ADC2 sample rate ++ * @author ++ * @date ++ * @param[in] samplerate: desired rate to be set ++ * @return void ++ */ ++unsigned long ak39_codec_set_adc2_samplerate(struct ak_codec_dai *dai, ++ unsigned int samplerate) ++{ ++ unsigned char mode_sel = 0; ++ unsigned char save_div = 0; ++ unsigned long out_sr = 0; ++ unsigned long i = 0; ++ struct ak39_codec *codec = to_ak39_codec(dai); ++ ++ //disable HCLK, and disable ADC interface ++ REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) &= ~(ADC2_CTRL_EN); ++ REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(ADC2_HCLK_EN); ++ REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(ADC2_CLK_EN); ++ ++ set_adc_highspeed(codec, samplerate); ++ ++ out_sr = get_adc2_osr_div(codec, &mode_sel, &save_div, samplerate); ++ ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) &= ~(1<<27); //reset adc from adc clk ++ i = 0; ++ while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_DIV_VLD)) ++ { ++ ++i; ++ if(i > 100000) ++ { ++ printk("adc set sr fail\n"); ++ return 0; ++ } ++ } ++ REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) &= ~(MASK_ADC2_DIV); ++ REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= ADC2_DIV(save_div); ++ ++ REG32(codec->analog_ctrl_base + FADEOUT_CTRL_REG) &= ~(1<analog_ctrl_base + FADEOUT_CTRL_REG) |= (mode_sel << ADC2_OSR_BIT); ++ ++ REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (ADC2_DIV_VLD); ++ i = 0; ++ while(REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) & (ADC2_DIV_VLD)) ++ { ++ ++i; ++ if(i > 100000) ++ { ++ printk("adc set clk reg fail\n"); ++ return 0; ++ } ++ } ++ REG32(codec->analog_ctrl_base + SOFT_RST_CTRL_REG) |= (1<<27); //release the reset adc from adc clk ++ ++ ++ REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (ADC2_CLK_EN);//enable ADC2 Clock ++ REG32(codec->analog_ctrl_base + HIGHSPEED_CLOCK_CTRL_REG) |= (ADC2_HCLK_EN);//enable ADC2 Clock ++ REG32(codec->adda_cfg_base + ADC2_CONFIG_REG) |= (ADC2_CTRL_EN); ++ return out_sr; ++} ++ ++ ++/** ++ * @brief set ADC23 channel ++ * @author ++ * @date ++ * @param[in] chnl: 1-mono; 2-stereo ++ * @return void ++ */ ++void ak39_codec_set_adc2_channels(struct ak_codec_dai *dai, unsigned int chnl) ++{ ++ ++} ++ ++ ++/** ++ * @brief cfg shutdown speaker GPIO ++ * @author ++ * @date ++ * @param[in] bOn: 1-power on; 0-power off ++ * @return void ++ */ ++void ak39_codec_speak_on(struct ak39_codec *codec, bool bOn) ++{ ++ unsigned int pin = codec->spkrshdn_gpio.pin; ++ ak_setpin_as_gpio(pin); ++ ak_gpio_cfgpin(pin, AK_GPIO_DIR_OUTPUT); ++ ak_gpio_setpin(pin, bOn); ++} ++ ++ ++/** ++ * @brief when playback start, ak39_codec_playback_start must be called to start output channel ++ * @author Cheng JunYi ++ * @revisor Wu Daochao(2012-08-23) ++ * @date ++ * @return void ++ */ ++static void ak39_codec_playback_start(struct ak_codec_dai *dai) ++{ ++ struct ak39_codec *codec = to_ak39_codec(dai); ++ ++ if(codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION] == CHNLDURATION_EVEROPEN) ++ return; ++ ++ ak39_codec_get_playmode(codec); ++ ak39_codec_stream_control(codec, MIXER_ADDR_DST_HP, 1); ++ ++ codec->outputing = true; ++ ++} ++ ++void ak39_start_to_play(struct ak_codec_dai *dai, ++ unsigned int channels, unsigned int samplerate) ++{ ++ ak39_codec_set_dac_samplerate(dai, samplerate); ++ ak39_codec_set_dac_channels(dai, channels); ++ ak39_codec_dac_open(dai); ++ ak39_codec_playback_start(dai); ++} ++ ++ ++static void ak39_codec_playback_stop(struct ak_codec_dai *dai) ++{ ++ struct ak39_codec *codec = to_ak39_codec(dai); ++ ++ //if we want to open some channel for ever, return ++ if(codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION] == CHNLDURATION_EVEROPEN) ++ return; ++ ++ /*close the dac output*/ ++ ak39_codec_stream_control(codec, MIXER_ADDR_DST_HP, 0); ++ codec->outputing = false; ++} ++ ++ ++static void ak39_codec_capture_start(struct ak_codec_dai *dai) ++{ ++ struct ak39_codec *codec = to_ak39_codec(dai); ++ ++ ak39_codec_stream_control(codec, MIXER_ADDR_DST_ADC2, 1); ++} ++ ++static void ak39_codec_capture_stop(struct ak_codec_dai *dai) ++{ ++ struct ak39_codec *codec = to_ak39_codec(dai); ++ ++ ak39_codec_stream_control(codec, MIXER_ADDR_DST_ADC2, 0); ++} ++ ++/**********************HPDet switch**************************/ ++ ++static int set_hpdet_status(struct ak39_codec *codec) ++{ ++ int plug_in = 0; ++ int irq_type; ++ ++ if(ak_gpio_getpin(codec->hpdet_gpio.pin) == codec->hp_on_value) ++ { ++ //hp is plugged in ++ plug_in = 1; ++ irq_type = (codec->irq_hp_on_type == IRQ_TYPE_LEVEL_LOW) ? ++ IRQ_TYPE_LEVEL_HIGH:IRQ_TYPE_LEVEL_LOW; ++ } ++ else ++ { ++ //hp is pulled out ++ plug_in = 0; ++ irq_type = codec->irq_hp_on_type; ++ } ++ ++ printk("detect the headphone plug %s.\n", plug_in ? "on":"out"); ++ irq_set_irq_type(codec->hp_det_irq, irq_type); ++ ++ set_playmode_switch(codec, plug_in); ++ ++ ak39_codec_speak_on(codec, !plug_in); ++ return 0; ++} ++ ++ ++/** ++ * @brief when hp state is changed,schedule hp_det_worker to config output \ ++ channel to speaker or not ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static void hp_det_worker(struct work_struct *work) ++{ ++ struct ak39_codec *codec = container_of(work, struct ak39_codec,d_work.work); ++ ++ set_hpdet_status(codec); ++ ++ ak_codec_ctl_event(SNDRV_CTL_ELEM_IFACE_HWDEP, ++ SNDRV_CTL_EVENT_MASK_VALUE, "HPDet switch"); ++ ++ enable_irq(codec->hp_det_irq); ++} ++ ++/** ++ * @brief hp det ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static irqreturn_t ak39_codec_hpdet_irq(int irq, void *dev_id) ++{ ++ struct ak39_codec *codec = dev_id; ++ ++ disable_irq_nosync(irq); ++ ++ schedule_delayed_work(&codec->d_work, msecs_to_jiffies(100)); ++ return IRQ_HANDLED; ++} ++ ++ ++/************** ++ * mixer interface ++ **************/ ++ ++ ++/***********************config channel duration*******************************/ ++#define AK39PCM_OUTPUTCHNL_DURATION(xname, xindex, addr) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ ++ .name = xname, .index = xindex, \ ++ .info = codec_ChnlDuration_info, \ ++ .get = codec_ChnlDuration_get, \ ++ .put = codec_ChnlDuration_put, \ ++ .private_value = addr \ ++} ++ ++/** ++ * @brief info callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_ChnlDuration_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int addr = kcontrol->private_value; ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ if(MIXER_ADDR_PLAY_DURATION == addr) ++ { ++ uinfo->value.integer.min = CHNLDURATION_MIN; ++ uinfo->value.integer.max = CHNLDURATION_MAX; ++ } ++ else ++ { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * @brief get callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_ChnlDuration_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); ++ struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); ++ ++ int addr = kcontrol->private_value; ++ if(addr != MIXER_ADDR_PLAY_DURATION) ++ { ++ return -EINVAL; ++ } ++ ++ ucontrol->value.integer.value[0] = codec->mixer_ch_duration[addr]; ++ return 0; ++} ++ ++/** ++ * @brief put callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_ChnlDuration_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); ++ struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); ++ ++ int addr = kcontrol->private_value; ++ int duration = ucontrol->value.integer.value[0]; ++ ++ printk("ak39 codec: ChnlDuration_put duration=%d\n",duration); ++ if(duration == codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION]){ ++ return 0; ++ } ++ if(addr != MIXER_ADDR_PLAY_DURATION) { ++ return -EINVAL; ++ } ++ if((duration > CHNLDURATION_MAX)||(duration < CHNLDURATION_MIN)) { ++ return -EINVAL; ++ } ++ ++ codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION] = duration; ++ if((duration == CHNLDURATION_CONSTANT) && ++ (!test_bit(0, &playback_statu))) { ++ ak39_codec_playback_stop(dai); ++ } ++ ++ return 0; ++} ++ ++/*********************select fixed output channel**********************************/ ++#define AK39PCM_DAC_OUT_MODE(xname, xindex, addr) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ ++ .name = xname, .index = xindex, \ ++ .info = codec_dac_outmode_info, \ ++ .get = codec_dac_outmode_get, \ ++ .put = codec_dac_outmode_put, \ ++ .private_value = addr \ ++} ++ ++/** ++ * @brief info callback. ++ * becsuse of arch-ak39 has dac output only, ++ * this interface is not used. ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_dac_outmode_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int addr = kcontrol->private_value; ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ if(MIXER_ADDR_OUTMODE_DAC == addr) ++ { ++ uinfo->value.integer.min = OUTMODE_MIN; ++ uinfo->value.integer.max = OUTMODE_MAX; ++ } ++ else ++ { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * @brief get callback ++ * becsuse of arch-ak39 has dac output only, ++ * this interface is not used. ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_dac_outmode_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int addr = kcontrol->private_value; ++ ++ if(addr != MIXER_ADDR_OUTMODE_DAC) ++ { ++ return -EINVAL; ++ } ++ ++ ucontrol->value.integer.value[0] = OUTMODE_HP; ++ return 0; ++} ++ ++/** ++ * @brief put callback ++ * becsuse of arch-ak39 has dac output only, ++ * this interface is not used. ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_dac_outmode_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ printk("ak39 platform not support out mode setting.\n"); ++ return 0; ++} ++ ++/**********************hp detect (read-only)****************************/ ++#define AK39PCM_SWITCH(xname, xindex, addr) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ ++ .access = SNDRV_CTL_ELEM_ACCESS_READ, \ ++ .name = xname, .index = xindex, \ ++ .info = codec_switch_info, \ ++ .get = codec_switch_get, \ ++ .private_value = addr \ ++} ++ ++/** ++ * @brief info callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_switch_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int addr = kcontrol->private_value; ++ if(MIXER_ADDR_HPDET != addr) ++ { ++ return -EINVAL; ++ } ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; ++ uinfo->count = 1; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = 1; ++ return 0; ++} ++ ++/** ++ * @brief get callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_switch_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); ++ struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); ++ ++ int addr = kcontrol->private_value; ++ if(MIXER_ADDR_HPDET != addr) ++ { ++ return -EINVAL; ++ } ++ ++ ucontrol->value.integer.value[0] = codec->mixer_switch[addr]; ++ PDEBUG("%s enter, value:%lu.\n", __func__, ucontrol->value.integer.value[0]); ++ return 0; ++} ++ ++/***************************VOLUME*********************/ ++#define AK39PCM_VOLUME(xname, xindex, addr) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ ++ .name = xname, .index = xindex, \ ++ .info = codec_volume_info, \ ++ .get = codec_volume_get, \ ++ .put = codec_volume_put, \ ++ .private_value = addr \ ++} ++ ++/** ++ * @brief info callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_volume_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int port = kcontrol->private_value; ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ ++ ++ if(port < 0 || port >= MIXER_PORT_VOL_COUNT) { ++ printk(KERN_ERR "%s port %d is invaild.\n", __func__, port); ++ return -EINVAL; ++ } ++ uinfo->value.integer.min = get_volume_info(port, VOL_INFO_MIN); ++ uinfo->value.integer.max = get_volume_info(port, VOL_INFO_MAX); ++ ++ return 0; ++} ++ ++/** ++ * @brief get callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_volume_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); ++ struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); ++ ++ int port = kcontrol->private_value; ++ if(port < 0 || port >= MIXER_PORT_VOL_COUNT) { ++ printk(KERN_ERR "%s port %d is invaild.\n", __func__, port); ++ return -EINVAL; ++ } ++ ++ ucontrol->value.integer.value[0] = get_mixer_volume(codec, port); ++ PDEBUG("%s enter, port:%d, volume:%lu.\n", ++ __func__, port, ucontrol->value.integer.value[0]); ++ return 0; ++} ++ ++/** ++ * @brief put callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_volume_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); ++ struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); ++ ++ int port = kcontrol->private_value; ++ int vol, old_vol; ++ int change = 0; ++ ++ vol = ucontrol->value.integer.value[0]; ++ ++ PDEBUG("%s enter, port:%d, volume:%d.\n", __func__, port, vol); ++ vol = volume_repair_range(port, vol); ++ ++ old_vol = set_mixer_volume(codec, port, vol); ++ if(vol != old_vol) { ++ change = 1; ++ ak39_set_gain(codec, port, vol); ++ } ++ ++ return change; ++} ++ ++/***************************ROUTE**************************/ ++#define AK39PCM_ROUTE(xname, xindex, addr) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ ++ .name = xname, \ ++ .index = xindex, \ ++ .info = codec_route_info, \ ++ .get = codec_route_get, \ ++ .put = codec_route_put, \ ++ .private_value = addr \ ++} ++ ++/** ++ * @brief info callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_route_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int addr = kcontrol->private_value; ++ ++ if(addr >= MIXER_DST_COUNT) ++ { ++ return -EINVAL; ++ } ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = SIGNAL_SRC_MAX; ++ ++ PDEBUG("%s enter, min:0, max:%d.\n", __func__, SIGNAL_SRC_MAX); ++ return 0; ++} ++ ++/** ++ * @brief get callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_route_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); ++ struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); ++ int dst = kcontrol->private_value; ++ ++ if(dst >= MIXER_DST_COUNT) ++ { ++ return -EINVAL; ++ } ++ ++ ucontrol->value.integer.value[0] = get_route_src_on_dst(codec, dst, SOURCE_MIXED_ALL_MASK); ++ PDEBUG("%s enter, value:%lu.\n", __func__, ucontrol->value.integer.value[0]); ++ return 0; ++} ++ ++/** ++ * @brief put callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++/*application has to call this callback with params value=0 to power down input&output devices*/ ++static int codec_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); ++ struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); ++ int change = 1; ++ unsigned long dst = kcontrol->private_value; ++ int src = 0; ++ ++ src = ucontrol->value.integer.value[0]; ++ PDEBUG("%s:set route: dst:%lu, src:%d.\n", __func__, dst, src); ++ ++ if((src < SIGNAL_SRC_MUTE) || (src > SIGNAL_SRC_MAX)) ++ return -EINVAL; ++ ++ if(dst == MIXER_ADDR_DST_HP) { ++ int plug_in = 0; ++ ++ if(codec->playmode == PLAYMODE_HP) ++ plug_in = 1; ++ else if(codec->playmode == PLAYMODE_SPEAKER) ++ plug_in = 0; ++ ++ set_playmode_switch(codec, plug_in); ++ ++ /*power on the speaker when headphone plug out.*/ ++ ak39_codec_speak_on(codec, !plug_in); ++ ++ }else if(dst == MIXER_ADDR_DST_ADC2) { ++ } ++ ++ if(get_route_src_on_dst(codec, dst, SOURCE_MIXED_ALL_MASK) == src) { ++ change = 0; ++ } ++ set_route_src_on_dst(codec, dst, src, SOURCE_MIXED_ALL_MASK); ++ ++ return change; ++} ++ ++/***************************ROUTE**************************/ ++#define AK39PCM_AEC(xname, xindex, addr) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ ++ .name = xname, \ ++ .index = xindex, \ ++ .info = codec_aec_info, \ ++ .get = codec_aec_get, \ ++ .put = codec_aec_put, \ ++ .private_value = addr \ ++} ++ ++/** ++ * @brief info callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_aec_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ //int port = kcontrol->private_value; ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ ++ printk( "get the aec info \n" ); ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = 10; ++ ++ return 0; ++} ++ ++/** ++ * @brief get callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_aec_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); ++ //struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); ++ ++ //int port = kcontrol->private_value; ++ ucontrol->value.integer.value[0] = dai->aec_flag; ++ //printk("%s enter, %d\n", __func__, ucontrol->value.integer.value[0]); ++ return 0; ++} ++ ++/** ++ * @brief put callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int codec_aec_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ak_codec_dai *dai = snd_kcontrol_chip(kcontrol); ++ //struct ak39_codec *codec = container_of(dai, struct ak39_codec, dai); ++ ++ int src = ucontrol->value.integer.value[0]; ++ int port = kcontrol->private_value; ++ ++ ++ printk("%s enter, %d %d \n", __func__, port, src); ++ dai->aec_flag = src; ++ ++ return 1; ++} ++ ++ ++ ++ ++static struct ak_proc_entry codec_pentries[] = { ++}; ++ ++static struct snd_kcontrol_new codec_controls[] = { ++ AK39PCM_VOLUME("Headphone Playback Volume", 0, MIXER_PORT_HP_VOL), ++ AK39PCM_VOLUME("LineIn Capture Volume", 0, MIXER_PORT_LINEIN_VOL), ++ AK39PCM_VOLUME("Mic Capture Volume", 0, MIXER_PORT_MIC_VOL), ++ AK39PCM_ROUTE("Headphone Playback Route", 0, MIXER_ADDR_DST_HP), ++ AK39PCM_ROUTE("ADC Capture Route", 0, MIXER_ADDR_DST_ADC2), ++ /*AK39PCM_ROUTE("LineIn Capture Route", 0, MIXER_ADDR_LINEINSRC),*/ ++ AK39PCM_SWITCH("HPDet switch", 0, MIXER_ADDR_HPDET), ++ AK39PCM_DAC_OUT_MODE("DAC out mode", 0, MIXER_ADDR_OUTMODE_DAC), ++ AK39PCM_OUTPUTCHNL_DURATION("duration of output channel", 0, MIXER_ADDR_PLAY_DURATION), ++ AK39PCM_AEC("Set the aec", 0, 0), ++}; ++ ++struct ak_codec_ops ak39_codec_ops = { ++ .dac_init = ak39_codec_dac_open, ++ .dac_exit = ak39_codec_dac_close, ++ .adc_init = ak39_codec_adc2_open, ++ .adc_exit = ak39_codec_adc2_close, ++ .set_dac_samplerate = ak39_codec_set_dac_samplerate, ++ .set_adc_samplerate = ak39_codec_set_adc2_samplerate, ++ .set_dac_channels = ak39_codec_set_dac_channels, ++ .set_adc_channels = ak39_codec_set_adc2_channels, ++ .playback_start = ak39_codec_playback_start, ++ .playback_end = ak39_codec_playback_stop, ++ .capture_start = ak39_codec_capture_start, ++ .capture_end = ak39_codec_capture_stop, ++ .start_to_play = ak39_start_to_play, ++}; ++ ++ ++static int ak39_codec_probe(struct platform_device *pdev) ++{ ++ int err = 0; ++ int i; ++ struct ak39_codec_platform_data *pdata = pdev->dev.platform_data; ++ struct ak39_codec *codec; ++ struct resource *res; ++ ++ printk("ak39_codec_probe enter.\n"); ++ ++ codec = kzalloc(sizeof(*codec), GFP_KERNEL); ++ if (!codec) ++ return -ENOMEM; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "akpcm_AnalogCtrlRegs"); ++ if(!res) ++ { ++ printk(KERN_ERR "no memory resource for analog_ctrl_res\n"); ++ err = -ENXIO; ++ goto out_free_codec; ++ } ++ ++ codec->analog_ctrl_base = ioremap(res->start, res->end - res->start + 1); ++ if (!codec->analog_ctrl_base) { ++ printk(KERN_ERR "could not remap analog_ctrl_res memory"); ++ err = -ENXIO; ++ goto out_free_codec; ++ } ++ ++ ++ //get ADC2 mode registers ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "akpcm_ADC2ModeCfgRegs"); ++ if(!res) ++ { ++ printk(KERN_ERR "no memory resource for adda_cfg_res\n"); ++ err = -ENXIO; ++ goto out_unremap_analog; ++ } ++ codec->adda_cfg_base = ioremap(res->start, res->end - res->start + 1); ++ if (!codec->adda_cfg_base) { ++ printk(KERN_ERR "could not remap adda_cfg_res memory"); ++ err = -ENXIO; ++ goto out_unremap_analog; ++ } ++ ++ ++ for(i=0; imixer_outmode[MIXER_ADDR_OUTMODE_DAC] = OUTMODE_HP; ++ codec->mixer_ch_duration[MIXER_ADDR_PLAY_DURATION] = CHNLDURATION_CONSTANT; ++ ++ /*FIXME:can add the code for worker vaild when switch auto mode*/ ++ INIT_DELAYED_WORK(&codec->d_work, hp_det_worker); ++ ++ codec->playmode = pdata->boutput_only; ++ ++ //config hp det gpio ++ codec->hpdet_gpio = pdata->hpdet_gpio; ++ ++ //config speaker shutdown gpio ++ codec->spkrshdn_gpio = pdata->spk_down_gpio; ++ ak_gpio_set(&(codec->spkrshdn_gpio)); ++ ++ //config speaker shutdown gpio ++ codec->spkrshdn_gpio = pdata->spk_down_gpio; ++ ak_gpio_set(&(codec->spkrshdn_gpio)); ++ ++ //set hp det pin irq ++ if(codec->hpdet_gpio.pin >= 0) { ++ ak_gpio_set(&(codec->hpdet_gpio)); ++ ++ if(AK_GPIO_OUT_LOW == codec->hp_on_value) ++ codec->irq_hp_on_type = IRQ_TYPE_LEVEL_LOW; ++ else if(AK_GPIO_OUT_HIGH == codec->hp_on_value) ++ codec->irq_hp_on_type = IRQ_TYPE_LEVEL_HIGH; ++ ++ codec->hp_det_irq = pdata->hpdet_irq; ++ codec->hp_on_value = pdata->hp_on_value; ++ ++ set_hpdet_status(codec); ++ err = request_irq(codec->hp_det_irq, ak39_codec_hpdet_irq, ++ IRQF_DISABLED, pdev->name, codec); ++ if(err) ++ { ++ printk(KERN_ERR "request irq error!"); ++ goto err_out; ++ } ++ } ++ ++ codec->dai.ops = &ak39_codec_ops; ++ codec->dai.num_kcontrols = ARRAY_SIZE(codec_controls); ++ codec->dai.kcontrols = codec_controls; ++ codec->dai.num_pentries = ARRAY_SIZE(codec_pentries); ++ codec->dai.pentries = codec_pentries; ++ codec->dai.entries_private = codec; ++ ++ if(ak_codec_register(&codec->dai)) ++ goto err_out; ++ ++ codec->used_hp_mute = pdata->bIsHPmuteUsed; ++ codec->hpmute_en_val = pdata->hp_mute_enable_value; ++ ++ if(codec->used_hp_mute){ ++ //config hp mute gpio ++ codec->hpmute_gpio = pdata->hpmute_gpio; ++ ak_gpio_set(&(codec->hpmute_gpio)); ++ } ++ ++ codec->outputing = false; ++ ++ platform_set_drvdata(pdev, codec); ++ return 0; ++ ++err_out: ++//out_unremap_adda: ++ iounmap(codec->adda_cfg_base); ++out_unremap_analog: ++ iounmap(codec->analog_ctrl_base); ++out_free_codec: ++ kfree(codec); ++ return err; ++} ++ ++static int ak39_codec_remove(struct platform_device *pdev) ++{ ++ struct ak39_codec *codec = platform_get_drvdata(pdev); ++ ++ free_irq(codec->hp_det_irq, codec); ++ ++ /*FIXME:can add the code for worker vaild when switch auto mode*/ ++ cancel_delayed_work_sync(&codec->d_work); ++ ++ iounmap(codec->analog_ctrl_base); ++ iounmap(codec->adda_cfg_base); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++static struct platform_driver ak39_codec_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ak39-codec", ++ }, ++ .probe = ak39_codec_probe, ++ .remove = ak39_codec_remove, ++}; ++ ++static int __init ak39_codec_init(void) ++{ ++ return platform_driver_register(&ak39_codec_driver); ++} ++ ++static void __exit ak39_codec_exit(void) ++{ ++ platform_driver_unregister(&ak39_codec_driver); ++} ++module_init(ak39_codec_init); ++module_exit(ak39_codec_exit); ++ ++MODULE_AUTHOR("anyka"); ++MODULE_DESCRIPTION("ak39 codec Driver"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/sound/arm/ak_pcm.c b/sound/arm/ak_pcm.c +new file mode 100644 +index 00000000..96ace240 +--- /dev/null ++++ b/sound/arm/ak_pcm.c +@@ -0,0 +1,1882 @@ ++/* ++ * akpcm soundcard ++ * Copyright (c) by Anyka, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++//#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++//#define CONFIG_PCM_DUMP 1 ++#define AK_PCM_DELAY_CLOSE_DAC ++ ++unsigned long playback_statu; ++ ++struct snd_akpcm { ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ spinlock_t mixer_lock; ++ struct snd_pcm_substream *playbacksubstrm; ++ struct snd_pcm_substream *capturesubstrm; ++ struct completion playbackHWDMA_completion; ++ struct completion captureHWDMA_completion; ++ u8 L2BufID_For_DAC; ++ u8 L2BufID_For_ADC23; ++ snd_pcm_uframes_t PlaybackCurrPos; ++ snd_pcm_uframes_t CaptureCurrPos; ++ unsigned long playbackStrmDMARunning;//bit[0]:strm state(running or not). bit[1]:DMA state(running or finished). bit[2]:strm state(suspend in frequest changed). ++ unsigned long captureStrmDMARunning;//bit[0]:strm state(running or not). bit[1]:DMA state(running or finished) ++ ++ struct tasklet_struct fetch_tasklet; ++ void * pp_buf_addr[2]; //Pingpong Buffer address ++ int playing_idx; ++ snd_pcm_uframes_t optimal_period_size; ++ int optimal_period_bytes; ++ bool use_optimal_period; ++ ++ struct timer_list stopoutput_work_timer; ++ struct tasklet_struct close_dac; ++ ++ struct ak_codec_dai *dai; ++ struct ak_codec_ops *ops; ++ ++#ifdef CONFIG_CPU_FREQ ++ struct notifier_block freq_transition; ++#endif ++ ++//#ifdef CONFIG_SUPPORT_AEC ++ struct tasklet_struct capture_aec_tasklet; ++ struct tasklet_struct playback_aec_tasklet; ++ T_AEC_INPUT p_aecin; ++ T_AEC_BUF p_aecbufs; ++ T_VOID *pfilter; ++ unsigned char *playback_data; ++ unsigned char *capture_data; ++ unsigned char *temp; ++//#endif ++ ++#ifdef CONFIG_PCM_DUMP ++ void* pcmDumpDataBuffer; ++ bool enableDump; ++ unsigned long pcmDumpSize; ++ struct timeval pcmDumpTime; ++#endif ++ ++ struct delayed_work ds_work; //delay start work ++}; ++ ++struct captureSync{ ++ unsigned long long adcCapture_bytes; ++ struct timeval tv; ++ unsigned int rate; ++ unsigned int frame_bits; ++}; ++ ++struct captureSync capSync; ++ ++static unsigned long long dac_clock; ++ ++ ++/************* ++ * PCM interface ++ *************/ ++#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE) ++#define USE_RATE (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_96000) ++#define USE_RATE_MIN 5500 ++#define USE_RATE_MAX 96000 ++ ++#define CAPTURE_USE_CHANNELS_MIN 1 ++#define CAPTURE_USE_CHANNELS_MAX 1 ++ ++#define PLAYBACK_USE_CHANNELS_MIN 2 ++#define PLAYBACK_USE_CHANNELS_MAX 2 ++ ++#define akpcm_playback_buf_bytes_max (64*1024) ++#define akpcm_playback_period_bytes_min 512 ++#define akpcm_playback_period_bytes_max 512 ++#define akpcm_playback_period_aligned 128 ++#define akpcm_playback_periods_min 4 ++#define akpcm_playback_periods_max 128 ++ ++#define DELAY_TIME_FOR_CLOSING_DAC (HZ * 30) ++ ++ ++#define akpcm_capture_buf_bytes_max (64*1024) ++#define akpcm_capture_period_bytes_min 512 ++#define akpcm_capture_periods_min 4 ++#define akpcm_capture_periods_max 64 ++ ++#ifdef CONFIG_PCM_DUMP ++#define akpcm_dump_data_size (2*1024*1024) ++#endif ++ ++ ++//#ifdef CONFIG_SUPPORT_AEC ++#define AEC_NN 128 ++#define AEC_TAIL (AEC_NN*10) ++#define AEC_CHANNELS 1 ++#define AEC_SAMPLERATE 8000 ++#define AEC_BITSPERSAMPLE 16 ++//#endif ++ ++ ++ static DEFINE_MUTEX(reboot_lock); ++ static struct snd_akpcm *reboot_info = NULL; ++ ++ /** ++ * @brief tell camera driver the audio capture samples for AV sync ++ * @author ++ * @date ++ * @input void ++ * @output void * ++ * @return void ++ */ ++void *getRecordSyncSamples(void) ++{ ++ return &capSync; ++} ++EXPORT_SYMBOL(getRecordSyncSamples); ++ ++/* return the dac_clock */ ++unsigned long long getPlaybackEclapseTime(void) ++{ ++ return dac_clock; ++} ++EXPORT_SYMBOL(getPlaybackEclapseTime); ++ ++void ak_close_dac_timer(unsigned long data) ++{ ++ struct snd_akpcm *akpcm = (struct snd_akpcm *)data; ++ ++ if (akpcm->ops->playback_end) { ++ akpcm->ops->playback_end(akpcm->dai); ++ } ++} ++ ++//#ifdef CONFIG_SUPPORT_AEC ++void ak37pcm_playback_aec(unsigned long data) ++{ ++ struct snd_akpcm *ak37pcm = (struct snd_akpcm *)data; ++ struct snd_pcm_substream *substream = ak37pcm->playbacksubstrm; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct snd_pcm_substream *capture_substream = ak37pcm->capturesubstrm; ++ struct snd_pcm_runtime *capture_runtime; ++ ++ unsigned int playback_pos; ++ unsigned long period_bytes = frames_to_bytes(runtime,runtime->period_size); ++ unsigned long buffer_bytes = frames_to_bytes(runtime,runtime->buffer_size); ++ ++ ++ //Check if the capture substream is opened yet ++ if (!capture_substream) ++ return; ++ ++ //FIXME: What is the relationship between substream and its belonging runtime ++ capture_runtime = capture_substream->runtime; ++ if (!capture_runtime) ++ return; ++ ++ //Check if the AEC library has been successfully opened. ++ if (runtime->rate != AEC_SAMPLERATE || period_bytes != akpcm_playback_period_bytes_min || !ak37pcm->pfilter) ++ return; ++ ++ //Make sure that the DMA of capturing is running ++ if (test_bit(0,&ak37pcm->captureStrmDMARunning) && ++ test_bit(1,&ak37pcm->captureStrmDMARunning)) { ++ ++ //FIXME: Figure out the position of last period that has just been finished ++ if (ak37pcm->PlaybackCurrPos <= 0) { ++ playback_pos = buffer_bytes - period_bytes; ++ } else { ++ playback_pos = ak37pcm->PlaybackCurrPos - period_bytes; ++ } ++ ++ //Something must go wrong. ++ if (playback_pos % akpcm_playback_period_bytes_min != 0 || playback_pos < 0) ++ printk("ak37pcm_playback_aec==>playback_pos=%d\n", playback_pos); ++ ++ /* ++ * FIXME: The AEC Lib just supports single channel by now. But we make a trick here by ++ * cheating the AEC Lib. Tha input data for the AEC Lib is actully stereo. By doing that, the ++ * system load could be lowered a little bit. ++ */ ++#if 0 ++ short *to = ak37pcm->playback_data; ++ short *from = vaddr + playback_pos; ++ int count = period_bytes / channels / 2; ++ if (count != AEC_NN) ++ printk("ak37pcm_playback_aec==>count=%d\n", count); ++ for (i = 0; i < count; i++) { ++ to[i] = from[i * 2]; ++ } ++#endif ++ /*We assume the channels is stereo basing on the truth */ ++ #if 0 ++ ak37pcm->p_aecbufs.buf_near = ak37pcm->p_aecbufs.buf_out = NULL; ++ ak37pcm->p_aecbufs.len_near = ak37pcm->p_aecbufs.len_out = 0; ++ ak37pcm->p_aecbufs.buf_far = vaddr + playback_pos; ++ ak37pcm->p_aecbufs.len_far = period_bytes / channels; ++ ret = AECLib_Control(ak37pcm->pfilter, &ak37pcm->p_aecbufs); ++ if (ret < 0) ++ printk("p=%d\n", ret); ++ #endif ++ } ++} ++ ++void ak37pcm_capture_aec(unsigned long data) ++{ ++ struct snd_akpcm *ak37pcm = (struct snd_akpcm *)data; ++ struct snd_pcm_substream *substream = ak37pcm->capturesubstrm; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct snd_pcm_substream *playback_substream = ak37pcm->playbacksubstrm; ++ struct snd_pcm_runtime *playback_runtime; ++ ++ //dma_addr_t vaddr = runtime->dma_area; ++ dma_addr_t paddr = runtime->dma_addr; ++ int period_bytes = frames_to_bytes(runtime,runtime->period_size); ++ int buffer_bytes = frames_to_bytes(runtime,runtime->buffer_size); ++ int capture_pos; ++ int ret; ++ ++ //Check if the playback substream is open ++ if (!playback_substream) ++ return; ++ ++ //FIXME: What is the relationship between substream and its belonging runtime ++ playback_runtime = playback_substream->runtime; ++ if (!playback_runtime) ++ return; ++ ++ /* ++ *Check if the AEC library has been successfully opened. ++ */ ++ if (runtime->rate != AEC_SAMPLERATE || period_bytes != akpcm_capture_period_bytes_min || !ak37pcm->pfilter) ++ return; ++ ++// if (test_bit(0,&ak37pcm->playbackStrmDMARunning) && ++// test_bit(1,&ak37pcm->playbackStrmDMARunning)) ++ { ++ ++ if (ak37pcm->CaptureCurrPos == 0) { ++ capture_pos = buffer_bytes - period_bytes; ++ } else { ++ capture_pos = ak37pcm->CaptureCurrPos - period_bytes; ++ } ++#if 0 ++ ak37pcm->p_aecbufs.buf_near = vaddr + capture_pos; ++ ak37pcm->p_aecbufs.len_near = period_bytes; ++ ak37pcm->p_aecbufs.buf_out = ak37pcm->p_aecbufs.buf_near; ++ ak37pcm->p_aecbufs.len_out = ak37pcm->p_aecbufs.len_near; ++ ak37pcm->p_aecbufs.buf_far = NULL; ++ ak37pcm->p_aecbufs.len_far = 0; ++ if (ak37pcm->p_aecbufs.len_near != akpcm_capture_period_bytes_min) ++ printk("ak37pcm_capture_aec==>len_near=%d\n", period_bytes); ++ ++ ret = AECLib_Control(ak37pcm->pfilter, &ak37pcm->p_aecbufs); ++ if (ret < 0) ++ printk("c=%d\n", ret); ++#endif ++ ak37pcm->p_aecbufs.buf_out = phys_to_virt(paddr+ak37pcm->CaptureCurrPos); ++ ak37pcm->p_aecbufs.len_out = akpcm_capture_period_bytes_min; ++ ret = AECLib_Control(ak37pcm->pfilter, &ak37pcm->p_aecbufs); ++ } ++} ++//#endif ++ ++/** ++ * @brief create new mixer interface ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static struct snd_card *cur_card; ++static struct snd_pcm *cur_pcm; ++static struct ak_codec_dai *cur_codec; ++int ak_codec_register(struct ak_codec_dai *dai) ++{ ++ int i, err; ++ struct snd_akpcm *akpcm; ++ struct snd_info_entry *entry; ++ ++ if (!dai) ++ return -1; ++ ++ if (!dai->ops) ++ return -1; ++ ++ if (!cur_card || !cur_pcm) { ++ printk("AK PCM: No card, No cur_pcm(failed)\n"); ++ return -1; ++ } ++ ++ akpcm = cur_card->private_data; ++ ++ if (cur_codec) { ++ printk("AK PCM: some codec has registered first(failed)\n"); ++ return -1; ++ } else { ++ cur_codec = dai; ++ akpcm->dai = dai; ++ akpcm->ops = dai->ops; ++ } ++ ++ strcpy(cur_card->mixername, "akpcm Mixer"); ++ ++ for (i = 0; i < dai->num_kcontrols; i++) { ++ err = snd_ctl_add(cur_card, snd_ctl_new1(&dai->kcontrols[i], dai)); ++ if (err < 0) ++ return err; ++ } ++ printk("AK PCM: create num_kcontrols=%d\n", dai->num_kcontrols); ++ ++ // register a proc file to tell user whether the chip DAC module has been fixed ++ for (i = 0; i < dai->num_pentries; i++) { ++ snd_card_proc_new (cur_card, dai->pentries[i].name, &entry); ++ snd_info_set_text_ops(entry, dai->entries_private, dai->pentries[i].cb); ++ } ++ ++ return 0; ++} ++ ++void ak_codec_ctl_event(unsigned int iface, unsigned int event, const char* ctl_name) ++{ ++ struct snd_ctl_elem_id elem_id; ++ struct snd_kcontrol *ctl_switch; ++ ++ /* find the corresponding switch control */ ++ memset(&elem_id, 0, sizeof(elem_id)); ++ elem_id.iface = iface; ++ ++ strcpy(elem_id.name, ctl_name); ++ ++ ctl_switch = snd_ctl_find_id(cur_card, &elem_id); ++ ++ if (ctl_switch) ++ snd_ctl_notify(cur_card, event, &ctl_switch->id); ++} ++ ++void alsabuf_to_ppbuf(unsigned long data) ++{ ++ struct snd_akpcm *akpcm = (struct snd_akpcm *)data; ++ struct snd_pcm_substream *substream = akpcm->playbacksubstrm; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ void *vaddr = runtime->dma_area; ++ snd_pcm_uframes_t avail; ++ int bytespersample = frames_to_bytes(runtime, 1); ++ int buffer_bytes = frames_to_bytes(runtime,runtime->buffer_size); ++ int period_bytes = akpcm->optimal_period_bytes; ++ int pend_bytes; ++ int dst_pos, cur_pos; ++ void *dst_addr = NULL; ++ ++ if (akpcm->playing_idx == 1) ++ dst_addr = akpcm->pp_buf_addr[0]; ++ else ++ dst_addr = akpcm->pp_buf_addr[1]; ++ ++ akpcm->PlaybackCurrPos += period_bytes; ++ if(akpcm->PlaybackCurrPos >= buffer_bytes) ++ { ++ akpcm->PlaybackCurrPos = akpcm->PlaybackCurrPos - buffer_bytes; ++ } ++ ++ avail = snd_pcm_playback_avail(runtime) + akpcm->optimal_period_size; ++ pend_bytes = (runtime->buffer_size - avail) * bytespersample; ++ ++ if (avail >= runtime->stop_threshold) { ++ printk("optimal=%u, original=%u, left=%u\n", (unsigned int)akpcm->optimal_period_size, ++ (unsigned int)runtime->period_size, (unsigned int)(runtime->buffer_size - snd_pcm_playback_avail(runtime))); ++ return; ++ } ++ ++ dst_pos = akpcm->PlaybackCurrPos + period_bytes; ++ cur_pos = akpcm->PlaybackCurrPos; ++ if (dst_pos > buffer_bytes) { ++ memcpy(dst_addr, vaddr + cur_pos, (buffer_bytes - cur_pos)); ++ memcpy(dst_addr + (buffer_bytes - cur_pos), vaddr, dst_pos - buffer_bytes); ++ } else { ++ memcpy(dst_addr, vaddr + cur_pos, period_bytes); ++ } ++ ++ if (pend_bytes < period_bytes) ++ memset(dst_addr + pend_bytes, 0, period_bytes - pend_bytes); ++} ++ ++ ++/* from the previous interrupt hanlder */ ++void dac_exit_tasklet(unsigned long data) ++{ ++ struct snd_akpcm *akpcm = (struct snd_akpcm *)data; ++ ++ if (akpcm->ops->dac_exit) ++ akpcm->ops->dac_exit(akpcm->dai); ++} ++ ++void akpcm_playback_interrupt_optimize(unsigned long data) ++{ ++ struct snd_akpcm *akpcm = (struct snd_akpcm *)data; ++ struct snd_pcm_substream *substream = akpcm->playbacksubstrm; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ u8 id = akpcm->L2BufID_For_DAC; ++ int bytespersample = frames_to_bytes(runtime, 1); ++ void *pdst_addr; ++ ++ snd_pcm_period_elapsed(substream); ++ ++ dac_clock += akpcm->optimal_period_bytes / bytespersample * 1000000 / runtime->rate; ++ ++ if(test_bit(0,&playback_statu) ++ && snd_pcm_playback_avail(runtime) < runtime->stop_threshold)//output stream is running ++ { ++ if (akpcm->playing_idx == 0) ++ akpcm->playing_idx = 1; ++ else ++ akpcm->playing_idx = 0; ++ ++ pdst_addr = (void *)virt_to_phys(akpcm->pp_buf_addr[akpcm->playing_idx]); ++ l2_combuf_dma((unsigned long)pdst_addr, id, akpcm->optimal_period_bytes, ++ (l2_dma_transfer_direction_t)MEM2BUF,1); ++ ++ tasklet_schedule(&akpcm->fetch_tasklet); ++ } ++ else //output strm has been stopped ++ { ++ printk("output stream stopped\n"); ++ clear_bit(1,&akpcm->playbackStrmDMARunning); //DMA has finished ++ complete(&(akpcm->playbackHWDMA_completion)); ++ ++#if defined(AK_PCM_DELAY_CLOSE_DAC) ++ /* delay to close output channel */ ++ akpcm->stopoutput_work_timer.expires = jiffies + DELAY_TIME_FOR_CLOSING_DAC; ++ add_timer(&akpcm->stopoutput_work_timer); ++ ++ //close the dac in order to eliminate the noise caused by interrupt of ++ // DAC data transfer ++ tasklet_schedule(&akpcm->close_dac); ++#endif ++ } ++} ++ ++ ++/** ++ * @brief DMA transfer for playback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++void akpcm_playback_interrupt(unsigned long data) ++{ ++ struct snd_akpcm *akpcm = (struct snd_akpcm *)data; ++ struct snd_pcm_substream *substream = akpcm->playbacksubstrm; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ dma_addr_t paddr = runtime->dma_addr; ++ void *vaddr = runtime->dma_area; ++ ++ u8 id = akpcm->L2BufID_For_DAC; ++ unsigned long period_bytes = 0; ++ unsigned long buffer_bytes = 0; ++ int bytespersample = frames_to_bytes(runtime, 1); ++ snd_pcm_uframes_t avail; ++ unsigned long pend_bytes; ++ ++ period_bytes = frames_to_bytes(runtime,runtime->period_size); ++ buffer_bytes = frames_to_bytes(runtime,runtime->buffer_size); ++ akpcm->PlaybackCurrPos += period_bytes; ++ if(akpcm->PlaybackCurrPos >= buffer_bytes) ++ { ++ akpcm->PlaybackCurrPos = 0; ++ } ++ snd_pcm_period_elapsed(substream); ++ ++ dac_clock += period_bytes / bytespersample * 1000000 / runtime->rate; ++ avail = snd_pcm_playback_avail(runtime); ++ pend_bytes = (runtime->buffer_size - avail) * bytespersample; ++#if 0 ++ int i; ++ unsigned short s; ++ ++ printk("_________________\n"); ++ for (i=0; i<64; i++) ++ { ++ s = *(unsigned short *)__va(paddr+akpcm->CaptureCurrPos+i*2); ++ if (s < 0x8000) ++ printk("%d\n", s); ++ else ++ printk("-%d\n", (unsigned short)(~s+1)); ++ } ++ printk("_________________\n"); ++#endif ++ ++ ++#ifdef CONFING_SUPPORT_AEC ++// tasklet_schedule(&akpcm->playback_aec_tasklet); ++#endif ++ ++ if(test_bit(0,&playback_statu) && avail < runtime->stop_threshold)//output stream is running ++ { ++ //printk(KERN_ERR "begin next dma"); ++ if (pend_bytes < period_bytes) ++ memset(vaddr+akpcm->PlaybackCurrPos + pend_bytes, 0, period_bytes - pend_bytes); ++ ++ //#ifdef CONFIG_SUPPORT_AEC ++ if( akpcm->dai->aec_flag == 1) ++ { ++ AECLib_DacInt(akpcm->pfilter, phys_to_virt(paddr+akpcm->PlaybackCurrPos), period_bytes); ++ } ++ //memcpy(akpcm->playback_data, akpcm->temp, period_bytes); ++ //#endif ++ ++ l2_combuf_dma(paddr+akpcm->PlaybackCurrPos, id, period_bytes, ++ (l2_dma_transfer_direction_t)MEM2BUF,1); ++ ++#ifdef CONFIG_PCM_DUMP ++ if(akpcm->enableDump){ ++ unsigned char *pcmDump_dma_area=runtime->dma_area; ++ if((akpcm->pcmDumpSize+akpcm_playback_period_bytes_min)<=akpcm_dump_data_size){ ++ //printk(KERN_ERR "akpcm->dumpsize=%d,akpcm->PlaybackCurrPos=%d \n",akpcm->dumpsize,akpcm->PlaybackCurrPos); ++ memcpy(akpcm->pcmDumpDataBuffer+akpcm->pcmDumpSize,pcmDump_dma_area+akpcm->PlaybackCurrPos,akpcm_playback_period_bytes_min); ++ akpcm->pcmDumpSize+=akpcm_playback_period_bytes_min; ++ }else{ ++ printk(KERN_ERR "akpcm->enableDump false\n"); ++ akpcm->enableDump=false; ++ } ++ } ++#endif ++ ++ } ++ else //output strm has been stopped ++ { ++ printk("output stream stopped\n"); ++ clear_bit(1,&akpcm->playbackStrmDMARunning); //DMA has finished ++ complete(&(akpcm->playbackHWDMA_completion)); ++ ++#if defined(AK_PCM_DELAY_CLOSE_DAC) ++ /* delay to close output channel */ ++ akpcm->stopoutput_work_timer.expires = jiffies + DELAY_TIME_FOR_CLOSING_DAC; ++ add_timer(&akpcm->stopoutput_work_timer); ++ ++ //close the dac in order to eliminate the noise caused by interrupt of ++ // DAC data transfer ++ tasklet_schedule(&akpcm->close_dac); ++#endif ++ } ++} ++ ++/** ++ * @brief DMA transfer for capture ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++void akpcm_capture_interrupt(unsigned long data) ++{ ++ struct snd_akpcm *akpcm = (struct snd_akpcm *)data; ++ struct snd_pcm_substream *substream = akpcm->capturesubstrm; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ dma_addr_t paddr = runtime->dma_addr; ++ u8 id = akpcm->L2BufID_For_ADC23; ++ ++ unsigned long period_bytes = 0; ++ unsigned long buffer_bytes = 0; ++ period_bytes = frames_to_bytes(runtime,runtime->period_size); ++ buffer_bytes = frames_to_bytes(runtime,runtime->buffer_size); ++ do_gettimeofday(&capSync.tv); ++ capSync.adcCapture_bytes += period_bytes; ++ akpcm->CaptureCurrPos += period_bytes; ++ if(akpcm->CaptureCurrPos >= buffer_bytes) ++ { ++ akpcm->CaptureCurrPos = 0; ++ } ++ snd_pcm_period_elapsed(substream); ++ ++//#ifdef CONFIG_SUPPORT_AEC ++ if( akpcm->dai->aec_flag == 1) ++ { ++ tasklet_schedule(&akpcm->capture_aec_tasklet); ++ } ++//#endif ++ if(test_bit(0,&akpcm->captureStrmDMARunning)) //input stream is running ++ { ++ ++ //#ifdef CONFIG_SUPPORT_AEC ++ if( akpcm->dai->aec_flag == 1) ++ { ++ AECLib_AdcInt(akpcm->pfilter, akpcm->capture_data, akpcm_capture_period_bytes_min); ++ ++ ++ l2_combuf_dma(virt_to_phys(akpcm->capture_data), id, akpcm_capture_period_bytes_min, ++ (l2_dma_transfer_direction_t)BUF2MEM,1); ++ } ++ //#else ++ else ++ { ++ l2_combuf_dma(paddr+akpcm->CaptureCurrPos, id, akpcm_capture_period_bytes_min, ++ (l2_dma_transfer_direction_t)BUF2MEM,1); ++ } ++ //#endif ++ } ++ else //input stream has been stopped ++ { ++ printk("input stream stopped\n"); ++ clear_bit(1,&akpcm->captureStrmDMARunning); ++ complete(&(akpcm->captureHWDMA_completion)); ++ ++ /* FIXME */ ++ if (akpcm->ops->adc_exit) ++ akpcm->ops->adc_exit(akpcm->dai); ++ } ++} ++ ++/** ++ * @brief trigger callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ dma_addr_t paddr = runtime->dma_addr; ++ void *vaddr = runtime->dma_area; ++ snd_pcm_uframes_t avail; ++ int avail_bytes, pend_bytes, period_bytes; ++ int start_pos0, start_pos1; ++ int bytespersample = frames_to_bytes(runtime, 1); ++ ++ void *vdst_addr0 = akpcm->pp_buf_addr[0]; ++ void *vdst_addr1 = akpcm->pp_buf_addr[1]; ++ void *pdst_addr0 = (void *)virt_to_phys(vdst_addr0); ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ { ++ u8 id = akpcm->L2BufID_For_DAC; ++ ++#if defined(AK_PCM_DELAY_CLOSE_DAC) ++ del_timer(&akpcm->stopoutput_work_timer); ++#endif ++ set_bit(0,&playback_statu); //set bit to inform that playback stream is running ++ set_bit(1,&akpcm->playbackStrmDMARunning); //set bit to inform that DMA is working ++ init_completion(&(akpcm->playbackHWDMA_completion)); ++ ++ if (akpcm->use_optimal_period) { ++ avail = snd_pcm_playback_avail(runtime); ++ avail_bytes = avail * bytespersample; ++ pend_bytes = (runtime->buffer_size - avail) * bytespersample; ++ period_bytes = akpcm->optimal_period_bytes; ++ start_pos0 = akpcm->PlaybackCurrPos; ++ start_pos1 = akpcm->PlaybackCurrPos + period_bytes; ++ ++ if (pend_bytes >= 2 * period_bytes) { ++ memcpy(vdst_addr0, vaddr + start_pos0, period_bytes); ++ memcpy(vdst_addr1, vaddr + start_pos1, period_bytes); ++ } else if (pend_bytes > period_bytes) { ++ memcpy(vdst_addr0, vaddr + start_pos0, period_bytes); ++ memcpy(vdst_addr1, vaddr + start_pos1, period_bytes); ++ memset(vdst_addr1 + pend_bytes - period_bytes, 0, 2 * period_bytes - pend_bytes); ++ } else { ++ memcpy(vdst_addr0, vaddr + start_pos0, period_bytes); ++ memset(vdst_addr0 + pend_bytes, 0, period_bytes - pend_bytes); ++ memset(vdst_addr1, 0, period_bytes); ++ } ++ ++ akpcm->playing_idx = 0; ++ akpcm->PlaybackCurrPos += period_bytes; ++ l2_clr_status(id); ++ l2_combuf_dma((unsigned long)pdst_addr0, id, period_bytes, ++ (l2_dma_transfer_direction_t)MEM2BUF,1);//start dma ++ }else { ++ l2_clr_status(id); ++ l2_combuf_dma(paddr + akpcm->PlaybackCurrPos, id, frames_to_bytes(runtime,runtime->period_size), ++ (l2_dma_transfer_direction_t)MEM2BUF,1);//start dma ++ ++ } ++ ++#ifdef CONFIG_PCM_DUMP ++ if(akpcm->enableDump){ ++ unsigned char *pcmDump_dma_area=runtime->dma_area; ++ if((akpcm->pcmDumpSize+akpcm_playback_period_bytes_min)<=akpcm_dump_data_size){ ++ //printk(KERN_ERR "akpcm->dumpsize=%d,akpcm->PlaybackCurrPos=%d \n",akpcm->dumpsize,akpcm->PlaybackCurrPos); ++ memcpy(akpcm->pcmDumpDataBuffer+akpcm->pcmDumpSize,pcmDump_dma_area+akpcm->PlaybackCurrPos,akpcm_playback_period_bytes_min); ++ akpcm->pcmDumpSize+=akpcm_playback_period_bytes_min; ++ }else{ ++ printk(KERN_ERR "akpcm->enableDump false\n"); ++ akpcm->enableDump=false; ++ } ++ } ++#endif ++ return 0; ++ } ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ clear_bit(0,&playback_statu); //stop playback stream ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++/** ++ * @brief trigger callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ //struct snd_pcm_runtime *runtime = substream->runtime; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ { ++ schedule_delayed_work(&akpcm->ds_work, msecs_to_jiffies(300)); ++ return 0; ++ } ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ clear_bit(0,&akpcm->captureStrmDMARunning); //stop capture stream ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static void delay_start_work(struct work_struct *work) ++{ ++ struct snd_akpcm *akpcm = container_of(work, struct snd_akpcm, ds_work.work); ++ struct snd_pcm_substream *substream = akpcm->capturesubstrm; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ ++ dma_addr_t paddr = runtime->dma_addr; ++ u8 id = akpcm->L2BufID_For_ADC23; ++ set_bit(0,&akpcm->captureStrmDMARunning); //set bit to inform that capture stream is running ++ set_bit(1,&akpcm->captureStrmDMARunning); //set bit to inform that DMA is working ++ init_completion(&(akpcm->captureHWDMA_completion)); ++ l2_clr_status(id); ++ l2_combuf_dma(paddr, id, akpcm_capture_period_bytes_min, ++ (l2_dma_transfer_direction_t)BUF2MEM,1); //start dma ++ ++} ++ ++/** ++ * @brief prepare callback,open DAC, power on hp/speaker ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_playback_prepare(struct snd_pcm_substream *substream) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ int bytespersample = frames_to_bytes(runtime, 1); ++ int last_period_bytes = akpcm->optimal_period_bytes; ++ ++ if(test_bit(1,&akpcm->playbackStrmDMARunning)) ++ { ++ wait_for_completion(&(akpcm->playbackHWDMA_completion)); ++ } ++ akpcm->PlaybackCurrPos = 0; ++ ++ if (((runtime->period_size % akpcm_playback_period_aligned) != 0) || ++ ((runtime->buffer_size % runtime->period_size) != 0)) { ++ ++ /*Calculate the optimal period size depending on the aligned period size*/ ++ if (runtime->period_size % akpcm_playback_period_aligned == 0) { ++ //Doesn't need changed the original period_size ++ akpcm->optimal_period_size = runtime->period_size; ++ } else { ++ akpcm->optimal_period_size = runtime->period_size + akpcm_playback_period_aligned ++ - (runtime->period_size % akpcm_playback_period_aligned); ++ } ++ akpcm->optimal_period_bytes = akpcm->optimal_period_size * bytespersample; ++ ++ /* Allocate or reallocate contigous for Pingpong Buffer */ ++ if ((last_period_bytes != 0) && (last_period_bytes != akpcm->optimal_period_bytes)) { ++ if(akpcm->pp_buf_addr[0]) ++ snd_free_pages(akpcm->pp_buf_addr[0], GFP_KERNEL); ++ ++ akpcm->pp_buf_addr[0] = snd_malloc_pages(akpcm->optimal_period_bytes * 2, GFP_KERNEL); ++ ++ printk(KERN_ERR"Pingpong Buffer Warning: Reallocate memory[old=%d, new=%d]\n", ++ last_period_bytes, akpcm->optimal_period_bytes); ++ } else { ++ if (akpcm->pp_buf_addr[0] == NULL) ++ akpcm->pp_buf_addr[0] = snd_malloc_pages(akpcm->optimal_period_bytes * 2, GFP_KERNEL); ++ } ++ ++ /* Decide whether to start Pingpong Buffer */ ++ if (akpcm->pp_buf_addr[0] == NULL) { ++ printk(KERN_ERR"Pingpong Buffer Warning: allocate memory failed!\n"); ++ akpcm->optimal_period_size = 0; ++ akpcm->optimal_period_bytes = 0; ++ akpcm->use_optimal_period = false; ++ } else { ++ akpcm->pp_buf_addr[1] = akpcm->pp_buf_addr[0] + akpcm->optimal_period_bytes; ++ akpcm->use_optimal_period = true; ++ } ++ } else { ++ akpcm->optimal_period_size = 0; ++ akpcm->optimal_period_bytes = 0; ++ akpcm->use_optimal_period = false; ++ } ++ ++ if (akpcm->use_optimal_period) { ++ l2_set_dma_callback(akpcm->L2BufID_For_DAC, akpcm_playback_interrupt_optimize,(unsigned long)akpcm); ++ }else ++ l2_set_dma_callback(akpcm->L2BufID_For_DAC, akpcm_playback_interrupt,(unsigned long)akpcm); ++ ++ /* FIXME */ ++ if (akpcm->ops->start_to_play) { ++ akpcm->ops->start_to_play(akpcm->dai, runtime->channels, runtime->rate); ++ } ++ ++ return 0; ++} ++ ++/** ++ * @brief prepare callback,open ADC23, power on mic ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_capture_prepare(struct snd_pcm_substream *substream) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ ++ l2_set_dma_callback(akpcm->L2BufID_For_ADC23,akpcm_capture_interrupt,(unsigned long)akpcm); ++ do_gettimeofday(&capSync.tv); ++ capSync.adcCapture_bytes = 0; ++ capSync.frame_bits = runtime->frame_bits; ++ capSync.rate = runtime->rate; ++ ++ akpcm->CaptureCurrPos = 0; ++ /* FIXME */ ++ if (akpcm->ops->set_adc_samplerate) ++ capSync.rate = akpcm->ops->set_adc_samplerate(akpcm->dai, runtime->rate); ++ ++ if (akpcm->ops->set_adc_channels) ++ akpcm->ops->set_adc_channels(akpcm->dai, runtime->channels); ++ ++ if (akpcm->ops->adc_init) ++ akpcm->ops->adc_init(akpcm->dai); ++ ++ if (akpcm->ops->capture_start) ++ akpcm->ops->capture_start(akpcm->dai); ++ ++ ++ return 0; ++} ++ ++/** ++ * @brief pointer callback,updata ringbuffer pointer ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static snd_pcm_uframes_t akpcm_playback_pointer(struct snd_pcm_substream *substream) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ return(bytes_to_frames(runtime,akpcm->PlaybackCurrPos)); //updata ringbuffer pointer ++} ++ ++/** ++ * @brief pointer callback, updata ringbuffer pointer ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static snd_pcm_uframes_t akpcm_capture_pointer(struct snd_pcm_substream *substream) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ return(bytes_to_frames(runtime,akpcm->CaptureCurrPos)); //updata ringbuffer pointer ++} ++ ++static struct snd_pcm_hardware akpcm_playback_hardware = { ++ .info = (SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_RESUME | ++ SNDRV_PCM_INFO_PAUSE | ++ SNDRV_PCM_INFO_MMAP_VALID), ++ .formats = USE_FORMATS, ++ .rates = USE_RATE, ++ .rate_min = USE_RATE_MIN, ++ .rate_max = USE_RATE_MAX, ++ .channels_min = PLAYBACK_USE_CHANNELS_MIN, ++ .channels_max = PLAYBACK_USE_CHANNELS_MAX, ++ .buffer_bytes_max = akpcm_playback_buf_bytes_max, ++ .period_bytes_min = akpcm_playback_period_bytes_min, ++ .period_bytes_max = akpcm_playback_period_bytes_max, ++ .periods_min = akpcm_playback_periods_min, ++ .periods_max = akpcm_playback_periods_max, ++ .fifo_size = 0, ++}; ++ ++static struct snd_pcm_hardware akpcm_capture_hardware = { ++ .info = (SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_RESUME | ++ SNDRV_PCM_INFO_MMAP_VALID), ++ .formats = USE_FORMATS, ++ .rates = USE_RATE, ++ .rate_min = USE_RATE_MIN, ++ .rate_max = USE_RATE_MAX, ++ .channels_min = CAPTURE_USE_CHANNELS_MIN, ++ .channels_max = CAPTURE_USE_CHANNELS_MAX, ++ .buffer_bytes_max = akpcm_capture_buf_bytes_max, ++ .period_bytes_min = akpcm_capture_period_bytes_min, ++ .period_bytes_max = akpcm_capture_period_bytes_min, ++ .periods_min = akpcm_capture_periods_min, ++ .periods_max = akpcm_capture_periods_max, ++ .fifo_size = 0, ++}; ++ ++/** ++ * @brief hw_params callback, malloc ringbuffer ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_playback_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *hw_params) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ if(BUF_NULL==akpcm->L2BufID_For_DAC) ++ { ++ akpcm->L2BufID_For_DAC = l2_alloc((l2_device_t)ADDR_DAC); //alloc l2 buffer for DAC ++ if(BUF_NULL==akpcm->L2BufID_For_DAC) ++ { ++ printk(KERN_ERR "alloc L2 buffer for DAC error!"); ++ return -ENOMEM; ++ } ++ } ++ return(snd_pcm_lib_malloc_pages(substream, ++ params_buffer_bytes(hw_params))); ++} ++ ++/** ++ * @brief hw_params callback, malloc ringbuffer ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_capture_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *hw_params) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ if(BUF_NULL==akpcm->L2BufID_For_ADC23) ++ { ++ akpcm->L2BufID_For_ADC23 = l2_alloc((l2_device_t)ADDR_ADC); //alloc l2 buffer for ADC23 ++ if(BUF_NULL==akpcm->L2BufID_For_ADC23) ++ { ++ printk(KERN_ERR "alloc L2 buffer for DAC error!"); ++ return -ENOMEM; ++ } ++ } ++ return(snd_pcm_lib_malloc_pages(substream, ++ params_buffer_bytes(hw_params))); ++} ++ ++/** ++ * @brief hw_free callback, free ringbuffer ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_playback_hw_free(struct snd_pcm_substream *substream) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ ++ if(test_bit(1,&akpcm->playbackStrmDMARunning)) ++ { ++ wait_for_completion(&(akpcm->playbackHWDMA_completion)); ++ } ++ if(BUF_NULL!=akpcm->L2BufID_For_DAC) ++ { ++ /* FIXME */ ++#if !defined(AK_PCM_DELAY_CLOSE_DAC) ++ if (akpcm->ops->playback_end) { ++ akpcm->ops->playback_end(akpcm->dai); ++ } ++#endif ++ if (akpcm->ops->dac_exit) { ++ akpcm->ops->dac_exit(akpcm->dai); ++ } ++ ++ l2_free((l2_device_t)ADDR_DAC); ++ akpcm->L2BufID_For_DAC = BUF_NULL; ++ } ++ return snd_pcm_lib_free_pages(substream); ++} ++ ++/** ++ * @brief hw_free callback, free ringbuffer ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_capture_hw_free(struct snd_pcm_substream *substream) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ ++ if(test_bit(1,&akpcm->captureStrmDMARunning)) ++ { ++ wait_for_completion(&(akpcm->captureHWDMA_completion)); ++ } ++ if(BUF_NULL!=akpcm->L2BufID_For_ADC23) ++ { ++ /* FIXME */ ++ if (akpcm->ops->adc_exit) ++ akpcm->ops->adc_exit(akpcm->dai); ++ if (akpcm->ops->capture_end) ++ akpcm->ops->capture_end(akpcm->dai); ++ ++ l2_free((l2_device_t)ADDR_ADC); ++ akpcm->L2BufID_For_ADC23 = BUF_NULL; ++ } ++ return(snd_pcm_lib_free_pages(substream)); ++} ++ ++/** ++ * @brief open callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_playback_open(struct snd_pcm_substream *substream) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ ++ akpcm->playbacksubstrm = substream; ++ runtime->hw = akpcm_playback_hardware; ++ dac_clock = 0; ++ ++ if (akpcm->pp_buf_addr[0]) { ++ printk(KERN_ERR"akpcm_playback_open==>pp_buf_addr[0]=0x%08x, memory leak happened\n", (unsigned int)akpcm->pp_buf_addr[0]); ++ } else { ++ akpcm->pp_buf_addr[0] = NULL; ++ akpcm->pp_buf_addr[1] = NULL; ++ } ++ akpcm->optimal_period_size = 0; ++ akpcm->optimal_period_bytes = 0; ++ ++ //#ifdef CONFIG_SUPPORT_AEC ++ if (!akpcm->playback_data) ++ { ++ akpcm->playback_data = kmalloc(akpcm_playback_period_bytes_min * 2, GFP_KERNEL); ++ if (!akpcm->playback_data) ++ { ++ printk("ak37pcm_playback_open==>allocate memory for playback_data failed!\n"); ++ } ++ } ++ //#endif ++ return 0; ++} ++ ++/** ++ * @brief open callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_capture_open(struct snd_pcm_substream *substream) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ ++ akpcm->capturesubstrm = substream; ++ runtime->hw = akpcm_capture_hardware; ++ akpcm->CaptureCurrPos = 0; ++ ++//#ifdef CONFIG_SUPPORT_AEC ++ if(!akpcm->capture_data) ++ { ++ akpcm->capture_data = kmalloc(akpcm_capture_period_bytes_min, GFP_KERNEL); ++ if (!akpcm->capture_data) ++ { ++ printk("akpcm_capture_open==>allocate memory for capture_data failed!\n"); ++ } ++ } ++ if(!akpcm->temp) ++ { ++ akpcm->temp= kmalloc(akpcm_capture_period_bytes_min, GFP_KERNEL); ++ if (!akpcm->temp) ++ { ++ printk("akpcm_capture_open==>allocate memory for temp failed!\n"); ++ } ++ //memset(akpcm->temp, 0x) ++ } ++//#endif ++ return 0; ++} ++ ++/** ++ * @brief close callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_playback_close(struct snd_pcm_substream *substream) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ ++ akpcm->playbacksubstrm=NULL; ++ dac_clock = 0; ++ ++ if (akpcm->pp_buf_addr[0] != NULL) { ++ snd_free_pages(akpcm->pp_buf_addr[0], GFP_KERNEL); ++ akpcm->pp_buf_addr[0] = NULL; ++ akpcm->pp_buf_addr[1] = NULL; ++ } ++ ++ //#ifdef CONFIG_SUPPORT_AEC ++ if (akpcm->playback_data) ++ { ++ kfree(akpcm->playback_data); ++ akpcm->playback_data = NULL; ++ } ++ //#endif ++ ++ return 0; ++} ++ ++/** ++ * @brief close callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_capture_close(struct snd_pcm_substream *substream) ++{ ++ struct snd_akpcm *akpcm = snd_pcm_substream_chip(substream); ++ capSync.adcCapture_bytes = 0; ++ akpcm->capturesubstrm=NULL; ++ return 0; ++} ++ ++/** ++ * @brief mmap callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int akpcm_pcm_mmap(struct snd_pcm_substream *substream, ++ struct vm_area_struct *vma) ++{ ++ return remap_pfn_range(vma, vma->vm_start, ++ substream->dma_buffer.addr >> PAGE_SHIFT, ++ vma->vm_end - vma->vm_start, vma->vm_page_prot); ++} ++ ++ ++static struct snd_pcm_ops akpcm_playback_ops = { ++ .open = akpcm_playback_open, ++ .close = akpcm_playback_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = akpcm_playback_hw_params, ++ .hw_free = akpcm_playback_hw_free, ++ .prepare = akpcm_playback_prepare, ++ .trigger = akpcm_playback_trigger, ++ .pointer = akpcm_playback_pointer, ++ .mmap = akpcm_pcm_mmap, ++}; ++ ++static struct snd_pcm_ops akpcm_capture_ops = { ++ .open = akpcm_capture_open, ++ .close = akpcm_capture_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = akpcm_capture_hw_params, ++ .hw_free = akpcm_capture_hw_free, ++ .prepare = akpcm_capture_prepare, ++ .trigger = akpcm_capture_trigger, ++ .pointer = akpcm_capture_pointer, ++ .mmap = akpcm_pcm_mmap, ++}; ++ ++ ++#ifdef CONFIG_CPU_FREQ ++ ++#define freq_to_akpcm(_n) container_of(_n, struct snd_akpcm, freq_transition) ++ ++/** ++ * @brief handle cpu frequency changing ++ * Note: need to handle in PLL changed mode only ++ * @author Cao LianMing ++ * @date 2011-07-26 ++ * @parm [in] nb : the nitifier_block data struct ++ * @parm [in] val : CPUFREQ_PRECHANGE / CPUFREQ_POSTCHANGE ++ * @parm [in] data : argument ++ * @return int : if successful return 0, otherwise return nagative ++ * @retval 0 : handle successful ++ * @retval <0 : handle failed ++ */ ++static int akpcm_cpufreq_transition(struct notifier_block *nb, ++ unsigned long val, void *data) ++{ ++ struct snd_akpcm *akpcm = freq_to_akpcm(nb); ++ struct cpufreq_freqs *freqs = data; ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_runtime *runtime; ++ ++ if (val == CPUFREQ_PRECHANGE) { ++ if (freqs->old_cpufreq.pll_sel != freqs->new_cpufreq.pll_sel) { ++ if (test_bit(0,&playback_statu)) { ++ printk("---- stop pcm playback\n"); ++ ++ substream = akpcm->playbacksubstrm; ++ /* suspend PCM data transmission */ ++ akpcm_playback_trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND); ++ set_bit(2, &akpcm->playbackStrmDMARunning); ++ wait_for_completion(&(akpcm->playbackHWDMA_completion)); ++ } else { ++ clear_bit(2, &akpcm->playbackStrmDMARunning); ++ } ++ } ++ } else if (val == CPUFREQ_POSTCHANGE) { ++ if (freqs->old_cpufreq.pll_sel != freqs->new_cpufreq.pll_sel) { ++ if (test_bit(2,&akpcm->playbackStrmDMARunning)) { ++ printk("---- restart pcm playback\n"); ++ ++ substream = akpcm->playbacksubstrm; ++ runtime = substream->runtime; ++ /* reconfig sample rate after PLL has been changed */ ++ /* FIXME */ ++ if (akpcm->ops->set_dac_samplerate) ++ akpcm->ops->set_dac_samplerate(akpcm->dai, runtime->rate); ++ ++ /* resume PCM data transmission */ ++ akpcm_playback_trigger(substream, SNDRV_PCM_TRIGGER_RESUME); ++ clear_bit(2, &akpcm->playbackStrmDMARunning); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * @brief register the akpcm cpufreq changing handle function to the cpufreq core ++ * @author Cao LianMing ++ * @date 2011-07-26 ++ * @parm [in] akpcm : the private data struct of the akpcm driver ++ * @return int : if register successful return 0, otherwise, return negative ++ * @retval 0 : successful ++ * @retval <0 : failed ++ */ ++static inline int akpcm_cpufreq_register(struct snd_akpcm *akpcm) ++{ ++ akpcm->freq_transition.notifier_call = akpcm_cpufreq_transition; ++ ++ return cpufreq_register_notifier(&akpcm->freq_transition, ++ CPUFREQ_TRANSITION_NOTIFIER); ++} ++ ++/** ++ * @brief unregister the akpcm cpufreq changing handle function from the cpufreq core ++ * @author Cao LianMing ++ * @date 2011-07-26 ++ * @parm [in] akpcm : the private data struct of the akpcm driver ++ * @return int : if unregister successful return 0, otherwise, return negative ++ * @retval 0 : successful ++ * @retval <0 : failed ++ */ ++static inline void akpcm_cpufreq_deregister(struct snd_akpcm *akpcm) ++{ ++ cpufreq_unregister_notifier(&akpcm->freq_transition, ++ CPUFREQ_TRANSITION_NOTIFIER); ++} ++ ++#else ++static inline int akpcm_cpufreq_register(struct snd_akpcm *akpcm) ++{ ++ return 0; ++} ++ ++static inline void akpcm_cpufreq_deregister(struct snd_akpcm *akpcm) ++{ ++} ++#endif ++ ++/** ++ * @brief Shutdown all ak ad/da modules ++ * @author Cheng JunYi ++ * @date 2011-09-27 ++ * @parm [in] in_akpcm : the private data struct of akpcm driver ++ * @return void ++ * @retval ++ */ ++static inline void akpcm_close(struct snd_akpcm *in_akpcm) ++{ ++ ++ struct snd_akpcm *akpcm = in_akpcm; ++ if(test_bit(1,&akpcm->playbackStrmDMARunning)) ++ { ++ wait_for_completion(&(akpcm->playbackHWDMA_completion)); ++ } ++ ++ if(test_bit(1,&akpcm->captureStrmDMARunning)) ++ { ++ wait_for_completion(&(akpcm->captureHWDMA_completion)); ++ } ++ ++ //close analog module ++ /* FIXME */ ++ if (akpcm->ops->dac_exit) ++ akpcm->ops->dac_exit(akpcm->dai); ++ if (akpcm->ops->playback_end) ++ akpcm->ops->playback_end(akpcm->dai); ++ if (akpcm->ops->adc_exit) ++ akpcm->ops->adc_exit(akpcm->dai); ++ if (akpcm->ops->capture_end) ++ akpcm->ops->capture_end(akpcm->dai); ++} ++ ++ ++/** ++ * @brief create new card ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int __devinit snd_card_akpcm_pcm(struct snd_akpcm *akpcm, int device, ++ int substreams) ++{ ++ struct snd_pcm *pcm; ++ struct snd_pcm_substream *substream; ++ int err; ++ err = snd_pcm_new(akpcm->card, "akpcm PCM", device, ++ substreams, substreams, &pcm); ++ if (err < 0) ++ return err; ++ akpcm->pcm = pcm; ++ cur_pcm = pcm; ++ ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &akpcm_playback_ops); //register callbacks ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &akpcm_capture_ops);//register callbacks ++ pcm->private_data = akpcm; ++ pcm->info_flags = 0; ++ strcpy(pcm->name, "akpcm PCM"); ++ ++ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; ++ snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, akpcm->card->dev, ++ akpcm_playback_buf_bytes_max, akpcm_playback_buf_bytes_max); ++ ++ substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; ++ snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, akpcm->card->dev, ++ akpcm_capture_buf_bytes_max, akpcm_capture_buf_bytes_max); ++ return 0; ++} ++ ++/************** ++ * proc interface ++ **************/ ++#ifdef CONFIG_PCM_DUMP ++ ++/** ++ * @brief Dump the ak ad/da registers to the proc file ++ * @author Cheng JunYi ++ * @date 2011-08-04 ++ * @parm [in] entry : snd handle of akpcm driver ++ * @parm [in] buffer : buffer of the proc file ++ * @return void ++ * @retval ++ */ ++static void akpcm_registers_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ ++ //snd_iprintf(buffer,"ADC23 clock Register2 =0x%lu\n",REG32(RegAddr.pAddress0800 + CLK_DIV_REG2)); ++ ++ //snd_iprintf(buffer,"Multi-func Control Register1 =0x%lu\n",REG32(RegAddr.pAddress0800 + MULTIPLE_FUN_CTRL_REG1)); ++ ++ //snd_iprintf(buffer,"Analog Control Register1 =0x%lu\n",REG32(RegAddr.pAddress0800 + ANALOG_CTRL_REG1_READ)); ++ //snd_iprintf(buffer,"Analog Control Register2 =0x%lu\n",REG32(RegAddr.pAddress0800 + ANALOG_CTRL_REG2_READ)); ++ //snd_iprintf(buffer,"Analog Control Register3 =0x%lu\n",REG32(RegAddr.pAddress0800 + ANALOG_CTRL_REG3)); ++ //snd_iprintf(buffer,"Analog Control Register4 =0x%lu\n",REG32(RegAddr.pAddress0800 + ANALOG_CTRL_REG4)); ++ ++ //snd_iprintf(buffer,"ADC2 configuration Register =0x%lu\n",REG32(RegAddr.pAddress2002D + ADC2MODE_CFG_REG)); ++ //snd_iprintf(buffer,"ADC2 Data Register =0x%lu\n",REG32(RegAddr.pAddress2002D + I2S_CONFIG_REG)); ++ ++ //snd_iprintf(buffer,"DAC configuration Register =0x%lu\n",REG32(RegAddr.pAddress2002E + DAC_CONFIG_REG)); ++ //snd_iprintf(buffer,"CPU Data Register =0x%lu\n",REG32(RegAddr.pAddress2002E + CPU_DATA_REG)); ++ ++} ++ ++ ++/** ++ * @brief Handle the write operation of proc file /proc/asound/card0/pcm-dumpctrl ++ * Start or stop dump data to the pcm-dumpdata file ++ * @author Cheng JunYi ++ * @date 2011-08-04 ++ * @parm [in] entry : snd handle of akpcm driver ++ * @parm [in] buffer : "open" means start dump pcm data to /proc/asound/card0/pcm-dumpdata; ++ "close" means stop dump pcm data to /proc/asound/card0/pcm-dumpdata ++ * @return void ++ * @retval ++ */ ++void akpcm_dumpctrl_write(struct snd_info_entry *entry, ++ struct snd_info_buffer *buffer) ++{ ++ char line[256]; ++ char str[256]; ++ struct snd_akpcm *akpcm=entry->private_data; ++ snd_info_get_line(buffer, line, 256); ++ snd_info_get_str(str, line, 256); ++ ++ if(strncmp("open", str, strlen("open"))==0){ ++ ++ memset(akpcm->pcmDumpDataBuffer,0,akpcm_dump_data_size); ++ akpcm->pcmDumpSize=0; ++ akpcm->enableDump=true; ++ do_gettimeofday(&akpcm->pcmDumpTime); ++ printk(KERN_ERR "akpcm->enableDump true\n"); ++ ++ }else if(strncmp("close", str, strlen("close"))==0){ ++ ++ akpcm->enableDump=false; ++ akpcm->pcmDumpSize=0; ++ printk(KERN_ERR "akpcm->enableDump false\n"); ++ ++ } ++} ++ ++ ++/** ++ * @brief Handle the read operation of proc file /proc/asound/card0/pcm-dumpctrl ++ * Show the last dump time ++ * @author Cheng JunYi ++ * @date 2011-08-04 ++ * @parm [in] entry : snd handle of akpcm driver ++ * @parm [in] buffer : buffer of the proc file, will return the last dump time ++ * @return void ++ * @retval ++ */ ++static void akpcm_dumpctrl_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ ++ struct snd_akpcm *akpcm=entry->private_data; ++ struct tm tm_result; ++ ++ time_to_tm(akpcm->pcmDumpTime.tv_sec,0,&tm_result); ++ snd_iprintf(buffer, "last dump timestamp: %lu-%02d-%02d %02d:%02d:%02d UTC\n",tm_result.tm_year + 1900, tm_result.tm_mon + 1, tm_result.tm_mday, tm_result.tm_hour,tm_result.tm_min,tm_result.tm_sec); ++ ++} ++ ++ ++/** ++ * @brief Handle the read operation of proc file /proc/asound/card0/pcm-dumpdata ++ * Return the pcm data to application ++ * @author Cheng JunYi ++ * @date 2011-08-04 ++ * @parm [in] entry : snd handle of akpcm driver ++ * @parm [in] buffer : buffer of the proc file, will return the pcm data ++ * @return void ++ * @retval ++ */ ++static long akpcm_dumpdata_read(struct snd_info_entry *entry, ++ void *file_private_data, ++ struct file *file, char __user *buf, ++ unsigned long count, unsigned long pos) ++{ ++ ++ long size; ++ ++ struct snd_akpcm *akpcm=entry->private_data; ++ ++ size = count; ++ if (pos + size > akpcm_dump_data_size) ++ size = akpcm_dump_data_size - pos; ++ ++ if (size > 0) { ++ if (copy_to_user(buf,akpcm->pcmDumpDataBuffer+pos,size)) ++ return -EFAULT; ++ } ++ ++ return size; ++} ++ ++ ++static struct snd_info_entry_ops akpcm_dumpdata_proc_ops = { ++ .read = akpcm_dumpdata_read, ++}; ++ ++#endif ++ ++ ++/** ++ * @brief Handle the reboot event ++ * @author Cheng JunYi ++ * @date 2011-09-27 ++ * @parm [in] nb : the nitifier_block data struct ++ * @parm [in] code : message ++ * @parm [in] unused : argument, unused ++ * @return int : handle successful or not ++ * @retval NOTIFY_DONE : handle successful ++ */ ++static int akpcm_reboot_notify(struct notifier_block *nb, ++ unsigned long code, void *unused) ++{ ++ struct snd_akpcm *akpcm; ++ ++ printk(KERN_ERR "akpcm_reboot_notify \n"); ++ ++ mutex_lock(&reboot_lock); ++ ++ if (!reboot_info) ++ goto out; ++ ++ akpcm = reboot_info; ++ akpcm_close(akpcm); ++ ++ out: ++ printk(KERN_ERR "akpcm_reboot_notify out\n"); ++ mutex_unlock(&reboot_lock); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block akpcm_reboot_notifier = { ++ .notifier_call = akpcm_reboot_notify, ++}; ++ ++ ++//#ifdef CONFIG_SUPPORT_AEC ++T_pVOID akpcm_capture_aec_kmalloc(T_U32 size) ++{ ++ return kmalloc(size, GFP_KERNEL | GFP_ATOMIC); ++} ++T_VOID akpcm_capture_aec_kfree(T_pVOID mem) ++{ ++ kfree(mem); ++} ++//#endif ++ ++/** ++ * @brief Init the device which was probed, and register a snd device ++ * @author Cheng MingJuan ++ * @date 2010-11-20 ++ * @parm [in] pdev : the device definition ++ * @return int : if init successful return 0, otherwise, return negative ++ * @retval 0 : successful ++ * @retval <0 : failed ++ */ ++static int __devinit snd_akpcm_probe(struct platform_device *devptr) ++{ ++ struct snd_card *card; ++ struct snd_akpcm *akpcm; ++ //struct snd_info_entry *entry; ++ int dev_id, err; ++ ++ memset(&capSync, 0, sizeof(capSync)); ++ //get analog control registers ++ ++ dev_id = devptr->id; ++ if (dev_id < 0) ++ dev_id = 0; ++ err = snd_card_create(dev_id, NULL, THIS_MODULE, sizeof(struct snd_akpcm), &card); ++ if (err < 0) ++ return err; ++ ++ snd_card_set_dev(card, &devptr->dev); ++ ++ akpcm = card->private_data; ++ akpcm->card = card; ++ cur_card = card; ++ ++ //init l2 buf for audio ++ akpcm->L2BufID_For_DAC = BUF_NULL; ++ akpcm->L2BufID_For_ADC23 = BUF_NULL; ++ ++ err = snd_card_akpcm_pcm(akpcm, 0, 1); ++ if (err < 0) ++ goto __out_free_card; ++ ++ //init tiimer to stop output channel ++ init_timer(&akpcm->stopoutput_work_timer); ++ akpcm->stopoutput_work_timer.function = ak_close_dac_timer; ++ akpcm->stopoutput_work_timer.data = (unsigned long)akpcm; ++ ++ akpcm->fetch_tasklet.func = alsabuf_to_ppbuf; ++ akpcm->fetch_tasklet.data = (unsigned long)akpcm; ++ akpcm->pp_buf_addr[0] = NULL; ++ ++ akpcm->close_dac.func = dac_exit_tasklet; ++ akpcm->close_dac.data = (unsigned long)akpcm; ++ ++ ++//#ifdef CONFIG_SUPPORT_AEC ++ akpcm->capture_aec_tasklet.func = ak37pcm_capture_aec; ++ akpcm->capture_aec_tasklet.data = (unsigned long)akpcm; ++ ++ akpcm->playback_aec_tasklet.func = ak37pcm_playback_aec; ++ akpcm->playback_aec_tasklet.data = (unsigned long)akpcm; ++//#endif ++ ++ INIT_DELAYED_WORK(&akpcm->ds_work, delay_start_work); ++ ++ clear_bit(0,&playback_statu); ++ clear_bit(1,&akpcm->playbackStrmDMARunning); ++ clear_bit(0,&akpcm->captureStrmDMARunning); ++ clear_bit(1,&akpcm->captureStrmDMARunning); ++ ++#ifdef CONFIG_PCM_DUMP ++ ++ snd_card_proc_new (akpcm->card, "registers", &entry); ++ snd_info_set_text_ops(entry, akpcm, akpcm_registers_read); ++ ++ akpcm->pcmDumpDataBuffer = vmalloc(akpcm_dump_data_size); ++ if (!akpcm->pcmDumpDataBuffer) ++ return -ENOMEM; ++ ++ memset(akpcm->pcmDumpDataBuffer, 0, akpcm_dump_data_size); ++ ++ snd_card_proc_new (akpcm->card, "pcm-dumpctrl", &entry); ++ entry->private_data = akpcm; ++ entry->mode = S_IFREG | S_IRUGO | S_IWUSR; ++ entry->c.text.write = akpcm_dumpctrl_write; ++ snd_info_set_text_ops(entry, akpcm, akpcm_dumpctrl_read); ++ ++ if (! snd_card_proc_new(akpcm->card, "pcm-dumpdata", &entry)) { ++ entry->content = SNDRV_INFO_CONTENT_DATA; ++ entry->private_data = akpcm; ++ entry->c.ops = &akpcm_dumpdata_proc_ops; ++ entry->size = akpcm_dump_data_size; ++ entry->mode = S_IFREG|S_IRUGO; ++ } ++ ++#endif ++ ++ strcpy(card->driver, "akpcm"); ++ strcpy(card->shortname, "Ak AD/DA"); ++ sprintf(card->longname, "Ak ADC DAC pcm input & output module %i", dev_id + 1); ++ ++ err = akpcm_cpufreq_register(akpcm); ++ if (err < 0) { ++ printk(KERN_ERR "Failed to register cpufreq\n"); ++ } ++ ++ mutex_lock(&reboot_lock); ++ reboot_info = akpcm; ++ mutex_unlock(&reboot_lock); ++ ++//#ifdef CONFIG_SUPPORT_AEC ++ memset(&akpcm->p_aecin, 0, sizeof(akpcm->p_aecin)); ++ memset(&akpcm->p_aecbufs, 0, sizeof(akpcm->p_aecbufs)); ++ akpcm->p_aecin.cb_fun.Malloc = akpcm_capture_aec_kmalloc; ++ akpcm->p_aecin.cb_fun.Free = akpcm_capture_aec_kfree; ++ akpcm->p_aecin.cb_fun.printf = (AEC_CALLBACK_FUN_PRINTF)printk; ++ //akpcm->p_aecin.m_info.m_Type = AEC_TYPE_2; ++ //akpcm->p_aecin.m_info.m_BitsPerSample = AEC_BITSPERSAMPLE; ++ akpcm->p_aecin.m_info.m_Channels = AEC_CHANNELS; ++ akpcm->p_aecin.m_info.m_SampleRate = AEC_SAMPLERATE; ++ ++ //akpcm->p_aecin.m_info.m_Private.m_aec.m_PreprocessEna = 0; ++ //akpcm->p_aecin.m_info.m_Private.m_aec.m_framelen = AEC_NN; ++ akpcm->p_aecin.m_info.m_Private.m_aec.m_tail = 1280; ++ akpcm->p_aecin.m_info.m_Private.m_aec.m_aecBypass = 0; ++ akpcm->p_aecin.m_info.m_Private.m_aec.m_framelen = 256; ++ akpcm->p_aecin.m_info.m_Private.m_aec.m_PreprocessEna = 1; ++ akpcm->p_aecin.m_info.m_Private.m_aec.AGClevel = 24576; ++ akpcm->p_aecin.m_info.m_Private.m_aec.maxGain = 3; ++ akpcm->p_aecin.m_info.m_Private.m_aec.DacVolume = 1024; ++ akpcm->p_aecin.m_info.m_Private.m_aec.AdcCutTime = 100; ++ akpcm->p_aecin.m_info.m_Private.m_aec.AdcMinSpeechPow = 1024; ++ akpcm->p_aecin.m_info.m_Private.m_aec.DacMinSpeechPow = 512; ++ akpcm->p_aecin.m_info.m_Private.m_aec.AdcSpeechMultiple = (T_U32)(1.8*(1<<14)); ++ akpcm->p_aecin.m_info.m_Private.m_aec.DacSpeechMultiple = (T_U32)(1.8*(1<<14)); ++ akpcm->p_aecin.m_info.m_Private.m_aec.AdcSpeechHoldTime = 900; ++ akpcm->p_aecin.m_info.m_Private.m_aec.DacSpeechHoldTime = 900; ++ akpcm->p_aecin.m_info.m_Private.m_aec.AdcConvergTime = 10000; ++ akpcm->p_aecin.m_info.m_Private.m_aec.DacConvergTime = 10000; ++ akpcm->pfilter = AECLib_Open(&akpcm->p_aecin); ++ if (akpcm->pfilter) { ++ printk("AEC function has been successfully activated!\n"); ++ } else { ++ printk("Failed to initialize AEC Lib, AEC function is closed\n"); ++ } ++//#endif ++ ++ ++ ++ err = snd_card_register(card); ++ if (err == 0) { ++ platform_set_drvdata(devptr, card); ++ return 0; ++ } ++ ++__out_free_card: ++ snd_card_free(card); ++ ++ return err; ++} ++ ++/** ++ * @brief De-init the device which will be removed, and unregister the snd device ++ * @author Cheng MingJuan ++ * @date 2010-11-22 ++ * @parm [in] pdev : the device definition ++ * @return int : if handle successful return 0, otherwise, return negative ++ * @retval 0 : successful ++ * @retval <0 : failed ++ */ ++static int __devexit snd_akpcm_remove(struct platform_device *devptr) ++{ ++ struct snd_card *card = platform_get_drvdata(devptr); ++ struct snd_akpcm *akpcm = card->private_data; ++ ++ akpcm_close(akpcm); ++ ++ akpcm_cpufreq_deregister(akpcm); ++ ++ del_timer(&akpcm->stopoutput_work_timer); ++ ++//#ifdef CONFIG_SUPPORT_AEC ++ AECLib_Close(akpcm->pfilter); ++ kfree(akpcm->capture_data); ++ kfree(akpcm->playback_data); ++//#endif ++ /* FIXME */ ++ snd_card_set_dev(card, NULL); ++ snd_card_free(card); ++ platform_set_drvdata(devptr, NULL); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++/** ++ * @brief suspend callback ++ * @author Cheng Mingjuan ++ * @revisor Wu Daochao(2012-09-24) ++ * @date ++ * @return void ++ */ ++static int snd_ak_suspend(struct platform_device *pdev, pm_message_t msg) ++{ ++ struct snd_card *card = platform_get_drvdata(pdev); ++ struct snd_akpcm *akpcm = card->private_data; ++ ++ /* FIXME */ ++ akpcm_close(akpcm); ++ ++ return 0; ++} ++ ++/** ++ * @brief resume callback ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int snd_ak_resume(struct platform_device *pdev) ++{ ++ return 0; ++} ++#else ++#define snd_ak_suspend NULL ++#define snd_ak_resume NULL ++#endif ++ ++ ++#define SND_AKPCM_DRIVER "snd_akpcm" ++ ++static struct platform_driver snd_akpcm_driver = { ++ .probe = snd_akpcm_probe, ++ .remove = __devexit_p(snd_akpcm_remove), ++ .suspend = snd_ak_suspend, ++ .resume = snd_ak_resume, ++ .driver = { ++ .name = SND_AKPCM_DRIVER ++ }, ++}; ++ ++/** ++ * @brief register driver ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static int __init alsa_card_akpcm_init(void) ++{ ++ int err; ++ ++ err = platform_driver_register(&snd_akpcm_driver); ++ if (err < 0) ++ return err; ++ ++ register_reboot_notifier(&akpcm_reboot_notifier); ++ ++ return 0; ++} ++ ++/** ++ * @brief unregister driver ++ * @author Cheng Mingjuan ++ * @date ++ * @return void ++ */ ++static void __exit alsa_card_akpcm_exit(void) ++{ ++ platform_driver_unregister(&snd_akpcm_driver); ++ unregister_reboot_notifier(&akpcm_reboot_notifier); ++} ++ ++module_init(alsa_card_akpcm_init); ++module_exit(alsa_card_akpcm_exit); ++ ++MODULE_AUTHOR("Anyka, Inc."); ++MODULE_DESCRIPTION("akpcm soundcard"); ++MODULE_LICENSE("GPL"); ++MODULE_SUPPORTED_DEVICE("{{ALSA,akpcm soundcard}}"); ++ +diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c +index c88d9741..18711a50 100644 +--- a/sound/soc/soc-core.c ++++ b/sound/soc/soc-core.c +@@ -1412,7 +1412,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) + struct snd_soc_dai_link *dai_link; + int ret, i, order; + +- mutex_lock(&card->mutex); ++ mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); + + if (card->instantiated) { + mutex_unlock(&card->mutex); +@@ -3123,6 +3123,7 @@ int snd_soc_register_card(struct snd_soc_card *card) + INIT_LIST_HEAD(&card->dapm_dirty); + card->instantiated = 0; + mutex_init(&card->mutex); ++ mutex_init(&card->dapm_mutex); + + mutex_lock(&client_mutex); + list_add(&card->list, &card_list); +diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c +index 9ae82a4e..6201fc54 100644 +--- a/sound/soc/soc-dapm.c ++++ b/sound/soc/soc-dapm.c +@@ -1947,6 +1947,8 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, + */ + int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) + { ++ int ret; ++ + /* + * Suppress early reports (eg, jacks syncing their state) to avoid + * silly DAPM runs during card startup. +@@ -1954,7 +1956,10 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) + if (!dapm->card || !dapm->card->instantiated) + return 0; + +- return dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); ++ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); ++ ret = dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); ++ mutex_unlock(&dapm->card->dapm_mutex); ++ return ret; + } + EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); + +@@ -2118,19 +2123,21 @@ err: + int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route, int num) + { +- int i, ret; ++ int i, ret = 0; + ++ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + for (i = 0; i < num; i++) { + ret = snd_soc_dapm_add_route(dapm, route); + if (ret < 0) { + dev_err(dapm->dev, "Failed to add route %s->%s\n", + route->source, route->sink); +- return ret; ++ break; + } + route++; + } ++ mutex_unlock(&dapm->card->dapm_mutex); + +- return 0; ++ return ret; + } + EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); + +@@ -2201,12 +2208,14 @@ int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, + int i, err; + int ret = 0; + ++ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + for (i = 0; i < num; i++) { + err = snd_soc_dapm_weak_route(dapm, route); + if (err) + ret = err; + route++; + } ++ mutex_unlock(&dapm->card->dapm_mutex); + + return ret; + } +@@ -2225,6 +2234,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) + struct snd_soc_dapm_widget *w; + unsigned int val; + ++ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); ++ + list_for_each_entry(w, &dapm->card->widgets, list) + { + if (w->new) +@@ -2234,8 +2245,10 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) + w->kcontrols = kzalloc(w->num_kcontrols * + sizeof(struct snd_kcontrol *), + GFP_KERNEL); +- if (!w->kcontrols) ++ if (!w->kcontrols) { ++ mutex_unlock(&dapm->card->dapm_mutex); + return -ENOMEM; ++ } + } + + switch(w->id) { +@@ -2275,6 +2288,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) + } + + dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); ++ mutex_unlock(&dapm->card->dapm_mutex); + return 0; + } + EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); +@@ -2334,6 +2348,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; ++ struct snd_soc_card *card = codec->card; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; +@@ -2360,7 +2375,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, + /* old connection must be powered down */ + connect = invert ? 1 : 0; + +- mutex_lock(&codec->mutex); ++ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); + + change = snd_soc_test_bits(widget->codec, reg, mask, val); + if (change) { +@@ -2382,7 +2397,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, + } + } + +- mutex_unlock(&codec->mutex); ++ mutex_unlock(&card->dapm_mutex); + return 0; + } + EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); +@@ -2431,6 +2446,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; ++ struct snd_soc_card *card = codec->card; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val, mux, change; + unsigned int mask, bitmask; +@@ -2451,7 +2467,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, + mask |= (bitmask - 1) << e->shift_r; + } + +- mutex_lock(&codec->mutex); ++ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); + + change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + if (change) { +@@ -2473,7 +2489,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, + } + } + +- mutex_unlock(&codec->mutex); ++ mutex_unlock(&card->dapm_mutex); + return change; + } + EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); +@@ -2510,6 +2526,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; ++ struct snd_soc_card *card = codec->card; + struct soc_enum *e = + (struct soc_enum *)kcontrol->private_value; + int change; +@@ -2519,7 +2536,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, + if (ucontrol->value.enumerated.item[0] >= e->max) + return -EINVAL; + +- mutex_lock(&codec->mutex); ++ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); + + change = widget->value != ucontrol->value.enumerated.item[0]; + if (change) { +@@ -2532,7 +2549,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, + } + } + +- mutex_unlock(&codec->mutex); ++ mutex_unlock(&card->dapm_mutex); + return ret; + } + EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); +@@ -2597,6 +2614,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; ++ struct snd_soc_card *card = codec->card; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val, mux, change; + unsigned int mask; +@@ -2615,7 +2633,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, + mask |= e->mask << e->shift_r; + } + +- mutex_lock(&codec->mutex); ++ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); + + change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + if (change) { +@@ -2637,7 +2655,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, + } + } + +- mutex_unlock(&codec->mutex); ++ mutex_unlock(&card->dapm_mutex); + return change; + } + EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); +@@ -2674,12 +2692,12 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + const char *pin = (const char *)kcontrol->private_value; + +- mutex_lock(&card->mutex); ++ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); + + ucontrol->value.integer.value[0] = + snd_soc_dapm_get_pin_status(&card->dapm, pin); + +- mutex_unlock(&card->mutex); ++ mutex_unlock(&card->dapm_mutex); + + return 0; + } +@@ -2697,17 +2715,16 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + const char *pin = (const char *)kcontrol->private_value; + +- mutex_lock(&card->mutex); ++ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); + + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_enable_pin(&card->dapm, pin); + else + snd_soc_dapm_disable_pin(&card->dapm, pin); + +- snd_soc_dapm_sync(&card->dapm); +- +- mutex_unlock(&card->mutex); ++ mutex_unlock(&card->dapm_mutex); + ++ snd_soc_dapm_sync(&card->dapm); + return 0; + } + EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch); +@@ -2824,18 +2841,22 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, + { + struct snd_soc_dapm_widget *w; + int i; ++ int ret = 0; + ++ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + for (i = 0; i < num; i++) { + w = snd_soc_dapm_new_control(dapm, widget); + if (!w) { + dev_err(dapm->dev, + "ASoC: Failed to create DAPM control %s\n", + widget->name); +- return -ENOMEM; ++ ret = -ENOMEM; ++ break; + } + widget++; + } +- return 0; ++ mutex_unlock(&dapm->card->dapm_mutex); ++ return ret; + } + EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls); + +@@ -2989,11 +3010,11 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, + int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, + struct snd_soc_dai *dai, int event) + { +- struct snd_soc_codec *codec = rtd->codec; ++ struct snd_soc_card *card = rtd->card; + +- mutex_lock(&codec->mutex); +- soc_dapm_stream_event(&codec->dapm, stream, dai, event); +- mutex_unlock(&codec->mutex); ++ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); ++ soc_dapm_stream_event(&card->dapm, stream, dai, event); ++ mutex_unlock(&card->dapm_mutex); + return 0; + } + diff --git a/br-ext-chip-anyka/board/ak3918ev200/kernel/patches/11_fix_yylloc_for_modern_computers.patch b/br-ext-chip-anyka/board/ak3918ev200/kernel/patches/11_fix_yylloc_for_modern_computers.patch new file mode 100644 index 00000000..d7088f90 --- /dev/null +++ b/br-ext-chip-anyka/board/ak3918ev200/kernel/patches/11_fix_yylloc_for_modern_computers.patch @@ -0,0 +1,11 @@ +--- a/scripts/dtc/dtc-lexer.lex.c_shipped 2015-08-07 22:08:04.000000000 +0300 ++++ b/scripts/dtc/dtc-lexer.lex.c_shipped 2021-11-01 22:15:12.347053553 +0300 +@@ -637,7 +637,7 @@ + #include "srcpos.h" + #include "dtc-parser.tab.h" + +-YYLTYPE yylloc; ++extern YYLTYPE yylloc; + + /* CAUTION: this will stop working if we ever use yyless() or yyunput() */ + #define YY_USER_ACTION \ diff --git a/br-ext-chip-anyka/board/ak3918ev200/kernel/patches/overlayfs.v13-3.4-rc7.patch b/br-ext-chip-anyka/board/ak3918ev200/kernel/patches/overlayfs.v13-3.4-rc7.patch new file mode 100644 index 00000000..9787cc64 --- /dev/null +++ b/br-ext-chip-anyka/board/ak3918ev200/kernel/patches/overlayfs.v13-3.4-rc7.patch @@ -0,0 +1,3363 @@ +diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking +index 4fca82e..42b0539 100644 +--- a/Documentation/filesystems/Locking ++++ b/Documentation/filesystems/Locking +@@ -62,6 +62,7 @@ ata *); + int (*removexattr) (struct dentry *, const char *); + void (*truncate_range)(struct inode *, loff_t, loff_t); + int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len); ++ struct file *(*open)(struct dentry *,struct file *,const struct cred *); + + locking rules: + all may block +@@ -89,6 +90,7 @@ listxattr: no + removexattr: yes + truncate_range: yes + fiemap: no ++open: no + Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on + victim. + cross-directory ->rename() has (per-superblock) ->s_vfs_rename_sem. +diff --git a/Documentation/filesystems/overlayfs.txt b/Documentation/filesystems/overlayfs.txt +new file mode 100644 +index 0000000..7161dc3 +--- /dev/null ++++ b/Documentation/filesystems/overlayfs.txt +@@ -0,0 +1,199 @@ ++Written by: Neil Brown ++ ++Overlay Filesystem ++================== ++ ++This document describes a prototype for a new approach to providing ++overlay-filesystem functionality in Linux (sometimes referred to as ++union-filesystems). An overlay-filesystem tries to present a ++filesystem which is the result over overlaying one filesystem on top ++of the other. ++ ++The result will inevitably fail to look exactly like a normal ++filesystem for various technical reasons. The expectation is that ++many use cases will be able to ignore these differences. ++ ++This approach is 'hybrid' because the objects that appear in the ++filesystem do not all appear to belong to that filesystem. In many ++cases an object accessed in the union will be indistinguishable ++from accessing the corresponding object from the original filesystem. ++This is most obvious from the 'st_dev' field returned by stat(2). ++ ++While directories will report an st_dev from the overlay-filesystem, ++all non-directory objects will report an st_dev from the lower or ++upper filesystem that is providing the object. Similarly st_ino will ++only be unique when combined with st_dev, and both of these can change ++over the lifetime of a non-directory object. Many applications and ++tools ignore these values and will not be affected. ++ ++Upper and Lower ++--------------- ++ ++An overlay filesystem combines two filesystems - an 'upper' filesystem ++and a 'lower' filesystem. When a name exists in both filesystems, the ++object in the 'upper' filesystem is visible while the object in the ++'lower' filesystem is either hidden or, in the case of directories, ++merged with the 'upper' object. ++ ++It would be more correct to refer to an upper and lower 'directory ++tree' rather than 'filesystem' as it is quite possible for both ++directory trees to be in the same filesystem and there is no ++requirement that the root of a filesystem be given for either upper or ++lower. ++ ++The lower filesystem can be any filesystem supported by Linux and does ++not need to be writable. The lower filesystem can even be another ++overlayfs. The upper filesystem will normally be writable and if it ++is it must support the creation of trusted.* extended attributes, and ++must provide valid d_type in readdir responses, at least for symbolic ++links - so NFS is not suitable. ++ ++A read-only overlay of two read-only filesystems may use any ++filesystem type. ++ ++Directories ++----------- ++ ++Overlaying mainly involved directories. If a given name appears in both ++upper and lower filesystems and refers to a non-directory in either, ++then the lower object is hidden - the name refers only to the upper ++object. ++ ++Where both upper and lower objects are directories, a merged directory ++is formed. ++ ++At mount time, the two directories given as mount options are combined ++into a merged directory: ++ ++ mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper /overlay ++ ++Then whenever a lookup is requested in such a merged directory, the ++lookup is performed in each actual directory and the combined result ++is cached in the dentry belonging to the overlay filesystem. If both ++actual lookups find directories, both are stored and a merged ++directory is created, otherwise only one is stored: the upper if it ++exists, else the lower. ++ ++Only the lists of names from directories are merged. Other content ++such as metadata and extended attributes are reported for the upper ++directory only. These attributes of the lower directory are hidden. ++ ++whiteouts and opaque directories ++-------------------------------- ++ ++In order to support rm and rmdir without changing the lower ++filesystem, an overlay filesystem needs to record in the upper filesystem ++that files have been removed. This is done using whiteouts and opaque ++directories (non-directories are always opaque). ++ ++The overlay filesystem uses extended attributes with a ++"trusted.overlay." prefix to record these details. ++ ++A whiteout is created as a symbolic link with target ++"(overlay-whiteout)" and with xattr "trusted.overlay.whiteout" set to "y". ++When a whiteout is found in the upper level of a merged directory, any ++matching name in the lower level is ignored, and the whiteout itself ++is also hidden. ++ ++A directory is made opaque by setting the xattr "trusted.overlay.opaque" ++to "y". Where the upper filesystem contains an opaque directory, any ++directory in the lower filesystem with the same name is ignored. ++ ++readdir ++------- ++ ++When a 'readdir' request is made on a merged directory, the upper and ++lower directories are each read and the name lists merged in the ++obvious way (upper is read first, then lower - entries that already ++exist are not re-added). This merged name list is cached in the ++'struct file' and so remains as long as the file is kept open. If the ++directory is opened and read by two processes at the same time, they ++will each have separate caches. A seekdir to the start of the ++directory (offset 0) followed by a readdir will cause the cache to be ++discarded and rebuilt. ++ ++This means that changes to the merged directory do not appear while a ++directory is being read. This is unlikely to be noticed by many ++programs. ++ ++seek offsets are assigned sequentially when the directories are read. ++Thus if ++ - read part of a directory ++ - remember an offset, and close the directory ++ - re-open the directory some time later ++ - seek to the remembered offset ++ ++there may be little correlation between the old and new locations in ++the list of filenames, particularly if anything has changed in the ++directory. ++ ++Readdir on directories that are not merged is simply handled by the ++underlying directory (upper or lower). ++ ++ ++Non-directories ++--------------- ++ ++Objects that are not directories (files, symlinks, device-special ++files etc.) are presented either from the upper or lower filesystem as ++appropriate. When a file in the lower filesystem is accessed in a way ++the requires write-access, such as opening for write access, changing ++some metadata etc., the file is first copied from the lower filesystem ++to the upper filesystem (copy_up). Note that creating a hard-link ++also requires copy_up, though of course creation of a symlink does ++not. ++ ++The copy_up may turn out to be unnecessary, for example if the file is ++opened for read-write but the data is not modified. ++ ++The copy_up process first makes sure that the containing directory ++exists in the upper filesystem - creating it and any parents as ++necessary. It then creates the object with the same metadata (owner, ++mode, mtime, symlink-target etc.) and then if the object is a file, the ++data is copied from the lower to the upper filesystem. Finally any ++extended attributes are copied up. ++ ++Once the copy_up is complete, the overlay filesystem simply ++provides direct access to the newly created file in the upper ++filesystem - future operations on the file are barely noticed by the ++overlay filesystem (though an operation on the name of the file such as ++rename or unlink will of course be noticed and handled). ++ ++ ++Non-standard behavior ++--------------------- ++ ++The copy_up operation essentially creates a new, identical file and ++moves it over to the old name. The new file may be on a different ++filesystem, so both st_dev and st_ino of the file may change. ++ ++Any open files referring to this inode will access the old data and ++metadata. Similarly any file locks obtained before copy_up will not ++apply to the copied up file. ++ ++On a file is opened with O_RDONLY fchmod(2), fchown(2), futimesat(2) ++and fsetxattr(2) will fail with EROFS. ++ ++If a file with multiple hard links is copied up, then this will ++"break" the link. Changes will not be propagated to other names ++referring to the same inode. ++ ++Symlinks in /proc/PID/ and /proc/PID/fd which point to a non-directory ++object in overlayfs will not contain vaid absolute paths, only ++relative paths leading up to the filesystem's root. This will be ++fixed in the future. ++ ++Some operations are not atomic, for example a crash during copy_up or ++rename will leave the filesystem in an inconsitent state. This will ++be addressed in the future. ++ ++Changes to underlying filesystems ++--------------------------------- ++ ++Offline changes, when the overlay is not mounted, are allowed to either ++the upper or the lower trees. ++ ++Changes to the underlying filesystems while part of a mounted overlay ++filesystem are not allowed. If the underlying filesystem is changed, ++the behavior of the overlay is undefined, though it will not result in ++a crash or deadlock. +diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt +index 0d04920..f06c91f 100644 +--- a/Documentation/filesystems/vfs.txt ++++ b/Documentation/filesystems/vfs.txt +@@ -364,6 +364,8 @@ struct inode_operations { + ssize_t (*listxattr) (struct dentry *, char *, size_t); + int (*removexattr) (struct dentry *, const char *); + void (*truncate_range)(struct inode *, loff_t, loff_t); ++ struct file *(*open) (struct dentry *, struct file *, ++ const struct cred *); + }; + + Again, all methods are called without any locks being held, unless +@@ -475,6 +477,12 @@ otherwise noted. + truncate_range: a method provided by the underlying filesystem to truncate a + range of blocks , i.e. punch a hole somewhere in a file. + ++ open: this is an alternative to f_op->open(), the difference is that this ++ method may return any open file, not necessarily originating from the ++ same filesystem as the one i_op->open() was called on. It may be useful ++ for stacking filesystems which want to allow native I/O directly on ++ underlying files. ++ + + The Address Space Object + ======================== +diff --git a/MAINTAINERS b/MAINTAINERS +index b362709..7ba226c 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -4998,6 +4998,13 @@ F: drivers/scsi/osd/ + F: include/scsi/osd_* + F: fs/exofs/ + ++OVERLAYFS FILESYSTEM ++M: Miklos Szeredi ++L: linux-fsdevel@vger.kernel.org ++S: Supported ++F: fs/overlayfs/* ++F: Documentation/filesystems/overlayfs.txt ++ + P54 WIRELESS DRIVER + M: Christian Lamparter + L: linux-wireless@vger.kernel.org +diff --git a/fs/Kconfig b/fs/Kconfig +index f95ae3a..e0c5d43 100644 +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -67,6 +67,7 @@ source "fs/quota/Kconfig" + + source "fs/autofs4/Kconfig" + source "fs/fuse/Kconfig" ++source "fs/overlayfs/Kconfig" + + config CUSE + tristate "Character device in Userspace support" +diff --git a/fs/Makefile b/fs/Makefile +index 2fb9779..fcd9788 100644 +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -106,6 +106,7 @@ obj-$(CONFIG_QNX6FS_FS) += qnx6/ + obj-$(CONFIG_AUTOFS4_FS) += autofs4/ + obj-$(CONFIG_ADFS_FS) += adfs/ + obj-$(CONFIG_FUSE_FS) += fuse/ ++obj-$(CONFIG_OVERLAYFS_FS) += overlayfs/ + obj-$(CONFIG_UDF_FS) += udf/ + obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ + obj-$(CONFIG_OMFS_FS) += omfs/ +diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c +index 6895493..c54ea90 100644 +--- a/fs/ecryptfs/main.c ++++ b/fs/ecryptfs/main.c +@@ -544,6 +544,13 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags + s->s_maxbytes = path.dentry->d_sb->s_maxbytes; + s->s_blocksize = path.dentry->d_sb->s_blocksize; + s->s_magic = ECRYPTFS_SUPER_MAGIC; ++ s->s_stack_depth = path.dentry->d_sb->s_stack_depth + 1; ++ ++ rc = -EINVAL; ++ if (s->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { ++ printk(KERN_ERR "eCryptfs: maximum fs stacking depth exceeded\n"); ++ goto out_free; ++ } + + inode = ecryptfs_get_inode(path.dentry->d_inode, s); + rc = PTR_ERR(inode); +diff --git a/fs/namei.c b/fs/namei.c +index c427919..e172a5b 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -328,6 +328,36 @@ static inline int do_inode_permission(struct inode *inode, int mask) + } + + /** ++ * inode_only_permission - check access rights to a given inode only ++ * @inode: inode to check permissions on ++ * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC, ...) ++ * ++ * Uses to check read/write/execute permissions on an inode directly, we do ++ * not check filesystem permissions. ++ */ ++int inode_only_permission(struct inode *inode, int mask) ++{ ++ int retval; ++ ++ /* ++ * Nobody gets write access to an immutable file. ++ */ ++ if (unlikely(mask & MAY_WRITE) && IS_IMMUTABLE(inode)) ++ return -EACCES; ++ ++ retval = do_inode_permission(inode, mask); ++ if (retval) ++ return retval; ++ ++ retval = devcgroup_inode_permission(inode, mask); ++ if (retval) ++ return retval; ++ ++ return security_inode_permission(inode, mask); ++} ++EXPORT_SYMBOL(inode_only_permission); ++ ++/** + * inode_permission - check for access rights to a given inode + * @inode: inode to check permission on + * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC, ...) +@@ -341,8 +371,6 @@ static inline int do_inode_permission(struct inode *inode, int mask) + */ + int inode_permission(struct inode *inode, int mask) + { +- int retval; +- + if (unlikely(mask & MAY_WRITE)) { + umode_t mode = inode->i_mode; + +@@ -352,23 +380,9 @@ int inode_permission(struct inode *inode, int mask) + if (IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; +- +- /* +- * Nobody gets write access to an immutable file. +- */ +- if (IS_IMMUTABLE(inode)) +- return -EACCES; + } + +- retval = do_inode_permission(inode, mask); +- if (retval) +- return retval; +- +- retval = devcgroup_inode_permission(inode, mask); +- if (retval) +- return retval; +- +- return security_inode_permission(inode, mask); ++ return inode_only_permission(inode, mask); + } + + /** +diff --git a/fs/namespace.c b/fs/namespace.c +index e608199..2551ec0 100644 +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -1325,6 +1325,24 @@ void drop_collected_mounts(struct vfsmount *mnt) + release_mounts(&umount_list); + } + ++struct vfsmount *clone_private_mount(struct path *path) ++{ ++ struct mount *old_mnt = real_mount(path->mnt); ++ struct mount *new_mnt; ++ ++ if (IS_MNT_UNBINDABLE(old_mnt)) ++ return ERR_PTR(-EINVAL); ++ ++ down_read(&namespace_sem); ++ new_mnt = clone_mnt(old_mnt, path->dentry, CL_PRIVATE); ++ up_read(&namespace_sem); ++ if (!new_mnt) ++ return ERR_PTR(-ENOMEM); ++ ++ return &new_mnt->mnt; ++} ++EXPORT_SYMBOL_GPL(clone_private_mount); ++ + int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, + struct vfsmount *root) + { +diff --git a/fs/open.c b/fs/open.c +index 5720854..3e132ba 100644 +--- a/fs/open.c ++++ b/fs/open.c +@@ -644,24 +644,24 @@ static inline int __get_file_write_access(struct inode *inode, + return error; + } + +-static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, +- struct file *f, +- int (*open)(struct inode *, struct file *), +- const struct cred *cred) ++static struct file *__dentry_open(struct path *path, struct file *f, ++ int (*open)(struct inode *, struct file *), ++ const struct cred *cred) + { + static const struct file_operations empty_fops = {}; + struct inode *inode; + int error; + ++ path_get(path); + f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | + FMODE_PREAD | FMODE_PWRITE; + + if (unlikely(f->f_flags & O_PATH)) + f->f_mode = FMODE_PATH; + +- inode = dentry->d_inode; ++ inode = path->dentry->d_inode; + if (f->f_mode & FMODE_WRITE) { +- error = __get_file_write_access(inode, mnt); ++ error = __get_file_write_access(inode, path->mnt); + if (error) + goto cleanup_file; + if (!special_file(inode->i_mode)) +@@ -669,8 +669,7 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, + } + + f->f_mapping = inode->i_mapping; +- f->f_path.dentry = dentry; +- f->f_path.mnt = mnt; ++ f->f_path = *path; + f->f_pos = 0; + file_sb_list_add(f, inode->i_sb); + +@@ -727,7 +726,7 @@ cleanup_all: + * here, so just reset the state. + */ + file_reset_write(f); +- mnt_drop_write(mnt); ++ mnt_drop_write(path->mnt); + } + } + file_sb_list_del(f); +@@ -735,8 +734,7 @@ cleanup_all: + f->f_path.mnt = NULL; + cleanup_file: + put_filp(f); +- dput(dentry); +- mntput(mnt); ++ path_put(path); + return ERR_PTR(error); + } + +@@ -762,14 +760,14 @@ cleanup_file: + struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, + int (*open)(struct inode *, struct file *)) + { ++ struct path path = { .dentry = dentry, .mnt = nd->path.mnt }; + const struct cred *cred = current_cred(); + + if (IS_ERR(nd->intent.open.file)) + goto out; + if (IS_ERR(dentry)) + goto out_err; +- nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->path.mnt), +- nd->intent.open.file, ++ nd->intent.open.file = __dentry_open(&path, nd->intent.open.file, + open, cred); + out: + return nd->intent.open.file; +@@ -797,11 +795,9 @@ struct file *nameidata_to_filp(struct nameidata *nd) + nd->intent.open.file = NULL; + + /* Has the filesystem initialised the file for us? */ +- if (filp->f_path.dentry == NULL) { +- path_get(&nd->path); +- filp = __dentry_open(nd->path.dentry, nd->path.mnt, filp, +- NULL, cred); +- } ++ if (filp->f_path.dentry == NULL) ++ filp = vfs_open(&nd->path, filp, cred); ++ + return filp; + } + +@@ -812,27 +808,48 @@ struct file *nameidata_to_filp(struct nameidata *nd) + struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags, + const struct cred *cred) + { +- int error; + struct file *f; ++ struct file *ret; ++ struct path path = { .dentry = dentry, .mnt = mnt }; + + validate_creds(cred); + + /* We must always pass in a valid mount pointer. */ + BUG_ON(!mnt); + +- error = -ENFILE; ++ ret = ERR_PTR(-ENFILE); + f = get_empty_filp(); +- if (f == NULL) { +- dput(dentry); +- mntput(mnt); +- return ERR_PTR(error); ++ if (f != NULL) { ++ f->f_flags = flags; ++ ret = vfs_open(&path, f, cred); + } ++ path_put(&path); + +- f->f_flags = flags; +- return __dentry_open(dentry, mnt, f, NULL, cred); ++ return ret; + } + EXPORT_SYMBOL(dentry_open); + ++/** ++ * vfs_open - open the file at the given path ++ * @path: path to open ++ * @filp: newly allocated file with f_flag initialized ++ * @cred: credentials to use ++ * ++ * Open the file. If successful, the returned file will have acquired ++ * an additional reference for path. ++ */ ++struct file *vfs_open(struct path *path, struct file *filp, ++ const struct cred *cred) ++{ ++ struct inode *inode = path->dentry->d_inode; ++ ++ if (inode->i_op->open) ++ return inode->i_op->open(path->dentry, filp, cred); ++ else ++ return __dentry_open(path, filp, NULL, cred); ++} ++EXPORT_SYMBOL(vfs_open); ++ + static void __put_unused_fd(struct files_struct *files, unsigned int fd) + { + struct fdtable *fdt = files_fdtable(files); +diff --git a/fs/overlayfs/Kconfig b/fs/overlayfs/Kconfig +new file mode 100644 +index 0000000..c4517da +--- /dev/null ++++ b/fs/overlayfs/Kconfig +@@ -0,0 +1,4 @@ ++config OVERLAYFS_FS ++ tristate "Overlay filesystem support" ++ help ++ Add support for overlay filesystem. +diff --git a/fs/overlayfs/Makefile b/fs/overlayfs/Makefile +new file mode 100644 +index 0000000..8f91889 +--- /dev/null ++++ b/fs/overlayfs/Makefile +@@ -0,0 +1,7 @@ ++# ++# Makefile for the overlay filesystem. ++# ++ ++obj-$(CONFIG_OVERLAYFS_FS) += overlayfs.o ++ ++overlayfs-objs := super.o inode.o dir.o readdir.o copy_up.o +diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c +new file mode 100644 +index 0000000..87dbeee +--- /dev/null ++++ b/fs/overlayfs/copy_up.c +@@ -0,0 +1,385 @@ ++/* ++ * ++ * Copyright (C) 2011 Novell Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "overlayfs.h" ++ ++#define OVL_COPY_UP_CHUNK_SIZE (1 << 20) ++ ++static int ovl_copy_up_xattr(struct dentry *old, struct dentry *new) ++{ ++ ssize_t list_size, size; ++ char *buf, *name, *value; ++ int error; ++ ++ if (!old->d_inode->i_op->getxattr || ++ !new->d_inode->i_op->getxattr) ++ return 0; ++ ++ list_size = vfs_listxattr(old, NULL, 0); ++ if (list_size <= 0) { ++ if (list_size == -EOPNOTSUPP) ++ return 0; ++ return list_size; ++ } ++ ++ buf = kzalloc(list_size, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ error = -ENOMEM; ++ value = kmalloc(XATTR_SIZE_MAX, GFP_KERNEL); ++ if (!value) ++ goto out; ++ ++ list_size = vfs_listxattr(old, buf, list_size); ++ if (list_size <= 0) { ++ error = list_size; ++ goto out_free_value; ++ } ++ ++ for (name = buf; name < (buf + list_size); name += strlen(name) + 1) { ++ size = vfs_getxattr(old, name, value, XATTR_SIZE_MAX); ++ if (size <= 0) { ++ error = size; ++ goto out_free_value; ++ } ++ error = vfs_setxattr(new, name, value, size, 0); ++ if (error) ++ goto out_free_value; ++ } ++ ++out_free_value: ++ kfree(value); ++out: ++ kfree(buf); ++ return error; ++} ++ ++static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len) ++{ ++ struct file *old_file; ++ struct file *new_file; ++ int error = 0; ++ ++ if (len == 0) ++ return 0; ++ ++ old_file = ovl_path_open(old, O_RDONLY); ++ if (IS_ERR(old_file)) ++ return PTR_ERR(old_file); ++ ++ new_file = ovl_path_open(new, O_WRONLY); ++ if (IS_ERR(new_file)) { ++ error = PTR_ERR(new_file); ++ goto out_fput; ++ } ++ ++ /* FIXME: copy up sparse files efficiently */ ++ while (len) { ++ loff_t offset = new_file->f_pos; ++ size_t this_len = OVL_COPY_UP_CHUNK_SIZE; ++ long bytes; ++ ++ if (len < this_len) ++ this_len = len; ++ ++ if (signal_pending_state(TASK_KILLABLE, current)) { ++ error = -EINTR; ++ break; ++ } ++ ++ bytes = do_splice_direct(old_file, &offset, new_file, this_len, ++ SPLICE_F_MOVE); ++ if (bytes <= 0) { ++ error = bytes; ++ break; ++ } ++ ++ len -= bytes; ++ } ++ ++ fput(new_file); ++out_fput: ++ fput(old_file); ++ return error; ++} ++ ++static char *ovl_read_symlink(struct dentry *realdentry) ++{ ++ int res; ++ char *buf; ++ struct inode *inode = realdentry->d_inode; ++ mm_segment_t old_fs; ++ ++ res = -EINVAL; ++ if (!inode->i_op->readlink) ++ goto err; ++ ++ res = -ENOMEM; ++ buf = (char *) __get_free_page(GFP_KERNEL); ++ if (!buf) ++ goto err; ++ ++ old_fs = get_fs(); ++ set_fs(get_ds()); ++ /* The cast to a user pointer is valid due to the set_fs() */ ++ res = inode->i_op->readlink(realdentry, ++ (char __user *)buf, PAGE_SIZE - 1); ++ set_fs(old_fs); ++ if (res < 0) { ++ free_page((unsigned long) buf); ++ goto err; ++ } ++ buf[res] = '\0'; ++ ++ return buf; ++ ++err: ++ return ERR_PTR(res); ++} ++ ++static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat) ++{ ++ struct iattr attr = { ++ .ia_valid = ++ ATTR_ATIME | ATTR_MTIME | ATTR_ATIME_SET | ATTR_MTIME_SET, ++ .ia_atime = stat->atime, ++ .ia_mtime = stat->mtime, ++ }; ++ ++ return notify_change(upperdentry, &attr); ++} ++ ++static int ovl_set_mode(struct dentry *upperdentry, umode_t mode) ++{ ++ struct iattr attr = { ++ .ia_valid = ATTR_MODE, ++ .ia_mode = mode, ++ }; ++ ++ return notify_change(upperdentry, &attr); ++} ++ ++static int ovl_copy_up_locked(struct dentry *upperdir, struct dentry *dentry, ++ struct path *lowerpath, struct kstat *stat, ++ const char *link) ++{ ++ int err; ++ struct path newpath; ++ umode_t mode = stat->mode; ++ ++ /* Can't properly set mode on creation because of the umask */ ++ stat->mode &= S_IFMT; ++ ++ ovl_path_upper(dentry, &newpath); ++ WARN_ON(newpath.dentry); ++ newpath.dentry = ovl_upper_create(upperdir, dentry, stat, link); ++ if (IS_ERR(newpath.dentry)) ++ return PTR_ERR(newpath.dentry); ++ ++ if (S_ISREG(stat->mode)) { ++ err = ovl_copy_up_data(lowerpath, &newpath, stat->size); ++ if (err) ++ goto err_remove; ++ } ++ ++ err = ovl_copy_up_xattr(lowerpath->dentry, newpath.dentry); ++ if (err) ++ goto err_remove; ++ ++ mutex_lock(&newpath.dentry->d_inode->i_mutex); ++ if (!S_ISLNK(stat->mode)) ++ err = ovl_set_mode(newpath.dentry, mode); ++ if (!err) ++ err = ovl_set_timestamps(newpath.dentry, stat); ++ mutex_unlock(&newpath.dentry->d_inode->i_mutex); ++ if (err) ++ goto err_remove; ++ ++ ovl_dentry_update(dentry, newpath.dentry); ++ ++ /* ++ * Easiest way to get rid of the lower dentry reference is to ++ * drop this dentry. This is neither needed nor possible for ++ * directories. ++ */ ++ if (!S_ISDIR(stat->mode)) ++ d_drop(dentry); ++ ++ return 0; ++ ++err_remove: ++ if (S_ISDIR(stat->mode)) ++ vfs_rmdir(upperdir->d_inode, newpath.dentry); ++ else ++ vfs_unlink(upperdir->d_inode, newpath.dentry); ++ ++ dput(newpath.dentry); ++ ++ return err; ++} ++ ++/* ++ * Copy up a single dentry ++ * ++ * Directory renames only allowed on "pure upper" (already created on ++ * upper filesystem, never copied up). Directories which are on lower or ++ * are merged may not be renamed. For these -EXDEV is returned and ++ * userspace has to deal with it. This means, when copying up a ++ * directory we can rely on it and ancestors being stable. ++ * ++ * Non-directory renames start with copy up of source if necessary. The ++ * actual rename will only proceed once the copy up was successful. Copy ++ * up uses upper parent i_mutex for exclusion. Since rename can change ++ * d_parent it is possible that the copy up will lock the old parent. At ++ * that point the file will have already been copied up anyway. ++ */ ++static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, ++ struct path *lowerpath, struct kstat *stat) ++{ ++ int err; ++ struct kstat pstat; ++ struct path parentpath; ++ struct dentry *upperdir; ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ char *link = NULL; ++ ++ ovl_path_upper(parent, &parentpath); ++ upperdir = parentpath.dentry; ++ ++ err = vfs_getattr(parentpath.mnt, parentpath.dentry, &pstat); ++ if (err) ++ return err; ++ ++ if (S_ISLNK(stat->mode)) { ++ link = ovl_read_symlink(lowerpath->dentry); ++ if (IS_ERR(link)) ++ return PTR_ERR(link); ++ } ++ ++ err = -ENOMEM; ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ goto out_free_link; ++ ++ override_cred->fsuid = stat->uid; ++ override_cred->fsgid = stat->gid; ++ /* ++ * CAP_SYS_ADMIN for copying up extended attributes ++ * CAP_DAC_OVERRIDE for create ++ * CAP_FOWNER for chmod, timestamp update ++ * CAP_FSETID for chmod ++ * CAP_MKNOD for mknod ++ */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); ++ cap_raise(override_cred->cap_effective, CAP_FOWNER); ++ cap_raise(override_cred->cap_effective, CAP_FSETID); ++ cap_raise(override_cred->cap_effective, CAP_MKNOD); ++ old_cred = override_creds(override_cred); ++ ++ mutex_lock_nested(&upperdir->d_inode->i_mutex, I_MUTEX_PARENT); ++ if (ovl_path_type(dentry) != OVL_PATH_LOWER) { ++ err = 0; ++ } else { ++ err = ovl_copy_up_locked(upperdir, dentry, lowerpath, ++ stat, link); ++ if (!err) { ++ /* Restore timestamps on parent (best effort) */ ++ ovl_set_timestamps(upperdir, &pstat); ++ } ++ } ++ ++ mutex_unlock(&upperdir->d_inode->i_mutex); ++ ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ ++out_free_link: ++ if (link) ++ free_page((unsigned long) link); ++ ++ return err; ++} ++ ++int ovl_copy_up(struct dentry *dentry) ++{ ++ int err; ++ ++ err = 0; ++ while (!err) { ++ struct dentry *next; ++ struct dentry *parent; ++ struct path lowerpath; ++ struct kstat stat; ++ enum ovl_path_type type = ovl_path_type(dentry); ++ ++ if (type != OVL_PATH_LOWER) ++ break; ++ ++ next = dget(dentry); ++ /* find the topmost dentry not yet copied up */ ++ for (;;) { ++ parent = dget_parent(next); ++ ++ type = ovl_path_type(parent); ++ if (type != OVL_PATH_LOWER) ++ break; ++ ++ dput(next); ++ next = parent; ++ } ++ ++ ovl_path_lower(next, &lowerpath); ++ err = vfs_getattr(lowerpath.mnt, lowerpath.dentry, &stat); ++ if (!err) ++ err = ovl_copy_up_one(parent, next, &lowerpath, &stat); ++ ++ dput(parent); ++ dput(next); ++ } ++ ++ return err; ++} ++ ++/* Optimize by not copying up the file first and truncating later */ ++int ovl_copy_up_truncate(struct dentry *dentry, loff_t size) ++{ ++ int err; ++ struct kstat stat; ++ struct path lowerpath; ++ struct dentry *parent = dget_parent(dentry); ++ ++ err = ovl_copy_up(parent); ++ if (err) ++ goto out_dput_parent; ++ ++ ovl_path_lower(dentry, &lowerpath); ++ err = vfs_getattr(lowerpath.mnt, lowerpath.dentry, &stat); ++ if (err) ++ goto out_dput_parent; ++ ++ if (size < stat.size) ++ stat.size = size; ++ ++ err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat); ++ ++out_dput_parent: ++ dput(parent); ++ return err; ++} +diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c +new file mode 100644 +index 0000000..c914c97 +--- /dev/null ++++ b/fs/overlayfs/dir.c +@@ -0,0 +1,602 @@ ++/* ++ * ++ * Copyright (C) 2011 Novell Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "overlayfs.h" ++ ++static const char *ovl_whiteout_symlink = "(overlay-whiteout)"; ++ ++static int ovl_whiteout(struct dentry *upperdir, struct dentry *dentry) ++{ ++ int err; ++ struct dentry *newdentry; ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ ++ /* FIXME: recheck lower dentry to see if whiteout is really needed */ ++ ++ err = -ENOMEM; ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ goto out; ++ ++ /* ++ * CAP_SYS_ADMIN for setxattr ++ * CAP_DAC_OVERRIDE for symlink creation ++ * CAP_FOWNER for unlink in sticky directory ++ */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); ++ cap_raise(override_cred->cap_effective, CAP_FOWNER); ++ override_cred->fsuid = 0; ++ override_cred->fsgid = 0; ++ old_cred = override_creds(override_cred); ++ ++ newdentry = lookup_one_len(dentry->d_name.name, upperdir, ++ dentry->d_name.len); ++ err = PTR_ERR(newdentry); ++ if (IS_ERR(newdentry)) ++ goto out_put_cred; ++ ++ /* Just been removed within the same locked region */ ++ WARN_ON(newdentry->d_inode); ++ ++ err = vfs_symlink(upperdir->d_inode, newdentry, ovl_whiteout_symlink); ++ if (err) ++ goto out_dput; ++ ++ ovl_dentry_version_inc(dentry->d_parent); ++ ++ err = vfs_setxattr(newdentry, ovl_whiteout_xattr, "y", 1, 0); ++ if (err) ++ vfs_unlink(upperdir->d_inode, newdentry); ++ ++out_dput: ++ dput(newdentry); ++out_put_cred: ++ revert_creds(old_cred); ++ put_cred(override_cred); ++out: ++ if (err) { ++ /* ++ * There's no way to recover from failure to whiteout. ++ * What should we do? Log a big fat error and... ? ++ */ ++ printk(KERN_ERR "overlayfs: ERROR - failed to whiteout '%s'\n", ++ dentry->d_name.name); ++ } ++ ++ return err; ++} ++ ++static struct dentry *ovl_lookup_create(struct dentry *upperdir, ++ struct dentry *template) ++{ ++ int err; ++ struct dentry *newdentry; ++ struct qstr *name = &template->d_name; ++ ++ newdentry = lookup_one_len(name->name, upperdir, name->len); ++ if (IS_ERR(newdentry)) ++ return newdentry; ++ ++ if (newdentry->d_inode) { ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ ++ /* No need to check whiteout if lower parent is non-existent */ ++ err = -EEXIST; ++ if (!ovl_dentry_lower(template->d_parent)) ++ goto out_dput; ++ ++ if (!S_ISLNK(newdentry->d_inode->i_mode)) ++ goto out_dput; ++ ++ err = -ENOMEM; ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ goto out_dput; ++ ++ /* ++ * CAP_SYS_ADMIN for getxattr ++ * CAP_FOWNER for unlink in sticky directory ++ */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ cap_raise(override_cred->cap_effective, CAP_FOWNER); ++ old_cred = override_creds(override_cred); ++ ++ err = -EEXIST; ++ if (ovl_is_whiteout(newdentry)) ++ err = vfs_unlink(upperdir->d_inode, newdentry); ++ ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ if (err) ++ goto out_dput; ++ ++ dput(newdentry); ++ newdentry = lookup_one_len(name->name, upperdir, name->len); ++ if (IS_ERR(newdentry)) { ++ ovl_whiteout(upperdir, template); ++ return newdentry; ++ } ++ ++ /* ++ * Whiteout just been successfully removed, parent ++ * i_mutex is still held, there's no way the lookup ++ * could return positive. ++ */ ++ WARN_ON(newdentry->d_inode); ++ } ++ ++ return newdentry; ++ ++out_dput: ++ dput(newdentry); ++ return ERR_PTR(err); ++} ++ ++struct dentry *ovl_upper_create(struct dentry *upperdir, struct dentry *dentry, ++ struct kstat *stat, const char *link) ++{ ++ int err; ++ struct dentry *newdentry; ++ struct inode *dir = upperdir->d_inode; ++ ++ newdentry = ovl_lookup_create(upperdir, dentry); ++ if (IS_ERR(newdentry)) ++ goto out; ++ ++ switch (stat->mode & S_IFMT) { ++ case S_IFREG: ++ err = vfs_create(dir, newdentry, stat->mode, NULL); ++ break; ++ ++ case S_IFDIR: ++ err = vfs_mkdir(dir, newdentry, stat->mode); ++ break; ++ ++ case S_IFCHR: ++ case S_IFBLK: ++ case S_IFIFO: ++ case S_IFSOCK: ++ err = vfs_mknod(dir, newdentry, stat->mode, stat->rdev); ++ break; ++ ++ case S_IFLNK: ++ err = vfs_symlink(dir, newdentry, link); ++ break; ++ ++ default: ++ err = -EPERM; ++ } ++ if (err) { ++ if (ovl_dentry_is_opaque(dentry)) ++ ovl_whiteout(upperdir, dentry); ++ dput(newdentry); ++ newdentry = ERR_PTR(err); ++ } else if (WARN_ON(!newdentry->d_inode)) { ++ /* ++ * Not quite sure if non-instantiated dentry is legal or not. ++ * VFS doesn't seem to care so check and warn here. ++ */ ++ dput(newdentry); ++ newdentry = ERR_PTR(-ENOENT); ++ } ++ ++out: ++ return newdentry; ++ ++} ++ ++static int ovl_set_opaque(struct dentry *upperdentry) ++{ ++ int err; ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ return -ENOMEM; ++ ++ /* CAP_SYS_ADMIN for setxattr of "trusted" namespace */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ old_cred = override_creds(override_cred); ++ err = vfs_setxattr(upperdentry, ovl_opaque_xattr, "y", 1, 0); ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ ++ return err; ++} ++ ++static int ovl_remove_opaque(struct dentry *upperdentry) ++{ ++ int err; ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ return -ENOMEM; ++ ++ /* CAP_SYS_ADMIN for removexattr of "trusted" namespace */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ old_cred = override_creds(override_cred); ++ err = vfs_removexattr(upperdentry, ovl_opaque_xattr); ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ ++ return err; ++} ++ ++static int ovl_dir_getattr(struct vfsmount *mnt, struct dentry *dentry, ++ struct kstat *stat) ++{ ++ int err; ++ enum ovl_path_type type; ++ struct path realpath; ++ ++ type = ovl_path_real(dentry, &realpath); ++ err = vfs_getattr(realpath.mnt, realpath.dentry, stat); ++ if (err) ++ return err; ++ ++ stat->dev = dentry->d_sb->s_dev; ++ stat->ino = dentry->d_inode->i_ino; ++ ++ /* ++ * It's probably not worth it to count subdirs to get the ++ * correct link count. nlink=1 seems to pacify 'find' and ++ * other utilities. ++ */ ++ if (type == OVL_PATH_MERGE) ++ stat->nlink = 1; ++ ++ return 0; ++} ++ ++static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev, ++ const char *link) ++{ ++ int err; ++ struct dentry *newdentry; ++ struct dentry *upperdir; ++ struct inode *inode; ++ struct kstat stat = { ++ .mode = mode, ++ .rdev = rdev, ++ }; ++ ++ err = -ENOMEM; ++ inode = ovl_new_inode(dentry->d_sb, mode, dentry->d_fsdata); ++ if (!inode) ++ goto out; ++ ++ err = ovl_copy_up(dentry->d_parent); ++ if (err) ++ goto out_iput; ++ ++ upperdir = ovl_dentry_upper(dentry->d_parent); ++ mutex_lock_nested(&upperdir->d_inode->i_mutex, I_MUTEX_PARENT); ++ ++ newdentry = ovl_upper_create(upperdir, dentry, &stat, link); ++ err = PTR_ERR(newdentry); ++ if (IS_ERR(newdentry)) ++ goto out_unlock; ++ ++ ovl_dentry_version_inc(dentry->d_parent); ++ if (ovl_dentry_is_opaque(dentry) && S_ISDIR(mode)) { ++ err = ovl_set_opaque(newdentry); ++ if (err) { ++ vfs_rmdir(upperdir->d_inode, newdentry); ++ ovl_whiteout(upperdir, dentry); ++ goto out_dput; ++ } ++ } ++ ovl_dentry_update(dentry, newdentry); ++ d_instantiate(dentry, inode); ++ inode = NULL; ++ newdentry = NULL; ++ err = 0; ++ ++out_dput: ++ dput(newdentry); ++out_unlock: ++ mutex_unlock(&upperdir->d_inode->i_mutex); ++out_iput: ++ iput(inode); ++out: ++ return err; ++} ++ ++static int ovl_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ struct nameidata *nd) ++{ ++ return ovl_create_object(dentry, (mode & 07777) | S_IFREG, 0, NULL); ++} ++ ++static int ovl_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++{ ++ return ovl_create_object(dentry, (mode & 07777) | S_IFDIR, 0, NULL); ++} ++ ++static int ovl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, ++ dev_t rdev) ++{ ++ return ovl_create_object(dentry, mode, rdev, NULL); ++} ++ ++static int ovl_symlink(struct inode *dir, struct dentry *dentry, ++ const char *link) ++{ ++ return ovl_create_object(dentry, S_IFLNK, 0, link); ++} ++ ++static int ovl_do_remove(struct dentry *dentry, bool is_dir) ++{ ++ int err; ++ enum ovl_path_type type; ++ struct path realpath; ++ struct dentry *upperdir; ++ ++ err = ovl_copy_up(dentry->d_parent); ++ if (err) ++ return err; ++ ++ upperdir = ovl_dentry_upper(dentry->d_parent); ++ mutex_lock_nested(&upperdir->d_inode->i_mutex, I_MUTEX_PARENT); ++ type = ovl_path_real(dentry, &realpath); ++ if (type != OVL_PATH_LOWER) { ++ err = -ESTALE; ++ if (realpath.dentry->d_parent != upperdir) ++ goto out_d_drop; ++ ++ /* FIXME: create whiteout up front and rename to target */ ++ ++ if (is_dir) ++ err = vfs_rmdir(upperdir->d_inode, realpath.dentry); ++ else ++ err = vfs_unlink(upperdir->d_inode, realpath.dentry); ++ if (err) ++ goto out_d_drop; ++ ++ ovl_dentry_version_inc(dentry->d_parent); ++ } ++ ++ if (type != OVL_PATH_UPPER || ovl_dentry_is_opaque(dentry)) ++ err = ovl_whiteout(upperdir, dentry); ++ ++ /* ++ * Keeping this dentry hashed would mean having to release ++ * upperpath/lowerpath, which could only be done if we are the ++ * sole user of this dentry. Too tricky... Just unhash for ++ * now. ++ */ ++out_d_drop: ++ d_drop(dentry); ++ mutex_unlock(&upperdir->d_inode->i_mutex); ++ ++ return err; ++} ++ ++static int ovl_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ return ovl_do_remove(dentry, false); ++} ++ ++ ++static int ovl_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ int err; ++ enum ovl_path_type type; ++ ++ type = ovl_path_type(dentry); ++ if (type != OVL_PATH_UPPER) { ++ err = ovl_check_empty_and_clear(dentry, type); ++ if (err) ++ return err; ++ } ++ ++ return ovl_do_remove(dentry, true); ++} ++ ++static int ovl_link(struct dentry *old, struct inode *newdir, ++ struct dentry *new) ++{ ++ int err; ++ struct dentry *olddentry; ++ struct dentry *newdentry; ++ struct dentry *upperdir; ++ struct inode *newinode; ++ ++ err = ovl_copy_up(old); ++ if (err) ++ goto out; ++ ++ err = ovl_copy_up(new->d_parent); ++ if (err) ++ goto out; ++ ++ upperdir = ovl_dentry_upper(new->d_parent); ++ mutex_lock_nested(&upperdir->d_inode->i_mutex, I_MUTEX_PARENT); ++ newdentry = ovl_lookup_create(upperdir, new); ++ err = PTR_ERR(newdentry); ++ if (IS_ERR(newdentry)) ++ goto out_unlock; ++ ++ olddentry = ovl_dentry_upper(old); ++ err = vfs_link(olddentry, upperdir->d_inode, newdentry); ++ if (!err) { ++ if (WARN_ON(!newdentry->d_inode)) { ++ dput(newdentry); ++ err = -ENOENT; ++ goto out_unlock; ++ } ++ newinode = ovl_new_inode(old->d_sb, newdentry->d_inode->i_mode, ++ new->d_fsdata); ++ if (!newinode) ++ goto link_fail; ++ ++ ovl_dentry_version_inc(new->d_parent); ++ ovl_dentry_update(new, newdentry); ++ ++ d_instantiate(new, newinode); ++ } else { ++link_fail: ++ if (ovl_dentry_is_opaque(new)) ++ ovl_whiteout(upperdir, new); ++ dput(newdentry); ++ } ++out_unlock: ++ mutex_unlock(&upperdir->d_inode->i_mutex); ++out: ++ return err; ++ ++} ++ ++static int ovl_rename(struct inode *olddir, struct dentry *old, ++ struct inode *newdir, struct dentry *new) ++{ ++ int err; ++ enum ovl_path_type old_type; ++ enum ovl_path_type new_type; ++ struct dentry *old_upperdir; ++ struct dentry *new_upperdir; ++ struct dentry *olddentry; ++ struct dentry *newdentry; ++ struct dentry *trap; ++ bool old_opaque; ++ bool new_opaque; ++ bool new_create = false; ++ bool is_dir = S_ISDIR(old->d_inode->i_mode); ++ ++ /* Don't copy up directory trees */ ++ old_type = ovl_path_type(old); ++ if (old_type != OVL_PATH_UPPER && is_dir) ++ return -EXDEV; ++ ++ if (new->d_inode) { ++ new_type = ovl_path_type(new); ++ ++ if (new_type == OVL_PATH_LOWER && old_type == OVL_PATH_LOWER) { ++ if (ovl_dentry_lower(old)->d_inode == ++ ovl_dentry_lower(new)->d_inode) ++ return 0; ++ } ++ if (new_type != OVL_PATH_LOWER && old_type != OVL_PATH_LOWER) { ++ if (ovl_dentry_upper(old)->d_inode == ++ ovl_dentry_upper(new)->d_inode) ++ return 0; ++ } ++ ++ if (new_type != OVL_PATH_UPPER && ++ S_ISDIR(new->d_inode->i_mode)) { ++ err = ovl_check_empty_and_clear(new, new_type); ++ if (err) ++ return err; ++ } ++ } else { ++ new_type = OVL_PATH_UPPER; ++ } ++ ++ err = ovl_copy_up(old); ++ if (err) ++ return err; ++ ++ err = ovl_copy_up(new->d_parent); ++ if (err) ++ return err; ++ ++ old_upperdir = ovl_dentry_upper(old->d_parent); ++ new_upperdir = ovl_dentry_upper(new->d_parent); ++ ++ trap = lock_rename(new_upperdir, old_upperdir); ++ ++ olddentry = ovl_dentry_upper(old); ++ newdentry = ovl_dentry_upper(new); ++ if (newdentry) { ++ dget(newdentry); ++ } else { ++ new_create = true; ++ newdentry = ovl_lookup_create(new_upperdir, new); ++ err = PTR_ERR(newdentry); ++ if (IS_ERR(newdentry)) ++ goto out_unlock; ++ } ++ ++ err = -ESTALE; ++ if (olddentry->d_parent != old_upperdir) ++ goto out_dput; ++ if (newdentry->d_parent != new_upperdir) ++ goto out_dput; ++ if (olddentry == trap) ++ goto out_dput; ++ if (newdentry == trap) ++ goto out_dput; ++ ++ old_opaque = ovl_dentry_is_opaque(old); ++ new_opaque = ovl_dentry_is_opaque(new) || new_type != OVL_PATH_UPPER; ++ ++ if (is_dir && !old_opaque && new_opaque) { ++ err = ovl_set_opaque(olddentry); ++ if (err) ++ goto out_dput; ++ } ++ ++ err = vfs_rename(old_upperdir->d_inode, olddentry, ++ new_upperdir->d_inode, newdentry); ++ ++ if (err) { ++ if (new_create && ovl_dentry_is_opaque(new)) ++ ovl_whiteout(new_upperdir, new); ++ if (is_dir && !old_opaque && new_opaque) ++ ovl_remove_opaque(olddentry); ++ goto out_dput; ++ } ++ ++ if (old_type != OVL_PATH_UPPER || old_opaque) ++ err = ovl_whiteout(old_upperdir, old); ++ if (is_dir && old_opaque && !new_opaque) ++ ovl_remove_opaque(olddentry); ++ ++ if (old_opaque != new_opaque) ++ ovl_dentry_set_opaque(old, new_opaque); ++ ++ ovl_dentry_version_inc(old->d_parent); ++ ovl_dentry_version_inc(new->d_parent); ++ ++out_dput: ++ dput(newdentry); ++out_unlock: ++ unlock_rename(new_upperdir, old_upperdir); ++ return err; ++} ++ ++const struct inode_operations ovl_dir_inode_operations = { ++ .lookup = ovl_lookup, ++ .mkdir = ovl_mkdir, ++ .symlink = ovl_symlink, ++ .unlink = ovl_unlink, ++ .rmdir = ovl_rmdir, ++ .rename = ovl_rename, ++ .link = ovl_link, ++ .setattr = ovl_setattr, ++ .create = ovl_create, ++ .mknod = ovl_mknod, ++ .permission = ovl_permission, ++ .getattr = ovl_dir_getattr, ++ .setxattr = ovl_setxattr, ++ .getxattr = ovl_getxattr, ++ .listxattr = ovl_listxattr, ++ .removexattr = ovl_removexattr, ++}; +diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c +new file mode 100644 +index 0000000..1a8e232 +--- /dev/null ++++ b/fs/overlayfs/inode.c +@@ -0,0 +1,375 @@ ++/* ++ * ++ * Copyright (C) 2011 Novell Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include "overlayfs.h" ++ ++int ovl_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ struct dentry *upperdentry; ++ int err; ++ ++ if ((attr->ia_valid & ATTR_SIZE) && !ovl_dentry_upper(dentry)) ++ err = ovl_copy_up_truncate(dentry, attr->ia_size); ++ else ++ err = ovl_copy_up(dentry); ++ if (err) ++ return err; ++ ++ upperdentry = ovl_dentry_upper(dentry); ++ ++ if (attr->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) ++ attr->ia_valid &= ~ATTR_MODE; ++ ++ mutex_lock(&upperdentry->d_inode->i_mutex); ++ err = notify_change(upperdentry, attr); ++ mutex_unlock(&upperdentry->d_inode->i_mutex); ++ ++ return err; ++} ++ ++static int ovl_getattr(struct vfsmount *mnt, struct dentry *dentry, ++ struct kstat *stat) ++{ ++ struct path realpath; ++ ++ ovl_path_real(dentry, &realpath); ++ return vfs_getattr(realpath.mnt, realpath.dentry, stat); ++} ++ ++int ovl_permission(struct inode *inode, int mask) ++{ ++ struct ovl_entry *oe; ++ struct dentry *alias = NULL; ++ struct inode *realinode; ++ struct dentry *realdentry; ++ bool is_upper; ++ int err; ++ ++ if (S_ISDIR(inode->i_mode)) { ++ oe = inode->i_private; ++ } else if (mask & MAY_NOT_BLOCK) { ++ return -ECHILD; ++ } else { ++ /* ++ * For non-directories find an alias and get the info ++ * from there. ++ */ ++ spin_lock(&inode->i_lock); ++ if (WARN_ON(list_empty(&inode->i_dentry))) { ++ spin_unlock(&inode->i_lock); ++ return -ENOENT; ++ } ++ alias = list_entry(inode->i_dentry.next, ++ struct dentry, d_alias); ++ dget(alias); ++ spin_unlock(&inode->i_lock); ++ oe = alias->d_fsdata; ++ } ++ ++ realdentry = ovl_entry_real(oe, &is_upper); ++ ++ /* Careful in RCU walk mode */ ++ realinode = ACCESS_ONCE(realdentry->d_inode); ++ if (!realinode) { ++ WARN_ON(!(mask & MAY_NOT_BLOCK)); ++ err = -ENOENT; ++ goto out_dput; ++ } ++ ++ if (mask & MAY_WRITE) { ++ umode_t mode = realinode->i_mode; ++ ++ /* ++ * Writes will always be redirected to upper layer, so ++ * ignore lower layer being read-only. ++ * ++ * If the overlay itself is read-only then proceed ++ * with the permission check, don't return EROFS. ++ * This will only happen if this is the lower layer of ++ * another overlayfs. ++ * ++ * If upper fs becomes read-only after the overlay was ++ * constructed return EROFS to prevent modification of ++ * upper layer. ++ */ ++ err = -EROFS; ++ if (is_upper && !IS_RDONLY(inode) && IS_RDONLY(realinode) && ++ (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) ++ goto out_dput; ++ } ++ ++ err = inode_only_permission(realinode, mask); ++out_dput: ++ dput(alias); ++ return err; ++} ++ ++ ++struct ovl_link_data { ++ struct dentry *realdentry; ++ void *cookie; ++}; ++ ++static void *ovl_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ void *ret; ++ struct dentry *realdentry; ++ struct inode *realinode; ++ ++ realdentry = ovl_dentry_real(dentry); ++ realinode = realdentry->d_inode; ++ ++ if (WARN_ON(!realinode->i_op->follow_link)) ++ return ERR_PTR(-EPERM); ++ ++ ret = realinode->i_op->follow_link(realdentry, nd); ++ if (IS_ERR(ret)) ++ return ret; ++ ++ if (realinode->i_op->put_link) { ++ struct ovl_link_data *data; ++ ++ data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL); ++ if (!data) { ++ realinode->i_op->put_link(realdentry, nd, ret); ++ return ERR_PTR(-ENOMEM); ++ } ++ data->realdentry = realdentry; ++ data->cookie = ret; ++ ++ return data; ++ } else { ++ return NULL; ++ } ++} ++ ++static void ovl_put_link(struct dentry *dentry, struct nameidata *nd, void *c) ++{ ++ struct inode *realinode; ++ struct ovl_link_data *data = c; ++ ++ if (!data) ++ return; ++ ++ realinode = data->realdentry->d_inode; ++ realinode->i_op->put_link(data->realdentry, nd, data->cookie); ++ kfree(data); ++} ++ ++static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz) ++{ ++ struct path realpath; ++ struct inode *realinode; ++ ++ ovl_path_real(dentry, &realpath); ++ realinode = realpath.dentry->d_inode; ++ ++ if (!realinode->i_op->readlink) ++ return -EINVAL; ++ ++ touch_atime(&realpath); ++ ++ return realinode->i_op->readlink(realpath.dentry, buf, bufsiz); ++} ++ ++ ++static bool ovl_is_private_xattr(const char *name) ++{ ++ return strncmp(name, "trusted.overlay.", 14) == 0; ++} ++ ++int ovl_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ int err; ++ struct dentry *upperdentry; ++ ++ if (ovl_is_private_xattr(name)) ++ return -EPERM; ++ ++ err = ovl_copy_up(dentry); ++ if (err) ++ return err; ++ ++ upperdentry = ovl_dentry_upper(dentry); ++ return vfs_setxattr(upperdentry, name, value, size, flags); ++} ++ ++ssize_t ovl_getxattr(struct dentry *dentry, const char *name, ++ void *value, size_t size) ++{ ++ if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && ++ ovl_is_private_xattr(name)) ++ return -ENODATA; ++ ++ return vfs_getxattr(ovl_dentry_real(dentry), name, value, size); ++} ++ ++ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) ++{ ++ ssize_t res; ++ int off; ++ ++ res = vfs_listxattr(ovl_dentry_real(dentry), list, size); ++ if (res <= 0 || size == 0) ++ return res; ++ ++ if (ovl_path_type(dentry->d_parent) != OVL_PATH_MERGE) ++ return res; ++ ++ /* filter out private xattrs */ ++ for (off = 0; off < res;) { ++ char *s = list + off; ++ size_t slen = strlen(s) + 1; ++ ++ BUG_ON(off + slen > res); ++ ++ if (ovl_is_private_xattr(s)) { ++ res -= slen; ++ memmove(s, s + slen, res - off); ++ } else { ++ off += slen; ++ } ++ } ++ ++ return res; ++} ++ ++int ovl_removexattr(struct dentry *dentry, const char *name) ++{ ++ int err; ++ struct path realpath; ++ enum ovl_path_type type; ++ ++ if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && ++ ovl_is_private_xattr(name)) ++ return -ENODATA; ++ ++ type = ovl_path_real(dentry, &realpath); ++ if (type == OVL_PATH_LOWER) { ++ err = vfs_getxattr(realpath.dentry, name, NULL, 0); ++ if (err < 0) ++ return err; ++ ++ err = ovl_copy_up(dentry); ++ if (err) ++ return err; ++ ++ ovl_path_upper(dentry, &realpath); ++ } ++ ++ return vfs_removexattr(realpath.dentry, name); ++} ++ ++static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type, ++ struct dentry *realdentry) ++{ ++ if (type != OVL_PATH_LOWER) ++ return false; ++ ++ if (special_file(realdentry->d_inode->i_mode)) ++ return false; ++ ++ if (!(OPEN_FMODE(flags) & FMODE_WRITE) && !(flags & O_TRUNC)) ++ return false; ++ ++ return true; ++} ++ ++static struct file *ovl_open(struct dentry *dentry, struct file *file, ++ const struct cred *cred) ++{ ++ int err; ++ struct path realpath; ++ enum ovl_path_type type; ++ ++ type = ovl_path_real(dentry, &realpath); ++ if (ovl_open_need_copy_up(file->f_flags, type, realpath.dentry)) { ++ if (file->f_flags & O_TRUNC) ++ err = ovl_copy_up_truncate(dentry, 0); ++ else ++ err = ovl_copy_up(dentry); ++ if (err) ++ return ERR_PTR(err); ++ ++ ovl_path_upper(dentry, &realpath); ++ } ++ ++ return vfs_open(&realpath, file, cred); ++} ++ ++static const struct inode_operations ovl_file_inode_operations = { ++ .setattr = ovl_setattr, ++ .permission = ovl_permission, ++ .getattr = ovl_getattr, ++ .setxattr = ovl_setxattr, ++ .getxattr = ovl_getxattr, ++ .listxattr = ovl_listxattr, ++ .removexattr = ovl_removexattr, ++ .open = ovl_open, ++}; ++ ++static const struct inode_operations ovl_symlink_inode_operations = { ++ .setattr = ovl_setattr, ++ .follow_link = ovl_follow_link, ++ .put_link = ovl_put_link, ++ .readlink = ovl_readlink, ++ .getattr = ovl_getattr, ++ .setxattr = ovl_setxattr, ++ .getxattr = ovl_getxattr, ++ .listxattr = ovl_listxattr, ++ .removexattr = ovl_removexattr, ++}; ++ ++struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, ++ struct ovl_entry *oe) ++{ ++ struct inode *inode; ++ ++ inode = new_inode(sb); ++ if (!inode) ++ return NULL; ++ ++ mode &= S_IFMT; ++ ++ inode->i_ino = get_next_ino(); ++ inode->i_mode = mode; ++ inode->i_flags |= S_NOATIME | S_NOCMTIME; ++ ++ switch (mode) { ++ case S_IFDIR: ++ inode->i_private = oe; ++ inode->i_op = &ovl_dir_inode_operations; ++ inode->i_fop = &ovl_dir_operations; ++ break; ++ ++ case S_IFLNK: ++ inode->i_op = &ovl_symlink_inode_operations; ++ break; ++ ++ case S_IFREG: ++ case S_IFSOCK: ++ case S_IFBLK: ++ case S_IFCHR: ++ case S_IFIFO: ++ inode->i_op = &ovl_file_inode_operations; ++ break; ++ ++ default: ++ WARN(1, "illegal file type: %i\n", mode); ++ iput(inode); ++ inode = NULL; ++ } ++ ++ return inode; ++ ++} +diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h +new file mode 100644 +index 0000000..1dd05f7 +--- /dev/null ++++ b/fs/overlayfs/overlayfs.h +@@ -0,0 +1,64 @@ ++/* ++ * ++ * Copyright (C) 2011 Novell Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ */ ++ ++struct ovl_entry; ++ ++enum ovl_path_type { ++ OVL_PATH_UPPER, ++ OVL_PATH_MERGE, ++ OVL_PATH_LOWER, ++}; ++ ++extern const char *ovl_opaque_xattr; ++extern const char *ovl_whiteout_xattr; ++extern const struct dentry_operations ovl_dentry_operations; ++ ++enum ovl_path_type ovl_path_type(struct dentry *dentry); ++u64 ovl_dentry_version_get(struct dentry *dentry); ++void ovl_dentry_version_inc(struct dentry *dentry); ++void ovl_path_upper(struct dentry *dentry, struct path *path); ++void ovl_path_lower(struct dentry *dentry, struct path *path); ++enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path); ++struct dentry *ovl_dentry_upper(struct dentry *dentry); ++struct dentry *ovl_dentry_lower(struct dentry *dentry); ++struct dentry *ovl_dentry_real(struct dentry *dentry); ++struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper); ++bool ovl_dentry_is_opaque(struct dentry *dentry); ++void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque); ++bool ovl_is_whiteout(struct dentry *dentry); ++void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry); ++struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *nd); ++struct file *ovl_path_open(struct path *path, int flags); ++ ++struct dentry *ovl_upper_create(struct dentry *upperdir, struct dentry *dentry, ++ struct kstat *stat, const char *link); ++ ++/* readdir.c */ ++extern const struct file_operations ovl_dir_operations; ++int ovl_check_empty_and_clear(struct dentry *dentry, enum ovl_path_type type); ++ ++/* inode.c */ ++int ovl_setattr(struct dentry *dentry, struct iattr *attr); ++int ovl_permission(struct inode *inode, int mask); ++int ovl_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags); ++ssize_t ovl_getxattr(struct dentry *dentry, const char *name, ++ void *value, size_t size); ++ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size); ++int ovl_removexattr(struct dentry *dentry, const char *name); ++ ++struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, ++ struct ovl_entry *oe); ++/* dir.c */ ++extern const struct inode_operations ovl_dir_inode_operations; ++ ++/* copy_up.c */ ++int ovl_copy_up(struct dentry *dentry); ++int ovl_copy_up_truncate(struct dentry *dentry, loff_t size); +diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c +new file mode 100644 +index 0000000..0797efb +--- /dev/null ++++ b/fs/overlayfs/readdir.c +@@ -0,0 +1,566 @@ ++/* ++ * ++ * Copyright (C) 2011 Novell Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "overlayfs.h" ++ ++struct ovl_cache_entry { ++ const char *name; ++ unsigned int len; ++ unsigned int type; ++ u64 ino; ++ bool is_whiteout; ++ struct list_head l_node; ++ struct rb_node node; ++}; ++ ++struct ovl_readdir_data { ++ struct rb_root *root; ++ struct list_head *list; ++ struct list_head *middle; ++ struct dentry *dir; ++ int count; ++ int err; ++}; ++ ++struct ovl_dir_file { ++ bool is_real; ++ bool is_cached; ++ struct list_head cursor; ++ u64 cache_version; ++ struct list_head cache; ++ struct file *realfile; ++}; ++ ++static struct ovl_cache_entry *ovl_cache_entry_from_node(struct rb_node *n) ++{ ++ return container_of(n, struct ovl_cache_entry, node); ++} ++ ++static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root, ++ const char *name, int len) ++{ ++ struct rb_node *node = root->rb_node; ++ int cmp; ++ ++ while (node) { ++ struct ovl_cache_entry *p = ovl_cache_entry_from_node(node); ++ ++ cmp = strncmp(name, p->name, len); ++ if (cmp > 0) ++ node = p->node.rb_right; ++ else if (cmp < 0 || len < p->len) ++ node = p->node.rb_left; ++ else ++ return p; ++ } ++ ++ return NULL; ++} ++ ++static struct ovl_cache_entry *ovl_cache_entry_new(const char *name, int len, ++ u64 ino, unsigned int d_type) ++{ ++ struct ovl_cache_entry *p; ++ ++ p = kmalloc(sizeof(*p) + len + 1, GFP_KERNEL); ++ if (p) { ++ char *name_copy = (char *) (p + 1); ++ memcpy(name_copy, name, len); ++ name_copy[len] = '\0'; ++ p->name = name_copy; ++ p->len = len; ++ p->type = d_type; ++ p->ino = ino; ++ p->is_whiteout = false; ++ } ++ ++ return p; ++} ++ ++static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd, ++ const char *name, int len, u64 ino, ++ unsigned int d_type) ++{ ++ struct rb_node **newp = &rdd->root->rb_node; ++ struct rb_node *parent = NULL; ++ struct ovl_cache_entry *p; ++ ++ while (*newp) { ++ int cmp; ++ struct ovl_cache_entry *tmp; ++ ++ parent = *newp; ++ tmp = ovl_cache_entry_from_node(*newp); ++ cmp = strncmp(name, tmp->name, len); ++ if (cmp > 0) ++ newp = &tmp->node.rb_right; ++ else if (cmp < 0 || len < tmp->len) ++ newp = &tmp->node.rb_left; ++ else ++ return 0; ++ } ++ ++ p = ovl_cache_entry_new(name, len, ino, d_type); ++ if (p == NULL) ++ return -ENOMEM; ++ ++ list_add_tail(&p->l_node, rdd->list); ++ rb_link_node(&p->node, parent, newp); ++ rb_insert_color(&p->node, rdd->root); ++ ++ return 0; ++} ++ ++static int ovl_fill_lower(void *buf, const char *name, int namelen, ++ loff_t offset, u64 ino, unsigned int d_type) ++{ ++ struct ovl_readdir_data *rdd = buf; ++ struct ovl_cache_entry *p; ++ ++ rdd->count++; ++ p = ovl_cache_entry_find(rdd->root, name, namelen); ++ if (p) { ++ list_move_tail(&p->l_node, rdd->middle); ++ } else { ++ p = ovl_cache_entry_new(name, namelen, ino, d_type); ++ if (p == NULL) ++ rdd->err = -ENOMEM; ++ else ++ list_add_tail(&p->l_node, rdd->middle); ++ } ++ ++ return rdd->err; ++} ++ ++static void ovl_cache_free(struct list_head *list) ++{ ++ struct ovl_cache_entry *p; ++ struct ovl_cache_entry *n; ++ ++ list_for_each_entry_safe(p, n, list, l_node) ++ kfree(p); ++ ++ INIT_LIST_HEAD(list); ++} ++ ++static int ovl_fill_upper(void *buf, const char *name, int namelen, ++ loff_t offset, u64 ino, unsigned int d_type) ++{ ++ struct ovl_readdir_data *rdd = buf; ++ ++ rdd->count++; ++ return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type); ++} ++ ++static inline int ovl_dir_read(struct path *realpath, ++ struct ovl_readdir_data *rdd, filldir_t filler) ++{ ++ struct file *realfile; ++ int err; ++ ++ realfile = ovl_path_open(realpath, O_RDONLY | O_DIRECTORY); ++ if (IS_ERR(realfile)) ++ return PTR_ERR(realfile); ++ ++ do { ++ rdd->count = 0; ++ rdd->err = 0; ++ err = vfs_readdir(realfile, filler, rdd); ++ if (err >= 0) ++ err = rdd->err; ++ } while (!err && rdd->count); ++ fput(realfile); ++ ++ return 0; ++} ++ ++static void ovl_dir_reset(struct file *file) ++{ ++ struct ovl_dir_file *od = file->private_data; ++ enum ovl_path_type type = ovl_path_type(file->f_path.dentry); ++ ++ if (ovl_dentry_version_get(file->f_path.dentry) != od->cache_version) { ++ list_del_init(&od->cursor); ++ ovl_cache_free(&od->cache); ++ od->is_cached = false; ++ } ++ WARN_ON(!od->is_real && type != OVL_PATH_MERGE); ++ if (od->is_real && type == OVL_PATH_MERGE) { ++ fput(od->realfile); ++ od->realfile = NULL; ++ od->is_real = false; ++ } ++} ++ ++static int ovl_dir_mark_whiteouts(struct ovl_readdir_data *rdd) ++{ ++ struct ovl_cache_entry *p; ++ struct dentry *dentry; ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ ++ override_cred = prepare_creds(); ++ if (!override_cred) { ++ ovl_cache_free(rdd->list); ++ return -ENOMEM; ++ } ++ ++ /* ++ * CAP_SYS_ADMIN for getxattr ++ * CAP_DAC_OVERRIDE for lookup ++ */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); ++ old_cred = override_creds(override_cred); ++ ++ mutex_lock(&rdd->dir->d_inode->i_mutex); ++ list_for_each_entry(p, rdd->list, l_node) { ++ if (p->type != DT_LNK) ++ continue; ++ ++ dentry = lookup_one_len(p->name, rdd->dir, p->len); ++ if (IS_ERR(dentry)) ++ continue; ++ ++ p->is_whiteout = ovl_is_whiteout(dentry); ++ dput(dentry); ++ } ++ mutex_unlock(&rdd->dir->d_inode->i_mutex); ++ ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ ++ return 0; ++} ++ ++static inline int ovl_dir_read_merged(struct path *upperpath, ++ struct path *lowerpath, ++ struct ovl_readdir_data *rdd) ++{ ++ int err; ++ struct rb_root root = RB_ROOT; ++ struct list_head middle; ++ ++ rdd->root = &root; ++ if (upperpath->dentry) { ++ rdd->dir = upperpath->dentry; ++ err = ovl_dir_read(upperpath, rdd, ovl_fill_upper); ++ if (err) ++ goto out; ++ ++ err = ovl_dir_mark_whiteouts(rdd); ++ if (err) ++ goto out; ++ } ++ /* ++ * Insert lowerpath entries before upperpath ones, this allows ++ * offsets to be reasonably constant ++ */ ++ list_add(&middle, rdd->list); ++ rdd->middle = &middle; ++ err = ovl_dir_read(lowerpath, rdd, ovl_fill_lower); ++ list_del(&middle); ++out: ++ rdd->root = NULL; ++ ++ return err; ++} ++ ++static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos) ++{ ++ struct list_head *l; ++ loff_t off; ++ ++ l = od->cache.next; ++ for (off = 0; off < pos; off++) { ++ if (l == &od->cache) ++ break; ++ l = l->next; ++ } ++ list_move_tail(&od->cursor, l); ++} ++ ++static int ovl_readdir(struct file *file, void *buf, filldir_t filler) ++{ ++ struct ovl_dir_file *od = file->private_data; ++ int res; ++ ++ if (!file->f_pos) ++ ovl_dir_reset(file); ++ ++ if (od->is_real) { ++ res = vfs_readdir(od->realfile, filler, buf); ++ file->f_pos = od->realfile->f_pos; ++ ++ return res; ++ } ++ ++ if (!od->is_cached) { ++ struct path lowerpath; ++ struct path upperpath; ++ struct ovl_readdir_data rdd = { .list = &od->cache }; ++ ++ ovl_path_lower(file->f_path.dentry, &lowerpath); ++ ovl_path_upper(file->f_path.dentry, &upperpath); ++ ++ res = ovl_dir_read_merged(&upperpath, &lowerpath, &rdd); ++ if (res) { ++ ovl_cache_free(rdd.list); ++ return res; ++ } ++ ++ od->cache_version = ovl_dentry_version_get(file->f_path.dentry); ++ od->is_cached = true; ++ ++ ovl_seek_cursor(od, file->f_pos); ++ } ++ ++ while (od->cursor.next != &od->cache) { ++ int over; ++ loff_t off; ++ struct ovl_cache_entry *p; ++ ++ p = list_entry(od->cursor.next, struct ovl_cache_entry, l_node); ++ off = file->f_pos; ++ if (!p->is_whiteout) { ++ over = filler(buf, p->name, p->len, off, p->ino, ++ p->type); ++ if (over) ++ break; ++ } ++ file->f_pos++; ++ list_move(&od->cursor, &p->l_node); ++ } ++ ++ return 0; ++} ++ ++static loff_t ovl_dir_llseek(struct file *file, loff_t offset, int origin) ++{ ++ loff_t res; ++ struct ovl_dir_file *od = file->private_data; ++ ++ mutex_lock(&file->f_dentry->d_inode->i_mutex); ++ if (!file->f_pos) ++ ovl_dir_reset(file); ++ ++ if (od->is_real) { ++ res = vfs_llseek(od->realfile, offset, origin); ++ file->f_pos = od->realfile->f_pos; ++ } else { ++ res = -EINVAL; ++ ++ switch (origin) { ++ case SEEK_CUR: ++ offset += file->f_pos; ++ break; ++ case SEEK_SET: ++ break; ++ default: ++ goto out_unlock; ++ } ++ if (offset < 0) ++ goto out_unlock; ++ ++ if (offset != file->f_pos) { ++ file->f_pos = offset; ++ if (od->is_cached) ++ ovl_seek_cursor(od, offset); ++ } ++ res = offset; ++ } ++out_unlock: ++ mutex_unlock(&file->f_dentry->d_inode->i_mutex); ++ ++ return res; ++} ++ ++static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end, ++ int datasync) ++{ ++ struct ovl_dir_file *od = file->private_data; ++ ++ /* May need to reopen directory if it got copied up */ ++ if (!od->realfile) { ++ struct path upperpath; ++ ++ ovl_path_upper(file->f_path.dentry, &upperpath); ++ od->realfile = ovl_path_open(&upperpath, O_RDONLY); ++ if (IS_ERR(od->realfile)) ++ return PTR_ERR(od->realfile); ++ } ++ ++ return vfs_fsync_range(od->realfile, start, end, datasync); ++} ++ ++static int ovl_dir_release(struct inode *inode, struct file *file) ++{ ++ struct ovl_dir_file *od = file->private_data; ++ ++ list_del(&od->cursor); ++ ovl_cache_free(&od->cache); ++ if (od->realfile) ++ fput(od->realfile); ++ kfree(od); ++ ++ return 0; ++} ++ ++static int ovl_dir_open(struct inode *inode, struct file *file) ++{ ++ struct path realpath; ++ struct file *realfile; ++ struct ovl_dir_file *od; ++ enum ovl_path_type type; ++ ++ od = kzalloc(sizeof(struct ovl_dir_file), GFP_KERNEL); ++ if (!od) ++ return -ENOMEM; ++ ++ type = ovl_path_real(file->f_path.dentry, &realpath); ++ realfile = ovl_path_open(&realpath, file->f_flags); ++ if (IS_ERR(realfile)) { ++ kfree(od); ++ return PTR_ERR(realfile); ++ } ++ INIT_LIST_HEAD(&od->cache); ++ INIT_LIST_HEAD(&od->cursor); ++ od->is_cached = false; ++ od->realfile = realfile; ++ od->is_real = (type != OVL_PATH_MERGE); ++ file->private_data = od; ++ ++ return 0; ++} ++ ++const struct file_operations ovl_dir_operations = { ++ .read = generic_read_dir, ++ .open = ovl_dir_open, ++ .readdir = ovl_readdir, ++ .llseek = ovl_dir_llseek, ++ .fsync = ovl_dir_fsync, ++ .release = ovl_dir_release, ++}; ++ ++static int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list) ++{ ++ int err; ++ struct path lowerpath; ++ struct path upperpath; ++ struct ovl_cache_entry *p; ++ struct ovl_readdir_data rdd = { .list = list }; ++ ++ ovl_path_upper(dentry, &upperpath); ++ ovl_path_lower(dentry, &lowerpath); ++ ++ err = ovl_dir_read_merged(&upperpath, &lowerpath, &rdd); ++ if (err) ++ return err; ++ ++ err = 0; ++ ++ list_for_each_entry(p, list, l_node) { ++ if (p->is_whiteout) ++ continue; ++ ++ if (p->name[0] == '.') { ++ if (p->len == 1) ++ continue; ++ if (p->len == 2 && p->name[1] == '.') ++ continue; ++ } ++ err = -ENOTEMPTY; ++ break; ++ } ++ ++ return err; ++} ++ ++static int ovl_remove_whiteouts(struct dentry *dir, struct list_head *list) ++{ ++ struct path upperpath; ++ struct dentry *upperdir; ++ struct ovl_cache_entry *p; ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ int err; ++ ++ ovl_path_upper(dir, &upperpath); ++ upperdir = upperpath.dentry; ++ ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ return -ENOMEM; ++ ++ /* ++ * CAP_DAC_OVERRIDE for lookup and unlink ++ * CAP_SYS_ADMIN for setxattr of "trusted" namespace ++ * CAP_FOWNER for unlink in sticky directory ++ */ ++ cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ cap_raise(override_cred->cap_effective, CAP_FOWNER); ++ old_cred = override_creds(override_cred); ++ ++ err = vfs_setxattr(upperdir, ovl_opaque_xattr, "y", 1, 0); ++ if (err) ++ goto out_revert_creds; ++ ++ mutex_lock_nested(&upperdir->d_inode->i_mutex, I_MUTEX_PARENT); ++ list_for_each_entry(p, list, l_node) { ++ struct dentry *dentry; ++ int ret; ++ ++ if (!p->is_whiteout) ++ continue; ++ ++ dentry = lookup_one_len(p->name, upperdir, p->len); ++ if (IS_ERR(dentry)) { ++ printk(KERN_WARNING ++ "overlayfs: failed to lookup whiteout %.*s: %li\n", ++ p->len, p->name, PTR_ERR(dentry)); ++ continue; ++ } ++ ret = vfs_unlink(upperdir->d_inode, dentry); ++ dput(dentry); ++ if (ret) ++ printk(KERN_WARNING ++ "overlayfs: failed to unlink whiteout %.*s: %i\n", ++ p->len, p->name, ret); ++ } ++ mutex_unlock(&upperdir->d_inode->i_mutex); ++ ++out_revert_creds: ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ ++ return err; ++} ++ ++int ovl_check_empty_and_clear(struct dentry *dentry, enum ovl_path_type type) ++{ ++ int err; ++ LIST_HEAD(list); ++ ++ err = ovl_check_empty_dir(dentry, &list); ++ if (!err && type == OVL_PATH_MERGE) ++ err = ovl_remove_whiteouts(dentry, &list); ++ ++ ovl_cache_free(&list); ++ ++ return err; ++} +diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c +new file mode 100644 +index 0000000..1d2d1e2 +--- /dev/null ++++ b/fs/overlayfs/super.c +@@ -0,0 +1,664 @@ ++/* ++ * ++ * Copyright (C) 2011 Novell Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "overlayfs.h" ++ ++MODULE_AUTHOR("Miklos Szeredi "); ++MODULE_DESCRIPTION("Overlay filesystem"); ++MODULE_LICENSE("GPL"); ++ ++struct ovl_config { ++ char *lowerdir; ++ char *upperdir; ++}; ++ ++/* private information held for overlayfs's superblock */ ++struct ovl_fs { ++ struct vfsmount *upper_mnt; ++ struct vfsmount *lower_mnt; ++ /* pathnames of lower and upper dirs, for show_options */ ++ struct ovl_config config; ++}; ++ ++/* private information held for every overlayfs dentry */ ++struct ovl_entry { ++ /* ++ * Keep "double reference" on upper dentries, so that ++ * d_delete() doesn't think it's OK to reset d_inode to NULL. ++ */ ++ struct dentry *__upperdentry; ++ struct dentry *lowerdentry; ++ union { ++ struct { ++ u64 version; ++ bool opaque; ++ }; ++ struct rcu_head rcu; ++ }; ++}; ++ ++const char *ovl_whiteout_xattr = "trusted.overlay.whiteout"; ++const char *ovl_opaque_xattr = "trusted.overlay.opaque"; ++ ++ ++enum ovl_path_type ovl_path_type(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ if (oe->__upperdentry) { ++ if (oe->lowerdentry && S_ISDIR(dentry->d_inode->i_mode)) ++ return OVL_PATH_MERGE; ++ else ++ return OVL_PATH_UPPER; ++ } else { ++ return OVL_PATH_LOWER; ++ } ++} ++ ++static struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe) ++{ ++ struct dentry *upperdentry = ACCESS_ONCE(oe->__upperdentry); ++ smp_read_barrier_depends(); ++ return upperdentry; ++} ++ ++void ovl_path_upper(struct dentry *dentry, struct path *path) ++{ ++ struct ovl_fs *ofs = dentry->d_sb->s_fs_info; ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ path->mnt = ofs->upper_mnt; ++ path->dentry = ovl_upperdentry_dereference(oe); ++} ++ ++void ovl_path_lower(struct dentry *dentry, struct path *path) ++{ ++ struct ovl_fs *ofs = dentry->d_sb->s_fs_info; ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ path->mnt = ofs->lower_mnt; ++ path->dentry = oe->lowerdentry; ++} ++ ++enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path) ++{ ++ ++ enum ovl_path_type type = ovl_path_type(dentry); ++ ++ if (type == OVL_PATH_LOWER) ++ ovl_path_lower(dentry, path); ++ else ++ ovl_path_upper(dentry, path); ++ ++ return type; ++} ++ ++struct dentry *ovl_dentry_upper(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ return ovl_upperdentry_dereference(oe); ++} ++ ++struct dentry *ovl_dentry_lower(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ return oe->lowerdentry; ++} ++ ++struct dentry *ovl_dentry_real(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ struct dentry *realdentry; ++ ++ realdentry = ovl_upperdentry_dereference(oe); ++ if (!realdentry) ++ realdentry = oe->lowerdentry; ++ ++ return realdentry; ++} ++ ++struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper) ++{ ++ struct dentry *realdentry; ++ ++ realdentry = ovl_upperdentry_dereference(oe); ++ if (realdentry) { ++ *is_upper = true; ++ } else { ++ realdentry = oe->lowerdentry; ++ *is_upper = false; ++ } ++ return realdentry; ++} ++ ++bool ovl_dentry_is_opaque(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ return oe->opaque; ++} ++ ++void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ oe->opaque = opaque; ++} ++ ++void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ WARN_ON(!mutex_is_locked(&upperdentry->d_parent->d_inode->i_mutex)); ++ WARN_ON(oe->__upperdentry); ++ BUG_ON(!upperdentry->d_inode); ++ smp_wmb(); ++ oe->__upperdentry = dget(upperdentry); ++} ++ ++void ovl_dentry_version_inc(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ WARN_ON(!mutex_is_locked(&dentry->d_inode->i_mutex)); ++ oe->version++; ++} ++ ++u64 ovl_dentry_version_get(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ WARN_ON(!mutex_is_locked(&dentry->d_inode->i_mutex)); ++ return oe->version; ++} ++ ++bool ovl_is_whiteout(struct dentry *dentry) ++{ ++ int res; ++ char val; ++ ++ if (!dentry) ++ return false; ++ if (!dentry->d_inode) ++ return false; ++ if (!S_ISLNK(dentry->d_inode->i_mode)) ++ return false; ++ ++ res = vfs_getxattr(dentry, ovl_whiteout_xattr, &val, 1); ++ if (res == 1 && val == 'y') ++ return true; ++ ++ return false; ++} ++ ++static bool ovl_is_opaquedir(struct dentry *dentry) ++{ ++ int res; ++ char val; ++ ++ if (!S_ISDIR(dentry->d_inode->i_mode)) ++ return false; ++ ++ res = vfs_getxattr(dentry, ovl_opaque_xattr, &val, 1); ++ if (res == 1 && val == 'y') ++ return true; ++ ++ return false; ++} ++ ++static void ovl_entry_free(struct rcu_head *head) ++{ ++ struct ovl_entry *oe = container_of(head, struct ovl_entry, rcu); ++ kfree(oe); ++} ++ ++static void ovl_dentry_release(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ if (oe) { ++ dput(oe->__upperdentry); ++ dput(oe->__upperdentry); ++ dput(oe->lowerdentry); ++ call_rcu(&oe->rcu, ovl_entry_free); ++ } ++} ++ ++const struct dentry_operations ovl_dentry_operations = { ++ .d_release = ovl_dentry_release, ++}; ++ ++static struct ovl_entry *ovl_alloc_entry(void) ++{ ++ return kzalloc(sizeof(struct ovl_entry), GFP_KERNEL); ++} ++ ++static inline struct dentry *ovl_lookup_real(struct dentry *dir, ++ struct qstr *name) ++{ ++ struct dentry *dentry; ++ ++ mutex_lock(&dir->d_inode->i_mutex); ++ dentry = lookup_one_len(name->name, dir, name->len); ++ mutex_unlock(&dir->d_inode->i_mutex); ++ ++ if (IS_ERR(dentry)) { ++ if (PTR_ERR(dentry) == -ENOENT) ++ dentry = NULL; ++ } else if (!dentry->d_inode) { ++ dput(dentry); ++ dentry = NULL; ++ } ++ return dentry; ++} ++ ++static int ovl_do_lookup(struct dentry *dentry) ++{ ++ struct ovl_entry *oe; ++ struct dentry *upperdir; ++ struct dentry *lowerdir; ++ struct dentry *upperdentry = NULL; ++ struct dentry *lowerdentry = NULL; ++ struct inode *inode = NULL; ++ int err; ++ ++ err = -ENOMEM; ++ oe = ovl_alloc_entry(); ++ if (!oe) ++ goto out; ++ ++ upperdir = ovl_dentry_upper(dentry->d_parent); ++ lowerdir = ovl_dentry_lower(dentry->d_parent); ++ ++ if (upperdir) { ++ upperdentry = ovl_lookup_real(upperdir, &dentry->d_name); ++ err = PTR_ERR(upperdentry); ++ if (IS_ERR(upperdentry)) ++ goto out_put_dir; ++ ++ if (lowerdir && upperdentry && ++ (S_ISLNK(upperdentry->d_inode->i_mode) || ++ S_ISDIR(upperdentry->d_inode->i_mode))) { ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ ++ err = -ENOMEM; ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ goto out_dput_upper; ++ ++ /* CAP_SYS_ADMIN needed for getxattr */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ old_cred = override_creds(override_cred); ++ ++ if (ovl_is_opaquedir(upperdentry)) { ++ oe->opaque = true; ++ } else if (ovl_is_whiteout(upperdentry)) { ++ dput(upperdentry); ++ upperdentry = NULL; ++ oe->opaque = true; ++ } ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ } ++ } ++ if (lowerdir && !oe->opaque) { ++ lowerdentry = ovl_lookup_real(lowerdir, &dentry->d_name); ++ err = PTR_ERR(lowerdentry); ++ if (IS_ERR(lowerdentry)) ++ goto out_dput_upper; ++ } ++ ++ if (lowerdentry && upperdentry && ++ (!S_ISDIR(upperdentry->d_inode->i_mode) || ++ !S_ISDIR(lowerdentry->d_inode->i_mode))) { ++ dput(lowerdentry); ++ lowerdentry = NULL; ++ oe->opaque = true; ++ } ++ ++ if (lowerdentry || upperdentry) { ++ struct dentry *realdentry; ++ ++ realdentry = upperdentry ? upperdentry : lowerdentry; ++ err = -ENOMEM; ++ inode = ovl_new_inode(dentry->d_sb, realdentry->d_inode->i_mode, ++ oe); ++ if (!inode) ++ goto out_dput; ++ } ++ ++ if (upperdentry) ++ oe->__upperdentry = dget(upperdentry); ++ ++ if (lowerdentry) ++ oe->lowerdentry = lowerdentry; ++ ++ dentry->d_fsdata = oe; ++ dentry->d_op = &ovl_dentry_operations; ++ d_add(dentry, inode); ++ ++ return 0; ++ ++out_dput: ++ dput(lowerdentry); ++out_dput_upper: ++ dput(upperdentry); ++out_put_dir: ++ kfree(oe); ++out: ++ return err; ++} ++ ++struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ int err = ovl_do_lookup(dentry); ++ ++ if (err) ++ return ERR_PTR(err); ++ ++ return NULL; ++} ++ ++struct file *ovl_path_open(struct path *path, int flags) ++{ ++ path_get(path); ++ return dentry_open(path->dentry, path->mnt, flags, current_cred()); ++} ++ ++static void ovl_put_super(struct super_block *sb) ++{ ++ struct ovl_fs *ufs = sb->s_fs_info; ++ ++ if (!(sb->s_flags & MS_RDONLY)) ++ mnt_drop_write(ufs->upper_mnt); ++ ++ mntput(ufs->upper_mnt); ++ mntput(ufs->lower_mnt); ++ ++ kfree(ufs->config.lowerdir); ++ kfree(ufs->config.upperdir); ++ kfree(ufs); ++} ++ ++static int ovl_remount_fs(struct super_block *sb, int *flagsp, char *data) ++{ ++ int flags = *flagsp; ++ struct ovl_fs *ufs = sb->s_fs_info; ++ ++ /* When remounting rw or ro, we need to adjust the write access to the ++ * upper fs. ++ */ ++ if (((flags ^ sb->s_flags) & MS_RDONLY) == 0) ++ /* No change to readonly status */ ++ return 0; ++ ++ if (flags & MS_RDONLY) { ++ mnt_drop_write(ufs->upper_mnt); ++ return 0; ++ } else ++ return mnt_want_write(ufs->upper_mnt); ++} ++ ++/** ++ * ovl_statfs ++ * @sb: The overlayfs super block ++ * @buf: The struct kstatfs to fill in with stats ++ * ++ * Get the filesystem statistics. As writes always target the upper layer ++ * filesystem pass the statfs to the same filesystem. ++ */ ++static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct dentry *root_dentry = dentry->d_sb->s_root; ++ struct path path; ++ ovl_path_upper(root_dentry, &path); ++ ++ if (!path.dentry->d_sb->s_op->statfs) ++ return -ENOSYS; ++ return path.dentry->d_sb->s_op->statfs(path.dentry, buf); ++} ++ ++/** ++ * ovl_show_options ++ * ++ * Prints the mount options for a given superblock. ++ * Returns zero; does not fail. ++ */ ++static int ovl_show_options(struct seq_file *m, struct dentry *dentry) ++{ ++ struct super_block *sb = dentry->d_sb; ++ struct ovl_fs *ufs = sb->s_fs_info; ++ ++ seq_printf(m, ",lowerdir=%s", ufs->config.lowerdir); ++ seq_printf(m, ",upperdir=%s", ufs->config.upperdir); ++ return 0; ++} ++ ++static const struct super_operations ovl_super_operations = { ++ .put_super = ovl_put_super, ++ .remount_fs = ovl_remount_fs, ++ .statfs = ovl_statfs, ++ .show_options = ovl_show_options, ++}; ++ ++enum { ++ Opt_lowerdir, ++ Opt_upperdir, ++ Opt_err, ++}; ++ ++static const match_table_t ovl_tokens = { ++ {Opt_lowerdir, "lowerdir=%s"}, ++ {Opt_upperdir, "upperdir=%s"}, ++ {Opt_err, NULL} ++}; ++ ++static int ovl_parse_opt(char *opt, struct ovl_config *config) ++{ ++ char *p; ++ ++ config->upperdir = NULL; ++ config->lowerdir = NULL; ++ ++ while ((p = strsep(&opt, ",")) != NULL) { ++ int token; ++ substring_t args[MAX_OPT_ARGS]; ++ ++ if (!*p) ++ continue; ++ ++ token = match_token(p, ovl_tokens, args); ++ switch (token) { ++ case Opt_upperdir: ++ kfree(config->upperdir); ++ config->upperdir = match_strdup(&args[0]); ++ if (!config->upperdir) ++ return -ENOMEM; ++ break; ++ ++ case Opt_lowerdir: ++ kfree(config->lowerdir); ++ config->lowerdir = match_strdup(&args[0]); ++ if (!config->lowerdir) ++ return -ENOMEM; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ } ++ return 0; ++} ++ ++static int ovl_fill_super(struct super_block *sb, void *data, int silent) ++{ ++ struct path lowerpath; ++ struct path upperpath; ++ struct inode *root_inode; ++ struct dentry *root_dentry; ++ struct ovl_entry *oe; ++ struct ovl_fs *ufs; ++ int err; ++ ++ err = -ENOMEM; ++ ufs = kmalloc(sizeof(struct ovl_fs), GFP_KERNEL); ++ if (!ufs) ++ goto out; ++ ++ err = ovl_parse_opt((char *) data, &ufs->config); ++ if (err) ++ goto out_free_ufs; ++ ++ err = -EINVAL; ++ if (!ufs->config.upperdir || !ufs->config.lowerdir) { ++ printk(KERN_ERR "overlayfs: missing upperdir or lowerdir\n"); ++ goto out_free_config; ++ } ++ ++ oe = ovl_alloc_entry(); ++ if (oe == NULL) ++ goto out_free_config; ++ ++ err = kern_path(ufs->config.upperdir, LOOKUP_FOLLOW, &upperpath); ++ if (err) ++ goto out_free_oe; ++ ++ err = kern_path(ufs->config.lowerdir, LOOKUP_FOLLOW, &lowerpath); ++ if (err) ++ goto out_put_upperpath; ++ ++ err = -ENOTDIR; ++ if (!S_ISDIR(upperpath.dentry->d_inode->i_mode) || ++ !S_ISDIR(lowerpath.dentry->d_inode->i_mode)) ++ goto out_put_lowerpath; ++ ++ sb->s_stack_depth = max(upperpath.mnt->mnt_sb->s_stack_depth, ++ lowerpath.mnt->mnt_sb->s_stack_depth) + 1; ++ ++ err = -EINVAL; ++ if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { ++ printk(KERN_ERR "overlayfs: maximum fs stacking depth exceeded\n"); ++ goto out_put_lowerpath; ++ } ++ ++ ++ ufs->upper_mnt = clone_private_mount(&upperpath); ++ err = PTR_ERR(ufs->upper_mnt); ++ if (IS_ERR(ufs->upper_mnt)) { ++ printk(KERN_ERR "overlayfs: failed to clone upperpath\n"); ++ goto out_put_lowerpath; ++ } ++ ++ ufs->lower_mnt = clone_private_mount(&lowerpath); ++ err = PTR_ERR(ufs->lower_mnt); ++ if (IS_ERR(ufs->lower_mnt)) { ++ printk(KERN_ERR "overlayfs: failed to clone lowerpath\n"); ++ goto out_put_upper_mnt; ++ } ++ ++ /* ++ * Make lower_mnt R/O. That way fchmod/fchown on lower file ++ * will fail instead of modifying lower fs. ++ */ ++ ufs->lower_mnt->mnt_flags |= MNT_READONLY; ++ ++ /* If the upper fs is r/o, we mark overlayfs r/o too */ ++ if (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY) ++ sb->s_flags |= MS_RDONLY; ++ ++ if (!(sb->s_flags & MS_RDONLY)) { ++ err = mnt_want_write(ufs->upper_mnt); ++ if (err) ++ goto out_put_lower_mnt; ++ } ++ ++ err = -ENOMEM; ++ root_inode = ovl_new_inode(sb, S_IFDIR, oe); ++ if (!root_inode) ++ goto out_drop_write; ++ ++ root_dentry = d_make_root(root_inode); ++ if (!root_dentry) ++ goto out_drop_write; ++ ++ mntput(upperpath.mnt); ++ mntput(lowerpath.mnt); ++ ++ oe->__upperdentry = dget(upperpath.dentry); ++ oe->lowerdentry = lowerpath.dentry; ++ ++ root_dentry->d_fsdata = oe; ++ root_dentry->d_op = &ovl_dentry_operations; ++ ++ sb->s_op = &ovl_super_operations; ++ sb->s_root = root_dentry; ++ sb->s_fs_info = ufs; ++ ++ return 0; ++ ++out_drop_write: ++ if (!(sb->s_flags & MS_RDONLY)) ++ mnt_drop_write(ufs->upper_mnt); ++out_put_lower_mnt: ++ mntput(ufs->lower_mnt); ++out_put_upper_mnt: ++ mntput(ufs->upper_mnt); ++out_put_lowerpath: ++ path_put(&lowerpath); ++out_put_upperpath: ++ path_put(&upperpath); ++out_free_oe: ++ kfree(oe); ++out_free_config: ++ kfree(ufs->config.lowerdir); ++ kfree(ufs->config.upperdir); ++out_free_ufs: ++ kfree(ufs); ++out: ++ return err; ++} ++ ++static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *raw_data) ++{ ++ return mount_nodev(fs_type, flags, raw_data, ovl_fill_super); ++} ++ ++static struct file_system_type ovl_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "overlayfs", ++ .mount = ovl_mount, ++ .kill_sb = kill_anon_super, ++}; ++ ++static int __init ovl_init(void) ++{ ++ return register_filesystem(&ovl_fs_type); ++} ++ ++static void __exit ovl_exit(void) ++{ ++ unregister_filesystem(&ovl_fs_type); ++} ++ ++module_init(ovl_init); ++module_exit(ovl_exit); +diff --git a/fs/splice.c b/fs/splice.c +index f847684..c124964 100644 +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -1299,6 +1299,7 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, + + return ret; + } ++EXPORT_SYMBOL(do_splice_direct); + + static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe, + struct pipe_inode_info *opipe, +diff --git a/include/linux/fs.h b/include/linux/fs.h +index 8de6755..0b531ab 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -489,6 +489,12 @@ struct iattr { + */ + #include + ++/* ++ * Maximum number of layers of fs stack. Needs to be limited to ++ * prevent kernel stack overflow ++ */ ++#define FILESYSTEM_MAX_STACK_DEPTH 2 ++ + /** + * enum positive_aop_returns - aop return codes with specific semantics + * +@@ -1507,6 +1513,11 @@ struct super_block { + + /* Being remounted read-only */ + int s_readonly_remount; ++ ++ /* ++ * Indicates how deep in a filesystem stack this SB is ++ */ ++ int s_stack_depth; + }; + + /* superblock cache pruning functions */ +@@ -1664,6 +1675,8 @@ struct inode_operations { + void (*truncate_range)(struct inode *, loff_t, loff_t); + int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, + u64 len); ++ struct file *(*open) (struct dentry *, struct file *, ++ const struct cred *); + } ____cacheline_aligned; + + struct seq_file; +@@ -2021,6 +2034,7 @@ extern long do_sys_open(int dfd, const char __user *filename, int flags, + extern struct file *filp_open(const char *, int, umode_t); + extern struct file *file_open_root(struct dentry *, struct vfsmount *, + const char *, int); ++extern struct file *vfs_open(struct path *, struct file *, const struct cred *); + extern struct file * dentry_open(struct dentry *, struct vfsmount *, int, + const struct cred *); + extern int filp_close(struct file *, fl_owner_t id); +@@ -2212,6 +2226,7 @@ extern sector_t bmap(struct inode *, sector_t); + #endif + extern int notify_change(struct dentry *, struct iattr *); + extern int inode_permission(struct inode *, int); ++extern int inode_only_permission(struct inode *, int); + extern int generic_permission(struct inode *, int); + + static inline bool execute_ok(struct inode *inode) +diff --git a/include/linux/mount.h b/include/linux/mount.h +index d7029f4..344a262 100644 +--- a/include/linux/mount.h ++++ b/include/linux/mount.h +@@ -66,6 +66,9 @@ extern void mnt_pin(struct vfsmount *mnt); + extern void mnt_unpin(struct vfsmount *mnt); + extern int __mnt_is_readonly(struct vfsmount *mnt); + ++struct path; ++extern struct vfsmount *clone_private_mount(struct path *path); ++ + struct file_system_type; + extern struct vfsmount *vfs_kern_mount(struct file_system_type *type, + int flags, const char *name, diff --git a/br-ext-chip-anyka/configs/unknown_unknown_ak3918ev200_openipc_defconfig b/br-ext-chip-anyka/configs/unknown_unknown_ak3918ev200_openipc_defconfig new file mode 100644 index 00000000..a87e03c9 --- /dev/null +++ b/br-ext-chip-anyka/configs/unknown_unknown_ak3918ev200_openipc_defconfig @@ -0,0 +1,99 @@ +# Architecture +BR2_arm=y +BR2_arm926t=y +BR2_ARM_EABI=y +# BR2_ARM_INSTRUCTIONS_THUMB is not set +BR2_KERNEL_HEADERS_VERSION=y +BR2_DEFAULT_KERNEL_VERSION="3.4.35" +BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_3_4=y + +# Toolchain +BR2_PER_PACKAGE_DIRECTORIES=y +BR2_GCC_VERSION_7_X=y +# BR2_TOOLCHAIN_USES_UCLIBC is not set +# BR2_TOOLCHAIN_BUILDROOT_UCLIBC is not set +# BR2_TOOLCHAIN_BUILDROOT_LIBC="uclibc" +BR2_TOOLCHAIN_USES_MUSL=y +BR2_TOOLCHAIN_BUILDROOT_MUSL=y +BR2_TOOLCHAIN_BUILDROOT_LIBC="musl" +BR2_TOOLCHAIN_BUILDROOT_CXX=y +BR2_TOOLCHAIN_BUILDROOT_LOCALE=y +BR2_TOOLCHAIN_BUILDROOT_USE_SSP=y + +# Kernel +BR2_LINUX_KERNEL=y +BR2_LINUX_KERNEL_CUSTOM_VERSION=y +BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="3.4.35" +BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y +BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL_ANYKA_PATH)/board/ak3918ev200/kernel/ak3918ev200.generic.config" +BR2_LINUX_KERNEL_UIMAGE=y +BR2_LINUX_KERNEL_XZ=y +BR2_LINUX_KERNEL_EXT_ANYKA_PATCHER=y +BR2_LINUX_KERNEL_EXT_ANYKA_PATCHER_LIST="$(BR2_EXTERNAL_ANYKA_PATH)/board/ak3918ev200/kernel/patches/ $(BR2_EXTERNAL_ANYKA_PATH)/board/ak3918ev200/kernel/overlay" + +# Filesystem +# BR2_TARGET_TZ_INFO is not set +BR2_TARGET_ROOTFS_CPIO=y +BR2_TARGET_ROOTFS_SQUASHFS=y +BR2_TARGET_ROOTFS_SQUASHFS4_XZ=y +BR2_ROOTFS_OVERLAY="$(TOPDIR)/../general/overlay" +BR2_ROOTFS_POST_BUILD_SCRIPT="$(TOPDIR)/../scripts/executing_commands_for_$(BR2_TOOLCHAIN_BUILDROOT_LIBC).sh" + +# OpenIPC configuration +BR2_TOOLCHAIN_BUILDROOT_VENDOR="openipc" +BR2_TARGET_GENERIC_ISSUE="Welcome to OpenIPC v2.2" +BR2_TARGET_GENERIC_HOSTNAME="openipc-ak3918ev200" +BR2_GLOBAL_PATCH_DIR="$(TOPDIR)/../general/package/all-patches" + +# OpenIPC packages +BR2_PACKAGE_BUSYBOX_CONFIG="$(TOPDIR)/../general/package/busybox/busybox.config" +BR2_PACKAGE_DROPBEAR=y +# BR2_PACKAGE_FDK_AAC_OPENIPC is not set +BR2_PACKAGE_FWPRINTENV_OPENIPC=y +BR2_PACKAGE_HASERL=y +BR2_PACKAGE_ANYKA_GPIO is not set +BR2_PACKAGE_ANYKA_OSDRV_AK3918EV200=y +BR2_PACKAGE_IPCTOOL=y +BR2_PACKAGE_JSON_C=y +BR2_PACKAGE_LAME_OPENIPC=y +BR2_PACKAGE_LIBCURL_OPENIPC=y +BR2_PACKAGE_LIBCURL_OPENIPC_CURL=y +# BR2_PACKAGE_LIBCURL_OPENIPC_VERBOSE is not set +# BR2_PACKAGE_LIBCURL_OPENIPC_PROXY_SUPPORT is not set +# BR2_PACKAGE_LIBCURL_OPENIPC_COOKIES_SUPPORT is not set +# BR2_PACKAGE_LIBCURL_OPENIPC_EXTRA_PROTOCOLS_FEATURES is not set +BR2_PACKAGE_LIBCURL_OPENIPC_MBEDTLS=y +BR2_PACKAGE_LIBEVENT_OPENIPC=y +BR2_PACKAGE_LIBEVENT_OPENIPC_REMOVE_PYSCRIPT=y +BR2_PACKAGE_LIBOGG_OPENIPC=y +BR2_PACKAGE_LIBWEBSOCKETS_OPENIPC=y +BR2_PACKAGE_LIBYAML=y +BR2_PACKAGE_MAJESTIC_FONTS=y +# BR2_PACKAGE_MAJESTIC_AK3918EV200=y +BR2_PACKAGE_MBEDTLS_OPENIPC=y +# BR2_PACKAGE_MBEDTLS_OPENIPC_PROGRAMS is not set +# BR2_PACKAGE_MBEDTLS_OPENIPC_COMPRESSION is not set +BR2_PACKAGE_MICROBE_WEB=y +# BR2_PACKAGE_MINI_SNMPD is not set +BR2_PACKAGE_MOTORS=y +BR2_PACKAGE_OPUS_OPENIPC=y +BR2_PACKAGE_OPUS_OPENIPC_FIXED_POINT=y +# BR2_PACKAGE_SSHPASS is not set +BR2_PACKAGE_UACME_OPENIPC=y +BR2_PACKAGE_VTUND_OPENIPC=y +BR2_PACKAGE_YAML_CLI=y + +# WiFi +BR2_PACKAGE_WIRELESS_TOOLS=y +BR2_PACKAGE_WPA_SUPPLICANT=y +BR2_PACKAGE_WPA_SUPPLICANT_CLI=y +BR2_PACKAGE_WPA_SUPPLICANT_NL80211=y +BR2_PACKAGE_WPA_SUPPLICANT_PASSPHRASE=y +BR2_PACKAGE_LINUX_FIRMWARE_OPENIPC=y +BR2_PACKAGE_LINUX_FIRMWARE_OPENIPC_MT7601U=y +BR2_PACKAGE_LINUX_FIRMWARE_OPENIPC_RTL8188EU=y +# BR2_PACKAGE_RTL8188EU is not set + +# WIREGUARD +BR2_PACKAGE_WIREGUARD_LINUX_COMPAT=y +BR2_PACKAGE_WIREGUARD_TOOLS=y diff --git a/br-ext-chip-anyka/external.mk b/br-ext-chip-anyka/external.mk index 6d04c2a5..bcbc7b64 100644 --- a/br-ext-chip-anyka/external.mk +++ b/br-ext-chip-anyka/external.mk @@ -1,4 +1,5 @@ include $(BR2_EXTERNAL_ANYKA_PATH)/linux/linux-ext-anyka_patcher.mk +include $(BR2_EXTERNAL_ANYKA_PATH)/package/anyka-osdrv-ak3918ev200/anyka-osdrv-ak3918ev200.mk include $(BR2_EXTERNAL_ANYKA_PATH)/package/anyka_patcher/anyka_patcher.mk include $(BR2_EXTERNAL_ANYKA_PATH)/package/aura-httpd/aura-httpd.mk include $(BR2_EXTERNAL_ANYKA_PATH)/package/fdk-aac-openipc/fdk-aac-openipc.mk @@ -14,7 +15,7 @@ include $(BR2_EXTERNAL_ANYKA_PATH)/package/libsrt-openipc/libsrt-openipc.mk include $(BR2_EXTERNAL_ANYKA_PATH)/package/libwebsockets-openipc/libwebsockets-openipc.mk include $(BR2_EXTERNAL_ANYKA_PATH)/package/linux-firmware-openipc/linux-firmware-openipc.mk include $(BR2_EXTERNAL_ANYKA_PATH)/package/majestic-fonts/majestic-fonts.mk -include $(BR2_EXTERNAL_ANYKA_PATH)/package/majestic/majestic.mk +#include $(BR2_EXTERNAL_ANYKA_PATH)/package/majestic/majestic.mk include $(BR2_EXTERNAL_ANYKA_PATH)/package/mbedtls-openipc/mbedtls-openipc.mk include $(BR2_EXTERNAL_ANYKA_PATH)/package/microbe-web/microbe-web.mk include $(BR2_EXTERNAL_ANYKA_PATH)/package/motors/motors.mk diff --git a/br-ext-chip-anyka/package/anyka-osdrv-ak3918ev200 b/br-ext-chip-anyka/package/anyka-osdrv-ak3918ev200 new file mode 120000 index 00000000..34dd9713 --- /dev/null +++ b/br-ext-chip-anyka/package/anyka-osdrv-ak3918ev200 @@ -0,0 +1 @@ +../../general/package/anyka-osdrv-ak3918ev200 \ No newline at end of file diff --git a/br-ext-chip-anyka/package/anyka_patcher b/br-ext-chip-anyka/package/anyka_patcher new file mode 120000 index 00000000..51400a76 --- /dev/null +++ b/br-ext-chip-anyka/package/anyka_patcher @@ -0,0 +1 @@ +../../general/package/anyka_patcher \ No newline at end of file diff --git a/building.sh b/building.sh index 1bdabc8f..7ba32291 100755 --- a/building.sh +++ b/building.sh @@ -60,6 +60,12 @@ sdk() { ################################################################################# +ak3918ev200() { + soc="ak3918ev200" + fresh && make PLATFORM=anyka BOARD=unknown_unknown_${soc}_openipc all && rename +} + +################################################################################# fh8833v100() { soc="fh8833v100" fresh && make PLATFORM=fullhan BOARD=unknown_unknown_${soc}_openipc all && rename @@ -390,6 +396,8 @@ xm550() { # Build firmware # +# ak3918ev200 # testing.. +# # fh8833v100 # testing.. # fh8852v100 # testing.. # fh8856v100 # testing.. diff --git a/general/package/anyka-osdrv-ak3918ev200/Config.in b/general/package/anyka-osdrv-ak3918ev200/Config.in new file mode 100644 index 00000000..82144a54 --- /dev/null +++ b/general/package/anyka-osdrv-ak3918ev200/Config.in @@ -0,0 +1,6 @@ +config BR2_PACKAGE_ANYKA_OSDRV_AK3918EV200 + bool "anyka-osdrv-ak3918ev200" + help + anyka-osdrv-ak3918ev200 - Anyka kernel modules and libs + + https://openipc.org diff --git a/general/package/anyka-osdrv-ak3918ev200/anyka-osdrv-ak3918ev200.mk b/general/package/anyka-osdrv-ak3918ev200/anyka-osdrv-ak3918ev200.mk new file mode 100644 index 00000000..333022ce --- /dev/null +++ b/general/package/anyka-osdrv-ak3918ev200/anyka-osdrv-ak3918ev200.mk @@ -0,0 +1,35 @@ +################################################################################ +# +# anyka-osdrv-ak3918ev200 +# +################################################################################ + +ANYKA_OSDRV_AK3918EV200_VERSION = +ANYKA_OSDRV_AK3918EV200_SITE = +ANYKA_OSDRV_AK3918EV200_LICENSE = MIT +ANYKA_OSDRV_AK3918EV200_LICENSE_FILES = LICENSE + + +define ANYKA_OSDRV_AK3918EV200_INSTALL_TARGET_CMDS + $(INSTALL) -m 755 -d $(TARGET_DIR)/etc/init.d + $(INSTALL) -m 755 -t $(TARGET_DIR)/etc/init.d $(BR2_EXTERNAL_ANYKA_PATH)/package/anyka-osdrv-ak3918ev200/files/script/S95anyka + + $(INSTALL) -m 755 -d $(TARGET_DIR)/etc/sensors + $(INSTALL) -m 644 -t $(TARGET_DIR)/etc/sensors $(BR2_EXTERNAL_ANYKA_PATH)/package/anyka-osdrv-ak3918ev200/files/sensor/config/*.conf + + $(INSTALL) -m 755 -d $(TARGET_DIR)/lib/modules/3.4.35/anyka + $(INSTALL) -m 644 -t $(TARGET_DIR)/lib/modules/3.4.35/anyka $(BR2_EXTERNAL_ANYKA_PATH)/package/anyka-osdrv-ak3918ev200/files/kmod/*.ko + + $(INSTALL) -m 755 -d $(TARGET_DIR)/usr/bin + $(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(BR2_EXTERNAL_ANYKA_PATH)/package/anyka-osdrv-ak3918ev200/files/script/load_anyka + # $(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(BR2_EXTERNAL_ANYKA_PATH)/package/anyka-osdrv-ak3918ev200/files/script/ircut_demo + + $(INSTALL) -m 755 -d $(TARGET_DIR)/usr/lib/sensors + # $(INSTALL) -m 644 -t $(TARGET_DIR)/usr/lib/sensors $(BR2_EXTERNAL_ANYKA_PATH)/package/anyka-osdrv-ak3918ev200/files/sensor/*.so + + $(INSTALL) -m 755 -d $(TARGET_DIR)/usr/lib + + # $(INSTALL) -m 644 -t $(TARGET_DIR)/usr/lib/ $(BR2_EXTERNAL_ANYKA_PATH)/package/anyka-osdrv-ak3918ev200/files/lib/xxx.so +endef + +$(eval $(generic-package)) diff --git a/general/package/anyka-osdrv-ak3918ev200/files/kmod/akcamera.ko b/general/package/anyka-osdrv-ak3918ev200/files/kmod/akcamera.ko new file mode 100644 index 0000000000000000000000000000000000000000..806206090351bd9ddecc09646b4aa32f9b232d2a GIT binary patch literal 20869 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 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_gc1024.ko b/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_gc1024.ko new file mode 100755 index 0000000000000000000000000000000000000000..d5dd0c86e569bdb3d46b97468482b59e58300bf2 GIT binary patch literal 7594 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 AYybcN literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_gc1034.ko b/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_gc1034.ko new file mode 100755 index 0000000000000000000000000000000000000000..ab3fd1614c6e127afd63f692775894247b5feccf GIT binary patch literal 7679 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 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_h42.ko b/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_h42.ko new file mode 100644 index 0000000000000000000000000000000000000000..93739ad507a7859dcb41891126c2e45e3061dd60 GIT binary patch literal 7533 zcmeHLZERcB89vu`(ln*3TiTI!QG01q2(yUgaT3}$FXmct2(yl zk2IYagtqKUAZ06bWg3cyKthO3<#XM{4!8AWp6hdx z8`H5Ov41<#@$)|KdCz;^^PY3A?>+bL=-#Pmnova(jbe@o(Z9rQsq)*>@GHl zMMaIW*j><#Ma6pWqGBD;_H}0W?D%yu9(_4t3*_=8uU2p&mh%|1j> zUf*5tG#x(IF>q~h0ouJrM`4NRoAwUmj-mgL^ns4T8nJI$2M-M7j#PhJG8pN>%x;5}g9b;i^UkP*+zJhtMA7VSE4eSKpH{GaDJu1ApBh}%fGl0=b=COa01gX~=b{#dtFu%Y*gYFA9sm{|vs- zm@1Y6qTmTUh_&g@V;xp4MB5+HRuZD{fCx@|L{2lFejHQIO#{#0kt@fh_DO>=X|MMU zTiOfNMti}HoVY)j`XTZ$bIBz{4TO5sR@4>ZoV{o2^xSk%pC|^ghv9hUEMwDi`Jgyj z3_!=et%$8bKUSojA38twKG#_7JLI&eL&tUs<`9`#Rf)5r>8o#r1^^a2N zmyG3tJOe7%-22tRiGmPs6*cc$MHl+7q;KT+U!~G2l}E;*`oz{^uE?WH^yL?FU+Q<@ z%=P8#uy0*bUM)nerXRNT4`ZFaiWs+|AMN&`&5-i9&=!O*wGQ0Blh{<_s1v9sP-|Q` zqtJVRZ_1oE@0O?7e`efc<3mXI~`~RsTiEG5e2K^rwv)Z+W0RM!&KN^xI=}Hcb^* zVsAC*M~VSFhXS(wbs2vr#%#lyz4S5m5#-k(KM%Pi@LU7uTpDmj*v9=&{&U%PG0ruf z6TiL&i2e0U4UlV63%mh2UxAocLdG+zb~W&N=v?r#4r4rqG1da#2md1M+){r`>W_ls z?9VKa`iG^y2E5jx|F+bhfgZ~^Q!DKskot$f?}Hq`+2KBG2plPLU0ZQJTIKoS8KkaC zoDemr%NL#y14#G?Y7%uE^&V`Fljabni#-EA2K*-KB;*nQ=Fn&&nvIzuD{f_IV*k(K zs0&uaHLv1)W1Rn7`OITcsM~Dr$CwkZCP`PAmILt0oDPQ~nHc0{-WzXg z`~pzFv9%G0cbNbu8sN~=Hf?4?$;3f3m59&sp=4T&0+o2^*jCgsiqw{?OhH`=5eet-S~KFMkGm?#ov{gMBsZf8(^5@&u68Ss4EU z*q?!NHhw8j09l=d_B$|GIGY(sq}mRdq2X*|BoQCh zTj%6JIDY6r*p~?3E@D;$UCp*#!EX3Yn(=75O<3_v$~4>V5pX{m9=0NF%|5@cxkbn6 z4`t)AL^!H%@9i0D(Z%|7t?01E^c}sudwX@mFs`o^J?-7Q_4R3eEF8OaeY93|btqcD1+PsyB98X$was5f6*h^N@!~#R<-o+5Y?U6^rOD02xP7?%a>4jHdX0fhW8wFTnYvI z(~i&Mn)8FN5;SW0cqq*{jWw-q+e|SQPN$6Bj{%(bWai}AE z8Fp?eWTD8~BXKsQYY?BbAt--cke%^8i;X@39pl4$>TG;>L8f2j?86Rg}M@P}u)}vf&-rIg1M6yHF~b?X)}YFjQ?n zsIW`neaTsr-4jq^u1VEKyG@D!m7Q8g)iedW=M{0-(O%7!&8^6bf&Xo6FR>Gq^JSix zBb8%_B8IO)&7n}^Zbe&tj}QUkE+IOBE)393gtH(K{+Yk}-9nrvb_sEbh<@F0R1YIB z`mo<}BKp-y9t75ZO^9>ku>F(7^Ai6m(LijrKZ$n(>1V&h0V3jvN`3 zq{NKGCxP^TiipXbCho=i4sj3SBlcomvfYER*zP5wT_>X5FL{ULQ6l_KN_~!qeo35T z>c0bIoDWHyl=z6m6GV)eBcktUs8P z&%H^+eS?iU5%D(?uSc9jG#W%a{zu@5X$7e~ol;g=b_-kk&=NuW&8^B+ItNHyF{4zLWQ1ctXek+%< zKi+pK&hhTWS#@&7xT5b!!u8R>-vd(ako=!O#)3w>ozFDhDdO+h=1U<(ERbhfwI2r0z_#3ffp{34 z-ygc{{}?!*1;4F=(8qUwl;FJ}A9XtqLwy$#RMJVyl{2hHJ+1f#@HqHd+0OoRWno2pmGM@K+t8nd z?=(x{wM%m9n6m-NDKA3=iHi0yaL&h&?T0JcPgL+{D)@61{M8EnW(EI41^;^m7jqwX zzKCcD+m}G{QqCO5r5rDbBBP0PD4XP~dMFwi4&ytQYUkZuL)2DDZwFIW#tfy*VJnT# z>`(YG$CIIqHEMpMAw1_?dg2$)d`H8$vBDPs11Mi~QL^t3`+dz@o6BL?H(;UAP&OV3 zg?yX+TU(k%yRUi6EtI$UMSI^}{q3EwZxT+c&3@6-#hZuFzEJ=6I~3`3l0AESd$xBw zH9Nbzf;M-!x@B9-JkQ4F@?dIRt ze1KPzNh=--r$fU@DH2$XuSO+s5p!6oUBtV!!7p}?q>gP2ZA?N_g{BROZlJ**pw$i@T|kIVXk&ozzVG?o zH53hx#ymH?z!i__k8c#-MvTCG@*(nn#DX5qJ4$iQ`71~vsf*HV)--q zf_QoJ-vg(*i{jBAoLj&}ex_O$3{IDVV!HHUx%|d5iPTYZ61_I`Gd;xtBR|t@>@PKm zr6rARvA?JrOG}NxrKJX-YwOJZx%nHUKYBfU3+D^vpjPz3mvfl291IjSv`c6UV1x8I zEyr|YzSfn~C63d3^My`5KQn;&I7d_Gse)gK%Nn$b|5`XdgZRv@5dAYXLVPO-42JU? z8uk6fz?NfYI|o*m7NI|AbQV{LzM0@a{shK3#1j~5^G`Oeme-a?}gSJQ@O9AC8tKHP=zmts!Mm~{1P-T5&G#&LeXoL?Yb z%ju)^0sI90SPVbbLADMyDWfm+?Z$9t@kXqJdrZdAlmFE|e1j`!tsq`i1ktkiOKx<}CdVAUC>2 zaVhxU$Rqi}60~Q~E=OvRW7F_;vQQSHNPqky;t%S`4Uu1s9P%TlGy}fflo!H8<$6`_ zH5pTG4qdUWqgZQnV0_<<|NQiK5#!lz{2WG2qfVh-BF?&b*nl-e^oddg=K}jP`zQLO z=L-=rSqei2)!BCV)TEy%QLh!UR-7m14dcXI#y*vF$k?x=-`iJ@J!wK^;Ti5n@}BWU zLBn68-N@lDVm&&>Q7?kN2HBoKUj(*P%$UDZNUCwv3DlFQZ{rNY z?b$bh&&&1Gr?cSimCLiQ0N(|^3}g(DOS6&iRH+;Z6nWk#BWl6?jA|pl-F6*vmFGGy z$7+$Ai|?1qS9f+IU&4W6K#u2F)h4R(OOeAIKTtKEI%>Z9aAl5mr4#OV=PR_ZLr`Ni|#56ghq^T!qg8T&fm6^QL6 z@NEs+csA9o1zrxB4|X%l%J6D#o%=w`F&D; z26AlIY@O7p#VOLR09&n2v=owOaQxbHI`kQII{s5o<&=f77z+gKFp zE}Q=`=9?F6g)b<*cwyw?RsScN1F*`N4o7270_|17E3a?<3{bzay%~pel>jFi;E+={ zYdO($>UJxWO3v}2bXJVxVaEN$i$tL57KM_!P}w!?H9CFBfuBH%t#wNoiO=?gtgcZ0 zP~Q*z$H6aFKLGtFKY>2;@|jPdUkm*|di7;{0?6tu^nVfb&pha zc(M9Rp#PFr9|7f9z7r;Z-V#94^KAGfE|$1RB3>3k)NrhKln`+%8_U>f$4(`8c4q7` zE29skGJ2nt%mTWSj+GfQW0o*;&PXb=^QaXa&ZS0D$zi>Hz8yA`M~|4HlzD?l*f9*X zcJ7UI!*<$A#-P?PxS2qmf@;cGe(Y;^anAOM3#C03vb)u_NF=Kq=c%9f2=~DuB_wMdH*nM+< z*TDl`DH2^DwKEy4Ds5)WQ41^6Epp7z-f{ycc0s2mj(|R7+KD7-g|-s}T_gQV2vs9CFR| zxNeDkU#L*=ZETU57vf=THr~~O-y;EF0?7SuM#VSgr&?ZzwYN~;L4Ci0pPjXFR8q<4 z*cQWR9*)IYL#>;)?l3mxVhMXN7BxaULM>ZETQ*tdpuH(!Cv)RzXCz~p@vE-Vp&P

5aGvDl0Qv^?bDKvK$rFt64MeLiH`tj|0yCCcba$* z-#f$uLYyV`VqLO7fVtQoB%)s@qQ6!0PRZj$*d3Shdx#i!E6y?HX(0V{B#ukGQ{n^> zbKXP5*n6dXO3Lq-cv8yw&ZoUch!}U8h;h$J`HNEif#d<`QtuifYOCajC67yfujKbj z{^FmAzHy}w+DCgPZ{LD=v?iF0riSWOf2q9lI zK770wL_Fj8eDxuBnWsMFdOH#Km|-HGF%FUQO1x9zgv6(bxTwHuN%)xDA5;8ur?H~s zUvPqWp5Umb_~+gNN}o@n`Ql&j1mk!-JUzuP@bL2#zgX^J)W<`>Q~YAZ0uKY?e0iaU z<~|=e#EmnwFPCkE3eOGAjbjtqZhXNRlKHS`?u`AARf;BXa{3lPE>%j!-ZBsW9XiO} zeU|g{S>x63^l;kav&Gy0dk=pO9pq|#e+K^mJP)7L`uMD=l!`yR*HWDJ<2b8ct{8Xp z6-l^18u%MPwmT*N7toDA;>mtK(|FH^*R#u4qCJ4hDcHXjZQiFSz6ovKtth?|ZQipe z-h=i8s+#{W+PrIFCeq#!wE2wTw+eX<$TO|_kAXYTt&CqJ?gQ^ezb?l=1fKBd{|KC= z8CApic@ms$#h(R_fLH9-cybX!yxCupt#R3&zB`t{#E;1E3U_Q7Jk#Lgw-y|DPznABxidS zJV;gbkAZVNhU`C9)qk>zKUT$mR>hyG;?Gy{SF89xs<@c{a~Fzl0=&*_ZxfFZv?waDRNLsr+V>?zfV-4F`{LTJ|4Qo6db?i~=BOT_v zarZ`$vq+Qt>$A|}Fs`ofSAaT|8?XLQ%l54mH{2VrXmlu-j76iNt*zTzcZiNq%eFSQ zcNn6h@0R|Keb6_AS8J;wdb)V?5Iq#_-*uBB`@H19!QP%--CoI_?yiW-J*KYR(dMS88Ic{G@O{b!BI|3bLn4_L+nzoi!DXiSdw6(NI zF7IC2T4WF^f7{)EUhXcMiNqlP9t}r%CE?k4%z({zXX0h7ytk27E0;H0wn?h4aJW40 zpENt0j%UW^c1UiUh$SpDnZx?rdtNbWjmFYPaUoO{x*k+Dre6VjOA$A*|xi3%Z9X|;nV=+H(rv=0rDJRz3{=Mk42WxW<<7a;_qWvSZ8FxlPHO{3YgmH@0^mqg|QLGc^ z--eiNh#4}x99$;@(~Cr2UJCrI$kWZh&m(s&z&nt))#!I6+RXK8U;}U&uoJo^&^4gn zTM>T({jLT61-u>dpprkNRw;);Qa zdC5vt?&UVMAB|S5Zyb3I2e98N(+R|z35T%GmFb;mv(Gklh6>83ANxOseACZ3P^l)2 z%Jd-izq>|>scH~8ZX2=R!@zL7a%;QMR||EWxSSYVQE$S$!_h=-nH-!B z4_3~=|9i$@qP9j3PaELz!OBVQ=|y30ZNt&HYr8C3xixH@tetIWxZEfQs#u@f7$Xop zxg7Ic^$7fL;u(vca_x~2^p8H^$Nqz=e~DJ6xeu4AF@T@pIGQnzHIS`=4`mz+$M%JI zqV@&MgMJv>nZV#~@Wa!c#^mGJnT$^E+l{^3VVtScE(Tdl?OVOKkhi)48TAJA+&5Z>`%U&UQ7B(RE#LyW(2$fY+RWX% z`kqhXULUMfnS-CgzY+5Y70%W%)GJNVO7&Ori)tW#s`hO7)#|sf9wFRiQ_HoxX5K)=m5i{*@J&$0s@b3CL+Gx5(qoOW2 ziqO6R_uW}=Yzw!{V6(G5p{grXKeP|R{)gb#Y9E6AQ=h<|cks_Yf&C)bKkv6!?Fk^O zzi|9bu>UQDv*TCo2_UP#(4PMXFF|;%_KRWvJHI^=N?)G23824(uyMJ+g9@7!HYt3S z#yEf%csP6|W0lfHyWrUQTu-8CAG3zqh~7fZMTDb_ad-B{YkUuN!ZX-N}PmK=pxL2k+}YG_dc!d;LZ>`}2Z!uzObmGap#(<<9G zMU!%G9y8C%VK>}<4naJ5l+p@M)IU5$uns zb<}qj1TMLYZ!!`8FCUWuu2Cy0mPz*9dq~zkc4+FJ%+bN0J$Chc;>PZ$icdWKH`&wM zyTj<*YnN;sjeO2L))kF*j-=DENNnS#JEOOk(^-2cZAK$^MmBGXblq;HhV0w3cCI{L za7K$(DzkpQ0ozCy`uNmB?c`%mid{)yEt-v626oA!P;+2$V0oxDa6{mxU|V2iU{%BF z(1w6SO2@~X)DX}qdN`_EMJpR|tZ_#oY!&mFl#`MOjrLL%MoLFL zpWldjNJsD@atd4*9d925{_(wp@l8O+@in2+j`O`A)gSK?;>9RXMaMe`?vIzi!NFgX z8;^1w@5^X2zMgLwF9RLmy6AYPAaMWhlp+Q@f6+tuCWN|8J?;36qKnpj2Lj!u?&Xh& z_haagy8>-kXt&X8y1zPxUPrC^2kf5J1gPzZdahItATJj%$>nIrQ8{1ci8<11hNg7j z58*dHM;&iFbnSaQ-}ap%d%+uE*H1*SBoWU6%wPL1k@tzakxK+<--r8*2&WDroVtn7 z?^XH%rOzn+xZ;m0ex8W_FDm()M8xGJDSt;{GbY1vEG0q~CSrW8inl4=u5cXK-YfDb z5pgaN@4=e_eCU6Uh<=;W;4Vy`bxy3@ zqQ5s4=YKZ&DI)yN5utlV$$zQjmlS6{XtxwT^lK%8cPpMyJgc~)_>+pCQ~VXhFDm}N z;vwXP@%ZdO_7)4}JUi^4dyCkn&>&*`ox~12ix6@7MTxirVnn>$bDuT9 zshhaW+iwkc!FR|Je_Y|i3MUjkPfQ>#^KduF$CTjgDe;Vg^G*QI0|x3V!P#3}+c){N zwC5?BD?!0!SMH@e+dM|`@pG<{8$vs5Y0P0i1b6 zn!Wsj^SrWpCE)frpegj&(n9X$9o$0uG%MF%`TrAbuXz3j)?V{G8ssq`?H>gWfFI#{ zD1V+QuXz5?`1m9g8OSrIYr2gppadiVZI+AaZ9889(r<~9FFG=aLdQv z2G4=7ReJi*mBkCjw{X0RvpF zM;Gw?0{)c+{M!ro_ZRS~1zhG{o+4@KNVy+X)JH69Tz$k!kL62dxxhzZGh>dV@EXZ) zFaB>`9U1j`i|^s)gGJl1%%U}7m+(UFY1|Jrbn{2E#tWunk6EsXUy+*YMvai~7V|@n z;M*FWwHed<48)fE<;^sQ%ek~^MmEK^$F_=(ZoPAhpKjZ}ReB?vw{4~6_HEL8@cx0` zy^+n^yZlz$yS8})bc)&1Y|G5!y?JyP@7pGx^2@Fq-y+6Rj%V{A(quA8rthQ0me?jG z^uADRiLoadsV@;*Vo^7enzj30;$|(C%?|P9VZ`M7x^LOwjHPpf@YAFEilL%c)UBdN7>!k z?#>#!ii9Dqno^`%`~<6N((AsE;F8t!kCQgNNeOoxZS_#u?<>PAgz3l?oMvlqo_o1$zEKO>Ka)&ljX{T)! zg=HEksoJ(mTXiRtcI1+Uc_<*V^+hVQW$atG67pf%o)*V z6yvQSM((GM6^cV@p*)H4c#QU;wPJwi#wSJGO0-aJQ%B%$q8yGEZV7cha(QTSccp;} z#l~=<$bG_Ew6uf9%R98fEn)R+>3kq?xq-%tO+>eGyP}=l6@t$lbYHnm8+kl*J zS1pg~C+BfqRjyhs`@!kY&_0)DSu%hB9(r=K1{vu`q7}SHQmvd#PvsnorO6uYd<(k6gR< ze*`~O*XMV>KJ4TDYPIH*eXRNP%%Ai5YzBW9_8*G<(+A(N=yIhR3zc}!@P1)bFus&I z7M7c1hza(u%Dor6xAgC7^|qlQ?C)r(#JOiX?kinbVED zPvv=ShHZ>}vyV|KZ37<#cm2^)QJz(4&wGl`>DT%OOO4uKN$v}5gR5&e2p3V`LcNB{ z`-l6tsu)9Etc+rgID=RJ&f}?tVvLq5htY;@boB;dqk(I<3plSFKlg7}S8?yGR@g@y z^nP6@=5z%0Tc|6j>wy?e0(p+t#95%pLXqd`<`8R&JQe$ep9ah&lsS+4gRVAdh2o3! zo1zk3Dm@>5v-tauv6r!iYuaCnL7FK3t6Kd7$8-%b&0!3C?qFYMm!7f{`J|~^Y0E() z3f7!3C$}OE?)@Nf3;3tiF!Wp`Y96iqW@^$W3)j#r;+oOpPLi#b5Dc&XqmZ#qZjmUc z3(_~+2awI5p_S*trHT!~@}j7KT4DPj?4Lo)XSEN({@0(wp7-#JpToWp_80y3LS6)N z^%su63HGm{u@=9O7lB;;h3&V%{&h5dR{O26f5UH&gmPcruZuu`35C5q5)`;aV3WYV zu`xF6I^yt`gqe$Ht&C&Y>4BlFHD_kkX*;Wqo9P_jMA|X4(?;ARBk#=G*?|R9pUK;^ zc6vrVydg)8^ulQ)VjFi-(u$+2IdD8SN(nP#rW3gVveHi0GzY#-aGx?}toT58q$kqd ztKt~w`E=4Y66%q$lXJZ)?aMXOkTs_s9UHr6Ow}~)re?~Sj+(NvSp<|ZvPQ~8P^!rs z%9DemBkI1KI%gyg?MpP%i6P0f`F-=vbSyS58(2Mdbo`#tdnZoZbIRX(xcg2kaR?nE z=;0j&8n(065A3vKB-LX{D?=IkK{IQo1x`;>&N0%7sRb)7B|Gm>DxN$|R)VH8Im((j zJDKOyQeT%Aqf<6!pQQU-cPHHm(eO7sdKQba^J$_BK>YX816ZdJa308O-j4c3mbc-o zjQTF>wK~T+cky+RhdSl|X)Oc1KK%d4_u}zW_tN@zUtVeZ?R#fF%J2Enl_y?HbgpcT z)A7N`N?9`*am;y#A}nR?gy9$z!Lhc|(>5C22JY913Ai?%1zXRg z^ASw+w4IKd5ogxQiGM|kq;1D^)l+#ZnK*#s6q(B9C=$0*DKm}1Tmz*10M20iG(~WE zL=4BtT2pz)%wa;GxqKCte7+h*j~?JT+>^5K8gs!jse3M6f}b|rtCQ%s6EgQ_JN}Qp zRd9LDz64+V+$)Rpw+{_`&Sg0RM{6MLkN1=Ghh=yDOBbUUWEpL;NPmaWz;Sd5#oMs^ zKpF`h>rV=q$Kdi7f>?AV???JO0qT!$|1Qst5<+&iCB5&3B*lwU)vDT6~?=~y~6$4;Hd2c(PKdFx6Fus zPYV79>+dF7gf0NteeMIE6Z|oO{Ro!*jWQm^{~05KNiZHKau`Q2j?k|N{j);^2P3K` zI6o(FySpAL^2+>11cSouTNu&Cdx^1Cpvs8&+Zp%4FC&!u8F#}!BQ8n47Xy1R4@SIb z;yVBZ@aLCdgnviiyudR8Ut%1DKaT$h$fuMb!N^yFn|3_!e+23)!P>3uH3r?AW5Xc0 z>Biyyn=gqV;ZXWYkh~9PvMESklA?=2+Oa9Z5WQdJbG}g&_j!Q1n`c;mUC2lkmQHcs zJ2}^GUco0IV;hU2xc4Eqc|{?RW=dEuUSlc>Z{AN&YJc z%%ec=|7YMOA7?+U*dOlx67em9d&Tp&@qgyUuVMZD`lyc=plM%c1s`WUpH;uU-N!$Kf_VZc{qb4#isxT4-T{0rxnOH!^dMai-U4|@@LRwifE*P( z0=@#7cLv)Jfb$)&24sFWkk1_NB<9}%&p-~lyopS3ewJ5--T~*cD(xQv=fdYs!T#oZ zmSxGG1&@Jy_6=^nKt*Yy7e)Vev?$pBd~dKkDmbsLEWBSipWl-W81Em@Gw1&o9{(e7 z)5o_X)-?FdLeKp-%GSDg>f>vqHuUGh&jdR$%58#k8|UbV;4JS#1hG2({op(wP3Yrw z`h`0FlREx*9sgw=U#sIU*YP*&I9^8i%~-q~MdH+M$Zsd&n~ph;Z#v?0N<3xf^n8Y& zdG&-oW8hs;X#5|5+7q%>IDar}Ii{X9XRI7vb*0kp%$(2Yj+HV$NoQ;tNVw!TiU0D5 z7wf5wU~}%b9}(Y->F9XwmdvkmKU(SfbUq!|^+;Dwe^)OJM!NfYS-zu(2FLH87#xmt z_iN;{>N|Lc^cHur$(*0)Hr|$Jr}37pRrnB*GMt*_gUFuF?+Ci@qeV||w`laf zQ1tY62`;`w^z?R0e2 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1035.ko b/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1035.ko new file mode 100755 index 0000000000000000000000000000000000000000..d22580644fc076eeb8cea27a5b4913ea27d16a11 GIT binary patch literal 8385 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 MX;aHYj2NQ&4+Z^JZvX%Q literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1145.ko b/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1145.ko new file mode 100755 index 0000000000000000000000000000000000000000..de75d0f3f606b3e063147d64d6c7983afb5507d9 GIT binary patch literal 8981 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 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1235.ko b/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1235.ko new file mode 100644 index 0000000000000000000000000000000000000000..0eea6c1bc48deaf8b042977a9216d3f582518a42 GIT binary patch literal 8218 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 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1245.ko b/general/package/anyka-osdrv-ak3918ev200/files/kmod/sensor_sc1245.ko new file mode 100644 index 0000000000000000000000000000000000000000..6322ec69bcb81606378000181156263db66e6119 GIT binary patch literal 8639 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 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/script/S95anyka b/general/package/anyka-osdrv-ak3918ev200/files/script/S95anyka new file mode 100755 index 00000000..48637f76 --- /dev/null +++ b/general/package/anyka-osdrv-ak3918ev200/files/script/S95anyka @@ -0,0 +1,73 @@ +#!/bin/sh + +DAEMON="majestic" +PIDFILE="/var/run/$DAEMON.pid" + +DAEMON_ARGS="-s" + +# shellcheck source=/dev/null +[ -r "/etc/default/$DAEMON" ] && . "/etc/default/$DAEMON" + +load_majestic() { + printf 'Starting %s: ' "$DAEMON" + [ -f /usr/bin/$DAEMON ] || echo -en "DISABLED, " + # shellcheck disable=SC2086 # we need the word splitting + start-stop-daemon -b -m -S -q -p "$PIDFILE" -x "/usr/bin/$DAEMON" \ + -- $DAEMON_ARGS + status=$? + if [ "$status" -eq 0 ]; then + echo "OK" + else + echo "FAIL" + fi + return "$status" +} + +# The daemon does not create a pidfile, and use "-m" to instruct start-stop-daemon to create one. +start() { + logger -s -p daemon.info -t anyka "Check MAC for Xiongmai devices" + if [ "$(fw_printenv -n ethaddr)" = "00:00:23:34:45:66" ]; then + logger -s -p daemon.info -t anyka "The eth0 interface has a lousy MAC, let's try to change it.." + XMMAC="$(ipcinfo --xm_mac)" && [ -n "${XMMAC}" ] && fw_setenv ethaddr ${XMMAC} && reboot -f || logger -s -p daemon.info -t anyka "It is not possible to install a new MAC on the eth0 interface.." + else + logger -s -p daemon.info -t anyka "The eth0 interface has a correct MAC - $(fw_printenv -n ethaddr)" + fi + # + logger -s -p daemon.info -t anyka "Loading of kernel modules and initialization of the video system has started" + export TZ=$(cat /etc/TZ) + #load_anyka + # + #load_majestic +} + +stop() { + printf 'Stopping %s: ' "$DAEMON" + [ -f /usr/bin/$DAEMON ] || echo -en "DISABLED, " + start-stop-daemon -K -q -p "$PIDFILE" + status=$? + if [ "$status" -eq 0 ]; then + rm -f "$PIDFILE" + echo "OK" + else + echo "FAIL" + fi + return "$status" +} + +restart() { + stop + sleep 1 + reload +} + +reload() { + load_majestic +} + +case "$1" in + start|stop|restart|reload) + "$1";; + *) + echo "Usage: $0 {start|stop|restart|reload}" + exit 1 +esac diff --git a/general/package/anyka-osdrv-ak3918ev200/files/script/ircut_demo b/general/package/anyka-osdrv-ak3918ev200/files/script/ircut_demo new file mode 100644 index 00000000..2d1f1ea7 --- /dev/null +++ b/general/package/anyka-osdrv-ak3918ev200/files/script/ircut_demo @@ -0,0 +1,87 @@ +#!/bin/sh + +# on EV200: +# GPIO1_0 -> GPIO8 (1*8+0 = 8) +# GPIO1_1 -> GPIO9 (1*8+1 = 9) + +# on EV300: +# GPIO1_3 -> GPIO11 +# GPIO1_2 -> GPIO10 + +#(normal mode) +ir_cut_enable() { + # pin_mux + echo "$gpio_0" >/sys/class/gpio/unexport + echo "$gpio_1" >/sys/class/gpio/unexport + echo "$gpio_0" >/sys/class/gpio/export + echo "$gpio_1" >/sys/class/gpio/export + + # dir + echo "out" >/sys/class/gpio/gpio$gpio_0/direction + echo "out" >/sys/class/gpio/gpio$gpio_1/direction + + # data, gpio_1: 0, gpio_0: 1 (normal mode) + echo "1" >/sys/class/gpio/gpio$gpio_0/value + echo "0" >/sys/class/gpio/gpio$gpio_1/value + + #sleep 1s + sleep 1 + + # back to original + echo "0" >/sys/class/gpio/gpio$gpio_0/value + echo "0" >/sys/class/gpio/gpio$gpio_1/value +} + +# (ir mode) +ir_cut_disable() { + # pin_mux + echo "$gpio_0" >/sys/class/gpio/unexport + echo "$gpio_1" >/sys/class/gpio/unexport + echo "$gpio_0" >/sys/class/gpio/export + echo "$gpio_1" >/sys/class/gpio/export + + # dir + echo "out" >/sys/class/gpio/gpio$gpio_0/direction + echo "out" >/sys/class/gpio/gpio$gpio_1/direction + + # data, gpio_1: 1, gpio_0: 0 (ir mode) + echo "0" >/sys/class/gpio/gpio$gpio_0/value + echo "1" >/sys/class/gpio/gpio$gpio_1/value + + #sleep 1s + sleep 1 + + # back to original + echo "0" >/sys/class/gpio/gpio$gpio_0/value + echo "0" >/sys/class/gpio/gpio$gpio_1/value +} + +gpio_0=0 +gpio_1=0 + +if [ $# -lt 2 ]; then + echo "usage : ./ircut_demo " + echo "for example:" + echo "ir mode : ./ircut_demo hi3516ev200 1" +else + if [ $1 = "hi3516ev200" ]; then + gpio_0=8 + gpio_1=9 + elif [ $1 = "hi3516ev300" ]; then + gpio_0=11 + gpio_1=10 + else + echo "wrong chipid: $1, please select: hi3516ev200 or hi3516ev300." + exit + fi + + if [ $2 -eq 0 ]; then + echo "normal mode, ir_cut on" + ir_cut_enable >/dev/null + elif [ $2 -eq 1 ]; then + echo "ir mode, ir_cut off" + ir_cut_disable >/dev/null + else + echo "invalid mode, please slect 0 or 1." + fi +fi diff --git a/general/package/anyka-osdrv-ak3918ev200/files/script/load_anyka b/general/package/anyka-osdrv-ak3918ev200/files/script/load_anyka new file mode 100755 index 00000000..5a6d0212 --- /dev/null +++ b/general/package/anyka-osdrv-ak3918ev200/files/script/load_anyka @@ -0,0 +1,196 @@ +#!/bin/sh +# +# This is part of OpenIPC.org project | 2020.08.01 +# +# ar0237 imx290 imx307 imx323 imx385 jxf22 ov2718 ov2718_2a sc2235 + + +# SoC detect +chipid=$(ipcinfo --chip_id) + +# MMZ config +mem_start=0x80000000; # phy mem start + +mem_total=$(fw_printenv -n totalmem | tr -d 'M') +mem_total=${mem_total:=64} + +os_mem_size=$(fw_printenv -n osmem | tr -d 'M') +os_mem_size=${os_mem_size:=32} + +report_error() +{ + echo "******* Error: There's something wrong, please check! *****" + exit 1 +} + +insert_mmz() +{ +} + +insert_detect() +{ + insert_mmz + insert_isp + SENSOR=ar0130 insert_sns +} + +remove_detect() +{ +} + +remove_sns() +{ + rmmod sensor_spi &> /dev/null + rmmod sensor_i2c &> /dev/null +} + +insert_isp() +{ +} + +insert_sns() +{ +} + +insert_ko() +{ + find . -maxdepth 1 -type f -name "sensor_*.ko" -exec insmod {} \; + insmod akcamera.ko +} + +remove_ko() +{ +} + +calc_mmz_info() +{ + mmz_start=`echo "$mem_start $os_mem_size" | + awk 'BEGIN { temp = 0; } + { + temp = $1/1024/1024 + $2; + } + END { printf("0x%x00000\n", temp); }'` + + mmz_size=`echo "$mem_total $os_mem_size" | + awk 'BEGIN { temp = 0; } + { + temp = $1 - $2; + } + END { printf("%dM\n", temp); }'` + echo "mmz_start: $mmz_start, mmz_size: $mmz_size" +} + + +######################parse arg################################### +b_arg_os_mem=0 +b_arg_total_mem=0 +b_arg_sensor=0 +b_arg_insmod=0 +b_arg_remove=0 +b_arg_online=1 + +for arg in $@ +do + if [ $b_arg_total_mem -eq 1 ]; then + b_arg_total_mem=0; + mem_total=$arg; + + if [ -z $mem_total ]; then + echo "[error] mem_total is null" + exit; + fi + fi + + if [ $b_arg_os_mem -eq 1 ] ; then + b_arg_os_mem=0; + os_mem_size=$arg; + + if [ -z $os_mem_size ]; then + echo "[error] os_mem_size is null" + exit; + fi + fi + + if [ $b_arg_sensor -eq 1 ] ; then + b_arg_sensor=0 + SENSOR=$arg; + fi + + case $arg in + "-i") + b_arg_insmod=1; + ;; + "-r") + b_arg_remove=1; + ;; + "-a") + b_arg_insmod=1; + b_arg_remove=1; + ;; + "-h") + load_usage; + ;; + "-sensor") + b_arg_sensor=1; + ;; + "-osmem") + b_arg_os_mem=1; + ;; + "-total") + b_arg_total_mem=1; + ;; + "-offline") + b_arg_online=0; + ;; + esac +done +#######################parse arg end######################## + +if [ $os_mem_size -ge $mem_total ] ; then + echo "[err] os_mem[$os_mem_size], over total_mem[$mem_total]" + exit; +fi + +calc_mmz_info; + +#######################Action############################### + +if [ $# -lt 1 ]; then + load_usage; + exit 0; +fi + +# Sensor config +# SENSOR=${SENSOR:=imx307} +# + +if [ -n "$SENSOR" ]; then + logger -s -p daemon.info -t anyka "Manualy set SENSOR as ${SENSOR}" +else + if fw_printenv -n sensor >/dev/null; then + SENSOR_ENV=$(fw_printenv -n sensor) + export SENSOR=${SENSOR_ENV} + logger -s -p daemon.info -t anyka "Get data from environment and set SENSOR as ${SENSOR}" + else + #insert_detect + SENSOR_DETECT=$(ipcinfo --short-sensor) + export SENSOR=${SENSOR_DETECT:=unknown} + #remove_detect + logger -s -p daemon.info -t anyka "Get data from ipcinfo and set SENSOR as ${SENSOR}" + fw_setenv sensor $SENSOR && logger -s -p daemon.info -t anyka "Write detected ${SENSOR} to U-Boot ENV" + fi +fi + +if [ $b_arg_remove -eq 1 ]; then + remove_ko; +fi + +cd /lib/modules/3.4.35/anyka + +if [ "$SENSOR" = "unknown" ]; then + exit 1 +else + if [ $b_arg_insmod -eq 1 ]; then + insert_ko; + fi +fi diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_9712.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_9712.conf new file mode 100644 index 0000000000000000000000000000000000000000..8bee9928d758df4fe6d15fb4566a91baecad1e74 GIT binary patch literal 62152 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 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_ar0130.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_ar0130.conf new file mode 100644 index 0000000000000000000000000000000000000000..b3a44cdd4cbc0b9a339c9aae0b9267019bb97d67 GIT binary patch literal 62744 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 J+qPHg{{Yb(!tek9 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_h42.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_h42.conf new file mode 100644 index 0000000000000000000000000000000000000000..1d1bbd7855d27b8822d623c6714d071716bebf17 GIT binary patch literal 62104 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 z zkEjNs8i;Bjs)48mq8f;5AgY0=2BI2>Y9OkCs0N}M*f(k*;$0rDGI^KL_LiguAM^1p zAAuFOIT!YP+!0uhJ2DcOA)CNl`ze@D_5fzH-+}x5ZZh*=KZMtWIt+8(-ltZmt4*G? z94tBI*L!&FR~2c}fbV4YOqXE4;FmD(vgKM1^DfIOOv1s;3>E%i@|lfyc_R10!|vwd zGoLa4$@B5a%ov|>|Czu$pJr!!0Kem5&tcy^Y_m^$UNYA4Np?U!E%!37#RuR25#D7S z_O|9-E&%WH9pGK|z`MM8$at44$DKqDWy`y~|H#U5Czg|m`P<)m`teEMd1BqG!l_(2 yuA(yD*m8ONx~5APw#092To|9XxFsIH>5_T#=g+%*Q9ORdrPo|@DI4e=`}lv$CBUEn literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1035_ja.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1035_ja.conf new file mode 100755 index 0000000000000000000000000000000000000000..a13b1d6d16418f272a63ab1b5ad8001a0fad1218 GIT binary patch literal 62192 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 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1035_yws.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1035_yws.conf new file mode 100644 index 0000000000000000000000000000000000000000..e189c5755eb93fcf16b813ae2e07350f689d83d8 GIT binary patch literal 62192 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%0A7?-=jsd9*wF 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 Ma4i{`zxTiY4_)Zt8~^|S literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1045.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1045.conf new file mode 100644 index 0000000000000000000000000000000000000000..a0ff7e4a498c179a5429b70e726f605085374c66 GIT binary patch literal 61896 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 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1045_ja.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1045_ja.conf new file mode 100755 index 0000000000000000000000000000000000000000..67b9a87ff4fb67889395c4eb4479316d61821ff3 GIT binary patch literal 61896 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 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1135.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1135.conf new file mode 100755 index 0000000000000000000000000000000000000000..ade23f3fe88e8a2e7a9f39bebaffd7f872ed0c80 GIT binary patch literal 69188 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 M;L0YYswb=e4*=&mfB*mh literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1135_yws.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1135_yws.conf new file mode 100755 index 0000000000000000000000000000000000000000..7173a4f1c79d755e1f610305b9ce67c3dc211784 GIT binary patch literal 62192 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 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1145.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1145.conf new file mode 100755 index 0000000000000000000000000000000000000000..80d183028b7059c37e20915fb676c0b13c521f3e GIT binary patch literal 68892 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 z zn}JehA|^|(nrJi2jq+Rz%_hDzE#GQ*){M@83^swL_esxi>Pr#aG!{s%9yQWV?MmbYUvDXHJXi?dWNHf)HEkR z*U*MJ8FUJyCd}>5{+>RkyJT}G3Jh@3kW+DAG>f}r>}EFa6F|}I+vOB@-#6R1J$L}~K!6zZtu&i7#+c(H?j z8y_=V@1*G?H%?5y*^dh=+k{yo@_ZzA5g4n6j_umjeh2d_du zUWsVf3iRsbuo9LbPP!EIAZQ8bzBmaI$bUS}^Stg67qRCIk|$i$p145U)%(cQH}qyW zpM(~y*szSY%KQRavZRe}WRRo7q|e-pgU{o$4*Z-GA`0m%7mK5vOZv{uLD?2>BtaIg zlPLMexF%--dK-!TuBjtth~Lc-d*z-EOeL}RPeUh_K zqTK&6|J?#s?gWexzNtP-Ymd~9C=H72q zajRR7DI*y-XnA*wU+0fe^Tz6Y3~PJGpCM1?bN}08ORJa?SAjqWA}T%9=gBJ?_0Q1T;aK| zTnpq{AlCx97Ra?gt_5-}kZXZl3*=fL*8;f~$hAPO1%3oA&|KwdiDK6F-2k)OGZF7= zfGAFSYT)9CJl`x0HO8I({_N+|J;C3fsdjR{w%=9{kj2rxuxmbM5h@fBk&z8F^H>yNdwE0F1C0&?9{BHPV;V2l>@iSXQ@u*vsL}BR_$NjRF#%@P{rj{s&jdz zDlEN9m6syVBR*Y9jp|yuS+yyxR-H==)gGn2RY7S?6_++qMWy+wZE1ztAJ-gEx=LAG TlZXHFj>=O7M_ILJnNt4`egom@ literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1145_yws.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1145_yws.conf new file mode 100755 index 0000000000000000000000000000000000000000..c561a6e1a3d669b2e25fb02385b3c652b7ef285a GIT binary patch literal 61896 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 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1235.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1235.conf new file mode 100755 index 0000000000000000000000000000000000000000..0410a559aa3f5d2f18cd15222231f05b2b02a892 GIT binary patch literal 69392 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%0AbyO@D{x-2j0d~<1>z}-e2#o!v12Rc{o3-}Z!M0v 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¥6VoX3~s`kS#g`?~$Q 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 SzuD}X&uCTtT3#wU!Tb;SwEI;6 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1235_2.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1235_2.conf new file mode 100755 index 0000000000000000000000000000000000000000..526ccad88ee267eecc06e66a241a7389cd280797 GIT binary patch literal 69392 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⁣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 literal 0 HcmV?d00001 diff --git a/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1245.conf b/general/package/anyka-osdrv-ak3918ev200/files/sensor/config/isp_sc1245.conf new file mode 100755 index 0000000000000000000000000000000000000000..19d0f9eb34801e2a2c22cf04093845bb1e9772c5 GIT binary patch literal 69608 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 literal 0 HcmV?d00001 diff --git a/general/package/anyka_patcher/Config.in b/general/package/anyka_patcher/Config.in new file mode 100644 index 00000000..256a811a --- /dev/null +++ b/general/package/anyka_patcher/Config.in @@ -0,0 +1,3 @@ + +config BR2_PACKAGE_ANYKA_PATCHER + bool diff --git a/general/package/anyka_patcher/anyka_patcher.mk b/general/package/anyka_patcher/anyka_patcher.mk new file mode 100644 index 00000000..30f5e1e9 --- /dev/null +++ b/general/package/anyka_patcher/anyka_patcher.mk @@ -0,0 +1,13 @@ +################################################################################ +# +# anyka_patcher +# +################################################################################ + +ANYKA_PATCHER_VERSION = 0.1 +ANYKA_PATCHER_INSTALL_TARGET = NO +ANYKA_PATCHER_INSTALL_STAGING = NO +ANYKA_PATCHER_SITE = +ANYKA_PATCHER_SOURCE = + +$(eval $(generic-package)) diff --git a/general/package/anyka_patcher/apply.sh b/general/package/anyka_patcher/apply.sh new file mode 100755 index 00000000..1dc00d99 --- /dev/null +++ b/general/package/anyka_patcher/apply.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +function log() { + echo "--- $@" +} + +function apply_patch() { + DST=$1 + SRC=$2 + + if [[ -d $SRC ]]; then + if [[ ${SRC:${#SRC}-1} != '/' ]]; then + log Apply \"$SRC\" as overlay directory + cp -r $SRC/* $DST/ + else + log Apply \"$SRC\" as patches directory + for P in $SRC/*.patch; do + patch -d $DST -p1 < $P + done + fi + else + log Apply \"$SRC\" as single patch + patch -d $DST -p1 < $SRC + fi +} + +function apply_patches() { + log Gonna apply "$@" + while [[ $# -ge 1 ]]; do + apply_patch $DST $1 + shift + done +} + +DST=$1 +shift + +if [ -f $DST/.anyka-patched ]; then + log Patched already + exit +fi + +apply_patches $@ +touch $DST/.anyka-patched From 96d3159d62eab8a4c86c42b2a3afd2d47ce86162 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov Date: Mon, 14 Feb 2022 16:57:38 +0300 Subject: [PATCH 7/9] Remove CONFIG_VT for FH8852V100 to avoid kernel crash && apply pty patch --- .../kernel/fh8833v100.generic.config | 4 +- .../kernel/fh8852v100.generic.config | 4 +- .../kernel/fh8856v100.generic.config | 4 +- .../kernel/patches/13_fix_openpty.patch | 144 ++++++++++++++++++ 4 files changed, 150 insertions(+), 6 deletions(-) create mode 100644 br-ext-chip-fullhan/board/fh8852v100/kernel/patches/13_fix_openpty.patch diff --git a/br-ext-chip-fullhan/board/fh8852v100/kernel/fh8833v100.generic.config b/br-ext-chip-fullhan/board/fh8852v100/kernel/fh8833v100.generic.config index f3b921ad..0dc5fbd7 100644 --- a/br-ext-chip-fullhan/board/fh8852v100/kernel/fh8833v100.generic.config +++ b/br-ext-chip-fullhan/board/fh8852v100/kernel/fh8833v100.generic.config @@ -825,8 +825,8 @@ CONFIG_INPUT=y # # Character devices # -CONFIG_VT=y -CONFIG_CONSOLE_TRANSLATIONS=y +# CONFIG_VT is not set +# CONFIG_CONSOLE_TRANSLATIONS is not set # CONFIG_VT_CONSOLE is not set CONFIG_HW_CONSOLE=y # CONFIG_VT_HW_CONSOLE_BINDING is not set diff --git a/br-ext-chip-fullhan/board/fh8852v100/kernel/fh8852v100.generic.config b/br-ext-chip-fullhan/board/fh8852v100/kernel/fh8852v100.generic.config index ae50e766..9d7fad14 100644 --- a/br-ext-chip-fullhan/board/fh8852v100/kernel/fh8852v100.generic.config +++ b/br-ext-chip-fullhan/board/fh8852v100/kernel/fh8852v100.generic.config @@ -826,8 +826,8 @@ CONFIG_INPUT=y # # Character devices # -CONFIG_VT=y -CONFIG_CONSOLE_TRANSLATIONS=y +# CONFIG_VT is not set +# CONFIG_CONSOLE_TRANSLATIONS is not set # CONFIG_VT_CONSOLE is not set CONFIG_HW_CONSOLE=y # CONFIG_VT_HW_CONSOLE_BINDING is not set diff --git a/br-ext-chip-fullhan/board/fh8852v100/kernel/fh8856v100.generic.config b/br-ext-chip-fullhan/board/fh8852v100/kernel/fh8856v100.generic.config index acd1e044..4e22b440 100644 --- a/br-ext-chip-fullhan/board/fh8852v100/kernel/fh8856v100.generic.config +++ b/br-ext-chip-fullhan/board/fh8852v100/kernel/fh8856v100.generic.config @@ -826,8 +826,8 @@ CONFIG_INPUT=y # # Character devices # -CONFIG_VT=y -CONFIG_CONSOLE_TRANSLATIONS=y +# CONFIG_VT is not set +# CONFIG_CONSOLE_TRANSLATIONS is not set # CONFIG_VT_CONSOLE is not set CONFIG_HW_CONSOLE=y # CONFIG_VT_HW_CONSOLE_BINDING is not set diff --git a/br-ext-chip-fullhan/board/fh8852v100/kernel/patches/13_fix_openpty.patch b/br-ext-chip-fullhan/board/fh8852v100/kernel/patches/13_fix_openpty.patch new file mode 100644 index 00000000..e00b7157 --- /dev/null +++ b/br-ext-chip-fullhan/board/fh8852v100/kernel/patches/13_fix_openpty.patch @@ -0,0 +1,144 @@ +--- a/drivers/tty/pty.c ++++ b/drivers/tty/pty.c +@@ -721,7 +721,18 @@ err_file: + return retval; + } + +-static struct file_operations ptmx_fops; ++static const struct file_operations ptmx_fops = ++{ ++ .llseek = no_llseek, ++ .read = tty_read, ++ .write = tty_write, ++ .poll = tty_poll, ++ .unlocked_ioctl = tty_ioctl, ++ .compat_ioctl = tty_compat_ioctl, ++ .open = ptmx_open, ++ .release = tty_release, ++ .fasync = tty_fasync, ++}; + + static void __init unix98_pty_init(void) + { +@@ -775,9 +786,6 @@ static void __init unix98_pty_init(void) + register_sysctl_table(pty_root_table); + + /* Now create the /dev/ptmx special device */ +- tty_default_fops(&ptmx_fops); +- ptmx_fops.open = ptmx_open; +- + cdev_init(&ptmx_cdev, &ptmx_fops); + if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) || + register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0) + +--- a/drivers/tty/tty_io.c ++++ b/drivers/tty/tty_io.c +@@ -137,21 +137,10 @@ EXPORT_SYMBOL(tty_mutex); + /* Spinlock to protect the tty->tty_files list */ + DEFINE_SPINLOCK(tty_files_lock); + +-static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); +-static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *); + ssize_t redirected_tty_write(struct file *, const char __user *, + size_t, loff_t *); +-static unsigned int tty_poll(struct file *, poll_table *); + static int tty_open(struct inode *, struct file *); +-long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +-#ifdef CONFIG_COMPAT +-static long tty_compat_ioctl(struct file *file, unsigned int cmd, +- unsigned long arg); +-#else +-#define tty_compat_ioctl NULL +-#endif + static int __tty_fasync(int fd, struct file *filp, int on); +-static int tty_fasync(int fd, struct file *filp, int on); + static void release_tty(struct tty_struct *tty, int idx); + static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); + static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); +@@ -962,7 +951,7 @@ static void tty_update_time(struct timespec *time) + * read calls may be outstanding in parallel. + */ + +-static ssize_t tty_read(struct file *file, char __user *buf, size_t count, ++ssize_t tty_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) + { + int i; +@@ -1141,7 +1130,7 @@ void tty_write_message(struct tty_struct *tty, char *msg) + * write method will not be invoked in parallel for each device. + */ + +-static ssize_t tty_write(struct file *file, const char __user *buf, ++ssize_t tty_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) + { + struct inode *inode = file->f_path.dentry->d_inode; +@@ -2002,7 +1991,7 @@ got_driver: + * may be re-entered freely by other callers. + */ + +-static unsigned int tty_poll(struct file *filp, poll_table *wait) ++unsigned int tty_poll(struct file *filp, poll_table *wait) + { + struct tty_struct *tty = file_tty(filp); + struct tty_ldisc *ld; +@@ -2059,7 +2048,7 @@ out: + return retval; + } + +-static int tty_fasync(int fd, struct file *filp, int on) ++int tty_fasync(int fd, struct file *filp, int on) + { + int retval; + tty_lock(); +@@ -3246,11 +3235,6 @@ struct tty_struct *get_current_tty(void) + } + EXPORT_SYMBOL_GPL(get_current_tty); + +-void tty_default_fops(struct file_operations *fops) +-{ +- *fops = tty_fops; +-} +- + /* + * Initialize the console device. This is called *early*, so + * we can't necessarily depend on lots of kernel help here. + +--- a/include/linux/tty.h ++++ b/include/linux/tty.h +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + #include + +@@ -470,7 +471,6 @@ extern int tty_perform_flush(struct tty_struct *tty, unsigned long arg); + extern dev_t tty_devnum(struct tty_struct *tty); + extern void proc_clear_tty(struct task_struct *p); + extern struct tty_struct *get_current_tty(void); +-extern void tty_default_fops(struct file_operations *fops); + extern struct tty_struct *alloc_tty_struct(void); + extern int tty_alloc_file(struct file *file); + extern void tty_add_file(struct tty_struct *tty, struct file *file); +@@ -482,6 +482,19 @@ extern void deinitialize_tty_struct(struct tty_struct *tty); + extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, + int first_ok); + extern int tty_release(struct inode *inode, struct file *filp); ++extern ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); ++extern ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *); ++extern unsigned int tty_poll(struct file *, poll_table *); ++ ++#ifdef CONFIG_COMPAT ++extern long tty_compat_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg); ++#else ++#define tty_compat_ioctl NULL ++#endif ++extern int tty_fasync(int fd, struct file *filp, int on); ++ ++ + extern int tty_init_termios(struct tty_struct *tty); + + extern struct tty_struct *tty_pair_get_tty(struct tty_struct *tty); From 1f023fda8141ffbf3e1a7e5a904deb7181fc1f2b Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov Date: Mon, 14 Feb 2022 22:27:57 +0300 Subject: [PATCH 8/9] Add JL1011 FE PHY support for FH8852v100 --- .../patches/15_jl1101_phy_support.patch | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 br-ext-chip-fullhan/board/fh8852v100/kernel/patches/15_jl1101_phy_support.patch diff --git a/br-ext-chip-fullhan/board/fh8852v100/kernel/patches/15_jl1101_phy_support.patch b/br-ext-chip-fullhan/board/fh8852v100/kernel/patches/15_jl1101_phy_support.patch new file mode 100644 index 00000000..58b46866 --- /dev/null +++ b/br-ext-chip-fullhan/board/fh8852v100/kernel/patches/15_jl1101_phy_support.patch @@ -0,0 +1,28 @@ +--- a/drivers/net/fh_gmac/fh_gmac_phyt.c ++++ b/drivers/net/fh_gmac/fh_gmac_phyt.c +@@ -83,6 +83,7 @@ + if (pGmac->phy_interface == PHY_INTERFACE_MODE_RMII) { + switch (pGmac->phydev->phy_id) { + case FH_GMAC_PHY_RTL8201: ++ case FH_GMAC_PHY_JL1101: + fh_mdio_write(bus, phyid, + gmac_phyt_rtl8201_page_select, 7); + fh_mdio_write(bus, phyid, +@@ -116,6 +117,7 @@ + } else if (pGmac->phy_interface == PHY_INTERFACE_MODE_MII) { + switch (pGmac->phydev->phy_id) { + case FH_GMAC_PHY_RTL8201: ++ case FH_GMAC_PHY_JL1101: + fh_mdio_write(bus, phyid, + gmac_phyt_rtl8201_page_select, 7); + fh_mdio_write(bus, phyid, +--- a/drivers/net/fh_gmac/fh_gmac_phyt.h ++++ b/drivers/net/fh_gmac/fh_gmac_phyt.h +@@ -11,6 +11,7 @@ + #define FH_GMAC_PHY_IP101G 0x02430C54 + #define FH_GMAC_PHY_RTL8201 0x001CC816 + #define FH_GMAC_PHY_TI83848 0xFFFFFFFF ++#define FH_GMAC_PHY_JL1101 0x937c4023 + + enum + { From fe5fb717967471154ef8d65c157a538dff89a3a6 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov Date: Mon, 14 Feb 2022 23:13:36 +0300 Subject: [PATCH 9/9] Add modules without MODVERSIONS for FH8852v100 --- .../files/kmod/bgm.ko | Bin 55152 -> 51268 bytes .../files/kmod/enc.ko | Bin 146188 -> 139612 bytes .../files/kmod/hevc.ko | Bin 450052 -> 443160 bytes .../files/kmod/isp.ko | Bin 60760 -> 57012 bytes .../files/kmod/jpeg.ko | Bin 68852 -> 64516 bytes .../files/kmod/media_process.ko | Bin 83672 -> 80564 bytes .../files/kmod/media_process_no_sc.ko | Bin 82208 -> 82220 bytes .../files/kmod/rtvbus.ko | Bin 45172 -> 40536 bytes .../files/kmod/vbus_ac.ko | Bin 8460 -> 7328 bytes .../files/kmod/vmm.ko | Bin 15908 -> 13876 bytes .../files/kmod/vpu.ko | Bin 116416 -> 111572 bytes 11 files changed, 0 insertions(+), 0 deletions(-) diff --git a/general/package/fullhan-osdrv-fh8852v100/files/kmod/bgm.ko b/general/package/fullhan-osdrv-fh8852v100/files/kmod/bgm.ko index 934702640b0d4d565498a8547811851bfa81229d..7769ffb2e0aca3299cda7fc3e035ec937a22feb4 100644 GIT binary patch delta 8390 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 VrjK`H0UJ?+i>EiBpIUpVq&6^qLNXeo30l- zlz1XTHx?EuDo4?nM29?>sAyzZR8*8$q}1UQ8mFj8(ca&^*Ug}NU9;!6?*H>V|L6N- zt-Wdgd*<{rnH{-BjqeKsuC;~wKgbI<2b+Qn?sE97;dA;~=Lgq5e$lJ>t)E)IenaIq zHa@+x_M)2~KjsTnk5$F$4%sz+NG$)rT}2nf@-Hs_(5J7guf6V?YZ`NpzGBDrOLr6% z6&+oB<#mmpJu>&iV~#(jAoyTD9xuK8(vM#m#17?l?FqrUb(yg}V|#_kuMETrk|P7L z+tz0jvU;rvHY6|G8QZ?AcW3ONHL(rJX9r`KY~MF{G8VK&y^qDN+3%1I!R1so%VF4qYu~MR9rs-9*tO(-*$c75 zm8GxjAldptEH7~gIu`5|^vbU<)#G7w;lY#)*BgQ`cn+QUU=Yj_CjA?9CE8gX%2peUU)VL zGLDKem`4x)IS6XkMfz>@1fM;~WPOksWMS{Rn4!666n-#z^i*1ua24<*^m5`h64~$( z3@evP{84n>w$&E69^J=BBVHLar^RnaFC9Y*5UvcqnHJukrk_G5>X=0e;)cy&sL!RroJ9A)@6x{3igHF!X z4eSt4qN@vnfPd&j82r=`(5XIs1wDL65VYID-_Xlcza-@#4EEnkF{6u$28W{;81gyX ztIM{dm)^@_X8j>_B}t$+v@Gj(a8eR$BN&=pp(y z&*4L9;Xh5&uc9Y%S$rJ-O>_nY$UrC7lf|07gJA3(E7$($ZgzzUCpZeda;bpD=;2r_ z7;zt;hpzcloOiSN^p54ZnBmYzl`!IAKvf5-L=*8EgEl>`BoaW#U z=!`8v&_H1n{x^kxkV1IWXJiFI{HoQZHV0jG>}r?hp$q8FJn~ODD8jIEsfMSaTllO@ zn=0;`H?9uKdUVM<7N+aaJ)c=^ft%48=LbO%zEr@kqWgBx%5IT+(D8i;C;wE0M=*@B z%=KG8k6yV{pr4_a$gtfGUPWgey1M(lkrsa)&Kuv+LI#88vF z+NZxybHJ9p`%;1SLiZf8+5&m#ysfJ(ScERxXLVgTEiL^y=-Fdw|CM=q8HP$S?Zmli zbOU;-jaKoXYD;r)JGyOsENF0x+>7o$Xm#OvTInxXE#QyR($Au+6Yp7Fi^^@<6(3SOKpwg{{X#msf%XOLxk6HuOruMY2mT# z;Gkg3eU!>I-cPA~>2_oC{GZ1TPMpYn6)o9oo6yZ*o_r_Oa zx(l7f7E)pV{pf}Rckk+n;QMI_ev+nNNz)5y`fYSI8Pqs~_3OFurKpFb>0{re8KWP> zFy4+6H&g|BfnB(P#Hw-4yAlM!4d^irQY(8>D|(ix+Tr+jrp50~)5GY2zcZYapZ2Jq zrA3%UPrYMk{39(qgQcR6v9>alcz_A2s#d2z4=%eewZL|CCk18+OjYOuXq^f((5XGD zG%dkL(M@~Dc5iCepmQ%`+pvQh(c>rMkVR81bSrx3EehrEyU;6_3UGg#egs|23SPXj zM?H;UX+vyxll~$t!5n(z^wqQMKhTvdZVNb90oHO#Te+0sW_0JFt9#rrY2iiaQqH=4 zD|^(bX%Q;WlXD2;x=#@(?aB`ad5W0~M!z2~|9EU6a z)B<=(T7s+4HEaPTPVhPOV!`SPersC%Z=#nORWw>EgPrI)#z?L8^XRMw(t92G6?(CW z_OEq~eve`0QVrft(>W}pE0@BLOZe8n1$$U3b0b+Gt<&vh%Pyj`j3)W zO?Y)$gfF7!*t5F5E+o;7Co*lUzk|-Xcy&b|K{phxX822V4M)vJ`(Hro4ASNFYc|q4 z33kyjN7#U2;ndZ$=T>y~U1X9O6|ewZ$rxyH{12mZSejS##pvRrb$oP%&?E0yM{Yu| zTxzi-I@SL&%FvS*;oE6?7(K$KTEy{AE%qXM_>9#W&!U^oUp>kFQ}Hk3u@K*ipGDf0 zOZn~p9`c`~IaVH!4oi!$1KoMp>Qa0bI670dJfAKI8z4KrKSH8I-ldXUPCDS z4)plfSShXVc@On3<1o9)HF_$|!3*e;y<$O$Gx#mK1-)eb5AREJ;O*k)OWIKb+ z=#@(~-iB`eJbCimqXNA@fuZKq)oF4@T7rwy^rdOK37x-hESSW(9JZm0Dpy|}b)k!S z=3I98L+CA>V<>Yf{`zMy^e}I$ZJ641^UJYEGcrGM-Q|~Ga^-c`CV#v+cF3-)Ux^)- zlhA`r{Gc#sq07WvT1~7F|4$eUS&x}h=AxOE5yj6DM>tGb_nAZHxQM?Qb3S4s$Y3-p z!%ZR)^37t~o6R1xPb8yp>lt&wTox~4V6qg;Z`{l`i_MA@d7f?44GFqTy)u#>GDk%U zHepVibLJup$+U-6P#F%GqoUH8Gqx|7xhw(k1Q7~!L&6fX!mJXh=@1NgV#{QJ#SagI zY_XRX6UUheFyzxHeMQKb79JM{!{XgMmWpLzFlEk*pALgeR@-d~$mT}OGYidP82*cy zB3_kzVkve?;>s}SRq!EUFerX3493JC#ex}eOBgJQxLY>kls)C%)Z?(qRbgFa*1+(p zFsK!2q&o4tVNfs9zzs0j^v~G##G`aSB7Po&mIt}`DRi?q5(b4L_F|Djl$&}YmD6go zQT7MJpjjH{-O@DLfJnMwb5i_yESR%ifML8Wx$TYE)&zykVK-6ZZxZoe2-Qi&BArw! z(n)$gRJ3r zPwWYUS&{g`dn4wW#b%Y+Xm*%=<{(_7g8X$kcGjU3WReFL6=X@2V@_=17N~fay%js+ zT-EP=QM?Y3@P2d5oHcnwONGbHBD2!0f%5y(Td~u2;J5o=;nte2@Vwl3zWPpEUMZR z7_>s!N2UKe7ED_9)uxj-a+hd4*RaEB7q9v2yMIN@r}<%+CLB_b6pH>+W|ck;S0 z+?v4uNJ?@_J3h*H63TDZ36YyTm@4Oq5qn{LE%_J0UY-a~f*V;$#46g+tcOb1C=xy> zJ4^4fS#~mK+xQf(1-Mbf9w2Ej2zXNjb`PkQT!^gjiprl10xG6 z<8E`v966P!#@SX0>t%o~;@4P3#m}&ciYJ7@wD^Bndi1cuy*#cFe zcJWhmmH6ja&}n;@xCguBTHX9G`SpzO*u*bmK|vXQC`XA|VOELXU^5c$V+f|~VbCK| zv027mCqzC4#mqiL5KP!q?b9Tf=k$B_hz@zXd-p*V*H)5_xe#Pf96qI3=3}o0~4vMtw zs5xUUiIhB>M;(Rdn}udER1M3-V`%~L2quTP56ir`mN_fZ6Lse$Pg@rrmmo<)B`*8< zhF83UHW80wM~8B`EdALqC^|pVjpig&XUv${7odyS`Na2gkvi-H4#_^@aO&FXKm~K7-a1*0W~ur__-osN_Z? zD1J4c&CE(D-6#DlhdA5Eu8G2@8el6^QRIgzmDj=vG7^8m&L#5Qv5}!8?&4|K z;rX9KPsV~Wk+Vky)Euoedu3;C#(A?M<|lOXAFOWTUYx(1;pJ=vH$iINYL18$Y{IOd z;ELDQ0;d_PUx1uCro@BkGV!Z)ftW)DS|RCc&2}*(3{LCQ4Fwp1>hsE*(c4(n{v9sG zf)0@%Cyj~3%WQ*8h8!~v8Os~?3AbKEg$8AY9bbafC;Q9r3-plKNtcVCXJZljVnN0& zuoSyUx@Hmg9cCZI{cX6{l+C!6V45XIq&k~KIx3iVJ^u32Og>VOFikUkmCVp9;y>9-E}WgY~-3US;Z65b+W z?-9Sx4lPpfg4^In={J#h-PZFW87@L~L($jJ+bO8{41T|+sX{+A$?zPGMKTJR1 z{uU!sq~Qm}-_UQi&xkbaoSAVunnkWq#9nMxn$>14l>hqMlPByKzCS^ukJvP+DC|=O zcfceYh)A6qMEZ6JD(Sp5>6XPGu)ZcEdml_y!8)f49cS!|Cowt1VV==ostE98Es(7W zpv>(e3t^x2fH`eu-5JH(lwxvQc6dtSPrN{syn*%9Y&2WUcC*XuH3!ULb40w4vHf*e z#~=}Z#_}h=#X&%Pn!zPr90tKRG>9lbHaGE?Ij@Q&=n_dVB$8lWJdR1*4T)DHl2NUA zHhnJ=Z&V!OVPE_l>#^;bcSQ}BCH^O)AI5K?kMGhT;-<`oTC>q?66aZ{MFv-|$g(%> z@c1{;^kBJ&pDMH695eI36@@pLO(H$p`YkP6bZxf-dL1wzVjs1A!Yu2F9Cew!=BQbA zcNE@kPMEW1$30PauQ_zNZ)VIzGw0qYV!l~yR+{x@tJ!Z3i;V4A>vpdv&-x=hs-f^%A7No%*^{E|C`J_vk0E6jLU4OGV9DHv(4->`^+J8 z#2ia;*IpaL2V&b*q5q6BZZbQ~0dw4(g1kU`Bq!3SWC=|fk@|yGfyE0SPPB>sp<{0Wix^CI!Hc0%=} zNW22GSR{VANc=jH_-#A27;)2Q!-Pn}c{AfXXdLE>I4BTtP$818&TJJ)*JBQeq?;5; zH!G4ZcodQ@*DQTBfq{h8GQc{Kge@WoJ46!pizFO37tFjt?4&C;E6f_PnwpBFZxu=3 zYmSO4U_v)Im=noxNhCqeV~_-eX1Peb3h@EX=OXc%9o}sYn^Pk3r^S8gB9Zu+k3;)AGY3U79u>)GQY77iNV@F*idbM)isVx#Cb(&~pngKBq(IFg1!@-HHz|^CR=k@*XM4u?(5LenL6m>-9H#a6^qK=hkqjiPF?&$cq;t(e@fp@Rk?d+j(zTd9=BS9Dc{6xA(m7_HS!`Co zR0G!9&}6ooJ?5Y}W=@+6X2vs7Mw`rhv&5`4Yel}YXcT#$)BKDUP-@gI14E?G922SG zvUSF@XyWCH#LG9!MB>$o=kW9`{)!A7-fVlT$Z@pY_D&IhJuJJAwP(8iTH7zWUME|2U%cWh{9{c$5^kd7sXe2v;D&;yhS`Q3=%Uo zEQq{ID<6*{REv*OV(T$6kM|2NM&X6xq3r(FJz^J+NY+_DLVuAhM5F>uVlypiJu6sIk^tnk(a zzd%31`9@TFv5EA*gb55ey6NEDBBEQw0S24(ytp?tor=P%#X_Fot;fax$5Y&|qVPhI zNB&OhUhz~m&tFI3o5T%t2}~%%HW_})LgWaG;uSRAOHqO*@go$-dS28+)N~YHE$+dJ zZ#^b{ow4$A6kaGk%=#!Y4SQ@Kd0F$nn-kY>98tWVL1o<|-o&7?&iXBSPX?7p!JEV< z*kY__#aDTGH4}wbiu^uk$a+ldXQdT+=E?e9BD%@_UBp7O%&ayW%vQ6@>^DcuNpsFz zHnU%e{BJRf%<_b9YRpEn&FnS@%u#d7oHv74qYQG)JhRxWFl)^wGtusw9&^wfGpEf3 zGh;T&Xp@<5mY9`ho!M-5n7!su#6&P|!;HCTX8u0PC~g**rDl~`Z?=k$GAqn!GwTnL zz1XZaJ5yx-kJ>P6X8(7Tpuns$Tg+}VV=jtUYSx;qX0JJJ&YL;^6QwH@6O_2thE{Xf zoHjH57$w+ZmYTI@yE$x5n>l}q(v_IiW~vU*=%;3!{)TPY{p-6 zhGw1FVGfuRX7GA&W~$F}Uyl+NioByNx2`u^Z0{D?|9j05b1cQ=UykG*D^Hk}e~wn{ z!4>{5$!a*#iX_kaOT<#M#%wqH%u#d3TsE`*8l}%OOU)XXdPHxwq08(uN6i^?+1#`k zIV?0Q%zCrU>@`QsX>-ZU`CCw!s$s$3B8TN>o!M&knEg;ceK_+de)_=4to84tqXpYV zPH25ldtc)}&`0eF7xlw^`$qz3`Ji_-T zZ$ud^nhkG8y4@TyC(Q*jYdMOSXO@~ZX0zF44w@6@JUmJ@&3r3zw8bnjtIax?D)7}}TFFrkbWWq^77QIjH$h(w$dvFEIdbcs1(&YD?!MCmHTU+|L#>t=I6#NUWnni=WZ z%ov+F87#bO5SvAxc|T5qFzy9uZXLG6gdZ&lOiss%_TF~C$eXXqsd<%8*V*` zoO&exNOE&b{4e^(To!Scv2Vm|$n)LF1>uQDlB`)~*e>2lGDn*-XCcwQPV~evJml<0 zw0k(0iY+`NK-ITRn(#r}N5toG=kQtSKk>}L-z})pY{+zCk5y6&d<{vg3Hb91-b-aS?yhB7PQAs&rAWc5&30CRQ~(Qk zDu8dEQk)P;z?Y#Z9gv3-@~vvh&g4w#Qj@QJ=_p>9h=1kV9ou;(3E~$rf<#8%khq>N zxg9z?nQ%^CDVCV2e!*TP{qJ}a$u!kZWZEoErmbc>)VSyn>Gw`?&*aI+ zbBNf?*O8L%3xX0c&fhSJ2k~PSk>R3C%TI=Q8OzfX4ul+MLp z9hAR)`KJU9EvLrAK_jFT`6F}9V|V{!Pknv zN6|7D*sgec?v8hG6ez8R-)-mqq$Dc&g_Fbm4;V+1PXkC@3OZUrhwW3x?MQwQyoZ3h alRHli4_uqaXkAGDgwVBJ6n;A8D2>lZqJ?o3yC0#TF}Cw9#T46%`dVTEFKx=eb_Jr(Iv?d4JxY|9{W9 zmyJFb+4xChQ$pJ84u^qjZYuvZl8hC`aw7tFvG~lv=YmVFFk%DSM}~JkF?eI?{*`aU zefsvEf*b$z?Yt-Q%;Gf*$|^kjy;apE73I~3pIKvWUK#ydY}*Q0_mz6xrG+nSO8mwOMmcUKjbdEEyJ_j>nP;p<~Qsrg{B z+g<1_+*RT(@s#XGl8m@H)^q96|GM2p6_qz3Rz;b6U$wW2A<2v8eDM1Bic0soz2$|w zO1-pgThZ((yLNXm(y4ZD-InI|7VScCQPoYARGpVwN<5bRbZ6Voez>sO zd!xIe(vF|%+EDw%K;b@LMU}U#V!vHe_wIe=MLx6~b4Xm7xp#b?-5|)AIq`SCrE)Qu?ao69FRt{JRl7^dOROqxpYz4) zy8;XUYAz3~Z8p~}-*Nu#mrls0sJPr+U2?Nk@(r=KC)Rx$Z1#P}wR`fSjAs|cu}+m$ zCFQ=|R_jLZEqrDArhWU}H}3Q9^SaCTmC?LqLBJRoFDfi5_Lf+EwteY$FZ=mA)x5H* zq6l5!tGbE4o1zwOI=riFpU+!^s+Lw1?WHwke(C*h-iBtV)@WJ&%FdQ{yVMoCZtxcQ z+*RH^CDlH==Ql*%Km3jT=!dEz_r6L`uy+=Ir*QjM)iyg)neG{J8P7hu(@v+Pe0RkO zscc+&Lna(8)!Jto%2?3(uy%UJF8RikA%KdfQ9IqL3<_jv8Xv#>W>y`2&pzhl9- zD+@6#Fo>$%Xp55aJ*-1=gzvJ)J_!u|*^Fn0CN4VQS@)?O{X%v5w&ji=CVk?zh5+VN ziO=qyVik2m^tzFg-Z`O@nedjS&))FFGj{zkMNn>cus=4%TzEmvRmZj4?viqQHb!4E z|K@QPyQsLN5*hBUu#$?7i!FJptfac=gcjZs`{`Z37zws*RbjcO+UvE_NnUt;`_D1m z%e-Ybd#kK8&X|AACHrVVgE7q=(NRyV3WNq5GPSab`{^?m4V_Tx&^X!{6M0+oNufAs z2Yc9Uj=R%w*3B9lrS>|4nQf1fjZr&)aMLIXV~xt-JW82=VcT09tRY~H(H(K|*X;ee zk|+*OB4Tz^)-8b#UNKj)lh!Q0YvVb;=d7fESz!%JzIFMz#_j~W8;%?q-;dNvG;$&yM&GVL?@^PbGhBFt$R)4;oJ?OP3?e>KO*FSh3mWPUC zo68w--p2>q*mYQ%iuQ8aVV5i4V-;fK;_E)Yy8%{N58tox1hSDtP*Dibd;~6 zl*w#aTIP$gj+|X)&`tn_i9*8@k9XZ#6E7FE&PeJs%b};O_ zw0>=gS$_28Z&(YJ$9w$j+q~TQdPkY<%}AU9y91a1wOcSkRVJ#@`6q5~UR%&jL_=NQWl zU)5r+o@2%Y4%~0L5&9d`j^hMOW3ll&=;W&mBT@QK&^ge>who-sYMv7vV@xR;Pj;pe zbNH%Ovtsd@7{iMro>O748#kV)55Mz>xn(YLk2_?Z8pu9mCYqZA&)#pw`P-Qiu8ieO zaFKDUE%k4-aY*4bMB(!kbn0r96`3tD9LArZBOf-5afDI-2imvYFnXlFfDT@aA2TLIV+|YSRhEXf+gKo`4!=c6uzYTgYU>FSw|1tePGmHWh zgyFw}4qj1)A0>r>y^w&`jz&-XRt|}%VKn}G1-cgZrIwB~{(eFNhVdTs)boZBvB}Qh zGw9y;4Z}0f)(gNP*iJ$o(MF`P9J=Ki9MMlkxF01-hN1slG$|Zd!tXzcq=UY zUFaOtc2Z;D%dqemR#g|%I|VxLX2ZzEJqxfU6z;bM)lL|a?nEi&a4~fM_c5@Pffsu8 z9>W+@cn!1_0Z!{rqM3{^006pw8w)2YLwp!9Uo&D`D(!y=!7P;BD$3odI)+DVg6Pn z_$&+&7*t5Zau|WmMPk8GKLPDTgbam$3Y~>%Rxf=rn%sA4Xi%<#PR|Stx)kVoRJ~aR zI>+uJ*57Wj3vI(OeK~Z)Rx~3lEU_25_sh^2_*R(z?bM6SV}tBI`p1~ZW>pt-AEtPV z(mP_sNB*7YrzLn){yhx&DTaanSsA`*IY8h-!HxH!d+#-jM%=SNj`^6TsD6eO-(e&` z2QT_>gPy{WkHtOx&xW2^fyGSvQfQxlu4!f zUM%2+(7mQ<^r=Dx&;@07nk;ZNbn*_28-?Gofb&lm6LMCqV^k4!Z3z;KA;HC zL(ifH!Fl=`bU{L>Pd_{{0b}7JMAXFs#X+~N54FH{=%fvy7Ca9+ZDDX-@H>pl!xFe2 zdK_aSI8Q5~bCGE?5@(HWg&t`@t7uT&6BhsH&<)Y1QK=T`f^JTpX&>bh6$67QWdJJjh zAly%fQW$FI1e?TR)I+x%#LQS~CvY$HFq*Jl2|mOGj$0R=f*!;&*QFZ209}Bkwm|-W zfev0Q@CVR62*)&DkA-Hj@p+iTB6uY}h(f8xuE1wco2&gPjI4{dnfNCUEm?8PSoj@b`X^!fL1-tokQ_UGj8zyaSBAFr;jjpQ3)An1=`X_cf@t(K zGAK|5RzU|Zx}JW5##)trg26B@fgWr`B5J7Xpr^14S0XZNTo&ekEA#*kQo%jxF6ePg z)h78r6y|^Ec+Iu?1sJ+M!FW=JW5+un`~&Ebk81QGbns#g7GU8FUes&C^bVZ7f)~S2 zLwNrCp#}46&`~Hru)0=3v}Oc>&W+^ReyVwx5E&;*dmXH>E1B?0(2iX z)ij*$SfkgVd%qE?@dwZ~S72X5S`42D-@YO|6-qCU3*~Q3!vr`?pf!RINGZ_4iv>Cd zx_Mn_Db9s%ygYOc@j#d2u$%*5X6O%#e=l@0PUFF+gI~m<{)2a8sZtW83<_;U_ycqs z=54<8+lQC;n~z60-MEf@m-*s=d2uX<4xemXYaB)$I*m*~yOB9$hQsKQ9uP;wY0(*B z`^S>c<2WVVA@+!aB+?rbCqX|A5g59Ak#K1gG{eW>?avd9Fu)Y zOo&AL(;)?`0V8IJIbt4(n)ZNvW{boKb0s;9DDszRG4c~C! zjt5inB8M>|PLeeaBXTn$F(67z5>v%=(BVMA$g;rMZ<^=$OYxqF!HEu|n=Et~1LPk~ zV~pJ3Fs4Z)J1e^I{7Ic8rjsaFj&z>L2V+N>!|;%3q++rc2XPV&TnPqd-!!*w{WZED z!gVDEEgHu4b#ycNyu(N(VNWNKX|~0{^ol?i6W+g?2>d6U6M(nOL7coz<(0wFUFez zX(kq9DU~PGLc2NG6OE8kq>v@11{`>n~&dPO<%(NQ8Ha1LC-7;1QPLE-_8a6$?P7 z>mE1H--dKsSHmBR%LIvd5eYUE#3V5lWV~0$%^fUv>KgdPJB$Jn@rp$sNWU#_o9o%o zp0x-^{Te{njcKuIovp_}*J+rW(xr*EeN@ak72$_5fJx+63NoDuF?GEio=ze>4`g^N zcsXwpH`s3m8UCnvi`Xu9iM`^0 zI4q8dQ{t=`dAglmte7Asi)msecos9xks)6!7JXu!*eJGuXpq32@0h<{feN&3u`}!w zhe3wBC(LF3A2^KM6lknAm7wcnjLfaz)p+dy(%w(~Z_^l&PT2;H@5ky%#BU=1V;XHB z{XJ*cIu-|5PO$_MOHl@i`ecjwpu^z|d@_M04(az%BYx?2o5LW}b?&g!Ndm2M?zGts zx+0N(8u&{*@qGncj+KMF7VRdMf{f=Q5#CKZR@zxH>r8wB;Cda8{kWlH8%W(RJ)J>& z;K;x5{T$*Ie9eyU6LZhCqves`$4W{5%QPB6=F=+nh@Bi)OAnK1_!-$#&bQ-blZaO;){*yOjU;cz8cBY| zVT_UmIN*rR3yg~lS2_0o3lQ*;X?RHVs86f|S+hp65too0+ z#s8XVq+}r(lqW;X5%b8OU}+)o^jdG(F`!A*s~2Q;-QTb|3A&!a(smK}90nSB2wSh% z1k!(s`d1j3*=*20?8Ufo4PuQZ(Xi9xIjHBwwr&->NHp|>^el;jyDqW)3NE$jBjGpr zP4IN2N48*QU1l?ZM1JX)@g#^0^JqYZm0~xEhV2)}#2FGLkHUis!;{5SF&$(Lv&d~| z0dgaz0vUxxoScg(N}?l*FAw~F(maE2y(@E(r_{{dDN0Aq~9>Qm5oWe-jCS)HT%S z<9UIcGL2pmvviz9CrpYnqz9wmY7iO4gN&a}jW){^vq@C2@oHnG;Tpwvc{Cv5Q8`Rn z3@m#O%M;>9U4wMcW{o8D1Rir-H{vjRJ&28UhQtBJaD&(gog_9q7kMX^Kk^LJhipWj zlPG5^S%>l#fGD3+EEZcqKLdJXm=co;?FgA-wpc8-iCyBLI3~`D&RupoNn)CqD;DoE z&az5YFGHKyD~^cMVpNfxpbNyhx&M9hPBx%tH_S0O;gJvH_0Ap;>thCqb-spd!*WBS zz7fSX6T}oTTPzT3#YVA1>=ye#?lZSfnV0(iW*VtCAl6RXn9v9Qg*pY~kiyMw0S2;c|%u0~DgZg(k^2k0= zVTX@Yg5SkdB>#-BRBr@dK}O_Xu}_gtV+4|)p<$~)6r_Rt8m4SD_!3rwYA!dJ68SWM zrD6klHXaH{L>wV!k&zEXM(O)(dmlLuhwc5~lc)%ZjB80`+)1JWQ3q_c)Yuj41G%P5 z)nLxBrp}vSz+#d}q97^cg_tAc#YmX+VdW>U!lFbTz|bL|!jrASlW&H8)ikom%L4~L z#14(A=)zkOPRV5QF05tbLY$>*!D1}@-vUw3da;j0F^0q(zwOsh2fm4s`EBr8Jjar2 z(LrQ08k~$naqB_E_kfr2rjdq|P+k%R=mS~r+}ofxV-5Q*_+Qg#B45C3I}(184IqYo ztmpzUa_{}f+;BDO(@itj^gR&uiMj)JaSt?>v{VxIbTL=V7d;@;FTE%5)W_yoel&WYOv7{;MV;;i@56RM zqRy2h`nCsT)JbZ@n})^nK+DR;OqY8KZ z1R6b|QM6$_Z zx|kz+M4wnMHjC|Ix7aTZi{qemM4gtwc+}1?R!kIA#0)V<%oj_=TCqWF6+6WqaZnt6 z)L3BEF!3NW4jlZ_Jj%DGO^`gZH;bJN?#2#gxnaE)BOiku8%PF;hRPKS$nQ9eX0ek* z`~mU{tR-SZm#q^>GNJBbM0B>ekG_z#irpCsY$JOr{Q zN%*CR=_LHKN%$9&@NYQ8#RxYYG7OQ3I4MT_A2bq7Adx@{i3D;;#48r-NyKXtdq~6^ zCJ}F(L_Fhh5b+Yk%*XvOAYwiZU@?h^btED-k%-tyBI2MpB_?&lj(F)}j#xmtQBxA} z>q*3K7yC&T(9atrFhL^284?j*gmqR{`(>MvgT7|cYz2XQ7|4}j;T|~k^ z^4B2JO%nZCx+x{$&>-C@Js>?PMjW;iNE90n?%O_Br+N%5pRk_yr`#brii&D@+l_$xT%$)k?g~(I1&l; zkVs&dL=%sRMz3vmk?>2C&J!z16sVR&ff`Bpbt-&F;bWjR|0ijH<17is_}_rYI9>FS zC}@M&Dt3~0VqG8+ZmDoM@l1SIAnijw}njH_vgYpZP(ud^4i6&(;ZID*0Qib0o4WAQ7)lY!mxQq%$cR z&)PaxOcK+@9MEb&j|??pqu3^Piv!}QI3-3rXJ-^ICW{$juIM50^@ESZ+nd_wxPYQY ztu$bWbch2aYB(z$@jNvA5=i(Zi&-T6JY+7OwaHOrpzvDR>q#6(8)a`MkzU*Lemi1^ z9J=JtBZnap2@jJEI7TXbT;Z+#us@HnN%9Mm-`cvEoG^{Z-`P5zd=L*5(gS1_b^`xV z+o6D*GL0!|{>a325Gy!|4BAK&2jLfNok4o>BqKdUK8gkQ_jY&z*@N{;dYXJ2ud!dW z!|TX1@e7PG8Ky|QIm;fj9rDR1QDW%1?e&J zFpe32vco;(xp==IJwg5qCCYrocE};$!3!4YUUCcibQrT0v$z>#P1?x>ygK`{tz*e6 z@Pb~to}9r7FP-ui=-(hd$@t_gi2o|+har|XcjMebLf4UxW3Wk2l8aE&5j#Ae+=(Z6 z=|S>#JjMOh4o@ZV$lom8PM(j=^EEp>o?MD90sYLdfrh_hA(F#1c>|j6bvr^0nS%mJ zPm+9y8nwgo$@y6Er3c6dFjn5M!&AvFtdAt7VVmrIZ*czq6eq5~$&tjX_a^B!^4l0x z(#|)bofuRk3SLA023w5uIQdV!sv5JybIDIJh@}U}$FS0pc;<1w<+pDV-m;l0W{LS? zrC2Yvh@E1eI4n+xvtrcW?er7HG%?$+n*z}%Hi)fam)I|kh?Ao64?BZcF-c4pb3~6= zBl;V4(alV$?f!gcLDPtP@+shzZ*-Q}l@SV!JpfPKvSr zvg4(aew5fFL%rB5j*1cgwj(5pnW9H*6nn){G4?-pybLj4tQR{$Kbve^2Ir(5Az928 zYsFTvR~!{*Mc2E^P%IXk#4d42G~VNPohU%Udv?TB67MLprAx&+*;`5M|LtO*IAAfb zKgzLzUmoYaZ?D+hL4JT}ML5!ui0=H*W~NvmHi{i$zc?n&iq4Ph_(@`>SO8j&=(RGm zh#g|TI3~`D@zZvKsbY>;DmIAiVxKrF&WN!e8>v=@V`+T25KZoZr5-m6*#?II}Ma&k9#d@(#>=j4EX))?E{Q}B` z8y0ABjN?lGA$&{nxt+nZSovRDH;O&tus9_;XKlYEF;grMYsD6^TO1N6!A-1bR&liVDL?0DrL<7I%wmO(R8n}L$W5ZYQ zHmi;DT|^fL$Rp?nah61K5sPd_fhf(9WsaR2-^If)&0r(hifD2h5ywHeuZnZ*^l!z& zw-{~@;tWdOisu24FBcoA5#B9(ANc~3Q}{Ubdw9Nx0#WH~(I+;Lc;DVlqO`r@AjtHG zK(qj+{?UzlW z{qjH-vynO*vx&s>TbJzJq!Yi~Bu~QM0m&X~V3wlWQb?2{UCfrfSh`ZI1O1H9Dnp0Z zO=7t8iv1!URt(o%yi*`C?Xh4Rt~`hJCxot23?~!13Cj)nko|Wf{NZadHQF_oM8WdK zM$C3Apj!q!_gfA_BqGd6<4YOK9!tWWE}bdjSdWgu%MtPue3K(Po}>)dH5fr8MqUqz zL@=SP^oAX&_RTmANMKr=C4Yr!I|qEjw10Ark|$H+t?>%SInn5$0!aRb)N}7ciA=42LZ-FU$h2N;1UW96Nc4L% zIX`e@B@PTL@YN&bD#OSiPr;uWk*o1D6w--*$hr0(U^#YUDLP)G)REL6e{aL&Q^_n$ zXYz3rldMMXksI+B1!OY*6b0Pocmk^p4R9PGQR#8=RDAJBuEk$%FoO>;meH$@;lRBv zN5lNk@BIU(oZ|2Y?mxv*cycqkFa_0#3(Q^RxZvdeZTJEk9TFF~f^s+&OAC^U3p_}f zk6%S0GsuruIo6p^2M(R$hzTUGc0`%Ofv>K1Y?wQdj-hCqYhm`+BSt)9;KN3Uz=xH; z9Ul~pgUz~T5Rd=w8o`Txyzaq=e&}WEO1lyRgq^4>=yw X=Qd+le-T)-#<6a0EDHaQmB#-EQ^ZW) diff --git a/general/package/fullhan-osdrv-fh8852v100/files/kmod/hevc.ko b/general/package/fullhan-osdrv-fh8852v100/files/kmod/hevc.ko index 1bb2324b4ccb5b559071b313f12a4475470da710..7c2c556afafd7e88f47767f55186afccce27d358 100644 GIT binary patch delta 75985 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 delta 11161 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 diff --git a/general/package/fullhan-osdrv-fh8852v100/files/kmod/jpeg.ko b/general/package/fullhan-osdrv-fh8852v100/files/kmod/jpeg.ko index e432fc7decc709903156aafdc597d1189459ad3d..6caa525ea668b469b66b04b8cf320021397e7593 100644 GIT binary patch delta 6748 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 RZdu~FC<*MPw;v?E{}13KEa3nE delta 8242 zcmZ9Q3v^V~xySbm34}aICJ#tp)Byq$Add+Ih#^XN2~t#E-lC$DkerZ#Nit?85G-p= zw6Ufwy3!uh^pakqjb7^I)_B3yT;*b5M~fJI1DPMHlc}Whtkcq@r+_iq9x~mfw57dh4g( zKl}BXtE1Jc1MQQ4^x?s$L;hdAzMGN)KB(29B75AZuTK8B+`rYU)s$`WYbctXy#Cse&0-s>jacT!e*Dn1 z=Yyd-zg9z!@$D}@lMvJ%_0|TAf`Tab&Nq(g3qHxvj~trnsC(-XU!bbc5pD zaV3YxO<=EqI$uqw5=FBeTg(1%VST3H2J7fj6f!MdlmhN`H&<@gS1-r5>(O>G6|Gd*R~=y$hOR(XS3C|F*laWNVH zoYeGragPVeuzCn&(2MSUHJdq_Nujl`UB0RN`kEUCFfGIWOx9(u5lJk&FJy+P${dZ` zF|}{Z8LziYE8FDr*83`a+u$?Lac}Hnry1$;DqjFiD{GC?38}tQ)xKc)jT6jEc%jac`Hm@Zuiy5iIL;@-@H&<&{rM6F^O=8YW?t=m-I|=;p^WWL^xqk=t&G{ zb6+$!uV2Hepe#Qn4hKp{u2CvQ-+ClJaioL~h4lS< zj=V5ta+*4c@{;jprA?)&GdJt~4;_2oZcEc=z3a_)sQq@+Fdncehx#vQ-e-zF2JOLp zm8tc&-d*TOQ=g-hUGR~FF7&wfF6l|{XNGeOonW-Utz1bekIyVzjY+gmvec)jV$=`h zD%A&@WMx;Y#hz`@L_GiMK(}GUE5yM7bXOi`DY_pS-h<88BKkb)GpEB|A{IRw6>>4csTQ3S zMo3GhjAZ2$7z8FL<$@!Ia4vKMW>kpA)Z=25TAwfy;^nCCSUgg{4tgMeWPs(+k&6Qe zSakSt6bvp=>LM!H!7~tL{??)skOLdWVQY#*7j)#}fODbym*Q~3Jv&@rsb2%#y$n%9eV7Is3Oc7L6_N(^ zmIhBl_j#16kop&(3t+!f^lKLTZi_w+-QvWc#eQ(4K5Xp!Z$}CY9fOR|oHY{C4Cu(k z8Rl8^Jd0iq-H%<-2wP6@LFf*wIf7#78t5jR3kBT&G~gDCTN`4vs+|&BWG}1}CALkoA$I zI|JQ*%Sh5*wzMA;hlX--fVW!oH0W0RMmIv}z#Bjw3Rd4Xa&|A;pU`*bsCX}~$Zzxg zD^F&`g!%Nbm)eXC@65NUM#Px9nZ#+6r4cLnKl(8bdspg5*CuM zs}Qz;^t+oJqx4Td9Un$_p=g**vZ-^(QFhN}GO0KxOie)rI$k043tNP(Bph^*aB$ox*NZ+|XiLx^`|Lh^ zAN%a>u~>vg95VI<{pV+D??#IDLoro{gu}vkJYZSw6lM#Hg-eC24C*TfpI-#)K`Ff= zjKS+Xy`&4XgoVNt!u24=H05vig)zcXynQ%NBV9mdK@;^xq$3G$-NFI#8b0Y}d6uw% zT#BFzcamt|N{&U4g(rmPq&_JFc~1jBZk%UvdXN~%UJ?#^NYuL~n)L<35)yW+N!a;G z*!4*Laj74W`eCVe;&5U=9-)7dddP6p%mU1Gx3Gx8Wgx!xKHSuZkD@*y`VD(3*|P;D}+_TK4E$m>~J(>k~kVX1~jv7e33$%#d3)0~}VW;qb(B(1fGlfl{b2AQR(IhlP7-#_B<$KiBc9ajZK{u4fVT#67!O%t z%xtqfiG-bp#K2a8Y=3$-pM4s#a}M~hO&ukn`^npFD!#xhZz55?oBReZlTscv*DOyZ z$KgOB;b$+2fu0i<&NFoz2|xSDGx(hsmWq=3X2p8aZBv~j9Y{HUU^%bjXA+4H0wf$Y zlQ;wpNcpgo$KQ_fkMO&Ug#9WK5iKR*Hy{i*@dl3ighOO8HbRkEUPyk6_cs!b8l}92 zoQ9c-b}leOS}ZJyuo!kYCsvafKmcUuLngzjpB1@yDwB9kh`9q?hFl=OLUxm*u|#*8 zY$mY_+Q|&e9Hhf8;Xz@yutzuqI`2f1F9fe*0Ay?!A>_?@ynmDbgzY2x1W3ny=-Wr9w$d~jZ7u;b?wTB8*?IkhQK9E!OEPdpS9d%hp=BbxI(FkJOYPt!$o&05W~d71k|6n=nmV_os?n^ zm?0y}a4eH!u=J!8xk%2&W*`xS2J##-fP~k*!X6U;ZVZSX6yl^e?C^STkbk-+J3ql% zNdaESP@xxH%5xw(RZvW%09mR(cF8>!rOi|?>#ttA?e>dGx#;?CM1X`RYzD)%F?0-R z6*O0wL+2{5C*yScWp}s*|3y?m?Z)p>Qpfp8K8wE`Nc7B$s~<()fn)7Pjj_Z~gGnU3 zr;-@9hy0uQ?~Z*D9`e*Lo4pP3 z=zqSvCwAnAs2=*dQNQqY*;p3>k>pS+Lx1?n^0A(1a{w9ozeANmxzIX8{|8hf4un$p z%+T+-x_s;aRF-xH z>k~Z`{RTBRdT53R4uKw0@QIAI7xx@Dh0;Pdr?eKkUybbi}I?ZXlQ6!)LLDY6pA)7-Xfze?Q1O- z6*jgPi{03AH!8N()s)JTidrnTXmd9xd!r;pMMa_1mh%1m?wnDV=|kW7opYWu&;MuU z+Wy3ltv`fRyFDS3HHB+PCjYMz)hKm|3c+18{tW!hp1)W9_Co0Nh4b#6?A{Xh*KMI@oj=4hw*B4oZ*ynO_@AQUf>n3jRq9SznUTIU z!{hO!6cnv0y+6r4W#XiXX=-{hQZK!2>9QiFjmGVe$tpB7ObgeB>E_;TX=e2H_Z%0( ze)cj{jJbb%`j*7T`H|COl;=EDwkTM2rKuQ|JxJ#b(o6nZZ`uB#D^we?<%`y7h8D9W zrtPWl^bx8tR;e^ysR%q)QzO*-&{@z4qC22{(1n&Zzvyr|VpKP*)wgTGUPD#34E^#` zgQ>rE=}3Lzn^Yq{MqM^asoJwa>I&$(vwF+yz1M2-BUB5_)$De(j5X+>=5Scr+}<^o zm6Pj2Wwv)EhQ~u{YPfQlQ@?j=zIQo#;WOv0yv_C*X41ZC3oddi z6?@YiGi+-bbV0UKK=27!xXBGe2cszt6{+SyzgrRZ65PQWE`#Qk5cFTDO3XL*xzfws zO10%bnnVxVQ4MWhQMlRQR;nyqsb<`>9rb4TewX%vnY7=P{yBW*@YzrN2HL(@_K;ht z>t?%KXk%`}ydPy_%?WHqC^y4whYo5?4D@f7PP)?ekAsfQP>Man@*Z>K z0hfQqpi*<8w{2VggI(%o=rSx??i?(2_n`Pk2I*Spl3!uyLaq2s&}k#imT!{yJsSO? z=Jo>)e=BScB)nH@KXX6y1*wzJ#a7EtL01)OsutRTZDD9|0nlr8_(;A4*-|h4txQmOo@i zErwn%`v)89!HmemU$vB44*wdgwk*;2Kzj<&;dn9|{*1ZykSo0&)-3Fzi_lzV(J>Th zhEhwU)a&qXc7GgaKegHHIOOv0hBfDU>=lyhDD;VSFSgrJp<|Wm#Psn6?TH&_CCYvJ< zyV4iJnsWT+pKNOpqwZa@%GM7-M@&#E8wU38V`kCeY3WbFYF}LM&kxeCKxg0470MXy zKrLp+VORXuur|*3SQOq4`jy#e&efT&mm3w}gWS4bASEwWC04vuN=sT8v z-f!z`pc~G~>ND3KaZO(hYt-wb0(R7s(7Yg%_LvL{XvnO)Qk-aX*LReq@YUR^*)LrJB-f8I%4$Ad7G~XDa z5yqB1JRGgU%&wJO&zNr@*QhmsoD^8!*N&o1>;%Px>#E$ z^pjbh&9tx{snB7MIELE#YV*Kxm*z9W`&`;8GpWzz@FQXkdZqv&9G!LMjeVDliBYw% zKU(uCX0Vm%HE8?dlXjXL`ckzHb5~!g-ap>_sc)hdZ6@`*G?zK2-<3KE1wWrsvynwu zkp!U=+e19FQO5{gYw)a_ppg>@wFvSI@zQ4Fl_NGjtScV0T3c^yFD9{$a;gLC+ty z`piyH2i=bqY@cthLBI3ns59&KL+ZzCTcx~y`r9p2PfT=eFTF_XjGU%2(eut|LzN1> zs4jJ-un?m{y+gQZ2y}&^8iNcxJysj%Xf#wZdX?tvAOj!491d9!MswgNNI#OOa4Xp& zp$|jq{8?AU=Ydxmst42~mFgAt3r`9Mgt-w`T)VIrbiQGz0nt(CLl+s!2{KUIzQXfM8jk>>_g z$eZ`-6Ev6qeM1%FKy-p-!g3Ou zU!CY3!d77$iMQoW(LKTep~5>hyEB$V-V$Lwc}Q1HqIZMC889G*2)xxf-wzB+)?DFe zb|R!65y(i^R{F8K)lgzIYDu`e$052L(++PN)Ulwm#!!5Jbgsty7M&-$0%Tk{-aOd( z^}=RhyRZ{vTo)OJ9T^P7rf5li3_w-N$af zDL8WAu9^sQAI6dVBkCf$m;4WQs#NfRp?oCb8%V_OATu#6q9f9v*`XxjDoMoEkiRxm zGl@LiVjlqg9O%S!%aKE(;1UuAi%E=Bjrcc-zeW5J8Swufb}*2g;icw-Nj6CIRub*r zA#5QXx@yZ%feTW#(S8hox(qf9Kp2Suhz2!Zu;2uve(&TMvj5CI~&kT%k``Dy*E(u|frE#jr`(ENmC{2-UULgQA6r z!YpC7Fke_KtPrjj)(dxByU0q|CWbCyk5J{w1BHpgEMcCoKv)dwk=ut|rhTgU$KrK! z0W>DvdNLB_9g#0UJ zn&>LfPeUzk_~1r5(hb!m0llK5ueTDUk!@I(AS>)8Q7&6__AT&ZmTwihmqBA6Nd&pN zlF10{FT#lBd_RQ4vmAl=&~S{PVD>lb2!_C-cz&vKITtN)Q!r z3Uh^p!o))Ok$?A{R$R}W+~H85{4R^p_k%sy-a$T4t+t4lq~ILHDGml6n~YQBX*7%y z4MY~@jK|?4VM~Msd+2iZUKw#MvEj0Ic9UMb{fkyC*NAB?<=gRmrO(Dn zm4HGz?#7`n0d2IO!2A?{6f5>PS|Sy!Bai56qv#USXMLr!0l{&dhIu9aO7b(T6VaPA zKMW6Ig^GdmMZXO#6wL{zKZ6#D4o-{`9Gs%t$QV>mbPf42CbZ~!atcm((ZPuni}O)* zzMnTIF*U?cLAGK~5*?hdGjR%vc9XB7OGSG~d`)DD-o%~?LxZHpn#hMxVbLw*Z%_fz z{u~zU(p9dVkX?Tt63P%1(*Ao4iTK^Le}M{!zmbVLt)||8fw$pmZ|fp|0MetbOrTcOi~g$kG#y< z?x`!OdvsML{v0w1-kb8A&BioAug?gWLfaxC@!GAu~kKsj|_UG{VL*}8uc)rqa zl;zWb&lc(fy0tzBWZmA5cOTl<;k%5yMJAILV(pCokoA5QdVm$YNyh$=tfKFAYcB1; zwP#;`)-`9 zEwvZYw{c zJ+zD-F{VrDrb)eCO5P4!km5 zdnhn=hPFK1H&RoH4yED(>t<-P!z;&Y!Jdf=d`sB~$q|eTq|b!xoS+5Y#o_`FQ|4js zbq}HUGs=>5O;zKmae?@3NN$uB=!>|(-Pzhbe(oyv6(Tn~L1b6xir|&^B7VF_Li@7g z-hu&LqP*g$Kx!ZE@!K5!VHLl8;4ch+yYP2zzD`UEo`ATxfUH3B1N@zvuoDTRU}M4y z@nz34S5(F=x1iYp`~rX{BBl3AB4T!jQ7{+O0^X|u9j zwybgYQfZ53Wksc{mb;p%>(lZpw{Wu-(@G1KS~J|V%*cM<@60=D%=6&qe&6>!XU?DZ zoHH|SIvvsW#~#Z)S?U^1;p&mWe^rWdtIJdbekJ11z~7`BZ&t;5Q+7_7_jJ_{OK05H zJSzK=gss~84TUGycq=MP76z+p$|@Jscye#LVPU36?MiGK2yt#l$s zMtyec&~Qy{b$M_BqB5hKRzKROCU~d6vdS`KxP~p++!Cm(t*j1KR4xqq?+VNh`sday zD6U28P{JEmFy+lHb4&g6g0=qQ(z2?u5?FnS4|(gq_xl6Ez??FFU0F%lLNp$Z)R>%| zG5vXis_L=@wR2$_b;-jwtr;TKS5;RQqtUh1i{Tj_+h_P2b1Ld;gNyui3(6~t=fjwm z_(SoJn-|WhtMOL^Yv%b&f@KTJI2x(FPtQutt~p=t=-9^}yK)i-flaFp)N&ZoqT^W=ltftnQ8DF2<*RJX7N7F0r4pvtp?y9I+ zH`Y!wxyp+577x&!hH_G1OiKuSab5ol<9SzgnKe=)5+^*6`cX}BY0yf# z+OmqEopq`4i(R+s7YaxlM_$sPpA)bq^zw*B z=Tck4d;4gK<`bcm-Zw0I@tn*Wnm4-772YF%^V@k>$H}-V;q8YJtLv(&gH?g*pdT5}d1F}5OD;X#VAV9XPs{DkU5jb0tXohK zu+=n=zMG)hsxlZW(>6f}>a4nN=PP z)Nu6E69)YAz3Z(UC<$Ja1tT^VJiEE4+3Tfa=JuE32jpIf4Z&I}O7q}nk4)9GXQb9k zGgDtqGjDzQA936@E?kD{XZo7c`(ChcPZ_k)-#jfQH%Vpn#)ea7Z9hs=Nh+_KF6gFb z|59)K=I`#FTH?mOJ0}=g(#C1KAM2H#sJ0|2m8L5djhkxN#r^@E4eb@ZAG!#-)YfL- z_uO$wssq;LcWBnmho)?t@ZS3dtNyoJN7cT4J|j6vU3HmKD=!48anMZ{^v2MCrfJEE zss-ld+&a-SwqQRsw^Uo3-?R-iz5BgakFZSf==jA~f^xfUy#w9VR6;j@Kb2}u-|yCP z%{%wIrxzjiyXQhfSRz)P{J9Ex)_-4~>F6+Y)6Gh)gCSn&Y7KNp3io+iZ-D;GTT@z{ z_;=02`+eF0Gwx$|+)wbCH_Qt)trk{7^YU8$e(GLx@5k=+M?6Zk7Cv+ZJ?zISXy=N>kLNu~E$XGz zZv19HHk-)@+}gj*+yn0PL+~wu&$;7qXy;*_Vu# zOjIfdtzo@W2Q$jPSazJL9dw5#BkCZsI0qK?X$CZ}Mfi(Xk!lWf(dD+EwagbkV_3^Q z&`U2Gqh-)ZXmJsIjQ?}D_*KyBD&R*F!>_CD=IVpKKr76XCoTFdN?9E_3SBdc5P$fKD2v6i0^TuQk&@afhaLEA?CG*IzF_?UcF$deJ5J z0|HCk*Dd~uZu&*&s#`I2J?*=^37wX3q5Quj{-8#GPxJjx;zIjjyT`k$*16{?=)S(u z7-GBUJ(2JYrJ7m^9ft*yXe|JG?E$|WdMyUL6`F0#gnp-R*9qHy^5^TJTTo}cbx&RS ze_38v3(UZ$?&Jqy&Q8T3Nj0mWOZqDXlU3~|bM>cgt;O8=sXKQ+eANf_^DFO1=;J*u zWMywJ_P(UY4m(X409_u1ypp%1{tcdY2RV1$Yo7ef z9Xbqa{%^2XNL^jf-#`9rn-i5VRH;KqpDd}x4gL2w4wN|l@z5oI{h`s(w?LQwr2og! zi=oZ@-54i(QlEm}fxZ+%qwVo(9SnE1PuuGRY&BOOa)(-A&6smx;68!gdB^6roT$^# zFD7L??&v6F&2FrcdKfq)^N@R9Y;DdYCc)3aD1tu@+x0N$iE6F~(pqH`p`#CQIa=DB za5ygA58H_U-FDWAS;#UAXIDG=4=giWsXQ2X&#-y_;R)$0VRbHU3(ej18_;?4+j}yG z8`X#A$;0mCuVCGM{e>jdh9Lz|cRr$6cY>Mzxi8lZYhumW(N5VB(DmvB#>uWL4|;y! zgHw(!gl@j5)(Ug==kDBxVU68^TYb`fq73jQA^A~`-Jr0cdM%rn$I4I z2xHIgk4LYt09Hy}Ys|ffhUeeo)V1H#j=0m0!dir}n<}wCLdT39#e}lj6?+A; z6aGZmU51*|kGSK~V0A9`W{i3N5qIvjur>!FB{tA4>JI2{Z*lKpN%lF^&EH@iMlCB~ z-T&<;m>oOn1?ahJUPyNIcIXCpm&#rCcB|!cGxz8O?TmTO#adr`T^FGqPVY}mh?da%+66NLZUp-ci%j2tmD5Lx&wDz4g&|I3A*v3 zy?qeMHDDu}DgG(&I~ND$Cg|p<^C!wERn#q@5_<8yfsa^--FyK0F@4 z!s^1jIgd-*p}X>NWFU;q+5zDf68grd zyU*_guRx!|3U-dctAV`p+jePXF&nceEdu^8w?H`=YeNF~82h zEV%j`s#chTrc*BymSTpew+YwxfPTPGTP%j38K4b|+iIv(j4;i47Q-LG+zYZKlIC#F zf!eSTDp9@c%4Q0SFug8isPf)mj-if&IvzYbg{OpPgk8d^(RN&$uoHBlg@C#04Rug-5^~A)ldinvmxjs)S-yyRu%Ql#ZWne4 zj|)5FRmdt3i-!%@QVcw~(oiWND@r9X>9w@KV5mK0B3e$4GgKt@30F@;c}a|9iRfD4 z3bC&PSzj~xnW5H;-jo=!9b3c@f!&`on<(^>XjqmohtwiezOX=8C@cYaqjF&t$qopc zL1vh@ABfCO4e z=1WF3O9mqx{b?Xke5G(b`AT@CTkD^UyG$Jd`LnJH$>$BVh=je3{3<-xt+_*QW5>Z+ z=mHlBmyq)f)g*eGaF4K+yb7Zu`na%5sPGid(MuvxZVDmk&qbPYqR1VhmDK!6UlL0$FI8 zu#NV47$fpNL-8ba#bA-skIwMqbX|@yBGDL+#qg`i+OXtfhMGxorc;? zqMi=1cYz^JWlFm3$R|;7Hi?4eB<7+~{OiTvBL3)6@E^vO1agdXsCi?O9TL5X#PDnr zwvh30~lg;6>J#dND@Pw2y%$is8MHU26&61Vn^HD1oEC)nYO=)d=mAH zvAIOJ$zu4KAzJ?siZ_q5i+2i}@xZ}qBgfm-b%NB{zXre6)jkqBXM)XT!n7P)7l3R= zljwuOu3Xz+dJXs_#vTm0j$qZ&@DJ=M6G4oL3uFZz65H_(+EJh=4;uaN0NH@jYi+iX zeQ=_Z={R>L*}9E{R{0>}^MqR_L!-Xg*W;-Ko6qZBbi?h|$hyM(bf*!6pbS;DDci0vv8L#=R`aHVj) zuvK_S*eO&u+Bb+5dWBiSslp;*t#H|mcmQAnR*GT0aJR5ccwDHa**8iQrUtn9gq}Ij*fLT;F0E9u54I9v zbTOap;K(XQ;A1#@f*>Aw3WSZqbs*!sbKx(*5lCKv4V%0b9U%XL&0`*j2DpS%g{8uj zQutB-z6v|;cm+2z6j)MeGjTEa4R&&nH&l4zBk_P^FylCu3UiSZGz|5vGzmrFW=2 zpBj}SeJvJ(`cud&66sJx_6d(n)rOVr#sQ&2euM*rjKW(n`A57CisqLt9d8CM(VZ+l z3uiis_#)bSp>N`kB==y$79GpDE#d8{I8e%vv{LL0JB2r#E)vzSSNfO83Gpvt@m-j5 zsiuSWckzNK{zwvUMA4!>5q=8N3NnNcKT}2k#ug&bh7V2w|EaG2A?nCm6UaxT#)nnO-fk~j_<$8%|W|7{A z9*N#T*5D`<-AWEb14TEI?;%S?uOr9e$QEs-%^;k3q6@VU0)9jyh@qZ*9~+J6<>YlZ z+C+QES23ibvq(JNXNz9Xk?M^O$%ySB??;10w~!B_0iyFmEO-q6=j2q#p}!jy$`q8+ z-h?R;zlZjZ(E#ypVMSlq>ybmA!?;a2yCpt_anU%0CBB1pJm4J{f2fHS{2Ook(xMbP zVo^W>lE`UDLeVLVI|;wU7tmg5uV*$OpEuI+${{yAME_}Q5K_K~{F}Y*5g)QXLFssc z%#eT_7Ce9+NClm=@4(^`e;fHUl2-H?HslZ3_@qIK_TM4P#lMbryo5A64Q9Q2(O|27 z%zq(&+<_dB8!l&{i8+@7{D`Rgv6+ZJi}uekMdDvXzK*07T|}-z4@Fmz&tX-FE+r!{ zrDTY6#vkA5)_)Xm2pg&YZ11(yh1Bb0w}P&x?nIK3&;?|sy@yjTqy9!$4dTxyQ!yo? z)5szA?igZ$diHE8DwG~BBbQ;FiblNB-@v9$o{G1-YnqOJV8teZ}`*hInu}>TQYy0%juQu%5SS-uyE<8eWPR~iMoW-HRir3?L zf;_6*8`)8ObE5vzK9%%;8PZkDJO#3r;o*&sjD3FX4%=chtFMw zj{)a2g<};R$BQ7T;cbgFkRr@oJSk)et!)KIy|u0Ufd0__qpnKHmn7HwVy39Obn9z1 z{g^y#pt?Fm_QK&z_SCHhpeTK~jBI@PM27Ze)P)bp;g?2hp>S%ZR@7_eP)()8;lU&P zr%Y{9uO*q9HAt@T5z1yrPNpk7dJN>gF`E6X6@HL14+o5=2fZIsmWwFuT<}m;k(CbgL|~2fv)iCv0B3a1N#hoj{pDw diff --git a/general/package/fullhan-osdrv-fh8852v100/files/kmod/media_process_no_sc.ko b/general/package/fullhan-osdrv-fh8852v100/files/kmod/media_process_no_sc.ko index b24453ad8fe433590740fb9b54faad8ac8b9f466..2e7331b917f2c050671f95abce29954ee6ec281e 100644 GIT binary patch literal 82220 zcmeFa51d?8ndg0ORrQ~A+N3(^NSj2tO@am{V098T!$hf&MxzN@`2$7>c6GWNI@(E! zqyu4=87ti#jM)}CNrMq0DFjz$24@3i)L|L7LeOEHyiNpl0U7636_<_-vt-_3W*k?{ zet-8*_w6#MGyCpm_w(-FPfnkEo^#G~p7WgNJm)#jIk)nuD_6hYvMdummRVpXQpOzn z9WPb0#4E7C%rh651nn}G7o*>bTmI{7bul6*D@PR3+w|mvCJOm{dno0&YqoJV+y5I z6%FZgv=h=_Z;!am$q_ecM%*ZkXMPm+Z*9ex%*=#yuiFkC#(caoo47eQ;+pJ;YZX_N zYcrpAYKn86qyvn+c(Ko^gPyk-b5CRX(@w%3aHg5HrMDV$acy?UG1=X2A~oVpb8d7J zsX=ENlXeG;7>jm6!rXcV51M$xy_KH@G-63h|zztK+2zSFUicRI%0>4>NEfrsy+<0xpO9 znLYKQt@+fdawr35LU{s@F&fJ?GvRSuROo|`mz?De%g66V`$9PCF(B*&%7A#;8+|p*LbndZ$rTEN|6hLGbzWiB+IL2i9~g~i|phu@BH)q=}!Bh0(Tqd|G@*|QCY8H2-&!C`nU z{(toHLZ#UrC`ZR(NaK+EtXl)myuKwz$Hztee1@?Qoz1|N7Og)7mH~GL+?fFP>eLZ< zGa2`?UxWK`@Qg6FdxMZ8WAT!y>Mpn0+~r#7 zZU^3!61f5AZOntXSzoejcxQ{R>r2E`S?lpNZaykr&SN}*dG80s*SXBGB7HB?_hQiZ z$Lah0s=hz0zGsn3kFU^>ylb4VQ;m5!Y>)Qm`E(2{R-3U@q18$acx@s>gK9HQKO_&y z+#WZSiF5J4ng);mZ$QJ#@ctOQeK-i8h1Z98kMjTS2>(Of7)y?vZbZK$dkqP5uRGG( zif&=<+xL31Av&egnKND-)%*3#w_SFD`NjCypM@v%$S2eEv5m zzaWA?{RZVjSu$zqYiKv}YvMEY;<(lqT%(;E&*NYFXo{tV-J!LI3N$iEV4eEzwD9Z z?s8*L)~doUI{uz{s95b)^R$t5QS+n0eD?G%y(7=nbLGr)flZ=5BgcjGy+1L~J&pd- z=Zg=%a;a07+v7}&^tZhgOWt?ed@?0EcQ zeFJ|z{hHy`hX?5)$FHBMocV(<)QeYj#XSx>wPKR+O!|@jtDf{nf_gEv3-73h`2-KV zcKtZYizIqLyqFg90iK+T7om>24Sk$o9p8lxj;HQ)OzPm+C4V_Mu# zS9mSqy>g-y&+kKTW)mo+}+K*;$jr3`XnS*2gy6@}BM!)#K zseCxk9|HeFtW92@ne(yC!Lbpu$J4KtX)o8wHv0QJM_Bt_ZXqysFJsrdtn~=H)PtQz zd=~oaku}Tg3G`i|65>gq6E**pFT2|7L%L95481xnL3`*NPqtMq8G*e*x!?PUQ_=cU zi1ay}yWfX?lgX52uV5=RBv`w}<4btgI;-_Oo;^UnZuk3@QomRu=@+v6@^k`wm6y*a zsE^p!FSBNQ{ZzjUc0hu)xIT4zSv1DY1E)h=>YL_aSSA@M^N*3bEfKiJIxJtSbuGa8 z6X5FS7;!yvD7+_w{x-lg$wfGB3(`m21xaK!jjXZ`CG0{;Hkx?b4DUkQ){{l>*x;dW zlVnAC;rbcsgt^R{+ip6-T9vG5ozogum%G!s+_M{#m3P6@Y#Z{D{@hr<);Rj_%Vegq zlDs$3pHIE_MCJ0-iHh(qZ9U>PV!I}>PaBg58HYk?shR37NltZRsn3lq&95pq=Z4Cu z)Kqt2YE^k5@d82}crCk7K^8pxN!Aw0j>dHXv^1vf#!fouGEbp(W`(hv?a_f8z~9-b zCme#QuIe$?4cZHe=0?^j4i^>0)`OP3ytWF zxM?cYK&SYG&Ueu@rDl^aV}p7+U;GcpHTNJX-rT6+y+879 zN8XnO@4~D3ESp=8=nCgzK6Av)(?(6^PUkT5IG(<1tUoK=>W@Vqd40(vZf*8XCvNWY z^jVhl2)JwQ2^r2%PJ0ExR4$b5ke-m1knWIX(WZH`%O}lbgE-%7nf1ZD?A7R8O3_v- z;EDM0o3x?+BZuDO^*>lsd;aFC@-Xvb7`gX!DmI13<7}a%K6+yv$Rk7CV3}DJbnEQ7 z5m*cudTe$g0!skX9F&bES=L;u2ljsAu8vh@$?@W|?<|>Iro5bT!)b{$!BO^tAY0=E0Lb(S~=z@yo%-N(}>TX1zB)jdwY4w64p-bpXrBmfc6GUmh5ro zOXuSAnG*1`K85cH9N2#2dfTKkT3?!ECnbQ_C+~)~yT`|)(cJEWf0oLCcLr-_4Y+58 zxPS4wuQNm4B%5$<5xO1db{4u;mUTdM=b%6B(`}nM?D^ZvHhNU!Tt~a2%laL3X$JZ- zpLJD6c?tPdOd%fbucZf)fuHDl^gy%t43GMj`+Uw@bKH*eSB>LljJ-W^ck3LtuW62} zd>=1(;7=0y_`T^)<-Z}n!X3)TIma*wzc1bmZx49-Ar8N-)Cz3Tb(JMc2CzltbHrB$ zUddgf*&pz60v4se5neT?C~Lzjn=ytz8e`GdLjN^4LLP;*s(vo$zv`cp*TMrIg~x_w z%Un}2_I`BKez&o>A3cJ7oWyULDl{czKb!Tf5Pli<1AS3C(`p&{R|@lxsml`SP3$uU z9h2VUw$Qg6ebc-Y&t4D4Kb-60o7bldb}DovGV|PMpThnaY!&s<>nn33LAu^P=xXlQ z+vH($Xnz~^4~FCZZ{^#Wh<|ae_+CY$$pt*i&T(7tttFGJ137$x(B#RCHy*xyD>|bx z8OqI4^oo3}(kn5Wb>BWfza%efE7Dh1vM>|&tPQaZUMUkQ;2r0rIgujjvyokB{54$<^kDpkJX) z9`bjT_D4fLMA}84&XhfA*l(L8vV$CpzK{;RdwvDhRAhR#_9H4MUxMawls@?x#)eA6j%gYm8)_XMOQh~_CD&SSf4{@miS?NyuEwRks;zJ?8fOHZqO+8B z;Jd@&EC~Hlh+D4myqa^XT_wT0@hZ9o2Jo5l?kH z@zfohu^h@-k_~79GvsUSL=WzH0O+= z4*@Nq>^7eR)^ZM*Ns6Cqun#!9B44^k`-gMQW9i!_=20@a%3DiiS1_->nly916s|+! zht_4$(abqueevI;=N6NVLj7p6v;|LN8K1Ax6Pl~@o}B0GNWJ>fZN*n#GO2I7 zgN<8DjVFd#M+Tkc#XGQfo^(y|Ti%|!dJHUZ%}oY-rp&}RjkigA_1Qc=Kflf9jBN0$ zE6L1Ijn2;uW2CWLiR{PhF51Ko%6N?|n&%{eTL!;ys?#};vK`sv;u+)UTA4lgi|;<& z0Ir6NeELJDn`vMDIm7s*ubA%bQ*x0t)k)I$xLujq=}^w!6k8 z^USG++k@ASaw z`uv^Fbo0&A-Pu*;4e3?od}bBW6YX#cZh9=!|S?mD`po45m_r zgB`r*QrEd}&+i>b<@X*)VM9(qmbPFI9evMr`?sa8+hf~>N;f{7>+MxuTPeFR`0@NI z_wjeF+P~3WwdcCjsy&`w$oO;96p7)t_6(LY=vemda$TygET3|`P4juUygm8qORUWo zr|plk7r&ys-Ffw;DV#au>)uz#`q0n3o)ORQ|2xi_L;LR5L<2g$nL^#I^Le{HY8*RGR953 z>wbjV#V6jRc4gzlZQt&SQwDuC^JmDX^cHr;EbJSOQLyo#L3Y?UE$kH%_MnR_zZ^@^ zXK?uEAwF#(U8z84!pqWEesT71<@b`VH0&wyo6olFE77BCwa?!d?yGAobf>rX^~X_j zKSh6~pA?7bG_s~=GS4&ktm$)6{oGiQ?NpySjJ+qHIQ!pZp7&vF79jmoQJ$`Wx&|oJg>=kU(+1jUi zb1J!qIa9E-t|mA`zy^BR!cQ<4ydKQ{D*Nm;+AGk^9_kb-;rc2bhws|chwqwOIwyEA z==T(uCf=U;8~==M3G2&}E9g_+?~n9|_&R^a5$1)zUMVfO(t1~@SN>Y<>F#ti9%h8G zwkOJ*Vf{GM;Ifa7;9&?Wyht(#@HD-r3Jm(yK|= z=KM9WoiqdS@)BF)x3=w5FwpQFhMwt^8G&H1;RsGulsXW^C`u+4z+dTw?Kz5hIN@|zZ`8morab(z;tH% z5Ilc~`72qxm3vZitM2ot%{|bebx?h+H^>p?HI}+RrTHQ{eckNa!ddeF4E!6xzY+W! z1N=H`s>ZMVhURP?G9g^y-8gKdm-CES_;7k;PLUW5LN{?Of-^+CG6-CPGx8Tl^}4t3 zOcmOg!}5=|F(&Lw*J~}TS9_AZ1mh&0sf;&H!P*eoOUBAON1;7Wxr3>^W3M>MUJ>5c z`+FSW8Kob>B^%LX(F;M^^9N_>8+DbRv*ES&!nDPH=ALfw9&8?R4$j)+Jh+@WkB$4K zH69)NBpYf+_Wv1ZQad#P9<`yiLcCr(D zqkVzXNS@jZ*Xq!Q(4Krjk+WlmbJ-$3nBw8FM(#*RA17^e9{Nr`anT*_zis3~k7y3@ zgf`0010QP$KV=_hPkMl{Ftc0Wy|+h%|Hfwh!3GdbXDFlf`@v6GljQAG`Uzx7zS&XM z?=O|lkgld*^`dl$PT3d>lLXqk*|Rey-y03-@W%ogz%haX!YAhibi|~-soYA&P_~uq ztgFw*b}|0li|8Fy&HHV%SAB=k4*DH!FHIYI3bZ?)SV8TpFX3D{@5fh_*DpcFktx~4 zS@am|*FEjPl>Rd5Pmo?3q~A?iJ|X4FuF)D-1m>N=Z{Z9+`)zFLyWR6H9WBT0r&I7B zzxle#KTzhpw-qW~(|wtJ6aJRaUu?Z+1!sOMJe)UD?$lzx?Hb;n3f^z#T{cWuN9}$Q zSarMjL?*TS9%$_Pl=i^b8OTGZLu8lzHF|X~XCTAD8OS}X+rm414!qU<`Ag~wukZ=? zd6x#h9QGuCPM%;Ii*xZpcH%cxhPu)R<8`6wcG~_rx>R()_g~w;F3NBYInLLIsC#IU z&%Z}$LwN8V|5`k>`-Xfv80pWNfJZCNfiqFXd#!^Z?-NzL|9xott)>VmO z9q}*iGwD}7`c!tg*Eaex5$LLLueF@>&9lg}bd~(47h&^#VB0FsM{3OtXiH7p1=86; z!oJIi>LGA8$ev*Dgdc)+6q_|iTk_3m&qm+9c`NV*+X5_hJnzJ;k+Heh19Q!gYXhsNTv%^D>1NUkh?BXy+=aOz&K2)+XM!h_Ej-g~ z7oLHp=etRFgSR`oshpzhlKfrn7JQCd^Mz;V-}8&6ZKy1|qwv6z{E)j$^@~m=+Q+5T zNdsR-yus)`ex}lF3J>V+X`?ATk~Tx`dh*4Om6XW@@9n&|^WIGR&_emO@T>61YT&C! z?sEH~*D@cgSR;k6W=4is=f(H69v`)T#};}dmMYx8K1I8PM#kk-;fUK>Unt#_Jp0n; zIH1rNbD2zX|9r0Cm$}KDeW|sjPzv)4%psF2a9^4`RkV4xi8&+1BkunCdgpNYgflW_ ziu1%f3#Ai_HaUk+Kj9pOmU!Es^Xc5Dox_!rPOlkqzx=6BJ0~tLl%Be?P&&D&%Xwm; z*ZFgBCLCm`Sg8Drdp*P8?xXC2CdQL+8k!gG9&$&VA@{}Nko$@~8{} zyB+p7FaMN9y>Q{%P2u4`ji0~gZ`Vj(L)+GH7LPn}4;p`);cVX7?`}Lk;AX7>w|n`3 z+glodzx&-}aX)&ZP{{%BJ3fGX6)GE=2HYg`Liob@fKEF9c^t$2cGP zMsd_l!K;DP0QCr&?O7i@A&EbGJ#vdF$+|Gc8dhhnVD(VSB|ibJ|Xf4t_5=IwfG z#P#M5GIRVR$4jv+HWhrk0G*R(Ja;p8tW%Hm74LF~i`dPix!3yGZT1oON%*Zhy$7Cr z!pSjS^zW-zv~jM3y&OM2?Bor8gZyEC&!TykFML(=)$qL(@AvcVN2NYJR6wsiwzBOm z_HE2l&Y=j{RqIQ8Q+ItUo*H~&Z)(rCq)T)UrWSZ@y70*V%edTl!wolVKskE0Vfg(R zyxR!R)@Qu)z{-aFs2dyc`?Mi57&CgS95>peq_y z(~fucN!!bKUvE#OW63MH2kz^+SaLvp(*EuKnpE$&ZWhmhsUOeRW%f8VlxH1zWG}ST zLI?Z(?>vl;VC8Z6g54gs_`a>}06K2Ool9RD4e|zF={oQ{0-ncob{Od6`&YM)x@mAV z!izX^Dxa=}o`9dFuhQ>&k9X^^+b4LJrw_}pLnPNa=NLKR%WxW565eId6tg>>0W;vq zl+Tllcv4`Ed!!vcq;JxgtgEn4Hq=S3=wCZ{R~Dfqd4T@IJ7?5AfKFPmXsdHLZhN{( zV-=@fBIWDaM&7wo^T5N%^S940l;poS{L)EByf~Tcbgb3^Cxeee{u6XxTP^!btt|-5 zWAW_m@aCElYs4eyg!@GYvi*G8a3?p}MIL8N$!vl3p=41;yo;yy(8sl`7j#HhftsF;tquLiFm`ebhAg98~WZ% zv=lRi<0msmesEXnTc>pQx7cyzsXl0B&*$+bYcnpujp_0F7=zcslSk&!DSZ2O6yM*79;o_*9rJY9Pwm7|rjAsabpS!^s);&ZU1=6vU&VvWhT_?cltu4pl&v&}X z4|sjQbX;M2-YGI?B!gZ)Z3wqyF`xA4I(^O6S6}Vvguted4^F!7LznLW8s-4)}9}5FZKDK;n%X#h01~WaR>cT(YioacGAh-`cr(Ax#XRhT)cpKo^Hm!JL;WR;RB$~Qd|6A zUwQ|6Wq#9$`yy$JZ=_67{-m|&#x*DALx)X2M1Nm$#69hdxHX(3PlM(c@rBJNuMa+n zSJF$YL5uCC{JZ#e+bkpWo$J^hJ34inMPJ zIQN~H=i$~k^!hwFE!xCqwIQ0A%aWy8AFhOvEd==`qq1t4cfFYrcO$x|U}($x=6H|OOh4%7%mO-& zc{Y1K`sEPs(e?N+=~493u=EydXq+{DG~?->X9v+?d&zs?a_kt!@K>zR=uZso z$3*5{S3H#eczq^6Ykg*oHhb2j?_bgyf*+w2`Yx1C7=KS#4Xc62VO^E0p`2_owOO;MV70g9 zYp@3z_F&t^S_hmO{J^p^>+Hd@bPDVEBPY-$lyhart-lHS;ZwS8$bBy0bJ*_z(e&`sK0vphx@N zjg-w|tLlzg>t6O$nX@m|z@LTLA-A7!sMcSnPvy8jq%{zJvCsOBiR@pNAWz3w7cyql zJ!r)p**}LX-*Of>qqJQpeTzK=I=0daEq#P=Z?urUA2t3QNKyAi%6FS=S>L4y$HBLq za1LjVv$hvX$7f_6t(n>{op7+N+D@W}wGZobw8uI)EzVr%a)e*)ux?f~KKN!zx!jO@ zSJ01U^oqujZ&ch<@LhW@tu>m*+IvafLqCDWz{)Y61>`J?%;Y&27`fMNL@p%*ee}B- z`zvNXI&&j!4BCa#3dXpb_OsB0PjFwD&s?!?OO4LDtuRy*H`kULQe8>pGj3QzcTtwL zdM5s|*|N(=3lp}vF`w|xGWu!jD0u~Y)Ez{wOtG`9`QHN1H2<})_H=-Q{fsOWKP7v} zJQmo2_czjJ0lF3#^M|9wA#BCZJ65J({p?5~&O+Mov+3N)n&OZ3E3{+K3)nvuGlCpu z3eRYtsC|IW=yfJ^@Co+J$BW*cc}3eMZ!F@igHCY@=Vbi3 zrMXl+@ACBTl@a*BH!|8ETjEg>-wW&T(~OhO4Kydib2a&MHD(&S4E8AZFT6cksH^M+ zM@u>U1AMbNi|@E<4#rf(+wa6RcG5LEPnx8!(HHQf^f+@=W4{1hcf)l5+XKxB+ul>Y z{_-L0Joe%2!Ry&?>+XJ%^-ucMWCnqApQJble31K|gJsT|XX{SAu?IOv_kVw37jsl| z`;dkF6Alw(yWJM#A0j`nT^~`N%H)GOGblI2`(DaMeO5!vCH3nldA;yHtZ!ID3)DX_ z^9gUyeg*616U@hBO+)TCs52bkWe?FX!uuBJ+8XRrel~qw1)KAM5z37Qv^@webVucJ zV47pHbugbuS0l&3kvj%62SKD1ol$bDS;<3s|R%(LA}dG{gKUE1dN z5jN_s?_-U{PG_3a<(&Mhlcn#z`rVT9AEul|Ti6AscQX#MQSkBJ{}Ai4Xb?O)j*mz5 zr(|2>-bx#J!RgcxKfuL`peI9*T z{2=8*S=P6%%rj8(K`AktiMNf zUn}(6gmb6*8-nng1J*b15wF9t%g_h0M*sULlvzWby#v#+!J4YQqiL{_MV;4azO_W& z^^HOZUlQOAqbUAH@HbLcXMbUN@O#g{rd+sJ)tPJbj9B9`mwnNLwDDkCG_kIx?{v_4 z$eC%-Hw1!VUo;E{@5rh5eXG8$jHh&0X%IQ$>=B>2#!xtQ9vR!kO!`Z0p z67-^X7svR1eVvW#ym~J8NJ1Sa8$-Bsh8^OP-5MKQ`ToOjz`AJ4c?3k)9prMYM-|v~0~0H+%JwaT-)^{j4MIdf@v0AWF0H z(&Xu$Ng~J#XBtER_! z1Ng}*c<~_qfIfIC(2^#T0FDtH>WzU@8@yq5lrpCQ7d1VIys-*C8o-lP@H}w!qd5YP_Mw(bIZ15T^+89Vjdz z%qMiSh&B@&!YeFv&k~*_93>nk>?QOQvV_%yrG)u}B;n`OJx%yNp-6Z*2oDiw2+Iib z35{$5lf*wK=Eup*_X$P9!-PYGAwrI@k+71GB82xAU%}XUmhdFuDB&<+FQK2XfzVD! z5#|!=2|uMP(REtiHtK%5);~yY(As8LfAC+98SY}sPGIZj-3wyP^`2;bPp&BQ>tMnw zdqcX)V8`oQZR{_6xPBYjFX)M`lMk{^CitzPlV+;>6uwp2J&EK&_et)q>V9}5ez0PZ z^M2=ESKoyko&7jY{}p2&duHH+F8F&!K-s0b?&3i zeQqiRze;)Yffqx$O zm*C|gV26Mm0(J=4gTOuq>~p|A2kdje{umhdAGp(6kFM44d8mIUjejR8v<<|o(8kwY z=oo4JHd8zNN_+g`PAA{kEMslZcQ%dOxnBak^P$aeV=}Mw`-^I)opzQwQ@uG7-ph-Q z?*hhhK`@pYNB=um`OvVt8C#93_st`6B>Azuk)14e{~CD+ZClBu@I_^08T~c|?L+ll zW}&iCeKq^o@9pDvRrcWz*vIz~`|wBY!!LX9PaM&hr~C-|w>ap0(dJGewz_zhEplht zp6agWJx$1fYc;TB3cnZl)7YhNvrI$QyEDD6GMIXLY#!;=q+|A4?8!Nvy}zC~X76^^ zgR8~D$4T8=X}2+XFY-U&4B&TN%J?Wfh+kgg6KmV;EFnI+=y?_(-|ME=p%XG%v)&z6oIf41~Z^DK9Mx%X=qT939qRXRrAqvQp51KN%O`)2W( z(qs0s@Ofj&6br52EIw6w)Ov<`8A74$QR}JFW9Aw1x%Xlh;)Xq@&YNO}cfL)C*?VFA zp!2Bnbm>uZs`Rzu(_UTUP}g}Ps5?D%&_)LzEI^OxzjKN}u{ zuDS2rR{G)v+>t>y45*%S3f${U|CzJL$DyfZ1RVn{-70UNC_O%Mg8Q8J09#x6Jz&kj z`|tAJ!necCseN7>^~rtOTbH;OgH3)lzkg*KbY`7!=w%NU(kY)y3phjCxUV#6 zkJA$HU*8DK4c;l>jfLvm%O2~SjN_xoWhy_%ecXA5?>RF1rlYkE{@2+B#QWowM_W&n zj=p$Z>FB)ctYPN%vC;`-o3lmc`=juWhLG(Dac+A)uYExFlSDbQsg~)6ZnfKkmubc%Pv$N<^5|p#y5~LjhW=kO6FN3 z)>hiZTly8sTTReM(a;;A=Tnh)oe77u-O78$)_!zdW?Vn7y{d0piPAqr-K9 zxmWoSu&AEwM^7I5bJq3@`{*Kj_q2Q>hhH2)M=e3uVL7<;p%gC;l$Swg3fQP+IV}dA zl>?qSSy_#o;3F-?;D=;a?IfJMTc4Jn4H*9KX4ap-u(;pM*`ny|r_Rmj$k9aHnfuWr z?jh1G@4*(h(YFOgNhe8b&nnyCFzEzo?>CT0OP~Mr-Rvm}qTx~AEZ-k?I*rZ%_kMK$X8`#pP)_sDpjWgm zEM#qsr!4ON!bdyr?4|8^Dp9Uy{fMXXPV0WY1#Qw?{a7iHTIcFJopyA;*7G=qU)*qR zO&>DMZTUeqS!SQ~QO4nZaDI;4o1N<37mGV-U>Y~A$0n<`Haj`mS_!`HH0vAnG6x)77`9F~0rZ$a{kMrnRd+^^6;5{kp<3_ax|pm3sO` z-Dl8!h%iszI_R#w!LQg@9CA$R1+8JO#cw3lf-j!h%kLYk^~#HO;fryvi*uKYrC%lE zp3X{ba=O!VxbLEVK|`K2GIwb40;h$0I|r6QKXYuKm3;a| zj~><(i!(aTc`w#EWg~Z*rf`1Ey`+n~!DC{&8yf7d!MQT$x4V+|x(nD74($P7uQm&m zFKQk!hroj`eLp_<{pi+xR_c2%>h~RWt{Y#}=Nyd3oq4gmeiQRz@o*aB)&RfI@fWWI zS35Yvb`PcI(AFH+j;$+U5M8Xk$41uae0c?S#g*~Yl(iSN7SNFPb zbr00nIkLB`l=0TI1N8|_QF(mZ^oB>$(Y($ zi}Aja_IgRTXXd!?Xd09pRl3#2$R1}tcMfBQIbshx+n6I(@&{u>1!Ok!!kCr(@t8GZ zpR=BHebe{cnq(`!7t2u`Po5mJGM_2eFfXjaK$-b<55I+4)>(^5KI7RBIxF^Z@ck3@ z6Ui%FE4hw&NgWTj503-KFTBsBHajuCKd|`yK-nMh4Cl~=eUA7gU29T5aDN9HK!@?@IXMeJj1bEPuUvAGv~$rn3~jc0X>xS`1*u9 z1l|O5`AODXerpK=CHxJ#(lX#BIwMbvG_ zVm`)!(kTvL9wvT@_$cv%gu{eU!Z2Zoa2p}IzU#izyhTS#7hng=7l^LrJ|$=V$V$9W ztSkXm&-}R%`8b@x4#*WM7jrhMb+<0Bxw68uXZ5?qvFxz(WoMB62y>3TLwsb|!S=hy zla2I;%leJk@1*Vzr38ib_QD0oaxS^kB0SkA?q7!-0H>t zel@%3h&#PFM8EkR3HCcrTs~M{_yl$MU7HKbh0=7xJuvQ<^q!zTzkL!fo~&ef|Es@R zRp!fd_P@iY*{eN|zO1Cd8KUHS% zF0Ge)_|EM4vBu;*W3nHHQ}>M3j@;pFh$CZJesjg#{d+>qtK2pIT_^|25!u zUzhW3EcF{#G`qFfTHJAaOkKu&uG#S?F(8g|La~74) z)$&6WX_xcD7KiVm(o&L zKI_>#lGg<9vJ+L$@SdbCeJ|a}H?}oNaDsPFC*PFbkKHojf4A-NPI;mYSPgYmM|{*B z0i7)fpVD>2AwQzL(C_4~G$TJT@Ie2U#awuxJKU_dmX*UFz}L0_bkkiQB#)6;vVi2OMG_LYAQesPT%Mu%@K zaZk7{fn8-KpLtPt4B#`(tSjm6pU%-6Ia6o9yU()!;>9W0kqOpk&Io6>gI_keZ%3{z zL8s?OkWSTEF|@nitI~nf?IHB)pr>1>+fR7E_j@oFbEZJ^1?Ye})~!0=;!jjIqhm43 zSd12szha^Emef9M2A!jhoId36J85Rb75>KLVORWr2pVi+?U`DMwKqxPGh8g+hw^yO zqzSyI1kV&6z@~Kbyf@Hq?bXtHcM6Y;@Sf#e-!Ng@@w+*P-RaPajpvT=u63%w`;ydf zY&G&cY*Qv9{d=&!-5HL}9{CVH$~)>WPc>jiqVFmDIQ5E2_Nmx)d3&H@CD}(U87S>u zGFXaVHdr}q)5dR~s95hAtYD{B&MrAodEa|TBY#mESTb12Vrxzbe0EFHtIDy= zC!Ev}zfpvo?6G>BL=HK0R+VqfzWP#@@#Nf7=GG^L8)xy^p z_picxvpb5fzGM}@`=b7D8q0q2Mct#;Z!Y&1efippEIz3|WbuI~*Ep7bR}gzoHr)G? ztN7+rc}^FaHDO+laMCUYVvX zWNVbPXf}3lxh8`RNO~iAm)ldy+6!BDy4;(FN7g%J(P9-&jcNa1S7<0V6!K2eTx*|TTA6V)k0fh}zmDdJoxJmn>>PJ9`45qwCEsM7O3dkW4wD`x?csjgrH*(Y zS@<|*#T%79Ox+kh9L=p5G<=l2i&$e=7fukKfsfycEge++ga3-XesKN=4}^EE!n+-x z!%o#2)tnx2H^k)c8*zIV>P&6O@y^(inz!k4EX%#dR)1|v0Bfdfqmj>S$Wgwh)A=?^ z>$LQyez*UE?b7+5F4yIss0@)O-JQBXV|$mUzh4C32=Bd%SX+QU#5>1(HFDh{~vydRx&Hl>Oob zg|=?$r7qBTa<8daXkD_1_hO+k9V^H?7cb+|S-fAJ5$6&7sO`yXUxF@UD5r9QT#u%#9TP-;BXVVc(XAue({J zv&2^FigJdy8y?@r*sOeleG7T3dCvjw-;Pcmz-}b}TE=D#IQ4#v_m2ke-@nMeS9LvU z!`>&(9GFYp)s$0vapW}tyaiY@Fpcp$DXTF#{M-6%*MaiSk&`~s-80DNuA#-c`z)}Z zwxZLYnB(qR3oe^;W$)R0e%T^4(3g3xiYp~;9Q+YqOgYd_z z@C)yJ==JbbaO{on;kX~i=TQDMG>UGM9dsm1yYWW}_kw=|?$3m{ z+xCwwoH57MZ-B}lC*Ry4=Z%u(+3&-jB;Ow6`$7|ZWPjX3Tg}^{8+&Ii@3q*m+VjWF z!089+KX!2CV-|Nq$) z*@w$lSmr~GGo(8_8~TygkdrUkh0f4iGHnI9waD^$oIsT~;@coAM%N$@}q zMC@BeTOr-@LzzYXoUWnWGsxj<@O5+*az!Mw1N&%&o?#}aw%t-WvYJ)Ljm zpLOeySM{wfM>+n_HRd?H`626{cjvV&p*fJnpPAH|9p%J}><^_!n1g&%39L5Y4`W9e z{@7TL%8%UUS=bv57)C>Y~lp#Gj?i zHOe$Fh7ICRWUgudGzGr?y16EQxGztdYay@BHP?Juiq5rPB}?LyXgu_;1sS97f_m!HQDWJfN3l0Grc)Tl+emXp zP{s$txk%w4K7#?}#mMvQH~4!SHiqmQ*<7JtW;L)3u&f7L;n`IyNy|sGlDx0HKKZ=E z8zg)I}LpbH~dM97?lpjy=dzZw2K)G>Si+ryf<(02;Dz9=! zC?}h1=@s~&@$s*B2Jn*{JiX)!`A+UQJ)it6zVokVr+)3i4<29(ZOk-FfBi?kq3}MH z{$C&cwnWNc`*Hv8TeLABJ1%;sJo@`!iQpdKp}7D1T=*Rgdp_R}8vGW+Yd0NnU+bKF z2UutM`Yz)}z9)I1jo+k9BHL+x+t2uZ#FOkR4R&pKA2yMP#u5LwXpiyT>oM|=Sw4+t zpmAvxjfcq#@93|>{?qs>$WNv?Pn)`zng>^7a)>(_{Qf&MXb#pJepe)O_n2Xy)X4op zm6u)5KFr0ZfGy1aiE=8RjO6S8z4)y_mejWk>6?C6No6W6Ss^^I691S;p+yV9c)M8}w>^6eD=f z^Y%C4|B1{0O_yZJ+L< zz8J{blf-jt7*%2g&gZ@7pAMa8euqn_Eu{PDB(GjL<7b;RuqSy}c$_docnx{W6La6m zaR1Kah$YA2HzXP}eGl(_XT`aX{!c0WE{8vse(e4BxyIZ~eBTkiFQd=P=<~9m&r8A8 zT{Sif$P3Hso02Rv=vhff6CQ-d1;h#$LF0Vl+_r6f9XIy$cigbOr>CPwmMt43}l7xyn0WVK386d*-~cJ^PAM8wW?Z^lD{%@Qpx|D_`e+ZO*wEwZ^xFN?#-PY z+k5(YI{i5|4%?RNVM-alz%AQ1_xE(S6PE zI2I90(HtX4r!HigbrW;eW|W7o)uTo2RX^B9Mj>n~q*eG8qR;X?HD~>+W3JM{9 z@j@Y_iP!%rk3Xhey_<6#n-H6D==Dusiy9YA3uERCSMbiHL_snLT=LEEDte=^oER3C zQA|0(ys-`83}BIpX@XZ@II5ms38#JtK1^ z7HkMAqli>7%}C`X(RysJoc9WW6Sz~a>l?^we%UG*3K3{BBsHF48=}$658I?{9>Gpi zD9W!>w55_$3_b)(h5{p`=r6ywt` zKTCs_6|-VttoK+v77ybP9>yX3jbhb}C*p}Pt}YkC!#Knn#?|FRco*M97ptN-W^zWk+oKmWPAKDqhEp6fpL(X0RXEC1n3_kH2+ zyS8q*q37e*Uvu?;{K}WVbmR-4yKCE)8+$sg|LE0^{pr{KMD>67)|+>#{(t(@um6ea z|GQgn*{S;9{Ijq9iR%CU@7=sp_5bsqef>{V|5Lwr%TCpQ`6n;A&p-FWAB64y?N7$s zA3gV9eh{|*U;pkU_s4(z!ykn0zxsD$?%(|NfB8Y!{?Gp9pZ-zx!}kB%zx?w*s(#r1 zFJAwre^mXj{j;zC^FOM7*uIG+>SBaMB2lMD@6oj2Vekr6{i!Zroet}U_^Yaq;|}wy z%U7qX>;I3YHA^%#y!oyKPngzx4B_G23}NAPQY@IBuq>~^zA@%MasQz&dTRWq)_;Ny z)cemA|7q}_ss7W*^QLLj`NQ9&_nvGLB-u2h(fixnIF0w_H@|6m(~Oz3&LiS)%bTXp zXr6W6Ti%qMIqS{my=7WcGk<4KpFz&-xyhMtZh6a`w>34N_m(+x&z~_1&|B@9Z+^?1 zx1N7N^Lex9zO8lMtd==%wJ*5v&2O3eHlp)pzxDjq3od-i-1F_$c^A%ptKE9RybI?7 znm7N#w|RfxVe9?v3%tMYxJd8sSa6a5_hRq;;@=YF;@?{6|GmU}zvNOuF1hrb{@;}M zo@x^$)wan0yV!eQyjYOMix>NU7kfF(>f^eSMfgR?>*lT<_KIznF0@zocVFt2U%7m3`$w0rz0$ts%C+l0_>m9Wm)RHY zAm_%e?zWz;McnV;c4gb8i!O!Ib?vKc?}?UOaN&-N(q0uK!Y@fJzGTr7Y~QsXq!gv2 zWx;#J)gN5lE+p;C*I%ia_81zIS-;L+3y5U5z4BU*wiQ>ewFSRSeCzAm)c_QMSa!)J z&}d(G;garMgzLSs=YU_gZs(>goBJ;V;rlx`_bI+|`}VEdcp0y=_R95Ful=xn^@mqq z`2i$p{m1Og^0mvaYEU~t9s>7~wbzApqGK!X-1QoEk7#dLZCriZCz1`5WMOjo@sgn& zL05zM5RSn2_iU0ZnB}?c#J8Fc?c7S}Gs}10Xx8=Q%tyNV&4;($Y}$LeCgk&7ljn=K z$F&0=haEUb>>WG1$aWS{n^*k-?QCjLT%o9 zqrG{D-M?*{-M4M)jjgoR;32=v?%I~S760hAEpmu%w|lm4-?m-YbNxFyy81q8G)h#o zaiiK@nt)X9-oE+fp6&0PM4n`F949llzsKvS-LtiS`>hjbeLv4Q59Htp`_wA3&Xs?I zNYG%b#P%)thU`gAtJ-A$5$WsJuDE{Z)=zHTc8h%jddTj-HP;hN@rm)N;$PU;cKFuS zZ?C`^VQ=1g!?x?BA{Oe;^$X#W-Q9CT=gz)xdm?-bVcJy|3!?Flmc>KJrJGWH9Z{F-lo%dpd63de#xizoXOBULD=rw|n)xyd7Kmb7Zn$Z@m!}Bx-0b;HC2Vm>yfnxw%ug z$Me*Q9sOIP^I|+tv$&_jlg4n?yg^<^@8%nO$Mbp@y?Y6S+|rTTc1zE8d6s*kdGGoa zc}qHO==@}l=a2L_FuC3vnrJ~0dFOONgWAnDHfqodr>E-1*`@1FvV{+b> zo-H2r9x3)O=S?V+@w_b_QdJz!9MAKz-heM&-W&99(~VoIx;H_K^1PYp)2P4W?TzPk zj7!{jp2mI4jh#CY)Uf7wo>y@9aaw23j*hlPOFMeHHZfG=CCo40xlRbJ25ZV&G5*t|FIEY9!r z&K-R{n7n?1MMso|4Q}b_+}gi+OV6Skayx*@^rYYfET}bUSfN>Fc#ySot9Qb_ZPOUz-enmb^tOk!CG?_R=~cu7_k z&vYiIx1%qF_KpK5O7@)tHj&HZ?e7VzYka%6Z_9Ob_jGM$u?mwrbKNRMl9gB15sp4@ zj~##}<%__ZazV}qb&M*Aw+ML?4J@Htb@cY!48j|3?C6)3pgQAOJGQW4pPaX46Gyv} z6RpKi)Ts(x+qT{Sty{YU&8fbi6Fd#tVB&7-WSdW3$l0nK6ReK!ZM&?E?QM6@j;`&S zbN#HB@Abtk*gcufo&CMrw!hc(ZSLyXx})d48926ma}N%NTdBoXse8wJg=|abjhnmP zyZF-7rAuwresA`6cH7I>UUl=ky@iB@W}g-t9^H4*bDtlXuLQB@L*czF=M2wwd?tFH zkb{QjdqMp60KPnkuMgrk1+f#9^PD==`-dR?V35Bzh`$%K(-Nc~3(7qZ#63a$l_36i zL40Qr|F-3kWj^6YW_1u>7sR&)@xj1B_^BZ74cc8F#GeS@|2atiOAvo0fX@xuyCR4e z25}+C|J|T`Z24oRF}$97>9HIsk1@=UYw}WHN@JwFb?5i9Kyr6eu}OvHcSoU#y9C;hQpd5 z4&h-O!oxVk8^$5tFb?5i9Kypm#2dyT-Y^c~VI0E4SVvU~9Qwu;s?$2IQmC!1t5c{> zSC_9&SC_9&*Vj*(qR`MVb*e&jn$xZ~DNLI-eY!$*y1IOIy1IOITJjQoBo|tS6wVSJ z4AvxG|BrhnTY~?`b^lj)_ya3eTxKuWv}jSP?YC3!PQCjg`(2m5OD9GD|H`;x=jJ|k zN!$AEOKx1$*12fu(xsPlZ{FU2>qYi^y#6j<2%kE8IyY@L)&;!QyxY{))>~7ohS-cm zvo*^)FWzFk#hM*^TkL{yrF(AHN(o1wxTmQnAaQS2M$RV7Np3WM>O_LK zxOa1P0u=X7eWPjJYMprE-idHD9a^wHEbiSnjpo03BEehSv{a?}_0Px?_x?Bd2|(QN zOQDe`?%isg0L0DtlQ@igxB zk@7|0N{8j2B`qn4;#Z#kB)z-IN{b~HSVeDQT+EYZ3hzos^Beuj{7sq*$x}L-pNr(b zk9?)0`9F{3f0TTsqxtHa|0K;e@|BL}Hv$Xu_wlZDG(Q*N$Nl6f9rEK(No(H~<;OD8 zA-?bNu5=V%O9bCf$WuDRhsYS4%DRtX!3pv248ebSVS326>Psi&|Gk;9J(qVhwCawK`bo>KkL{q)->l+6o&5tRsbQFL4IR2#h zXY!Sf<}Z!pH<6)qG+#2}Khw-bWGEfdCm+gJNJsh68AK)%YPK5Pyerh|0K=dk*_on!{+M~ z)%BlgW-7W>=@8$1(vOo4@x7O{{4nAC*g*Odk^GxT%MTak50jQZC9MA?((-#n`(u;u zKhw;gk#Ca-+xNyI!k=f!*PM>#KkHZKFY|$XrK9=jNdFsI@|BMEf4n;Um>-w~(flQm z`d5&zbToh0I0D9>e5IrLy%G9vCSU0&eW%9jPcwItuXHqjJRAM>W%9!(T(9#DA;e#= zljCNJzh1UP(k&ECN77nPS4Psx&qdP8-xWzKe~A_p@A2_3i=>r5U&_{d{QOiTt^B@7 zTK2?7zc7FO^}j!oukv#xJl+%Fi=>rbjHE^HQ<1dtH;yaVn6XJu=nvms$piBrzx~@H z<^OwS-vd}>Rrh~(?^P$vHI#fOUG=I{NnsAQ;Z%AZFkmRi#F=E|Hn!mw0|pG8iM5hy zZ&6O8+MAe5NmtFHno72G+A31asZ=Ye=zXb|Y8mP(GRoiQbI$iX&-b~zD}Rr&&;6Xw zIp6dDJm)#jpJVMSZtW{hy(O5&x%9OwPJOT948Kot>WNeE#~v~tuLnqgIyNWLhFSwEW_|608`n`@XJRjwnE1{En|2Kkjd}0&z=^Iq`Z--9q{ofDH zb$wmm!^;16p_6<6zX87!{&jil{!jOA75)T_ZR9@u`QW_YpyL}+<+B(%xh|hxWv>M~ zxwqE^&ifU-lJfaItnBqeC)f72Dg7tV$+bS9^jD#iYrRwH|ADcUT$%ck>}C3r zW?mX!md4+e##x^G0)u&`5jwe!Z%FAMhEDGF2f=xt2k*z*^4SZ{`#^l;nC0~caNf`1 zxI{h)=K|hO^8OkBUi@=Mp1BY@xwmh1gm*`-SxOx|8xHI*)K@BfEp&3NA9R87H_vQ` zPOj~DsPKKz$#wWHr9TIqTU;Rd9&WrJQk*|qSb6qvdb{G(4=8T!D^9&KSaxvXs}-ld zUvY*XQJi`zSe9_%3ly(F`H<(M4r2I^{`1bjAA8^ndeFN(#eCsi;N6PXfDb9&2;QqW z(=%7uqy0X`*TVm(^1l&$OmT+aqx>^`dCGf;nVaGNn5RhJZQvYV-QngJRVmM1{&p%( z`@M?Oe&t#CV-HteOBJWSM{$N9RGfPC*)}l#xbQ0#x8W7H;T5NzIwu;wKym6_iZgt- z;?$=WM8hW)r(Ui&!&fLyJ?GqL_+`9RN)Mt3d%FroPY~Saz9?51J36l^!%kC zVcd~tE{9H@z(0TeDLJ)ap={$GYp?)~SWVE7z~D-RccL*PHpTmYRMUV{A1Q0c3LPOkMGD*P(w zx+Blr3Z2~RcY^ad7=OLr2hQhjcuL}Y;8}1!kHU6BJ_^p~GH@ETJcfega~YoJ zgY$U}&kMo%yavGfuf&h};I2HdJ3Kg_-71foZ1o|3Pp*M}lT#c{ezpm+|R& z6r9i1`11TQIG?xi{zt)k6ptgLe69x7V(q;RoX@TJ{LjG;KELAm)%d~ZTRd;U4?bVR zcFgklBsibD@%8^za6X^o`H#T4=H$~i3eM+TP>eSGacJm#p2+LxfOE~rm&e86eE!Ji z=PGbMr{v3z>DY^Z?#MN@(8-@`F;aBxz;X2S@!cf=;gWF#cS#QsUS8?jU@g zxfwdSPG6Y6T=QAzL+J2SF-)qpxwf%xXNBhD+LaxJy z*RxnpgifyY?JEAupp$F8QkDPp(8+cDNfmz^baEZOO4uG6C2&$Ykg`Ef1X(bom}fxs(#v`lk50bD*fZo z$+g}U#P9C+ggn>W3;*QWf045H7<6*2`{!ags~Cpv4}bl=SItL%3!Qu}Jo)QSGsA~) z^Vx*rufjihf%yLi_;m0*;o12(zk#!jdwW||c&^J~dp9}U{q!=$d8xyDa_(@~kK0xF z(_o*x9Ui>>QKfTg-lW2x5C83oqkEdfOlO@IW#R_x|Luxj%J{*3{y|Nj z(&;}u66Okez~RaxVdo1K&Ix}LW;JwvH`5)H&5hu_iqqb1WuN9cW{Y>ExCIXT)BJZS zo&NVKPJ8uFS!@KEl#_<N#+PR>%;4BfcJrONr(P(u{N|9ob8?bbnp?S&yckvXYVBQ4siZ< z;ZHIvz^8+|{(6#G4PFG!^3TGo7160WkQ!m3t zE$(pPD;1}HbbeINUJ!EX{S_hS_}I_Nv43_vj#f3Ki_=-obS8%>qFXOe{x5j>4r|O z?X`Ou_8x*x?(GeM^Zgfp{YZO3b3l4tf=;gO)q=Yt*Sre77R1{-0Ts&kY$n5v*T1ya zihu6NHA(2?-hVM00Q~#-uK?$JJU)N*;0M8RnG@Kn4aQ@)o}=3j<14{GcjTE4geTYO zFY_|;cMo)Otv3aFuIYzP?$b;AtXp^FndhOC>+t2Gvi^P#om_`!{kbE@#6x4SA)Q2*|jY|rmaigP|QLvhX@Dir7Zp-OR{pIa5@dhU!9Li;>l zZ&i8^_zuN+zV1_;=j$QGdA<&xPn>Mezhg@0{R%#V;`77%6aJYK_6pv=n5%T&kEm9h z_aj;qe=FL@HpO}WVW;A}|In{E*T?rM&h_y_igSJZnBrU?pQ_4->){oObG>|(;#?1J zQ=IGJ+ZE?}_%6k{9zLKr*SAL$=lb`U;#?ok%MQzr>*q5R=X!Ug;#}`;Q2cEuuMWk} z0^h0l`QUpLp9#KS@nY~JieCbr!^K8>Og5K;Pgi^)c!lCug4Zg3HTYJ=uLJK^{5|0P zir0g4-&-GFGx(_D8^E)<_-BvFW(#NU+fgW4n4+y|YUMaKKNkAd@jNxyzddrPff{>d{hKqu!A;m>z^JfZ&` zYW_y<-@oMb-R}|JAAY=CRpnjWc)3GyrhBL2w0A^t>PHo)zHbHo*u%{y_A5@^T;o(f z-^f<{7}Cf0PciJ<^VM0n*U9%#nQWd97lZSCR9BukW(_#sV|ADLIp!naeE-z@?*rcj zj?=6S&-hp3pF48RFm!TX{?z%;9eL(Y(8+aqbME4fJo6UZ>m=8Dhp4Q@Lg?h$eo$P{ zS3oD%dZ!BC4xL=<(*r%vd>%Tvj=wU{bIlNR@@)L`&kGa;dY<_mbaEZODA03F?wM{r zq{DZowXcf6f1W9Xe{yZFT#i>3 zY$plDsrS@J^<9cn-?cWX_bE<&ZhNSc_j9slk4ffsq<_Fo-ca6?%$?*p5VQFIBy%4* z#$3-Ceo)QF9)WJT^?$PY0XgQ^&cBYNjKV)TheTdK z{sVj&exD`$q?DU)Y{xn7WD_(0hYL;Kzifd{el~Ojx)Xmf8olzy04A1vexe_o`n;4e zfBE7E&H=Uh)!^6v;Vadiw}W#$;GB~FZwKf7?&-qs0>Af(D~CaC_&#udOp}xL+QGY{UNn4YvV%l$Z{{rv=q=9R+Q&9hx zfj`-i{h;?>1Xnz^hX39KiUOicUXF z-zsokzmG}$&EQ>_Z`DGh|C`hNzaO0A1)rm$elPf)Z@>I$c(>*8ILd<03lB;7eWu)kjk&gXxZg42IZn*Yt<6)O=R%82?$ z!TG$hZ*QL!|6CtD8S8_u;rzq-h1c(gerv%~Z9cxo!QXOG>3Nop8%HocdD&t2oL5t;t?fZx6UikrN>!^mH)%3mw} zkXg^TGYW{2hZWK2Ps;p7(aa9aUuQufTZA=NCO+oEHANG`dHy=A)aKJp8~^Yi22?MUb_bdK+zLHp(V z7GvUn>G6n<{Ym)K@IQ6)>!qMJz7g;a^YUuX{{+6a?nMT-dK?jRJ;=wOj~|WS+}Gpv z_p`5{zNW%~ya{`rDkwOHf~Y9+n#8>wtc9!#RIt7?%H? z;N?#~d9mkN38W8Y=*#ab&_8$gFV=Ydaqv!zdA(>CwD&I99zlCL01xEPKtG7mZW8`B z*fS@D_Rhk&kni7k`y=r052o=~;5#08<`CVOzA}ABI0a&Hn-Tu7`f?!pd6_+@^0EIImZFL|=lr%K+@vLT7&Nggyi5_51%z4i;#f(yR_e9e6i zp5=Kn_+CV`TjJje&i8>%h7A$7aFu`5@AF8111&cpu|KA8JAT9B;VA%|U1#lAcGP`-A22UGPuNE&Dp%SROwC z&;IZf^Z>_4!8g8o?M%-vLSDvF;rZtTl#M@V?cAH zytsRp*K^UcY8F{@*8jQSMG*N61<%L#qJI|Uhw*(FZG-nreEh}mzx$zg!>Ws~n&IK6 z44mn00QU#;w<(Q(oZ*r9?I>6J?@7~NLR^E3jD5zAdLi=4_aW$w{7d-JP&?(1jDz+0 z9dO?7Oo+W<@b_JN8C;zHJ8-@q*bmO|RcM)9??DmS_HhxyrBr?Y9sd2n^vB?z>-~QI znrBjw7+({q0s?*|`v+q4?`Vfvl{zb3vh=)VWSw?k{f zzbUvXXk@|QNZ}vT`+5#ElwYmz*Wmx>=ay~;wf1rfQ9j6AiRjb7`95T|@R{Iq7Oi>K z`(FZn)ui_w^87vEd|%X;&sy-uA3GyBf80*~@tym;|IdRzeCo|bp39de`N2bc;&6T4 zjg5u%rl8IgG?>Dr3ySz>7XQrVpJG!}vt|oE|5{Tx8{gC_jnJ}L5qinRpldhZvJPKg ztO>u#%I~$(y=_xw_jLd68>g8$k#9r+S{rZxQ=zG0OxWtmh}k^HQuE`NzKW8Fx;RNeRtyH=J#a4{(% z_<(Pi$_do*74F;gxB`nNLlxJ*Xd*R@?W!%RH26wSD zwMvuE`M7e9b`Dl1tjp)7!`>7$IiJ{ZEj?O-6SZhnve6Wc7gXLRZhH*TH#;<>Guh9m(TazvW;@-~zwxTAN~~e2xpo*SjzE;sj)?kWDqkt!bd!GA=NF&B|EWN1V>0xiGU2>x9|PtT){!iR%% z8gdf}e74_yzTRFkmDsKQEf3e>g|Jn?_nr;2hc0O&W z+15_6y(BBPVT;{GnGIX)R2!<;T0pzwC7TUfdHvN}?oe zWbXrn6_wjexa}oEH`iboPVHe4%6``h?qH!4;Wo<%HpmDiY&4K*tBlaYL=j34_YrdY zj4+UQjV8mpa!ZZze*NYeQH9%VL|uXsQ7N!nZ-m}lnq?D?bmz3~IJ6vY!r?dO$gHXq z`3`LNV9~xCn(Mf)h~2g$-Cb?qF@eObJSLF1sYkk$xwGgYI(?&$bPKl2M9+JXG2H5r z@zYt)(5;*>LDLqaqGE%OFd^dRw)z<7Y&}ML+yuSG4=Rzyu1E4rS#^4P1J$827qRm( zz7Qfqnc9s7*@{YTqZsDanHpCJIt}A0Av%nVAHqbRxRXZ`lc8V&J!97p)fbzL0m?P# zmo69d=sUE|mlTk1^pTQK^*+&16eN;;E>lC-ab@WKPJWYx^B$4LRgzRi`YAecff4P(ffV+NND+t5`aI%tB(b?Ni6m^I z-W}N-D$ETFEZUjqlA*_iCrqD$46^|z`AT3$kLHLI0+j&5avnR^ooI zZ)|T1oHy7Vm0VCiTSv!o4&0xWGszOMjXjaDy>E+(sm6M7zHi>tj7_r`e<3z;YQqQG z5!B8{?bP&QA7|5so0SFZ6U#jSx3o06qCglsZ?)6kS?(@`o$}7IQ{Gv{nZq`=V?!u4 zvz>iOACzSa?1QWl|@IV)4erM%n0ot9)Lr{W^-pf+U!(88T(8%+6x!#yS1^^G{d{wh0CnlzzwQu z>TX)S2^+CSC^z!*4zgz#!iLQpt;g>5C7X9aklyD7*#wM&n|X{Eq?|j$LA}KhcHgV!Lgr>-siq zsSR6d!d%0={WKA*OUT*Q~3)o1V%)N;mzgnc4& zLP9-b@US1hnG^)2nyJ8obpERxhsk+!wgO*i+y9~PGMF0gDa_x$E1ox(>Iq88w?;I> z>Ur~*F05I!cwWtdtEz=9Sbo)_C5vkoFT5)1uwvmgORit2gH$eGeog2wANx&N2iK|P z#88MckZtcko?~Sr!SIA;cM~l2XC|DRBmE5J7G3s@uDDU=6vWS1Lht`#{U1GUbG?S_|r>q|`bR@7f> zcc*;k{1tAe<26lX?Zw61qjZ@B8+QY%t*O~hW(-gAenRC^8{Wd?owd#DvAN^=wl&6; z6!*5}PS;`1GOzN~VMFX@3}xK7)((6~R#|Dg!397wcI>>l&Q61v_29mwx|dFObw|$K z;cLd+gXVmW=VNNZ>cB?XH)5Odwp+~Rwk`0`+|X|7Td?sdrWCH?{QjOzrgc*z=6sDc zxPN1l;BC!g@gaw9yWW~cY{rS@)cRJ~^keeGy_Tu<6I^$hL>Je*!>b-26{iYr^H5jZ zQmJXz*bN)l8n~V7hBXa!T;Zh6z_0X!a4HKA-1HXn4di_-JxUOljL5#RI4AKQ<(Bn! zPH#^vZ8g|R*ljJk8a2b6pY6hZIKZHA*V!`j;TYJLuS=baYTPxZE*{SRC3>=t4Y5{z#Z)}l#1kJ?Vh+o{;R)KJfHf>n9V8goB<`!)0t5cyaL|wAD zl!Cs>{d#ew(b%xrpby*x7xxTD6Iw-sYS=b9v^#7b+t_F}WBuF?OSVGn^Fhp&J}0N2 zJ$$2QUz}6oyP{N}G_~=!_rr#hX<3LWQ!FLOO19B7Y;JXTgk1}kF%5+~zVW6f)0G)A z0@E`4bmb-0Z@$U4f7xo+C8o8RbFr{? zWYm`%r*uzSsY?YhKlGpW%(I4UH|>wpUw{a!4x$R|n(Q%@|XpC%C62=&Z&| zK9a%GS7C$l5|7*O|#XR z*rl7IT!WgHx^=4?>S|Wk*(bS}P&OvFzCh+eM3zD59G--jnJ_jLmrEBtX6{F!2_~4s z7c-?UPjPiDJSZ%kK4u(x8VgYaR zRV}HgdFN^-WE~ofScCb<1&>Ra%r@ZjeT|zoL?V`U6W(66QxDaQTyx%H+p|%gCm>1reW6A=){w@i`RN$1CYs-T~4rB zLni9i;VRuWMU4HnKLvwUhH=2nR)Q4!Qj_aPIrB-u%e+O`RW%^#mp1sBJOp6b=5q3i-;v7IgU1KcUy;%`8kSP303U_0fHuwX^D}6 zT~!9773Nn#hmJ^*yU;a@moA?_Z)wf)MT@RkSY6{TBRCRamMkk_I$_~zb{OOM#o z9e4hct14=i&%dJPs(H&62AScyl`UADNLWfZbFSkoC!EZ%BVf*iB(b>JQkxsN!#Ss} zEN(lg;(chYGll0@)Mpixc+U zE+XFKTjO*Xz)9lalFg3iZ~7L4FHqa0BtVbVWFLV zBEk&|@?BQC=iwQDy7N0`);^GS=MrJJoCv%8u2~%KQ&)<M_6k$gXP?Zi7ar=65AQ-RaI6*nco zZzbM?@}&DA)JHC`8tsO7OWZUN7sX8n z5q3v}9~R8UdmeP>{#%SM1!Q~$M0^CF--=JJM*R|}CCpC2Ucr9BK_J5q0U7=X-I3n} zUQi+Oo9|42kzg6|KNF^0kl%bKuM*^bUbI&Wq`d|p^V>v@`e`NKg?ALmQ7?RdHvUDt zcMVK7$IULmKEZy$L7+`1kaoxD&iaIHwwHDy%3~GYXCddlU-aJ!r2p-7e>rY?i9d*& zJ;J%a7yV@enLh64McyEM8!-7Nl)qp$Uam@h5#>(A$2QA|3sBBP`0F8##mxY*ByL8< z{TLA&GV&W3$x{-hk_i7D#4FJ6gbxuv7&pg+cjA3D`rAQ--5z3g!i4+-bKG~@%oY5#@tL!T#a1(I{$8S;a| zxz7ywQXuuiz~oyJCV_Xth(pAmApgYu=#O}Bitb&+=a64w4f+`o{(3RCaQxXPI3}3D zJD_yuJ~NDODvF_b%y?mK{V@1^@l-0UVkhW0=#Mf)sp{_=tJw-cDWFmCsGVfegroqHKD zi5Ijh1*-*D5_{1fh4%>d3+^FqLOlx~5j;RdzQ%+fB_e-$=K&GVfZ!2gVccY&@7$Au z6@n`Tx$jJTKI)mcAj`B6E3!;0abcEeBQDA^?Zm}dW-AdN7~nSn;!Cp3HsTdorjvMO zmf22Rnq|6xwj6=ePED9@x-Ub$lB2wO$gj#WyNJuPOfRu2%kbL)Oi#~rr29u`FGRL$ zywgmaD_AYaePS3-Es%B_h|AG0foxx`M6~-hBHDdB@fxIGbiT_?y;JlqB0hvM2xPnG zyVJ@0F^&<@{$~ibiTgI;I|PS`c!B;95%st8Lg2Y*M?j{t56E`Rmp?h)R{)c1kZvOK z*DA>GYcu^F#FJuX8!`pZ5d z{N)une<|Wu<7O!lcG|?flZf`(FYfz^2sbMFF(Ufm^jR)kIT89QBI4ggJOkwmjAO2F zK=gvyPM=GJ|5_r-sYS4zn2g!)Z?k;1fwO!%iD%&3Cd9L2rjP!1A$>$_mNG!RDsI2S z9nZ!06v*$uI88)610}%Ep#2K+d&#tK<~a93!Q{oxeH57dA1q*F?NDVLAne1Pc~9_maiHyK(*|BE7Xlq_17vw-ZsGJBdgSzc0)FFdfMA z3BM;x-a)=KZnhCGLVUzakWSHelP^U75x*0KyXYjXPL8C zAUIdBTChcMo8V5te!+c$hXu`5&R(`)O0Z0@O0Y?=L$F(Lx8PpEVZpU7|)1bjhWrVuf^10AMKO)Fxn^ak(e1KJ{mLoiF;yZg!rwPIY4|YW)2d+9W#fB12J=$hP6Q98z5yXA?mKyQdm?P56~q@} zrjqy~zClOak8dv!e;hN_#Gk~>O5#sZA3)Y~H*ngy3DXNquEe-U{0OceMIRGAe+Ars zi*bl}dcuC6mEniM8J^!`B~Jj!2i^^T7%xj!IUKD4V%+3+H#wdh6+A}F!Fi|FxhII= z*}`)K^8}{~<_jhTQ-Y?>*(m`gu}Rpm)j(WFR@OW066^so9r$K}Nqz#?|HLMYhs18o z|A+^0erW_EA1T2qLEN*l?laZ^VgK-2$N7;L`roxK<5d8tjaj1kGi*sc}_@7I} zJgA%q{}sfUX#c=zbtn&V__OPG@K;Ta`KevMD~!4IJEW^t{525auZakIt;CDak6A8Q zyel9gzcU1Fx&H(0iX8cFA);JRofbQQ%t#5_vT?mYd>ZXlILj~g4$Mo0A7Qx9qMs4rpZSX|!Teox)<+EM4y+IA zdGv?%h^gW~A$XAZ0On7^kD@+fSO;OfGF(30u`ZDmy`A>{iS{RU57YewtZ#^pASQ-& zlN{kGBGyM(uC#~Dn%G}4uaod2bn3+XRCLx$?CU6J(TC}db=Cc%=P_N+ARm&x9^x$5 zPHDfDya(kb`W|8j#yjDi#3>2;jU4*lP5vF%zRBCkv0k)Q{AUw?jQj{6B<_xzA>qTs z>oG4A&VCb{g!Mb&y~K9RISYvu~y&2qXD`H}J(BtDMx3g1h7%8d)mZzaQ>kACFsvD_bpJt_Yty8i<8A$p4L z|ABrY`X1WDI>3O~FQ>mFZd{@L>GZc0{Ym^ErjF~eQL)#IdmwiuNsfE%DFjC&G(}xZW!fUe56OScjJM z9-;d&^fS>%h$CpP!jCdNd(mGc{f6#OpnZ$pOLwe`^oh=KDwdnD;}p}!aVqv8t{)}7 zF8UireT)Ahy6<%3BK?=q{Y;!^L~kd)>dpt$Ysr6({E6O5{2%lO;Z4K}H;&PNIr$va zm+0NZ7v1^8rjPtLNT28(#F=iqqh3YchVmD^lXwx1z1-mzZut2#7CrG^BVx54zF>b$!7P~>t z5A!gd(*0hnqY#hBIt;_F!@39Yc2($?Yl6)p!;po zu2x}wL4Fqc0kKxjV{gSe5&34UQxQkwu03Cjc^dhUwDV8P`2P*$i|)^&zKGv-^Q72E zrF~t3`#yA^jdTzXOFMjr+bb=0r#sKb%5gnT{Z=_oeMh;#tZiu!T~66&Io{$xCT>pGEL=gT1L= zxXB`;n^jnX+sgj!5IEqux6OtREZf`Qj4Y6?_24;f7nj&WlK9J}ZPSKAJdIG-5_+)C z8Qh!?<_<4Pt=+J`zOhh0y<50?Gm=ujVcj}B5@HH%Q7pdd+L?IJxqhuFoVQ|GVO<+i zyc$o~Y(|;B*#oy<44Q+%@Mn(_{AFA52kV;$miG+0oWa!69<1M$?Lq+FUm<>pQV!f>{SPv7-mI(^l%u+|Q@6#g-v-d;0) z)1FUKax3OSQ{W(nx3>|T{#f2T8a{zNpeMQC{7Zk-I|RMGDc$(C-f?i0gMN72g?~EU z;d_i(Zx#2)$9u2%iPl9QC2VgG+H~!<8>(h4tzqP%?u*WglAKu;w9JIY- zu*ZC+@DI0Y1A9mCTiY9hy)n4?!`mB$gSMA*X_h_rrQn{Ae?EQx#BXiy@Pp`^bdbZ_ zdmUWcTLpVQebb0O-{Q@j z_GSOep$~%Q!8lvJN`Esw0eXK#)qG7Q{G}?~ULplBzewwD2(bhPK$>Hs> Rt7?BOC>Puw3=aSE{{d81hvWbN literal 82208 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 diff --git a/general/package/fullhan-osdrv-fh8852v100/files/kmod/rtvbus.ko b/general/package/fullhan-osdrv-fh8852v100/files/kmod/rtvbus.ko index 8446772e4c63b1dcb7a038b827c0193096d4d275..5ce6975bc16b8a00741c25d6c9583dd1d8f66e5b 100644 GIT binary patch delta 7987 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 OGqHnlP7WS8kokY+r9K(} delta 9591 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 ayl>HRrIs4Q9v|ggfng1bghYAd_WuF$9;u)J diff --git a/general/package/fullhan-osdrv-fh8852v100/files/kmod/vbus_ac.ko b/general/package/fullhan-osdrv-fh8852v100/files/kmod/vbus_ac.ko index 72a68a6ab3e475da73f769b755a1f34cb253104e..a5187717f4c99ecb954e17889b84081d32261f6c 100644 GIT binary patch delta 1596 zcmZ9MO-NKx6vxkdI^&FvI*#K<_#9s;G~w5bn2spwSUQ;>ffTeTaKs2BQpCQ*oC={9 zE{q%H!bM1+L5oBeEhG#?1WSzERUo0%D%wO5W&O{4*Bai1JHPWk=iGDe+>bZj^r&V+ z4>|S`H(R9vKjlz1CBpWiC!j|-tNwwZgslCT^(1d zbozt5MGFV((@I;XhkR#D(&qX9J+o;uP!@W7NBqm21L(A zd-_N@0Mk}QcDqAyD#@ydqzA;c^xGL}@c(ZK^DUPeII0 zPo$TZuaKKwfNbyS71)kA{SLNEcFx~{of{Yb30sTP=?ak^r*ols1uf``iWRPT3e+?b z?>+w5e)drcbs=Z3xncB3T?A%O8V!KUcA_(gc}S%(Oa2|H`FK2Jrw39`89I@sE%|e% zjv-9=OGGa%37ZzZWzjnp{oSH>E!v?PQyD)zU$&u(O(#2imV|PPuAe{ajVfX->vxi| zn_BbydUkn2tcmCc=F;kk8ZktJzR7IIU6XoS(u<4A@c^hM6IDu%N&hAj<32MZM870m zxEY-9X5tXMlH*{E3r@k%I*4YO$A}h~D6jiWvp`>^OoQU}EBv%oY@6VuB&b2`2I$Gm-aH`k#Y5(W2xM z6MY+9JC2=TB0n+7qzmM@J4p=ZW!7M%COtZil1KQN7!i=fwvyIL)Xfazj6gM6tmGYx z;;l`xLOuSPXl>?m(Q1iGj4ZQ?%5Rc&o%vMNqvkVJv&`qI;!Wxt$89pk`LMC9^F*hX z<7`A_+!M7yT+&9wH*Hi5<##(LJVYBf4X^l+A8~f)m<#lZQeVWm1jPlt;s(?fRLD@T zeQioWD89_BQE1dOK7attIxP{0P q3_OAXz-SyPMWisb8`c(P#pYA3nA7uYjre~j#2dZP7Q*5-c>G@lL#ajp delta 2112 zcmZ8hZD?C%6h8Mh&5|Z*xQPXe_J_hmW-ADmIfkqSp?2Vp!hRJi%pWQ^hI6YQ&$;)#Hsrv2pYxpe ze7#?HuKVoX^UBw*>zhfB4n(@u{qu)Up$5TuY=9oT$lj@1>@mY&w%j zNE=e1%Gm3A ztSa_Xe|WpklbJ+LgHv;0MmZ{P+eP;+&d{!VXQ$Hec)F^3d3DHaJdWD`x87X(FZO&1 z9*qm@?k>srvn){E_c&}ZEp|LXQWR=wCpn!i$t@Wpo;Kr@=WTyEtlLsTKEvm2_ibDF zD1!@oygx{Ay(H}QK#9tY+KSq|pFR^-K6{D$bPdS5&2K_?74X~8y<)Kc&(I?U^ADga z1>7eQMGJTYoIB{pkYw^3-&WKbzg5)ao7q4f(;2D|FC9jJDd8PL<_(-+qVW`B= z1+NGC$wzMjzjG3eVJ<*2T`2OuXz@JpO9+sYJ`oPHCU?*mMFH1}_-YZ~DB`~s@jr^V zLq;Jw${%Y59@DVRLq)uqW~0)@zn`TCZ5HwqB>~7jd=NS@y>h#)g~u`Sr`##U;jls3igA zy+&u`jk1Cpbv2SVTB~F36{KE6Y{>Yadcb}85v%=>@e9~}uqe`mjP9BNcLYVcOm?Pb zSn4tUt_kOnn{dxeS>)!q3n8|82AutHuS{9QZ?InI47T4p&^bjf2FoGT<3B_Q10kj# zFV4Ua`SMVS);S?tgxCqsffpt`R*;bYy6ndZKNn~>xK%;)Dr~mc`M!jK%kX)#Y*jo@ z^n|6GU!LUgjX=|gPYYKo5WCm!VMEchJ;i>FMo6h^9)nhiL&V__?ugkc?*%W%ZWAg^ U;x>&Lno<`@V{y0HB*d=qAG`A;r~m)} diff --git a/general/package/fullhan-osdrv-fh8852v100/files/kmod/vmm.ko b/general/package/fullhan-osdrv-fh8852v100/files/kmod/vmm.ko index 3f4d515dd2201c1436c5bf90cccb05017c663f74..e24bd54af0c87e7f81cd604c703c4b336cb13a99 100644 GIT binary patch delta 2544 zcmZ9OeN0t#7{{M`cnJvg0#`t}<3hYjynBNya7zmZIHuWfbIBGkRM_s~MJOcWLSd1i zqCb3Ly|KdSA8gUCHCxPdW6T!4ZLGDHlRx^SHdfYXvyD~X?>RqH&u^T4&i8qq-*cYd z@AsT@nL2VgFqY<9EfJ0guZdhqk&P0Ldl#-STs1rQ$Y;CzKOL)k^mD`a<)3W@W8eFh zb9a2Scb6*^a{V11%1_^KR6W_-9%$|E=;%o+I9OWJTH{dk$|(D=&PkuvoU` zBU$tQ=9YGGJc8SZGKq|gazr~~3^kikq?R+@kBf)>-Ut(ydM~piw3JzBI9zI7PDhDT zDjZfoEZ`8QJO$1BD6Q+ES$dnL)rUE|(p{1Yb#J)jkj}z|I>Z(7kM_|e{ z%}yDDp2Yp#WY6V!;#_hX)+i*qxuaSuVR{K}9y*2{&Qz8wTS2XWv zHesFgw~?6N2O36<`ebv_rWf&;hhXl?kN05kyh#2UE4Gh3gF_%u;VgL=v({SvV$FJT1ly!_fE*V&ul2O%Wz8F! zw>87@(P3>aFwZPf8@!rZK?7gQY7!ezOHQb@WgS6Sli69Pt#2`BXHpMfYe?uu@;FXM zb5h%9$#d8u5^_1o`$tKnOg*=H@2Sm?ZEzb#d>DLO+}VdMz*y{>>m!ku4ddZ?T6-vjCNLMoW+w!#G7%*k7b%`V+yX3@h!YD zCmhlfMzLEAND>(&F>9Da+$4#(YveN`H^?5m>13_--7t2d>8M{uK5kc^n)kh>0$YM= zc}r9DTpoNVC|T{PsEM8{w5m#07oZlPZo{3d{(_nfBGf#U8WkUO7MRG&;1!zk%2Rr0q6ODhU>UzCVb+| zv5Gj-k-Ss76g)ZYO7@9=nGVrKS53VoogDP eCQ%Gt)o!-6-{(_Z9AdF_$pIiO86h%c@g# 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 hpWYGhif_k3